pi-herdsman 0.25.0 → 0.26.0

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
@@ -15,7 +15,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
15
15
  // package.json
16
16
  var package_default = {
17
17
  name: "pi-herdsman",
18
- version: "0.25.0",
18
+ version: "0.26.0",
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",
@@ -944,6 +944,7 @@ function validate(value2, kind) {
944
944
  const error2 = v.error;
945
945
  const codes = [
946
946
  "empty_result",
947
+ "execution_error",
947
948
  "result_too_large",
948
949
  "write_failure",
949
950
  "aborted"
@@ -5638,7 +5639,7 @@ function coordinationBody(action, args) {
5638
5639
  return typeof text === "string" && text.trim() ? { label, text } : void 0;
5639
5640
  }
5640
5641
  function coordinationHeader(tool, verb, target, theme) {
5641
- const title = tool === "agent" ? verb === "list" ? "list agents" : verb === "transcript" ? "read agent transcript" : `${verb} agent` : tool === "staff" ? verb === "transcript" ? "read staff transcript" : `${verb} ${verb === "delegate" || verb === "resume" ? "project" : verb === "stop" ? "lead" : "staff"}` : tool === "peer" ? verb === "list" ? "list peers" : "message peer" : `${verb} supervisor`;
5642
+ const title = tool === "agent" ? verb === "list" ? "list agents" : verb === "transcript" ? "read agent transcript" : `${verb} agent` : tool === "staff" ? verb === "transcript" ? "read staff transcript" : `${verb} ${verb === "delegate" || verb === "resume" ? "project" : verb === "stop" ? "lead" : verb === "remove" ? "worktree" : "staff"}` : tool === "peer" ? verb === "list" ? "list peers" : "message peer" : `${verb} supervisor`;
5642
5643
  return `${humanText(theme, "toolTitle", theme.bold(title))}${target ? ` ${humanText(theme, "accent", target)}` : ""}`;
5643
5644
  }
5644
5645
  function renderExpandedCoordinationCall(tool, action, args, theme, header) {
@@ -5843,7 +5844,7 @@ function agentHierarchy(details) {
5843
5844
  }
5844
5845
  function expandedResultLines(tool, action, details, args, theme) {
5845
5846
  const display = value(details.display_name) || value(details.agent) || value(details.label) || value(details.session) || value(args.agent) || value(args.label) || "agent";
5846
- const heading = action === "list" ? tool === "staff" ? "staff" : tool === "peer" ? "peer" : "agents" : action === "inspect" ? `inspect ${display}` : action === "delegate" || action === "continue" ? `${display} started` : tool === "supervisor" ? "sent to supervisor" : action === "steer" ? "steering queued" : action === "interrupt" ? "interrupt accepted" : tool === "agent" && action === "reply" ? `reply sent to ${display}` : action === "close" ? `${display} closed` : `${action} ${display}`;
5847
+ const heading = action === "list" ? tool === "staff" ? "staff" : tool === "peer" ? "peer" : "agents" : action === "inspect" ? `inspect ${display}` : tool === "staff" && action === "remove" ? "remove worktree" : action === "delegate" || action === "continue" ? `${display} started` : tool === "supervisor" ? "sent to supervisor" : action === "steer" ? "steering queued" : action === "interrupt" ? "interrupt accepted" : tool === "agent" && action === "reply" ? `reply sent to ${display}` : action === "close" ? `${display} closed` : `${action} ${display}`;
5847
5848
  const lines = [heading];
5848
5849
  const fields = [
5849
5850
  ["definition", details.definition ?? details.agent_definition],
@@ -5852,6 +5853,10 @@ function expandedResultLines(tool, action, details, args, theme) {
5852
5853
  details.session ?? details.session_id ?? details.pi_session_id ?? details.chiefSessionId
5853
5854
  ],
5854
5855
  ["branch", tool === "staff" ? details.branch : void 0],
5856
+ [
5857
+ "checkout path",
5858
+ tool === "staff" && action === "remove" ? details.checkout_path : void 0
5859
+ ],
5855
5860
  [
5856
5861
  "task",
5857
5862
  tool === "staff" && action === "resume" ? details.presentation_task : void 0
@@ -5876,6 +5881,12 @@ function expandedResultLines(tool, action, details, args, theme) {
5876
5881
  for (const [label, field] of fields)
5877
5882
  if (field !== void 0 && field !== "")
5878
5883
  lines.push(`${label}: ${String(field)}`);
5884
+ if (tool === "staff" && action === "remove") {
5885
+ lines.push(
5886
+ `checkout: ${details.checkout_removed === true ? "removed" : details.checkout_state === "present" ? "preserved" : `unconfirmed (${value(details.checkout_state) || "unknown"})`}`,
5887
+ `assignment: ${details.assignment_retired === true ? "retired" : details.assignment_retired === false ? "not retired" : "unconfirmed"}`
5888
+ );
5889
+ }
5879
5890
  if (action === "list" && tool === "agent")
5880
5891
  lines.push(
5881
5892
  "",
@@ -6003,6 +6014,12 @@ function renderCoordinationResult(tool, action, result, options, theme, context
6003
6014
  0,
6004
6015
  0
6005
6016
  );
6017
+ if (!failed && tool === "staff" && action === "remove" && expanded && (details.checkout_state !== void 0 || details.checkout_removed !== void 0))
6018
+ return new WidthSafeText(
6019
+ expandedResultLines(tool, action, details, args, theme).join("\n"),
6020
+ 0,
6021
+ 0
6022
+ );
6006
6023
  if (failed) {
6007
6024
  const raw = resultContent(result);
6008
6025
  const fallback = expanded ? raw.length > 4e3 ? `${raw.slice(0, 3999)}\u2026` : raw || "Operation failed" : collapseDisplayText(raw, 240) || "Operation failed";
@@ -6114,6 +6131,11 @@ ${humanText(theme, "warning", ` ! cleanup warning \xB7 Ctrl+O`)}` : ""),
6114
6131
  0,
6115
6132
  0
6116
6133
  );
6134
+ if (tool === "staff" && action === "remove") {
6135
+ const branch = value(details.branch) || value(args.branch) || "worktree";
6136
+ const outcome = details.checkout_removed === true ? details.assignment_retired === true ? `removed ${branch} \xB7 assignment retired` : `removed ${branch} \xB7 assignment retirement unconfirmed` : details.checkout_state === "present" ? `removal failed \xB7 ${branch} preserved` : `removal outcome unknown \xB7 ${branch}`;
6137
+ return new WidthSafeText(statusLine(theme, "success", "\u2713", outcome), 0, 0);
6138
+ }
6117
6139
  return new WidthSafeText(
6118
6140
  statusLine(theme, "success", "\u2713", `sent to ${display}`),
6119
6141
  0,
@@ -6823,6 +6845,7 @@ import {
6823
6845
  } from "node:fs";
6824
6846
  import { createHash as createHash6, randomUUID as randomUUID7 } from "node:crypto";
6825
6847
  import { basename as basename5, dirname as dirname6, join as join8 } from "node:path";
6848
+ import { isDeepStrictEqual } from "node:util";
6826
6849
  function sessionLeadRoleState(entries) {
6827
6850
  const entry = [...entries].reverse().find(
6828
6851
  (candidate) => candidate?.type === "custom" && candidate.customType === "pi-herdsman-role"
@@ -6911,6 +6934,29 @@ function fsyncDirectory(directory) {
6911
6934
  closeSync8(fd);
6912
6935
  }
6913
6936
  }
6937
+ function publishRecord(path, content) {
6938
+ const directory = dirname6(path);
6939
+ const temporary = join8(directory, `.${basename5(path)}.${randomUUID7()}.tmp`);
6940
+ let fd;
6941
+ try {
6942
+ fd = openSync8(temporary, "wx", 384);
6943
+ writeFileSync5(fd, content, "utf8");
6944
+ fsyncSync4(fd);
6945
+ closeSync8(fd);
6946
+ fd = void 0;
6947
+ renameSync6(temporary, path);
6948
+ chmodSync7(path, 384);
6949
+ fsyncDirectory(directory);
6950
+ return path;
6951
+ } finally {
6952
+ if (fd !== void 0) closeSync8(fd);
6953
+ try {
6954
+ unlinkSync6(temporary);
6955
+ } catch (error2) {
6956
+ if (error2.code !== "ENOENT") throw error2;
6957
+ }
6958
+ }
6959
+ }
6914
6960
  function withChiefMessageLock(path, operation) {
6915
6961
  const lock = `${path}.lock`;
6916
6962
  const lease = acquireProcessLock(lock, { name: "Chief message lock" });
@@ -7035,6 +7081,18 @@ function writeChiefMessageUnlocked(record, runtime, clearQuarantine) {
7035
7081
  mkdirSync7(directory, { recursive: true, mode: 448 });
7036
7082
  chmodSync7(directory, 448);
7037
7083
  const path = chiefMessagePath(runtime, record.toSessionId, record.id);
7084
+ if (record.kind !== "project_assignment") {
7085
+ try {
7086
+ statSync4(path);
7087
+ const existing = readChiefMessage(path);
7088
+ if (!isDeepStrictEqual(existing, record))
7089
+ throw new Error(
7090
+ "Chief message ID already exists with a different payload"
7091
+ );
7092
+ } catch (error2) {
7093
+ if (error2.code !== "ENOENT") throw error2;
7094
+ }
7095
+ }
7038
7096
  const temporary = join8(directory, `.${record.id}.${randomUUID7()}.tmp`);
7039
7097
  const content = `${JSON.stringify(record)}
7040
7098
  `;
@@ -7553,32 +7611,13 @@ function writePeerLeadRecord(runtime, record) {
7553
7611
  const live = livePeerClaim(runtime, record.piSessionId);
7554
7612
  if (live.pid !== record.claim.pid || live.id !== record.claim.id)
7555
7613
  throw new Error("Peer lead process-lock generation changed");
7556
- const directory = peerDirectory(runtime);
7614
+ peerDirectory(runtime);
7557
7615
  const path = peerLeadRecordPath(runtime, record.piSessionId);
7558
7616
  const content = `${JSON.stringify(record)}
7559
7617
  `;
7560
7618
  if (Buffer.byteLength(content, "utf8") > PEER_LEAD_RECORD_MAX_BYTES)
7561
7619
  throw new Error("Peer lead record is too large");
7562
- const temporary = join8(directory, `.${basename5(path)}.${randomUUID7()}.tmp`);
7563
- let fd;
7564
- try {
7565
- fd = openSync8(temporary, "wx", 384);
7566
- writeFileSync5(fd, content, "utf8");
7567
- fsyncSync4(fd);
7568
- closeSync8(fd);
7569
- fd = void 0;
7570
- renameSync6(temporary, path);
7571
- chmodSync7(path, 384);
7572
- fsyncDirectory(directory);
7573
- return path;
7574
- } finally {
7575
- if (fd !== void 0) closeSync8(fd);
7576
- try {
7577
- unlinkSync6(temporary);
7578
- } catch (error2) {
7579
- if (error2.code !== "ENOENT") throw error2;
7580
- }
7581
- }
7620
+ return publishRecord(path, content);
7582
7621
  }
7583
7622
  function removePeerLeadRecord(runtime, piSessionId, expected) {
7584
7623
  const path = peerLeadRecordPath(runtime, piSessionId);
@@ -8026,6 +8065,7 @@ function validProjectMessage(value2) {
8026
8065
  "branch",
8027
8066
  "fromSessionId",
8028
8067
  "text",
8068
+ "kind",
8029
8069
  "createdAt",
8030
8070
  "resultBindings"
8031
8071
  ].includes(key)
@@ -8037,7 +8077,7 @@ function validProjectMessage(value2) {
8037
8077
  "fromSessionId",
8038
8078
  "text",
8039
8079
  "createdAt"
8040
- ].every((key) => Object.hasOwn(r, key)) && r.version === 2 && UUID3.test(String(r.id)) && validNativeIdentity(r.repoKey) && validNativeIdentity(r.branch) && validSession(r.fromSessionId) && typeof r.text === "string" && r.text.length > 0 && (r.resultBindings === void 0 || Array.isArray(r.resultBindings) && r.resultBindings.every(isResultBinding)) && Number.isInteger(r.createdAt) && r.createdAt >= 0 && Buffer.byteLength(`${JSON.stringify(r)}
8080
+ ].every((key) => Object.hasOwn(r, key)) && r.version === 2 && UUID3.test(String(r.id)) && validNativeIdentity(r.repoKey) && validNativeIdentity(r.branch) && validSession(r.fromSessionId) && typeof r.text === "string" && r.text.length > 0 && (r.kind === void 0 || r.kind === "execution_failure") && (r.resultBindings === void 0 || Array.isArray(r.resultBindings) && r.resultBindings.every(isResultBinding)) && Number.isInteger(r.createdAt) && r.createdAt >= 0 && Buffer.byteLength(`${JSON.stringify(r)}
8041
8081
  `, "utf8") <= COORDINATION_MESSAGE_MAX_BYTES;
8042
8082
  }
8043
8083
  function projectMessageBytes(record) {
@@ -8066,9 +8106,27 @@ function writeProjectMessage(record, runtime) {
8066
8106
  fsyncSync4(fd);
8067
8107
  closeSync8(fd);
8068
8108
  fd = void 0;
8069
- renameSync6(temporary, path);
8070
- chmodSync7(path, 384);
8071
- fsyncDirectory(directory);
8109
+ let existing;
8110
+ try {
8111
+ if (statSync4(path).size > COORDINATION_MESSAGE_MAX_BYTES)
8112
+ throw new Error("large");
8113
+ existing = JSON.parse(readFileSync8(path, "utf8"));
8114
+ } catch (error2) {
8115
+ if (error2.code !== "ENOENT")
8116
+ throw new Error(`Unable to verify existing project message ${path}`, {
8117
+ cause: error2
8118
+ });
8119
+ }
8120
+ if (existing !== void 0) {
8121
+ if (!validProjectMessage(existing) || JSON.stringify(existing) !== JSON.stringify(record))
8122
+ throw new Error(
8123
+ "Project message identity conflicts with durable payload"
8124
+ );
8125
+ } else {
8126
+ renameSync6(temporary, path);
8127
+ chmodSync7(path, 384);
8128
+ fsyncDirectory(directory);
8129
+ }
8072
8130
  } finally {
8073
8131
  if (fd !== void 0) closeSync8(fd);
8074
8132
  try {
@@ -8296,31 +8354,12 @@ function readLeadCoordinationState(runtime, piSessionId) {
8296
8354
  function writeLeadCoordinationState(runtime, state) {
8297
8355
  if (!state.build || !validLeadState(state))
8298
8356
  throw new Error("Invalid lead coordination state");
8299
- const directory = leadStateDirectory(runtime);
8357
+ leadStateDirectory(runtime);
8300
8358
  const path = leadCoordinationStatePath(runtime, state.piSessionId);
8301
8359
  const content = JSON.stringify(state) + "\n";
8302
8360
  if (Buffer.byteLength(content, "utf8") > LEAD_STATE_MAX_BYTES)
8303
8361
  throw new Error("Lead coordination state is too large");
8304
- const temporary = join8(directory, `.${basename5(path)}.${randomUUID7()}.tmp`);
8305
- let fd;
8306
- try {
8307
- fd = openSync8(temporary, "wx", 384);
8308
- writeFileSync5(fd, content, "utf8");
8309
- fsyncSync4(fd);
8310
- closeSync8(fd);
8311
- fd = void 0;
8312
- renameSync6(temporary, path);
8313
- chmodSync7(path, 384);
8314
- fsyncDirectory(directory);
8315
- return path;
8316
- } finally {
8317
- if (fd !== void 0) closeSync8(fd);
8318
- try {
8319
- unlinkSync6(temporary);
8320
- } catch (e) {
8321
- if (e.code !== "ENOENT") throw e;
8322
- }
8323
- }
8362
+ return publishRecord(path, content);
8324
8363
  }
8325
8364
  function invalidateLeadCoordinationState(runtime, piSessionId, expectedInstanceId) {
8326
8365
  const path = leadCoordinationStatePath(runtime, piSessionId);
@@ -10051,6 +10090,30 @@ function createAgentController(pi, options) {
10051
10090
  return false;
10052
10091
  }
10053
10092
  };
10093
+ const recordDependencies = {
10094
+ persistedTranscriptReady: persistedTranscriptReady2,
10095
+ cascadePlan: (items, state) => managedAgentCascadePlanFromSnapshot(items, state, sameIdentity),
10096
+ assertCascadeSafe: (items) => assertManagedAgentCascadeSafe(items, sameIdentity),
10097
+ runtimeForLabel: (label) => runtimes.get(label),
10098
+ sameIdentity,
10099
+ runtimeIdentityState: (runtime) => ({
10100
+ version: 5,
10101
+ build: options.build,
10102
+ runId: runtime.runId,
10103
+ ownerSessionId: runtime.ownerSessionId,
10104
+ workspaceId: runtime.workspaceId,
10105
+ agentLabel: runtime.label,
10106
+ paneId: runtime.paneId,
10107
+ piSessionId: runtime.piSessionId ?? "",
10108
+ piSessionFile: runtime.piSessionFile,
10109
+ cwd: runtime.cwd,
10110
+ updatedAt: 0
10111
+ })
10112
+ };
10113
+ const listRecordDependencies = {
10114
+ ...recordDependencies,
10115
+ persistedTranscriptReady: options.persistedTranscriptReady
10116
+ };
10054
10117
  const snapshots = (context, signal, allWorkspaces = false, allowTranscriptDefinitionFallback = true) => snapshot(
10055
10118
  context,
10056
10119
  signal,
@@ -10096,26 +10159,7 @@ function createAgentController(pi, options) {
10096
10159
  ownerSessionId,
10097
10160
  scope,
10098
10161
  unresolvedMailboxState,
10099
- {
10100
- persistedTranscriptReady: options.persistedTranscriptReady,
10101
- cascadePlan: (items, state) => managedAgentCascadePlanFromSnapshot(items, state, sameIdentity),
10102
- assertCascadeSafe: (items) => assertManagedAgentCascadeSafe(items, sameIdentity),
10103
- runtimeForLabel: (label) => runtimes.get(label),
10104
- sameIdentity,
10105
- runtimeIdentityState: (runtime) => ({
10106
- version: 5,
10107
- build: options.build,
10108
- runId: runtime.runId,
10109
- ownerSessionId: runtime.ownerSessionId,
10110
- workspaceId: runtime.workspaceId,
10111
- agentLabel: runtime.label,
10112
- paneId: runtime.paneId,
10113
- piSessionId: runtime.piSessionId ?? "",
10114
- piSessionFile: runtime.piSessionFile,
10115
- cwd: runtime.cwd,
10116
- updatedAt: 0
10117
- })
10118
- }
10162
+ listRecordDependencies
10119
10163
  );
10120
10164
  const leadTabLabel = async (context) => {
10121
10165
  const name = pi.getSessionName() ?? context.sessionManager.getSessionName();
@@ -11043,26 +11087,7 @@ function createAgentController(pi, options) {
11043
11087
  context.sessionManager.getSessionId(),
11044
11088
  scope,
11045
11089
  unresolved,
11046
- {
11047
- persistedTranscriptReady: persistedTranscriptReady2,
11048
- cascadePlan: (items, parent) => managedAgentCascadePlanFromSnapshot(items, parent, sameIdentity),
11049
- assertCascadeSafe: (items) => assertManagedAgentCascadeSafe(items, sameIdentity),
11050
- runtimeForLabel: (label) => runtimes.get(label),
11051
- sameIdentity,
11052
- runtimeIdentityState: (runtime) => ({
11053
- version: 5,
11054
- build: options.build,
11055
- runId: runtime.runId,
11056
- ownerSessionId: runtime.ownerSessionId,
11057
- workspaceId: runtime.workspaceId,
11058
- agentLabel: runtime.label,
11059
- paneId: runtime.paneId,
11060
- piSessionId: runtime.piSessionId ?? "",
11061
- piSessionFile: runtime.piSessionFile,
11062
- cwd: runtime.cwd,
11063
- updatedAt: 0
11064
- })
11065
- }
11090
+ recordDependencies
11066
11091
  ).available_tools;
11067
11092
  if (!actions.includes("read_agent_transcript")) {
11068
11093
  if (candidate.presence.kind === "unknown")
@@ -13296,26 +13321,7 @@ ${result.stderr ?? ""}`.trim(),
13296
13321
  ownerSessionId,
13297
13322
  options.scope,
13298
13323
  unresolvedMailboxState,
13299
- {
13300
- persistedTranscriptReady: persistedTranscriptReady2,
13301
- cascadePlan: (snapshot2, state) => managedAgentCascadePlanFromSnapshot(snapshot2, state, sameIdentity),
13302
- assertCascadeSafe: (items) => assertManagedAgentCascadeSafe(items, sameIdentity),
13303
- runtimeForLabel: (label) => runtimes.get(label),
13304
- sameIdentity,
13305
- runtimeIdentityState: (runtime) => ({
13306
- version: 5,
13307
- build: options.build,
13308
- runId: runtime.runId,
13309
- ownerSessionId: runtime.ownerSessionId,
13310
- workspaceId: runtime.workspaceId,
13311
- agentLabel: runtime.label,
13312
- paneId: runtime.paneId,
13313
- piSessionId: runtime.piSessionId ?? "",
13314
- piSessionFile: runtime.piSessionFile,
13315
- cwd: runtime.cwd,
13316
- updatedAt: 0
13317
- })
13318
- }
13324
+ recordDependencies
13319
13325
  ).available_tools ?? []).flatMap((tool) => {
13320
13326
  switch (tool) {
13321
13327
  case "list_agents":
@@ -14207,7 +14213,7 @@ function collectSessionUsage(ctx, ownedChildren, openOwnedSession, leadRoots = [
14207
14213
  // extension/managed-agent-runtime.ts
14208
14214
  import { randomUUID as randomUUID9 } from "node:crypto";
14209
14215
  import { Type as Type2 } from "typebox";
14210
- import { isDeepStrictEqual } from "node:util";
14216
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
14211
14217
  var DELEGATING_AGENT_ROLE_CHARTER = `## Delegating agent role
14212
14218
  ${DELEGATING_AGENT_SCOPE_DESCRIPTION}
14213
14219
  ${FILE_HANDOFF_GUIDANCE}`;
@@ -14293,7 +14299,7 @@ function sameSessionPath2(left, right) {
14293
14299
  function sameManagedAgentDurableState(left, right) {
14294
14300
  const { updatedAt: _leftUpdatedAt, ...leftDurable } = left;
14295
14301
  const { updatedAt: _rightUpdatedAt, ...rightDurable } = right;
14296
- return isDeepStrictEqual(leftDurable, rightDurable);
14302
+ return isDeepStrictEqual2(leftDurable, rightDurable);
14297
14303
  }
14298
14304
  function validateManagedAgentIdentity(input) {
14299
14305
  const { state, mailbox, env } = input;
@@ -14352,6 +14358,7 @@ function createManagedAgentExecutionState() {
14352
14358
  assignment: void 0,
14353
14359
  initialized: false,
14354
14360
  latest: "",
14361
+ observation: void 0,
14355
14362
  pendingResult: void 0,
14356
14363
  pendingStateTransition: false,
14357
14364
  pendingInterruptReplacement: void 0,
@@ -15082,7 +15089,7 @@ function registerManagedAgentResultCapture(pi, options) {
15082
15089
  pi.on("message_end", (event, ctx) => {
15083
15090
  const state = options.state();
15084
15091
  if (!state?.activeRequestId || options.pendingResult()) return;
15085
- const message = event.message ?? event;
15092
+ const message = event.message;
15086
15093
  if (message?.role !== "assistant") return;
15087
15094
  if (options.delegationEnabled && options.hasUndeliveredDirectChildWork(
15088
15095
  state,
@@ -15093,9 +15100,30 @@ function registerManagedAgentResultCapture(pi, options) {
15093
15100
  });
15094
15101
  }
15095
15102
  function registerManagedAgentActivityHandlers(pi, execution, options) {
15096
- pi.on("turn_end", (_event, ctx) => {
15103
+ pi.on("agent_start", () => {
15104
+ const requestId = execution.assignment?.activeRequestId;
15105
+ execution.observation = requestId ? { requestId } : void 0;
15106
+ });
15107
+ pi.on("agent_before_settle", (event) => {
15108
+ const observation = execution.observation;
15109
+ if (observation && observation.requestId === execution.assignment?.activeRequestId)
15110
+ observation.outcome = event.outcome;
15111
+ });
15112
+ pi.on("turn_end", (event, ctx) => {
15097
15113
  options.touchActivity();
15098
- if (!execution.assignment?.activeRequestId) return;
15114
+ const state = execution.assignment;
15115
+ if (!state?.activeRequestId) return;
15116
+ if (event?.message?.role === "assistant") {
15117
+ const observation = execution.observation ?? (execution.observation = { requestId: state.activeRequestId });
15118
+ if (observation.requestId === state.activeRequestId) {
15119
+ observation.outcome = event.outcome;
15120
+ observation.responseEntryId = event.messageEntryId;
15121
+ observation.responseText = contentText3(
15122
+ event.message.content,
15123
+ ""
15124
+ ).trim();
15125
+ }
15126
+ }
15099
15127
  const usage = ctx.getContextUsage();
15100
15128
  const percent = usage?.percent == null ? void 0 : Math.round(usage.percent);
15101
15129
  const model = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : void 0;
@@ -15173,6 +15201,7 @@ function registerManagedAgentSettlementHandlers(pi, execution, options) {
15173
15201
  writeAgentState(mailbox, nextState);
15174
15202
  execution.assignment = nextState;
15175
15203
  execution.agentStartedAt = void 0;
15204
+ execution.observation = void 0;
15176
15205
  const model = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : void 0;
15177
15206
  const thinking = ctx.thinkingLevel;
15178
15207
  execution.pendingStateTransition = false;
@@ -15209,7 +15238,10 @@ function registerManagedAgentSettlementHandlers(pi, execution, options) {
15209
15238
  ctx.sessionManager.getEntries()
15210
15239
  ))
15211
15240
  return;
15212
- const completed = !aborted && Boolean(execution.latest);
15241
+ const observation = execution.observation;
15242
+ const outcome = observation?.requestId === state.activeRequestId ? observation.outcome : void 0;
15243
+ const responseText = observation?.requestId === state.activeRequestId && observation.outcome === "completed" && observation.responseEntryId ? observation.responseText?.trim() ?? "" : "";
15244
+ const completed = !aborted && outcome === "completed" && !!responseText;
15213
15245
  const result = {
15214
15246
  version: 5,
15215
15247
  runId: state.runId,
@@ -15219,10 +15251,10 @@ function registerManagedAgentSettlementHandlers(pi, execution, options) {
15219
15251
  agentLabel: state.agentLabel,
15220
15252
  paneId: state.paneId,
15221
15253
  status: completed ? "completed" : "failed",
15222
- ...completed ? { text: execution.latest } : {
15254
+ ...completed ? { text: responseText } : {
15223
15255
  error: {
15224
- code: aborted ? "aborted" : "empty_result",
15225
- message: aborted ? "Agent execution was cancelled" : "Agent produced no assistant text"
15256
+ code: aborted ? "aborted" : outcome === "error" ? "execution_error" : "empty_result",
15257
+ message: aborted ? "Agent execution was cancelled" : outcome === "error" ? "Agent model execution failed" : "Agent produced no assistant text"
15226
15258
  }
15227
15259
  },
15228
15260
  contextUsage: ctx.getContextUsage(),
@@ -15767,6 +15799,7 @@ This is the session-start definition snapshot. Use list_agents for live Agent st
15767
15799
  reportMetadata: (state, ctx, patch) => metadataPublisher.report(state, ctx, patch),
15768
15800
  setLatest: (value2) => {
15769
15801
  execution.latest = value2;
15802
+ if (!value2) execution.observation = void 0;
15770
15803
  },
15771
15804
  startAgent: () => execution.agentStartedAt = Date.now()
15772
15805
  });
@@ -15777,6 +15810,7 @@ This is the session-start definition snapshot. Use list_agents for live Agent st
15777
15810
  hasUndeliveredDirectChildWork: (state, entries) => (controller?.hasUndeliveredDirectChildWork ?? hasUndeliveredDirectChildWork)(state, entries),
15778
15811
  setLatest: (value2) => {
15779
15812
  execution.latest = value2;
15813
+ if (!value2) execution.observation = void 0;
15780
15814
  }
15781
15815
  });
15782
15816
  registerManagedAgentActivityHandlers(pi, execution, {
@@ -16586,7 +16620,7 @@ function createLeadSessionStartRuntime(host) {
16586
16620
  }
16587
16621
  }
16588
16622
  if (sessionSignal.aborted) return;
16589
- if (host.roleTransitions.activeManager())
16623
+ if (host.isLead() && host.roleTransitions.chiefModeInactive())
16590
16624
  await host.publishLeadRole(
16591
16625
  ctx,
16592
16626
  "inactive",
@@ -16669,9 +16703,12 @@ This is the session-start definition snapshot. Use list_agents for live Agent st
16669
16703
  async agentSettled(ctx, aborted) {
16670
16704
  host.leadInboxRuntime.agentSettled();
16671
16705
  host.controller.settleAsks(ctx, host.controller.sessionSignal());
16672
- host.herdRun.agentSettled(ctx, aborted);
16673
- await host.controller.settleResults(ctx, host.controller.sessionSignal()).catch(() => {
16674
- });
16706
+ try {
16707
+ await host.herdRun.agentSettled(ctx, aborted);
16708
+ } finally {
16709
+ await host.controller.settleResults(ctx, host.controller.sessionSignal()).catch(() => {
16710
+ });
16711
+ }
16675
16712
  },
16676
16713
  toolCall(event, ctx) {
16677
16714
  if (!host.isCurrentChief(ctx) || (host.currentChiefAllowedTools(ctx) ?? host.chiefTools).includes(
@@ -17535,6 +17572,7 @@ function registerLeadRuntime(pi, options) {
17535
17572
  ...options.coordinationHost,
17536
17573
  currentManager: (...args) => roleTransitions.currentManager(...args),
17537
17574
  currentWorktreeScope: (ctx) => roleTransitions.currentWorktreeScope(ctx),
17575
+ currentProjectAssignment: (ctx) => currentProjectAssignment(ctx),
17538
17576
  projectAssignmentForScope: (...args) => roleTransitions.projectAssignmentForScope(...args),
17539
17577
  publishProjectMessage: (...args) => roleTransitions.publishProjectMessage(...args),
17540
17578
  currentChiefAuthority: (...args) => roleTransitions.currentChiefAuthority(...args),
@@ -17628,7 +17666,12 @@ function registerLeadRuntime(pi, options) {
17628
17666
  );
17629
17667
  };
17630
17668
  const roleHost = options.roleTransitionHost;
17669
+ const effectiveLeadRole = () => leadRuntime.chiefMode === "active" ? "chief" : leadRuntime.controllerRole === "manager" && !leadRuntime.roleSuspended ? "manager" : leadRuntime.roleSuspended ? void 0 : "lead";
17631
17670
  let leadMetadataQueue = Promise.resolve();
17671
+ let tabRenameQueue = Promise.resolve();
17672
+ let tabRenameRevision = 0;
17673
+ let ownedTab;
17674
+ let reportedTabRenameFailure;
17632
17675
  let reportedRoleMetadataFailure;
17633
17676
  const publishLeadRole = (ctx, _mode, generation) => {
17634
17677
  const sessionId = ctx.sessionManager.getSessionId();
@@ -17640,7 +17683,7 @@ function registerLeadRuntime(pi, options) {
17640
17683
  return;
17641
17684
  const paneId = roleHost.paneId();
17642
17685
  if (!paneId) return;
17643
- const role2 = leadRuntime.chiefMode === "active" ? "chief" : leadRuntime.controllerRole === "manager" && !leadRuntime.roleSuspended ? "manager" : leadRuntime.roleSuspended ? void 0 : "lead";
17686
+ const role2 = effectiveLeadRole();
17644
17687
  const leadName = role2 === "lead" ? roleHost.getSessionName()?.trim() : void 0;
17645
17688
  const args = [
17646
17689
  "pane",
@@ -17691,8 +17734,92 @@ function registerLeadRuntime(pi, options) {
17691
17734
  } catch {
17692
17735
  }
17693
17736
  });
17737
+ scheduleTabRename(ctx, leadMetadataQueue);
17694
17738
  return leadMetadataQueue;
17695
17739
  };
17740
+ const scheduleTabRename = (ctx, afterMetadata) => {
17741
+ const revision = ++tabRenameRevision;
17742
+ const sessionId = ctx.sessionManager.getSessionId();
17743
+ const sessionGeneration = leadRuntime.sessionGeneration;
17744
+ const current = () => revision === tabRenameRevision && sessionGeneration === leadRuntime.sessionGeneration && sessionId === ctx.sessionManager.getSessionId();
17745
+ tabRenameQueue = tabRenameQueue.catch(() => {
17746
+ }).then(async () => {
17747
+ await afterMetadata.catch(() => {
17748
+ });
17749
+ if (!current()) return;
17750
+ const role2 = effectiveLeadRole();
17751
+ if (!role2) return;
17752
+ let label;
17753
+ if (role2 === "chief") label = "Chief";
17754
+ else if (role2 === "manager") label = "Manager";
17755
+ else {
17756
+ try {
17757
+ label = await currentProjectAssignment(ctx) ? "Managed Lead" : "Lead";
17758
+ } catch (error2) {
17759
+ options.sessionStartOptions.appendRoleError(ctx, error2);
17760
+ label = "Lead";
17761
+ }
17762
+ }
17763
+ if (!current()) return;
17764
+ const paneId = roleHost.paneId();
17765
+ if (!paneId) return;
17766
+ const paneResult = await roleHost.runHerdr(pi, ctx, [
17767
+ "pane",
17768
+ "get",
17769
+ paneId
17770
+ ]);
17771
+ if (!current()) return;
17772
+ const pane = paneResult?.pane;
17773
+ if (!pane || pane.pane_id !== paneId || typeof pane.workspace_id !== "string" || !pane.workspace_id || typeof pane.tab_id !== "string" || !pane.tab_id)
17774
+ return;
17775
+ const tabResult = await roleHost.runHerdr(pi, ctx, [
17776
+ "tab",
17777
+ "list",
17778
+ "--workspace",
17779
+ pane.workspace_id
17780
+ ]);
17781
+ if (!current()) return;
17782
+ const tabs = tabResult?.tabs;
17783
+ if (!Array.isArray(tabs)) return;
17784
+ const tabIds = /* @__PURE__ */ new Set();
17785
+ if (tabs.some((entry) => {
17786
+ if (!entry || typeof entry !== "object" || typeof entry.tab_id !== "string" || !entry.tab_id || typeof entry.label !== "string" || !entry.label || tabIds.has(entry.tab_id))
17787
+ return true;
17788
+ tabIds.add(entry.tab_id);
17789
+ return false;
17790
+ }))
17791
+ return;
17792
+ const index = tabs.findIndex((tab2) => tab2.tab_id === pane.tab_id);
17793
+ if (index < 0) return;
17794
+ const tab = tabs[index];
17795
+ if (typeof tab.label !== "string") return;
17796
+ if (tab.label === label) return;
17797
+ const previousLabel = ownedTab?.tabId === tab.tab_id ? ownedTab.label : void 0;
17798
+ if (previousLabel !== void 0 ? tab.label !== previousLabel : tab.label !== String(index + 1) && !["Lead", "Managed Lead", "Manager", "Chief"].includes(tab.label))
17799
+ return;
17800
+ if (!current()) return;
17801
+ await roleHost.runHerdr(pi, ctx, ["tab", "rename", tab.tab_id, label], {
17802
+ noResult: true,
17803
+ timeout: 1e4
17804
+ });
17805
+ ownedTab = { tabId: tab.tab_id, label };
17806
+ reportedTabRenameFailure = void 0;
17807
+ }).catch((error2) => {
17808
+ if (!current()) return;
17809
+ const key = `${sessionId}:${sessionGeneration}`;
17810
+ if (reportedTabRenameFailure === key) return;
17811
+ reportedTabRenameFailure = key;
17812
+ try {
17813
+ roleHost.appendDurableError(
17814
+ pi,
17815
+ ctx,
17816
+ "pi_herdsman_state_error",
17817
+ error2
17818
+ );
17819
+ } catch {
17820
+ }
17821
+ });
17822
+ };
17696
17823
  const queueLeadMetadata = (ctx, metadata) => {
17697
17824
  if (leadRuntime.controllerRole === "manager") {
17698
17825
  void publishLeadRole(
@@ -17839,6 +17966,7 @@ function registerLeadRuntime(pi, options) {
17839
17966
  coordinationHealthy: () => coordinationRuntime.coordinationHealthy(),
17840
17967
  managerDiagnostic,
17841
17968
  managerDiagnosticEvents,
17969
+ assignmentDelivered: (ctx) => void publishLeadRole(ctx, "inactive", leadRuntime.chiefModeGeneration),
17842
17970
  isCurrentChief: (ctx) => options.leadRuntime.chiefMode === "active" && !!options.leadRuntime.chiefLease && options.leadRuntime.chiefLease.descriptor.piSessionId === ctx.sessionManager.getSessionId(),
17843
17971
  prepareSupervisionMessage: (ctx) => supervisionUiRuntime.prepareMessage(ctx),
17844
17972
  livePeerLead: (...args) => coordinationRuntime.livePeerLead(...args),
@@ -18062,11 +18190,26 @@ function registerLeadRuntime(pi, options) {
18062
18190
  herdRunRuntime = createLeadHerdRunRuntime({
18063
18191
  pi,
18064
18192
  entryName: options.herdRunEntryName,
18193
+ activeRole: () => activeLeadRole(options.leadRuntime),
18194
+ isManagedLead: () => !!leadExecution?.managed,
18065
18195
  hasPendingStarts: () => controller.hasPendingStarts(),
18066
18196
  listAgentStates: () => controller.listAgentStates(),
18067
- currentWorktreeScope: (ctx) => roleTransitions.currentWorktreeScope(ctx),
18068
- projectAssignmentForScope: (...args) => roleTransitions.projectAssignmentForScope(...args),
18197
+ currentProjectAssignment: (ctx) => currentProjectAssignment(ctx),
18069
18198
  publishProjectMessage: (...args) => roleTransitions.publishProjectMessage(...args),
18199
+ publishChiefFailure: async (ctx, message, reportId, role2, createdAt) => {
18200
+ const chief = await roleTransitions.currentChiefAuthority(ctx);
18201
+ if (!chief) return false;
18202
+ await coordinationRuntime.queueChiefRecord(
18203
+ role2 === "manager" ? "manager_message" : "lead_message",
18204
+ message,
18205
+ ctx,
18206
+ reportId,
18207
+ createdAt,
18208
+ void 0,
18209
+ { piSessionId: chief.piSessionId, leaseId: chief.leaseId }
18210
+ );
18211
+ return true;
18212
+ },
18070
18213
  requestStatusRefresh: () => statusRuntime.requestRefresh(),
18071
18214
  queueLeadPresentation: options.queueLeadPresentation,
18072
18215
  appendDurableError: options.herdRunServices.appendDurableError
@@ -18212,8 +18355,10 @@ function registerLeadRuntime(pi, options) {
18212
18355
  if (assignment) {
18213
18356
  leadExecution = { mode, valid: false, managed: true };
18214
18357
  statusRuntime.requestRefresh();
18358
+ publishLeadRole(ctx, "inactive", leadRuntime.chiefModeGeneration);
18215
18359
  return;
18216
18360
  }
18361
+ publishLeadRole(ctx, "inactive", leadRuntime.chiefModeGeneration);
18217
18362
  } catch (error2) {
18218
18363
  if (signal?.aborted) return;
18219
18364
  leadExecution = {
@@ -18223,6 +18368,7 @@ function registerLeadRuntime(pi, options) {
18223
18368
  };
18224
18369
  statusRuntime.requestRefresh();
18225
18370
  options.sessionStartOptions.appendRoleError(ctx, error2);
18371
+ publishLeadRole(ctx, "inactive", leadRuntime.chiefModeGeneration);
18226
18372
  return;
18227
18373
  }
18228
18374
  try {
@@ -18289,6 +18435,7 @@ function registerLeadRuntime(pi, options) {
18289
18435
  const baseline = leadToolState.getLeadTools() ?? leadToolState.normalizeBaseTools(pi.getActiveTools());
18290
18436
  const targetTools = leadToolState.projectLeadTools(baseline, definition);
18291
18437
  if (!await roleTransitions.takeover(ctx)) return false;
18438
+ publishLeadRole(ctx, "inactive", leadRuntime.chiefModeGeneration);
18292
18439
  try {
18293
18440
  persistLeadExecution("orchestrate", baseline);
18294
18441
  leadToolState.setLeadTools(baseline);
@@ -18398,6 +18545,10 @@ function registerLeadRuntime(pi, options) {
18398
18545
  "agent_start",
18399
18546
  (_event, ctx) => agentEvents.agentStart(ctx)
18400
18547
  );
18548
+ pi.on(
18549
+ "agent_before_settle",
18550
+ (event) => herdRunRuntime.beforeSettle(event)
18551
+ );
18401
18552
  pi.on(
18402
18553
  "agent_settled",
18403
18554
  (event, ctx) => agentEvents.agentSettled(ctx, event.aborted)
@@ -18415,10 +18566,10 @@ function registerLeadRuntime(pi, options) {
18415
18566
  if (!options.hasPane()) return;
18416
18567
  options.queueLeadPresentation(ctx, event?.name ?? options.sessionName());
18417
18568
  });
18418
- pi.on(
18419
- "turn_end",
18420
- (_event, ctx) => options.queueLeadPresentation(ctx)
18421
- );
18569
+ pi.on("turn_end", (event, ctx) => {
18570
+ herdRunRuntime.turnEnd(event);
18571
+ options.queueLeadPresentation(ctx);
18572
+ });
18422
18573
  pi.on(
18423
18574
  "session_compact",
18424
18575
  (_event, ctx) => options.queueLeadPresentation(ctx)
@@ -19435,103 +19586,400 @@ function createLeadSupervisionRuntime(host) {
19435
19586
  }
19436
19587
  function createLeadHerdRunRuntime(host) {
19437
19588
  let leadAgentStartedAt;
19589
+ let leadReportId;
19438
19590
  let herdRunStartedAt;
19591
+ let herdReportId;
19439
19592
  let leadSettled = true;
19593
+ let observation;
19440
19594
  let lastSettledRun;
19441
- const latestMeaningfulAssistantResponse = (ctx, startedAt) => {
19442
- const entries = ctx.sessionManager.getBranch();
19443
- for (let i = entries.length - 1; i >= 0; i--) {
19444
- const entry = entries[i];
19445
- if (entry.type !== "message" || entry.message.role !== "assistant")
19446
- continue;
19447
- if ((entry.message.timestamp ?? 0) < startedAt) continue;
19448
- const text = contentText4(entry.message.content, "").trim();
19449
- if (text) return text;
19595
+ const publishProjectHandoff = async (ctx, message, reportId = herdReportId, kind, createdAt = Date.now(), target) => {
19596
+ const role2 = host.activeRole();
19597
+ if (kind === "execution_failure" && !reportId) return false;
19598
+ if (target === "project") {
19599
+ if (role2 !== "lead") return false;
19600
+ const assignment = await host.currentProjectAssignment(ctx);
19601
+ if (!assignment) return false;
19602
+ return !!await host.publishProjectMessage(
19603
+ ctx,
19604
+ assignment,
19605
+ message,
19606
+ [],
19607
+ "herd_run",
19608
+ reportId,
19609
+ kind
19610
+ );
19450
19611
  }
19612
+ if (target === "chief") {
19613
+ if (kind !== "execution_failure" || role2 === "chief") return false;
19614
+ if (role2 === "lead" && await host.currentProjectAssignment(ctx))
19615
+ return false;
19616
+ return host.publishChiefFailure(ctx, message, reportId, role2, createdAt);
19617
+ }
19618
+ return false;
19451
19619
  };
19452
- const publishProjectHandoff = async (ctx, message) => {
19453
- const scope = await host.currentWorktreeScope(ctx);
19454
- if (!scope) return;
19455
- const assignment = host.projectAssignmentForScope(
19456
- scope,
19457
- ctx.sessionManager.getSessionId()
19458
- );
19459
- if (!assignment) return;
19460
- await host.publishProjectMessage(ctx, assignment, message, [], "herd_run");
19461
- };
19620
+ let finishing;
19621
+ let idleFinishing;
19462
19622
  const maybeFinish = (ctx) => {
19463
- if (herdRunStartedAt === void 0 || !leadSettled) return;
19623
+ if (finishing) return finishing;
19624
+ if (herdRunStartedAt === void 0 || !leadSettled)
19625
+ return Promise.resolve();
19464
19626
  const sessionId = ctx.sessionManager.getSessionId();
19465
19627
  if (host.hasPendingStarts() || host.listAgentStates().some(
19466
19628
  ({ state }) => state.ownerSessionId === sessionId
19467
19629
  ))
19468
- return;
19630
+ return Promise.resolve();
19469
19631
  const startedAt = herdRunStartedAt;
19470
19632
  const completedAt = Date.now();
19471
- try {
19472
- host.pi.appendEntry(host.entryName, {
19473
- phase: "finished",
19474
- sessionId,
19475
- startedAt,
19476
- completedAt
19477
- });
19478
- herdRunStartedAt = void 0;
19479
- host.requestStatusRefresh();
19480
- host.queueLeadPresentation(ctx);
19481
- const outcome = lastSettledRun;
19482
- lastSettledRun = void 0;
19483
- const message = outcome?.aborted ? "Lead execution was cancelled before a completed response." : outcome?.summary ? `Herd run settled.
19633
+ finishing = (async () => {
19634
+ try {
19635
+ const outcome = lastSettledRun;
19636
+ if (outcome?.outcome === "completed") {
19637
+ const message = outcome.summary ? `Herd run settled.
19484
19638
 
19485
- ${outcome.summary}` : "Herd run settled.";
19486
- void publishProjectHandoff(ctx, message).catch(
19487
- (error2) => host.appendDurableError(host.pi, ctx, "pi_herdsman_state_error", error2)
19488
- );
19489
- } catch (error2) {
19490
- host.appendDurableError(host.pi, ctx, "pi_herdsman_state_error", error2);
19491
- }
19492
- };
19493
- const begin = (ctx) => {
19494
- if (herdRunStartedAt !== void 0) return;
19495
- const startedAt = leadAgentStartedAt ?? Date.now();
19496
- herdRunStartedAt = startedAt;
19497
- host.queueLeadPresentation(ctx);
19498
- try {
19499
- host.pi.appendEntry(host.entryName, {
19500
- phase: "started",
19501
- sessionId: ctx.sessionManager.getSessionId(),
19502
- startedAt
19503
- });
19504
- } catch (error2) {
19505
- host.appendDurableError(host.pi, ctx, "pi_herdsman_state_error", error2);
19506
- }
19507
- host.requestStatusRefresh();
19508
- };
19509
- return {
19510
- begin,
19511
- maybeFinish,
19639
+ ${outcome.summary}` : "Herd run settled without a verified Lead integration summary.";
19640
+ let reportMessage = message;
19641
+ let pending;
19642
+ const managed = host.isManagedLead();
19643
+ if (managed) {
19644
+ const entries = ctx.sessionManager.getEntries();
19645
+ pending = entries.find(
19646
+ (entry) => entry?.type === "custom" && entry.customType === host.entryName && entry.data?.sessionId === sessionId && entry.data?.phase === "report_pending" && entry.data?.reportId === herdReportId
19647
+ )?.data;
19648
+ reportMessage = pending?.message ?? message;
19649
+ if (!pending)
19650
+ host.pi.appendEntry(host.entryName, {
19651
+ phase: "report_pending",
19652
+ sessionId,
19653
+ startedAt,
19654
+ reportId: herdReportId,
19655
+ message: reportMessage,
19656
+ target: "project"
19657
+ });
19658
+ }
19659
+ const assignment = managed ? pending ? pending.target === "project" ? await host.currentProjectAssignment(ctx) : void 0 : await host.currentProjectAssignment(ctx) : void 0;
19660
+ if (managed && !assignment) return;
19661
+ if (assignment) {
19662
+ if (!await host.publishProjectMessage(
19663
+ ctx,
19664
+ assignment,
19665
+ reportMessage,
19666
+ [],
19667
+ "herd_run",
19668
+ herdReportId
19669
+ ))
19670
+ return;
19671
+ host.pi.appendEntry(host.entryName, {
19672
+ phase: "report_sent",
19673
+ sessionId,
19674
+ startedAt,
19675
+ reportId: herdReportId
19676
+ });
19677
+ }
19678
+ } else if (outcome) {
19679
+ const entries = ctx.sessionManager.getEntries();
19680
+ const pending = entries.find(
19681
+ (entry) => entry?.type === "custom" && entry.customType === host.entryName && entry.data?.sessionId === sessionId && entry.data?.phase === "report_pending" && entry.data?.runReportId === herdReportId
19682
+ )?.data;
19683
+ if (pending) {
19684
+ const sent = entries.some(
19685
+ (entry) => entry?.type === "custom" && entry.customType === host.entryName && entry.data?.sessionId === sessionId && entry.data?.phase === "report_sent" && entry.data?.reportId === pending.reportId
19686
+ );
19687
+ if (!sent) {
19688
+ if (!await publishProjectHandoff(
19689
+ ctx,
19690
+ pending.message,
19691
+ pending.reportId,
19692
+ pending.kind,
19693
+ pending.createdAt,
19694
+ pending.target
19695
+ ))
19696
+ return;
19697
+ host.pi.appendEntry(host.entryName, {
19698
+ phase: "report_sent",
19699
+ sessionId,
19700
+ startedAt: pending.startedAt,
19701
+ reportId: pending.reportId,
19702
+ runReportId: herdReportId
19703
+ });
19704
+ }
19705
+ }
19706
+ } else if (!outcome) {
19707
+ const pending = ctx.sessionManager.getEntries().find(
19708
+ (entry) => entry?.type === "custom" && entry.customType === host.entryName && entry.data?.sessionId === sessionId && entry.data?.phase === "report_pending" && entry.data?.reportId === herdReportId
19709
+ )?.data;
19710
+ const message = pending?.message ?? "Lead execution outcome was not preserved across restoration; work remains unresolved.";
19711
+ const reportCreatedAt = typeof pending?.createdAt === "number" && Number.isFinite(pending.createdAt) ? pending.createdAt : Date.now();
19712
+ const target = pending ? pending.target : host.isManagedLead() ? "project" : "chief";
19713
+ if (!pending)
19714
+ host.pi.appendEntry(host.entryName, {
19715
+ phase: "report_pending",
19716
+ sessionId,
19717
+ startedAt,
19718
+ reportId: herdReportId,
19719
+ message,
19720
+ kind: "execution_failure",
19721
+ createdAt: reportCreatedAt,
19722
+ target
19723
+ });
19724
+ if (!await publishProjectHandoff(
19725
+ ctx,
19726
+ message,
19727
+ herdReportId,
19728
+ "execution_failure",
19729
+ reportCreatedAt,
19730
+ target
19731
+ ))
19732
+ return;
19733
+ host.pi.appendEntry(host.entryName, {
19734
+ phase: "report_sent",
19735
+ sessionId,
19736
+ startedAt,
19737
+ reportId: herdReportId
19738
+ });
19739
+ }
19740
+ host.pi.appendEntry(host.entryName, {
19741
+ phase: "finished",
19742
+ sessionId,
19743
+ startedAt,
19744
+ completedAt,
19745
+ reportId: herdReportId
19746
+ });
19747
+ lastSettledRun = void 0;
19748
+ herdRunStartedAt = void 0;
19749
+ herdReportId = void 0;
19750
+ host.requestStatusRefresh();
19751
+ host.queueLeadPresentation(ctx);
19752
+ } catch (error2) {
19753
+ host.appendDurableError(host.pi, ctx, "pi_herdsman_state_error", error2);
19754
+ }
19755
+ })().finally(() => {
19756
+ finishing = void 0;
19757
+ });
19758
+ return finishing;
19759
+ };
19760
+ const begin = (ctx) => {
19761
+ if (herdRunStartedAt !== void 0) return;
19762
+ const startedAt = leadAgentStartedAt ?? Date.now();
19763
+ herdRunStartedAt = startedAt;
19764
+ herdReportId = randomUUID10();
19765
+ host.queueLeadPresentation(ctx);
19766
+ try {
19767
+ host.pi.appendEntry(host.entryName, {
19768
+ phase: "started",
19769
+ sessionId: ctx.sessionManager.getSessionId(),
19770
+ startedAt,
19771
+ reportId: herdReportId
19772
+ });
19773
+ } catch (error2) {
19774
+ host.appendDurableError(host.pi, ctx, "pi_herdsman_state_error", error2);
19775
+ }
19776
+ host.requestStatusRefresh();
19777
+ };
19778
+ const restorePendingReports = async (ctx, entries) => {
19779
+ const pending = /* @__PURE__ */ new Map();
19780
+ for (const entry of entries) {
19781
+ if (entry?.type !== "custom" || entry.customType !== host.entryName)
19782
+ continue;
19783
+ const data = entry.data;
19784
+ if (data?.sessionId !== ctx.sessionManager.getSessionId() || typeof data.reportId !== "string")
19785
+ continue;
19786
+ if (data.phase === "report_pending" && typeof data.message === "string") {
19787
+ const previous = pending.get(data.reportId);
19788
+ const createdAt = typeof data.createdAt === "number" && Number.isFinite(data.createdAt) ? data.createdAt : data.startedAt;
19789
+ if (previous && (previous.message !== data.message || previous.kind !== data.kind || previous.createdAt !== createdAt || previous.runReportId !== data.runReportId || previous.target !== data.target))
19790
+ throw new Error(
19791
+ "Lead report identity conflicts with durable payload"
19792
+ );
19793
+ pending.set(data.reportId, {
19794
+ startedAt: data.startedAt,
19795
+ message: data.message,
19796
+ kind: data.kind,
19797
+ createdAt,
19798
+ runReportId: data.runReportId,
19799
+ target: data.target === "project" || data.target === "chief" ? data.target : void 0
19800
+ });
19801
+ }
19802
+ if (data.phase === "report_sent") pending.delete(data.reportId);
19803
+ }
19804
+ for (const [reportId, report] of pending) {
19805
+ if (await publishProjectHandoff(
19806
+ ctx,
19807
+ report.message,
19808
+ reportId,
19809
+ report.kind,
19810
+ report.createdAt,
19811
+ report.target
19812
+ ))
19813
+ host.pi.appendEntry(host.entryName, {
19814
+ phase: "report_sent",
19815
+ sessionId: ctx.sessionManager.getSessionId(),
19816
+ startedAt: report.startedAt,
19817
+ reportId,
19818
+ ...report.runReportId ? { runReportId: report.runReportId } : {}
19819
+ });
19820
+ }
19821
+ };
19822
+ return {
19823
+ begin,
19824
+ maybeFinish,
19512
19825
  agentStarted: () => {
19513
19826
  leadAgentStartedAt = Date.now();
19827
+ leadReportId = randomUUID10();
19828
+ observation = {};
19514
19829
  leadSettled = false;
19515
19830
  },
19516
- agentSettled: (ctx, aborted) => {
19831
+ turnEnd: (event) => {
19832
+ if (!observation || event?.message?.role !== "assistant") return;
19833
+ observation = {
19834
+ outcome: event.outcome,
19835
+ responseEntryId: event.messageEntryId,
19836
+ responseText: contentText4(event.message.content, "").trim()
19837
+ };
19838
+ },
19839
+ beforeSettle: (event) => {
19840
+ if (observation) observation.outcome = event?.outcome;
19841
+ },
19842
+ agentSettled: async (ctx, aborted) => {
19517
19843
  const turnStartedAt = leadAgentStartedAt;
19518
19844
  leadAgentStartedAt = void 0;
19519
19845
  const herdOwnsHandoff = herdRunStartedAt !== void 0;
19520
- const summary = !aborted && turnStartedAt !== void 0 ? latestMeaningfulAssistantResponse(ctx, turnStartedAt) : void 0;
19521
- if (herdOwnsHandoff) lastSettledRun = { aborted, summary };
19846
+ const current = observation;
19847
+ observation = void 0;
19848
+ const successful = !aborted && current?.outcome === "completed" && current.responseEntryId !== void 0;
19849
+ const summary = successful ? current.responseText : void 0;
19850
+ if (herdOwnsHandoff)
19851
+ lastSettledRun = { aborted, outcome: current?.outcome, summary };
19522
19852
  leadSettled = true;
19523
- maybeFinish(ctx);
19524
- if (!herdOwnsHandoff && turnStartedAt !== void 0) {
19525
- const message = aborted ? "Lead execution was cancelled before a completed response." : summary;
19526
- if (message)
19527
- void publishProjectHandoff(ctx, message).catch(
19528
- (error2) => host.appendDurableError(
19529
- host.pi,
19853
+ if (!successful && !aborted && turnStartedAt !== void 0) {
19854
+ const reportCreatedAt = Date.now();
19855
+ const partial = current?.responseText?.slice(0, 1e3);
19856
+ const detail = partial ? `
19857
+
19858
+ Partial response (not completed):
19859
+ ${partial}` : "";
19860
+ const message = `${current?.outcome === "error" ? "Lead model execution failed before completion." : "Lead execution settled without verified completion."}${detail}`;
19861
+ try {
19862
+ const target = host.activeRole() === "lead" && host.isManagedLead() ? "project" : "chief";
19863
+ host.pi.appendEntry(host.entryName, {
19864
+ phase: "report_pending",
19865
+ sessionId: ctx.sessionManager.getSessionId(),
19866
+ startedAt: turnStartedAt,
19867
+ reportId: leadReportId,
19868
+ ...herdOwnsHandoff ? { runReportId: herdReportId } : {},
19869
+ message,
19870
+ kind: "execution_failure",
19871
+ createdAt: reportCreatedAt,
19872
+ target
19873
+ });
19874
+ const published = await publishProjectHandoff(
19875
+ ctx,
19876
+ message,
19877
+ leadReportId,
19878
+ "execution_failure",
19879
+ reportCreatedAt,
19880
+ target
19881
+ );
19882
+ if (published)
19883
+ host.pi.appendEntry(host.entryName, {
19884
+ phase: "report_sent",
19885
+ sessionId: ctx.sessionManager.getSessionId(),
19886
+ startedAt: turnStartedAt,
19887
+ reportId: leadReportId,
19888
+ ...herdOwnsHandoff ? { runReportId: herdReportId } : {}
19889
+ });
19890
+ } catch (error2) {
19891
+ host.appendDurableError(
19892
+ host.pi,
19893
+ ctx,
19894
+ "pi_herdsman_state_error",
19895
+ error2
19896
+ );
19897
+ }
19898
+ } else if (aborted && turnStartedAt !== void 0) {
19899
+ const reportCreatedAt = Date.now();
19900
+ const message = "Lead execution was cancelled before a completed response.";
19901
+ try {
19902
+ host.pi.appendEntry(host.entryName, {
19903
+ phase: "report_pending",
19904
+ sessionId: ctx.sessionManager.getSessionId(),
19905
+ startedAt: turnStartedAt,
19906
+ reportId: leadReportId,
19907
+ createdAt: reportCreatedAt,
19908
+ ...herdOwnsHandoff ? { runReportId: herdReportId } : {},
19909
+ message,
19910
+ target: "project"
19911
+ });
19912
+ if (await publishProjectHandoff(
19913
+ ctx,
19914
+ message,
19915
+ leadReportId,
19916
+ void 0,
19917
+ reportCreatedAt,
19918
+ "project"
19919
+ ))
19920
+ host.pi.appendEntry(host.entryName, {
19921
+ phase: "report_sent",
19922
+ sessionId: ctx.sessionManager.getSessionId(),
19923
+ startedAt: turnStartedAt,
19924
+ reportId: leadReportId,
19925
+ ...herdOwnsHandoff ? { runReportId: herdReportId } : {}
19926
+ });
19927
+ } catch (error2) {
19928
+ host.appendDurableError(
19929
+ host.pi,
19930
+ ctx,
19931
+ "pi_herdsman_state_error",
19932
+ error2
19933
+ );
19934
+ }
19935
+ } else if (!herdOwnsHandoff && successful && summary && turnStartedAt !== void 0 && leadReportId) {
19936
+ try {
19937
+ if (host.isManagedLead()) {
19938
+ const sessionId = ctx.sessionManager.getSessionId();
19939
+ const pending = ctx.sessionManager.getEntries().some(
19940
+ (entry) => entry?.type === "custom" && entry.customType === host.entryName && entry.data?.sessionId === sessionId && entry.data?.phase === "report_pending" && entry.data?.reportId === leadReportId
19941
+ );
19942
+ if (!pending)
19943
+ host.pi.appendEntry(host.entryName, {
19944
+ phase: "report_pending",
19945
+ sessionId,
19946
+ startedAt: turnStartedAt,
19947
+ reportId: leadReportId,
19948
+ message: summary,
19949
+ target: "project"
19950
+ });
19951
+ }
19952
+ const assignment = !host.isManagedLead() || ctx.sessionManager.getEntries().some(
19953
+ (entry) => entry?.type === "custom" && entry.customType === host.entryName && entry.data?.sessionId === ctx.sessionManager.getSessionId() && entry.data?.phase === "report_pending" && entry.data?.reportId === leadReportId && entry.data?.target !== "project"
19954
+ ) ? void 0 : await host.currentProjectAssignment(ctx);
19955
+ if (assignment) {
19956
+ if (await host.publishProjectMessage(
19530
19957
  ctx,
19531
- "pi_herdsman_state_error",
19532
- error2
19533
- )
19958
+ assignment,
19959
+ summary,
19960
+ [],
19961
+ "herd_run",
19962
+ leadReportId
19963
+ ))
19964
+ host.pi.appendEntry(host.entryName, {
19965
+ phase: "report_sent",
19966
+ sessionId: ctx.sessionManager.getSessionId(),
19967
+ startedAt: turnStartedAt,
19968
+ reportId: leadReportId
19969
+ });
19970
+ }
19971
+ } catch (error2) {
19972
+ host.appendDurableError(
19973
+ host.pi,
19974
+ ctx,
19975
+ "pi_herdsman_state_error",
19976
+ error2
19534
19977
  );
19978
+ }
19979
+ }
19980
+ await maybeFinish(ctx);
19981
+ if (!herdOwnsHandoff && turnStartedAt !== void 0) {
19982
+ leadReportId = void 0;
19535
19983
  }
19536
19984
  },
19537
19985
  restoreSession: (entries, sessionId) => {
@@ -19541,19 +19989,103 @@ ${outcome.summary}` : "Herd run settled.";
19541
19989
  sessionId,
19542
19990
  host.entryName
19543
19991
  );
19544
- lastSettledRun = void 0;
19992
+ herdReportId = restoreHerdRunReportId(entries, sessionId, host.entryName);
19993
+ if (herdRunStartedAt !== void 0 && !herdReportId) {
19994
+ herdReportId = randomUUID10();
19995
+ host.pi.appendEntry(host.entryName, {
19996
+ phase: "started",
19997
+ sessionId,
19998
+ startedAt: herdRunStartedAt,
19999
+ reportId: herdReportId
20000
+ });
20001
+ }
20002
+ const hasReportIntent = entries.some(
20003
+ (entry) => entry?.type === "custom" && entry.customType === host.entryName && entry.data?.sessionId === sessionId && !!herdReportId && (entry.data?.reportId === herdReportId || entry.data?.runReportId === herdReportId) && entry.data?.phase === "report_sent"
20004
+ );
20005
+ const hasUnsentHerdIntent = entries.some((entry) => {
20006
+ const data = entry?.data;
20007
+ return entry?.type === "custom" && entry.customType === host.entryName && data?.sessionId === sessionId && data?.phase === "report_pending" && !!herdReportId && (data.reportId === herdReportId || data.runReportId === herdReportId) && !entries.some(
20008
+ (sent) => sent?.type === "custom" && sent.customType === host.entryName && sent.data?.sessionId === sessionId && sent.data?.phase === "report_sent" && sent.data?.reportId === data.reportId
20009
+ );
20010
+ });
20011
+ lastSettledRun = hasReportIntent && !hasUnsentHerdIntent ? { aborted: false, outcome: "reported" } : void 0;
19545
20012
  leadSettled = herdRunStartedAt === void 0;
19546
20013
  },
19547
20014
  finishIfIdle: (ctx) => {
19548
- if (herdRunStartedAt !== void 0 && ctx.isIdle()) {
19549
- leadSettled = true;
19550
- maybeFinish(ctx);
19551
- }
20015
+ if (!ctx.isIdle()) return;
20016
+ if (idleFinishing) return;
20017
+ idleFinishing = (async () => {
20018
+ await restorePendingReports(ctx, ctx.sessionManager.getEntries());
20019
+ const currentEntries = ctx.sessionManager.getEntries();
20020
+ const hasUnsentHerdIntent = currentEntries.some((entry) => {
20021
+ const data = entry?.data;
20022
+ return entry?.type === "custom" && entry.customType === host.entryName && data?.sessionId === ctx.sessionManager.getSessionId() && data?.phase === "report_pending" && !!herdReportId && (data.reportId === herdReportId || data.runReportId === herdReportId) && !currentEntries.some(
20023
+ (sent) => sent?.type === "custom" && sent.customType === host.entryName && sent.data?.sessionId === ctx.sessionManager.getSessionId() && sent.data?.phase === "report_sent" && sent.data?.reportId === data.reportId
20024
+ );
20025
+ });
20026
+ if (hasUnsentHerdIntent) return;
20027
+ if (herdReportId && currentEntries.some(
20028
+ (entry) => entry?.type === "custom" && entry.customType === host.entryName && entry.data?.sessionId === ctx.sessionManager.getSessionId() && entry.data?.phase === "report_sent" && (entry.data?.reportId === herdReportId || entry.data?.runReportId === herdReportId)
20029
+ ))
20030
+ lastSettledRun = { aborted: false, outcome: "reported" };
20031
+ if (herdRunStartedAt !== void 0) {
20032
+ leadSettled = true;
20033
+ if (!lastSettledRun) {
20034
+ const pending = ctx.sessionManager.getEntries().find(
20035
+ (entry) => entry?.type === "custom" && entry.customType === host.entryName && entry.data?.sessionId === ctx.sessionManager.getSessionId() && entry.data?.phase === "report_pending" && entry.data?.reportId === herdReportId
20036
+ )?.data;
20037
+ const message = pending?.message ?? "Lead execution outcome was not preserved across restoration; work remains unresolved.";
20038
+ const reportCreatedAt = typeof pending?.createdAt === "number" && Number.isFinite(pending.createdAt) ? pending.createdAt : Date.now();
20039
+ const target = pending ? pending.target : host.isManagedLead() ? "project" : "chief";
20040
+ if (!pending)
20041
+ host.pi.appendEntry(host.entryName, {
20042
+ phase: "report_pending",
20043
+ sessionId: ctx.sessionManager.getSessionId(),
20044
+ startedAt: herdRunStartedAt,
20045
+ reportId: herdReportId,
20046
+ message,
20047
+ kind: "execution_failure",
20048
+ createdAt: reportCreatedAt,
20049
+ target
20050
+ });
20051
+ const published = await publishProjectHandoff(
20052
+ ctx,
20053
+ message,
20054
+ herdReportId,
20055
+ "execution_failure",
20056
+ reportCreatedAt,
20057
+ target
20058
+ );
20059
+ if (published)
20060
+ host.pi.appendEntry(host.entryName, {
20061
+ phase: "report_sent",
20062
+ sessionId: ctx.sessionManager.getSessionId(),
20063
+ startedAt: herdRunStartedAt,
20064
+ reportId: herdReportId
20065
+ });
20066
+ if (published)
20067
+ lastSettledRun = { aborted: false, outcome: "reported" };
20068
+ }
20069
+ await maybeFinish(ctx);
20070
+ }
20071
+ })().catch(
20072
+ (error2) => host.appendDurableError(
20073
+ host.pi,
20074
+ ctx,
20075
+ "pi_herdsman_state_error",
20076
+ error2
20077
+ )
20078
+ ).finally(() => {
20079
+ idleFinishing = void 0;
20080
+ });
19552
20081
  },
19553
20082
  startedAt: () => herdRunStartedAt,
19554
20083
  shutdown: () => {
19555
20084
  leadAgentStartedAt = void 0;
19556
20085
  herdRunStartedAt = void 0;
20086
+ herdReportId = void 0;
20087
+ leadReportId = void 0;
20088
+ observation = void 0;
19557
20089
  lastSettledRun = void 0;
19558
20090
  leadSettled = true;
19559
20091
  }
@@ -19579,6 +20111,18 @@ function restoreHerdRunStartedAt(entries, sessionId, entryName) {
19579
20111
  }
19580
20112
  return active;
19581
20113
  }
20114
+ function restoreHerdRunReportId(entries, sessionId, entryName) {
20115
+ let reportId;
20116
+ for (const entry of entries) {
20117
+ if (entry?.type !== "custom" || entry.customType !== entryName) continue;
20118
+ const data = entry.data;
20119
+ if (data?.sessionId !== sessionId) continue;
20120
+ if (data.phase === "started" && typeof data.reportId === "string")
20121
+ reportId = data.reportId;
20122
+ if (data.phase === "finished") reportId = void 0;
20123
+ }
20124
+ return reportId;
20125
+ }
19582
20126
  function createLeadProjectRuntime(host) {
19583
20127
  const stopExecution = async (session, target, ctx, signal) => {
19584
20128
  const release = claimDelegationLock(target.workspace_id, session);
@@ -19654,6 +20198,345 @@ function createLeadProjectRuntime(host) {
19654
20198
  )
19655
20199
  );
19656
20200
  };
20201
+ const inspectRemovalTarget = async (branch, ctx, signal) => {
20202
+ const manager = await host.currentManager(ctx);
20203
+ const lease = host.managerLease();
20204
+ if (!manager || !lease || !host.sameManagerDescriptor(manager, lease.descriptor))
20205
+ throw new Error("Manager lease is no longer active");
20206
+ const group = await host.worktreeGroupScope(
20207
+ host.pi,
20208
+ ctx,
20209
+ manager.workspaceId,
20210
+ signal
20211
+ );
20212
+ if (group.repoKey !== manager.repoKey || group.primaryWorkspaceId !== manager.workspaceId)
20213
+ throw new Error("Manager worktree group is not authoritative");
20214
+ const topology = await host.runHerdr(
20215
+ host.pi,
20216
+ ctx,
20217
+ ["worktree", "list", "--workspace", manager.workspaceId],
20218
+ { signal }
20219
+ );
20220
+ if (topology?.source?.source_workspace_id !== manager.workspaceId || topology?.source?.repo_key !== manager.repoKey || typeof topology?.source?.source_checkout_path !== "string" || !Array.isArray(topology?.worktrees))
20221
+ throw new Error("Herdr worktree topology is not authoritative");
20222
+ const assignments = host.listProjectAssignments(host.supervisionRuntime(), manager.repoKey).filter((assignment2) => assignment2.branch === branch);
20223
+ if (assignments.length !== 1)
20224
+ throw new Error("Branch is not one exact current project assignment");
20225
+ const assignment = assignments[0];
20226
+ const current = host.readProjectAssignment(
20227
+ host.supervisionRuntime(),
20228
+ manager.repoKey,
20229
+ branch
20230
+ );
20231
+ if (!current || current.id !== assignment.id || current.repoKey !== manager.repoKey)
20232
+ throw new Error("Project assignment changed during removal");
20233
+ const matches = topology.worktrees.filter(
20234
+ (worktree2) => worktree2?.branch === branch
20235
+ );
20236
+ if (matches.length !== 1)
20237
+ throw new Error("Branch does not identify exactly one Herdr worktree");
20238
+ const worktree = matches[0];
20239
+ if (typeof worktree.path !== "string" || !worktree.path || worktree.is_linked_worktree !== true)
20240
+ throw new Error("Herdr worktree checkout path is unavailable");
20241
+ const checkoutPath = resolve6(worktree.path);
20242
+ if (checkoutPath === resolve6(topology.source.source_checkout_path))
20243
+ throw new Error("The Manager's primary checkout cannot be removed");
20244
+ if (worktree.open_workspace_id === manager.workspaceId)
20245
+ throw new Error("The Manager's primary workspace cannot be removed");
20246
+ if (!assignment.piSessionFile)
20247
+ throw new Error(
20248
+ "Project checkout provenance is unavailable; worktree preserved"
20249
+ );
20250
+ if (assignment.piSessionFile) {
20251
+ try {
20252
+ const session = host.SessionManager.open(assignment.piSessionFile);
20253
+ if (session.getSessionId() !== assignment.id || resolve6(session.getCwd()) !== checkoutPath)
20254
+ throw new Error();
20255
+ } catch {
20256
+ throw new Error(
20257
+ "Persisted project session provenance does not match the checkout; worktree preserved"
20258
+ );
20259
+ }
20260
+ }
20261
+ const [gitRoot, gitCommonDir, gitInventory] = await Promise.all([
20262
+ host.pi.exec("git", ["rev-parse", "--show-toplevel"], {
20263
+ cwd: checkoutPath,
20264
+ signal,
20265
+ timeout: 1e4
20266
+ }),
20267
+ host.pi.exec(
20268
+ "git",
20269
+ ["rev-parse", "--path-format=absolute", "--git-common-dir"],
20270
+ { cwd: checkoutPath, signal, timeout: 1e4 }
20271
+ ),
20272
+ host.pi.exec("git", ["worktree", "list", "--porcelain", "-z"], {
20273
+ cwd: topology.source.source_checkout_path,
20274
+ signal,
20275
+ timeout: 1e4
20276
+ })
20277
+ ]);
20278
+ let gitWorktrees;
20279
+ try {
20280
+ gitWorktrees = parseGitWorktreeInventory(
20281
+ String(gitInventory.stdout ?? "")
20282
+ );
20283
+ if (gitRoot.code !== 0 || gitRoot.killed || gitRoot.stderr?.trim() || gitCommonDir.code !== 0 || gitCommonDir.killed || gitCommonDir.stderr?.trim() || gitInventory.code !== 0 || gitInventory.killed || gitInventory.stderr?.trim() || realpathSync5(String(gitRoot.stdout ?? "").trim()) !== realpathSync5(checkoutPath) || realpathSync5(String(gitCommonDir.stdout ?? "").trim()) !== realpathSync5(manager.repoKey) || !gitWorktrees || gitWorktrees.filter(
20284
+ (entry) => resolve6(entry.path) === checkoutPath && entry.branch === branch
20285
+ ).length !== 1)
20286
+ throw new Error();
20287
+ } catch {
20288
+ throw new Error(
20289
+ "Git could not verify the exact linked checkout and repository; worktree preserved"
20290
+ );
20291
+ }
20292
+ const git = await host.pi.exec(
20293
+ "git",
20294
+ ["status", "--porcelain=v1", "--untracked-files=all"],
20295
+ { cwd: checkoutPath, signal, timeout: 1e4 }
20296
+ );
20297
+ if (git.code !== 0 || git.killed || git.stderr?.trim() || git.stdout?.trim())
20298
+ throw new Error(
20299
+ "Checkout is dirty or could not be verified; worktree preserved"
20300
+ );
20301
+ const leads = await host.liveLead(ctx, assignment.id);
20302
+ if (leads.length > 1)
20303
+ throw new Error(
20304
+ "Assigned Lead identity is ambiguous; worktree preserved"
20305
+ );
20306
+ if (leads.length === 1) {
20307
+ const lead = leads[0];
20308
+ const state = host.readLeadCoordinationState(
20309
+ host.supervisionRuntime(),
20310
+ assignment.id
20311
+ );
20312
+ host.requireCompatibleBuild(
20313
+ host.HERDSMAN_BUILD,
20314
+ state?.build,
20315
+ "remove_worktree",
20316
+ `Lead ${assignment.id}`
20317
+ );
20318
+ if (!worktree.open_workspace_id || lead.workspace_id !== worktree.open_workspace_id || !state || (state.role ?? "lead") !== "lead" || state.piSessionId !== assignment.id)
20319
+ throw new Error(
20320
+ "Assigned Lead placement is ambiguous; worktree preserved"
20321
+ );
20322
+ }
20323
+ return {
20324
+ manager,
20325
+ assignment,
20326
+ worktree,
20327
+ checkoutPath,
20328
+ sourceCheckoutPath: topology.source.source_checkout_path,
20329
+ liveLead: leads[0]
20330
+ };
20331
+ };
20332
+ const assertNoUnrelatedPiExecution = async (target, ctx, signal) => {
20333
+ if (!target.worktree.open_workspace_id) return;
20334
+ const inventory = await host.herdrSessionSnapshot(host.pi, ctx, signal);
20335
+ const owned = /* @__PURE__ */ new Set([target.assignment.id]);
20336
+ const states = host.scanAgentStates().states.map(({ state }) => state);
20337
+ let changed = true;
20338
+ while (changed) {
20339
+ changed = false;
20340
+ for (const state of states) {
20341
+ if (state.workspaceId === target.worktree.open_workspace_id && owned.has(state.ownerSessionId) && !owned.has(state.piSessionId)) {
20342
+ owned.add(state.piSessionId);
20343
+ changed = true;
20344
+ }
20345
+ }
20346
+ }
20347
+ if (inventory.agents.some(
20348
+ (agent) => agent.workspace_id === target.worktree.open_workspace_id && host.isPiAgent(agent) && !owned.has(host.herdrSessionId(agent) ?? "")
20349
+ ))
20350
+ throw new Error(
20351
+ "Unrelated or ambiguously identified Pi execution occupies the worktree; worktree preserved"
20352
+ );
20353
+ };
20354
+ const removeWorktree = async (branch, ctx, signal) => {
20355
+ if (activeRole() !== "manager")
20356
+ throw new Error("Only Managers can remove project worktrees");
20357
+ if (!ctx.hasUI)
20358
+ throw new Error("Worktree removal requires interactive confirmation");
20359
+ const operationSignal = signal ?? ctx.signal;
20360
+ operationSignal?.throwIfAborted();
20361
+ const candidate = await inspectRemovalTarget(branch, ctx, operationSignal);
20362
+ await assertNoUnrelatedPiExecution(candidate, ctx, operationSignal);
20363
+ const answer = await ctx.ui.input(
20364
+ `Remove worktree for branch ${branch} at ${candidate.checkoutPath} from project ${candidate.manager.repoKey}? This removes the checkout and associated Herdr workspace; running managed execution may be stopped. The Git branch and Pi conversation remain, and the project assignment is retired after removal. Type remove to confirm.`,
20365
+ void 0,
20366
+ { signal: operationSignal }
20367
+ );
20368
+ operationSignal?.throwIfAborted();
20369
+ if (answer !== "remove")
20370
+ return {
20371
+ content: [
20372
+ {
20373
+ type: "text",
20374
+ text: `Removal of ${branch} cancelled; no changes made.`
20375
+ }
20376
+ ],
20377
+ details: { ok: false, action: "remove", branch, cancelled: true }
20378
+ };
20379
+ return host.withProjectWorkLock(
20380
+ `${candidate.manager.repoKey}\0${branch}`,
20381
+ async () => {
20382
+ let reopened = false;
20383
+ let leadStopped = false;
20384
+ const removed = await host.withProjectAssignmentLock(
20385
+ candidate.manager.repoKey,
20386
+ branch,
20387
+ async () => {
20388
+ operationSignal?.throwIfAborted();
20389
+ const current = await inspectRemovalTarget(
20390
+ branch,
20391
+ ctx,
20392
+ operationSignal
20393
+ );
20394
+ if (current.assignment.id !== candidate.assignment.id || current.checkoutPath !== candidate.checkoutPath || !host.sameManagerDescriptor(current.manager, candidate.manager))
20395
+ throw new Error(
20396
+ "Removal target changed during confirmation; worktree preserved"
20397
+ );
20398
+ await assertNoUnrelatedPiExecution(current, ctx, operationSignal);
20399
+ if (current.liveLead) {
20400
+ try {
20401
+ await stopExecution(
20402
+ current.assignment.id,
20403
+ current.liveLead,
20404
+ ctx,
20405
+ operationSignal
20406
+ );
20407
+ leadStopped = true;
20408
+ } catch (error2) {
20409
+ throw new Error(
20410
+ `Managed execution could not be stopped; worktree was preserved: ${error2 instanceof Error ? error2.message : String(error2)}`
20411
+ );
20412
+ }
20413
+ }
20414
+ const inventory = await host.herdrSessionSnapshot(
20415
+ host.pi,
20416
+ ctx,
20417
+ operationSignal
20418
+ );
20419
+ const occupants = inventory.agents.filter(
20420
+ (agent) => agent.workspace_id === current.worktree.open_workspace_id && host.isPiAgent(agent)
20421
+ );
20422
+ if (occupants.length)
20423
+ throw new Error(
20424
+ `${leadStopped ? "Assigned Lead stopped; " : ""}unrelated or remaining Pi execution occupies the worktree; removal was not attempted`
20425
+ );
20426
+ let workspaceId = current.worktree.open_workspace_id;
20427
+ if (!workspaceId) {
20428
+ const opened = await host.runHerdr(
20429
+ host.pi,
20430
+ ctx,
20431
+ [
20432
+ "worktree",
20433
+ "open",
20434
+ "--workspace",
20435
+ current.manager.workspaceId,
20436
+ "--branch",
20437
+ branch,
20438
+ "--no-focus"
20439
+ ],
20440
+ { signal: operationSignal }
20441
+ );
20442
+ workspaceId = opened?.workspace?.workspace_id;
20443
+ if (!workspaceId || workspaceId === current.manager.workspaceId || opened?.worktree?.branch !== branch || resolve6(opened?.worktree?.path ?? "") !== current.checkoutPath)
20444
+ throw new Error(
20445
+ "Herdr reopened a different or ambiguous worktree; checkout preserved"
20446
+ );
20447
+ reopened = true;
20448
+ }
20449
+ let final;
20450
+ try {
20451
+ final = await inspectRemovalTarget(
20452
+ branch,
20453
+ ctx,
20454
+ operationSignal
20455
+ );
20456
+ } catch (error2) {
20457
+ if (!leadStopped) throw error2;
20458
+ throw new Error(
20459
+ `Assigned Lead stopped; checkout was preserved because final verification failed: ${error2 instanceof Error ? error2.message : String(error2)}`
20460
+ );
20461
+ }
20462
+ if (final.assignment.id !== current.assignment.id || final.worktree.open_workspace_id !== workspaceId || final.checkoutPath !== current.checkoutPath)
20463
+ throw new Error(
20464
+ "Removal target changed before native removal; worktree preserved"
20465
+ );
20466
+ try {
20467
+ operationSignal?.throwIfAborted();
20468
+ return {
20469
+ result: await host.runHerdr(
20470
+ host.pi,
20471
+ ctx,
20472
+ ["worktree", "remove", "--workspace", workspaceId],
20473
+ { signal: operationSignal }
20474
+ )
20475
+ };
20476
+ } catch (error2) {
20477
+ return {
20478
+ error: error2 instanceof Error ? error2.message : String(error2),
20479
+ reopened
20480
+ };
20481
+ }
20482
+ }
20483
+ ).catch((error2) => {
20484
+ if (!leadStopped && !reopened) throw error2;
20485
+ throw new Error(
20486
+ `${leadStopped ? "Assigned Lead stopped; " : ""}${reopened ? "Herdr workspace reopened; " : ""}worktree removal did not complete: ${error2 instanceof Error ? error2.message : String(error2)}`
20487
+ );
20488
+ });
20489
+ let assignmentRetired = false;
20490
+ let retirementError;
20491
+ const checkoutState = gitPathStatus(candidate.checkoutPath);
20492
+ const checkoutRemoved = checkoutState === "absent";
20493
+ try {
20494
+ await host.reconcileProjectAssignments(
20495
+ ctx,
20496
+ candidate.manager.repoKey,
20497
+ candidate.manager.workspaceId,
20498
+ candidate.sourceCheckoutPath,
20499
+ true,
20500
+ branch
20501
+ );
20502
+ assignmentRetired = !host.readProjectAssignment(
20503
+ host.supervisionRuntime(),
20504
+ candidate.manager.repoKey,
20505
+ branch
20506
+ );
20507
+ } catch (error2) {
20508
+ assignmentRetired = !host.readProjectAssignment(
20509
+ host.supervisionRuntime(),
20510
+ candidate.manager.repoKey,
20511
+ branch
20512
+ );
20513
+ retirementError = error2 instanceof Error ? error2.message : String(error2);
20514
+ }
20515
+ return {
20516
+ content: [
20517
+ {
20518
+ type: "text",
20519
+ text: checkoutRemoved ? assignmentRetired ? `${leadStopped ? "Assigned Lead stopped; " : ""}removed checkout for ${branch}; assignment retired. Git branch and Pi conversation preserved.` : `${leadStopped ? "Assigned Lead stopped; " : ""}checkout removed for ${branch}; assignment retirement is unconfirmed${retirementError ? `: ${retirementError}` : "."}` : checkoutState === "present" ? `${leadStopped ? "Assigned Lead stopped; " : ""}worktree removal was not confirmed for ${branch}; checkout remains and assignment ${assignmentRetired ? "was retired" : "was not retired"}${"error" in removed ? `: ${removed.error}` : "."}` : `${leadStopped ? "Assigned Lead stopped; " : ""}worktree removal result is unknown for ${branch}; assignment ${assignmentRetired ? "was retired" : "retirement is unconfirmed"}${"error" in removed ? `: ${removed.error}` : "."}`
20520
+ }
20521
+ ],
20522
+ details: {
20523
+ ok: checkoutRemoved && assignmentRetired,
20524
+ action: "remove",
20525
+ branch,
20526
+ checkout_path: candidate.checkoutPath,
20527
+ lead_stopped: leadStopped,
20528
+ checkout_removed: checkoutRemoved,
20529
+ assignment_retired: assignmentRetired,
20530
+ forced: false,
20531
+ checkout_state: checkoutRemoved ? "removed" : checkoutState,
20532
+ ..."error" in removed ? { removal_error: removed.error } : {},
20533
+ ..."error" in removed && removed.reopened ? { workspace_reopened: true } : {},
20534
+ ...retirementError ? { retirement_error: retirementError } : {}
20535
+ }
20536
+ };
20537
+ }
20538
+ );
20539
+ };
19657
20540
  const {
19658
20541
  pi,
19659
20542
  leadRuntime,
@@ -20330,6 +21213,11 @@ function createLeadProjectRuntime(host) {
20330
21213
  throw new Error("Only Managers can control project work");
20331
21214
  return activateProjectLead(params, ctx, signal);
20332
21215
  }
21216
+ if (params.action === "remove") {
21217
+ if (activeRole() !== "manager")
21218
+ throw new Error("Only Managers can remove project worktrees");
21219
+ return removeWorktree(params.branch, ctx, signal);
21220
+ }
20333
21221
  if (params.action === "stop") {
20334
21222
  if (activeRole() !== "manager")
20335
21223
  throw new Error("Only Managers can control project work");
@@ -20546,7 +21434,13 @@ function createLeadProjectRuntime(host) {
20546
21434
  next_action: "Report activity returns asynchronously; continue only independent work, otherwise end the turn. Do not poll."
20547
21435
  });
20548
21436
  };
20549
- return { stopExecution, stop, activateProjectLead, executeStaff };
21437
+ return {
21438
+ stopExecution,
21439
+ stop,
21440
+ activateProjectLead,
21441
+ removeWorktree,
21442
+ executeStaff
21443
+ };
20550
21444
  }
20551
21445
  function readLeadSessionIds(inventory, mailboxes, workspaceId, dependencies) {
20552
21446
  const panes = inventory.panes;
@@ -21257,6 +22151,7 @@ function createLeadCoordinationRuntime(host) {
21257
22151
  return 0;
21258
22152
  importResultBindings2(pi, ctx, record.resultBindings, "project_message");
21259
22153
  const backlog = record.createdAt < manager.createdAt;
22154
+ const actionableFailure = backlog && record.kind === "execution_failure";
21260
22155
  pi.sendMessage(
21261
22156
  {
21262
22157
  customType: "pi-herdsman-project_message",
@@ -21271,7 +22166,7 @@ ${record.text}`,
21271
22166
  fromSessionId: record.fromSessionId
21272
22167
  }
21273
22168
  },
21274
- backlog ? { triggerTurn: false } : { deliverAs: "followUp", triggerTurn: true }
22169
+ backlog && !actionableFailure ? { triggerTurn: false } : { deliverAs: "followUp", triggerTurn: true }
21275
22170
  );
21276
22171
  if (hasProjectMessageReceipt(record)) consumeProjectMessage(record);
21277
22172
  return 1;
@@ -21701,7 +22596,18 @@ function createLeadInboxRuntime(host) {
21701
22596
  outcome: "resolved",
21702
22597
  triggerTurn: options?.triggerTurn === true
21703
22598
  });
21704
- return result;
22599
+ await result;
22600
+ if (projectAssignment) {
22601
+ try {
22602
+ host.assignmentDelivered?.(ctx);
22603
+ } catch (error2) {
22604
+ try {
22605
+ appendDurableError2(pi, ctx, "pi_herdsman_state_error", error2);
22606
+ } catch {
22607
+ }
22608
+ }
22609
+ }
22610
+ return;
21705
22611
  } catch (error2) {
21706
22612
  managerDiagnostic("inbox_catch", { category: "send" });
21707
22613
  if (projectAssignment)
@@ -22221,7 +23127,7 @@ function createLeadRoleTransitions(state, host) {
22221
23127
  lease.release();
22222
23128
  }
22223
23129
  };
22224
- const publishProjectMessage = async (ctx, assignment, message, files = [], operation = "message_supervisor") => {
23130
+ const publishProjectMessage = async (ctx, assignment, message, files = [], operation = "message_supervisor", reportId, kind) => {
22225
23131
  const sessionId = ctx.sessionManager.getSessionId();
22226
23132
  const runtime = host.supervisionRuntime();
22227
23133
  const current = host.readProjectAssignment(
@@ -22230,7 +23136,7 @@ function createLeadRoleTransitions(state, host) {
22230
23136
  assignment.branch
22231
23137
  );
22232
23138
  if (!current || current.id !== sessionId) return void 0;
22233
- const id = randomUUID10();
23139
+ const id = reportId ?? randomUUID10();
22234
23140
  const createdAt = Date.now();
22235
23141
  const prepared = await host.prepareCoordinationInput(
22236
23142
  ctx,
@@ -22245,6 +23151,7 @@ function createLeadRoleTransitions(state, host) {
22245
23151
  branch: assignment.branch,
22246
23152
  fromSessionId: sessionId,
22247
23153
  text: candidate,
23154
+ ...kind ? { kind } : {},
22248
23155
  ...resultBindings.length ? { resultBindings: [...resultBindings] } : {},
22249
23156
  createdAt
22250
23157
  })
@@ -22256,6 +23163,7 @@ function createLeadRoleTransitions(state, host) {
22256
23163
  branch: assignment.branch,
22257
23164
  fromSessionId: sessionId,
22258
23165
  text: prepared.text,
23166
+ ...kind ? { kind } : {},
22259
23167
  ...prepared.resultBindings.length ? { resultBindings: prepared.resultBindings } : {},
22260
23168
  createdAt
22261
23169
  };
@@ -22269,6 +23177,18 @@ function createLeadRoleTransitions(state, host) {
22269
23177
  assignment.branch
22270
23178
  );
22271
23179
  if (!latest || latest.id !== sessionId) return void 0;
23180
+ const existing = listProjectMessages(
23181
+ runtime,
23182
+ assignment.repoKey,
23183
+ assignment.branch
23184
+ ).find((candidate) => candidate.id === id);
23185
+ if (existing) {
23186
+ if (existing.fromSessionId !== sessionId || existing.text !== record.text || existing.kind !== record.kind || JSON.stringify(existing.resultBindings ?? []) !== JSON.stringify(record.resultBindings ?? []))
23187
+ throw new Error(
23188
+ "Project message identity conflicts with durable payload"
23189
+ );
23190
+ return existing;
23191
+ }
22272
23192
  host.writeProjectMessage(record, runtime);
22273
23193
  return record;
22274
23194
  }
@@ -23393,7 +24313,8 @@ var MANAGER_TOOLS = [
23393
24313
  ...STAFF_TOOLS,
23394
24314
  "delegate_project",
23395
24315
  "resume_project",
23396
- "stop_lead"
24316
+ "stop_lead",
24317
+ "remove_worktree"
23397
24318
  ];
23398
24319
  var LEAD_COORDINATION_TOOLS = [
23399
24320
  ...AGENT_COORDINATION_TOOLS,
@@ -23408,7 +24329,7 @@ ${LEAD_SUPERVISOR_PEER_GUIDANCE}
23408
24329
  For assigned project work, automatic result handoffs contain summary text but no file attachments. When the Manager needs an actual artifact for review, decision-making, or onward delegation, explicitly transfer the relevant evidence through message_supervisor.files.
23409
24330
  ${FILE_HANDOFF_GUIDANCE}`;
23410
24331
  var MANAGER_ROLE_CONTRACT = `## Manager role
23411
- You are the verified Manager for this Pi session. Supervise only project Leads established by exact current project assignments; configuration changes operating guidance and ordinary tool availability, never authority. Project execution belongs to assigned Leads and their Agent trees. Coordination messages and reports are nonterminal and do not establish completion.`;
24332
+ You are the verified Manager for this Pi session. Supervise only project Leads established by exact current project assignments; configuration changes operating guidance and ordinary tool availability, never authority. Project execution belongs to assigned Leads and their Agent trees. Coordination messages and reports are nonterminal and do not establish completion. Use remove_worktree only for an explicitly requested checkout-removal action; never infer removal from completion, review, or inactivity. The user must confirm interactively.`;
23412
24333
  var SUPERVISION_CONTEXT_TYPE = "pi-herdsman-supervision-context";
23413
24334
  var SUPERVISOR_STATE_TYPE = "pi-herdsman-supervisor-state";
23414
24335
  var STALE_AFTER_MS = 10 * 6e4;
@@ -23926,7 +24847,8 @@ Use reply_agent with agent="${ask.agentLabel}" to answer this question.`,
23926
24847
  ...STAFF_TOOLS,
23927
24848
  "delegate_project",
23928
24849
  "resume_project",
23929
- "stop_lead"
24850
+ "stop_lead",
24851
+ "remove_worktree"
23930
24852
  ];
23931
24853
  if (processRole === "managed-agent") {
23932
24854
  leadStatusRuntime = createAgentStatusRuntime();
@@ -24078,6 +25000,7 @@ ${globalInstructions}
24078
25000
  writeChiefMessage,
24079
25001
  herdrSessionSnapshot,
24080
25002
  isPiAgent,
25003
+ listProjectAssignments,
24081
25004
  herdrSessionId,
24082
25005
  messageLimits: messageLimits2,
24083
25006
  prepareMessageInput,
@@ -24378,9 +25301,48 @@ ${summary}`,
24378
25301
  });
24379
25302
  if (process.env.HERDR_PANE_ID)
24380
25303
  pi.registerCommand("manager", {
24381
- description: "Activate Manager mode, or open its overview when already active",
24382
- getArgumentCompletions: (prefix) => ["leave"].filter((value2) => value2.startsWith(prefix.trim())).map((value2) => ({ value: value2, label: value2 })),
25304
+ description: "Activate Manager mode, open its overview, or remove an assigned worktree",
25305
+ getArgumentCompletions: (prefix) => {
25306
+ const input = prefix.trimStart();
25307
+ const [command, branchPrefix] = input.split(/\s+/u);
25308
+ if (!command)
25309
+ return ["leave", "remove"].map((value2) => ({
25310
+ value: value2,
25311
+ label: value2
25312
+ }));
25313
+ if (command !== "remove")
25314
+ return input.includes(" ") ? null : ["leave", "remove"].filter((value2) => value2.startsWith(command)).map((value2) => ({ value: value2, label: value2 }));
25315
+ if (input.trim().split(/\s+/u).length > 2) return null;
25316
+ const lease = leadRuntime.managerLease;
25317
+ if (!lease) return [];
25318
+ try {
25319
+ return listProjectAssignments(
25320
+ supervisionRuntime(),
25321
+ lease.descriptor.repoKey
25322
+ ).map(({ branch }) => branch).filter((branch) => branch.startsWith(branchPrefix ?? "")).map((branch) => ({
25323
+ value: `remove ${branch}`,
25324
+ label: branch
25325
+ }));
25326
+ } catch {
25327
+ return [];
25328
+ }
25329
+ },
24383
25330
  handler: async (rawArgs, ctx) => {
25331
+ const args = rawArgs.trim().split(/\s+/u).filter(Boolean);
25332
+ if (args[0] === "remove") {
25333
+ if (args.length !== 2) {
25334
+ ctx.ui.notify("Usage: /manager remove <branch>", "error");
25335
+ return;
25336
+ }
25337
+ const result = await leadRuntimes.projectRuntime.removeWorktree(
25338
+ args[1],
25339
+ ctx
25340
+ );
25341
+ ctx.ui.notify(
25342
+ result.content[0]?.text ?? `Removal of ${args[1]} finished.`
25343
+ );
25344
+ return;
25345
+ }
24384
25346
  await leadCommandRuntime.runRoleCommand("manager", rawArgs, ctx);
24385
25347
  }
24386
25348
  });
@@ -24548,6 +25510,23 @@ ${summary}`,
24548
25510
  renderCall: (a, t, c) => renderCoordinationCall("staff", "resume", a, t, c),
24549
25511
  renderResult: (r, o, t, c) => renderCoordinationResult("staff", "resume", r, o, t, c)
24550
25512
  });
25513
+ pi.registerTool({
25514
+ ...staffTool,
25515
+ name: "remove_worktree",
25516
+ label: "remove worktree",
25517
+ description: "Remove an exact assigned project worktree with human confirmation. Preserves its Git branch and Pi conversation; never forces removal.",
25518
+ parameters: staffBranchParameters,
25519
+ promptSnippet: void 0,
25520
+ execute: (id, p, signal, update, ctx) => staffTool.execute(
25521
+ id,
25522
+ { action: "remove", branch: p.branch },
25523
+ signal,
25524
+ update,
25525
+ ctx
25526
+ ),
25527
+ renderCall: (a, t, c) => renderCoordinationCall("staff", "remove", a, t, c),
25528
+ renderResult: (r, o, t, c) => renderCoordinationResult("staff", "remove", r, o, t, c)
25529
+ });
24551
25530
  pi.registerTool({
24552
25531
  ...staffTool,
24553
25532
  name: "stop_lead",