pi-herdsman 0.22.2 → 0.24.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.22.2",
18
+ version: "0.24.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",
@@ -90,12 +90,14 @@ var package_default = {
90
90
  "@earendil-works/pi-ai": "*",
91
91
  "@earendil-works/pi-coding-agent": "*",
92
92
  "@earendil-works/pi-tui": "*",
93
+ "@howaboua/pi-codex-conversion": "^3.0.45",
93
94
  typebox: "*"
94
95
  },
95
96
  devDependencies: {
96
97
  "@earendil-works/pi-ai": "1.1.0",
97
98
  "@earendil-works/pi-coding-agent": "1.1.0",
98
99
  "@earendil-works/pi-tui": "1.1.0",
100
+ "@howaboua/pi-codex-conversion": "3.0.45",
99
101
  esbuild: "^0.28.2",
100
102
  typebox: "^1.3.7"
101
103
  },
@@ -112,6 +114,11 @@ var package_default = {
112
114
  ],
113
115
  publishConfig: {
114
116
  registry: "https://registry.npmjs.org/"
117
+ },
118
+ peerDependenciesMeta: {
119
+ "@howaboua/pi-codex-conversion": {
120
+ optional: true
121
+ }
115
122
  }
116
123
  };
117
124
 
@@ -553,6 +560,8 @@ function releaseProcessLock(claimDir, ownerPath, claim, name) {
553
560
  }
554
561
 
555
562
  // extension/mailbox.ts
563
+ var MANAGED_AGENT_BOOTSTRAP_LIMIT_BYTES = 64 * 1024;
564
+ var BOOTSTRAP_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
556
565
  var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
557
566
  var PREFIX = "__PI_HERDSMAN_AGENT_V5__:";
558
567
  var MAILBOX_PROTOCOL_LIMIT_BYTES = 1024 * 1024;
@@ -563,6 +572,7 @@ var MailboxClaimOccupiedError = class extends Error {
563
572
  code = "MAILBOX_CLAIM_OCCUPIED";
564
573
  };
565
574
  var LIMITS = {
575
+ bootstrap: MANAGED_AGENT_BOOTSTRAP_LIMIT_BYTES,
566
576
  state: 64 * 1024,
567
577
  request: MAILBOX_PROTOCOL_LIMIT_BYTES,
568
578
  ask: MAILBOX_PROTOCOL_LIMIT_BYTES,
@@ -699,6 +709,41 @@ function validate(value2, kind) {
699
709
  if (Buffer.byteLength(text, "utf8") > LIMITS[kind])
700
710
  throw new Error("Mailbox record is too large");
701
711
  const v = value2;
712
+ if (kind === "bootstrap") {
713
+ const allowed2 = [
714
+ "version",
715
+ "build",
716
+ "runId",
717
+ "ownerSessionId",
718
+ "workspaceId",
719
+ "agentLabel",
720
+ "participants"
721
+ ];
722
+ if (Object.keys(v).some((key) => !allowed2.includes(key)))
723
+ throw new Error("Unknown mailbox field");
724
+ if (!isRuntimeBuild(v.build))
725
+ throw new Error("Invalid runtime build identity");
726
+ for (const field of [
727
+ "runId",
728
+ "ownerSessionId",
729
+ "workspaceId",
730
+ "agentLabel"
731
+ ])
732
+ if (typeof v[field] !== "string" || !v[field].trim())
733
+ throw new Error(`Invalid mailbox field: ${field}`);
734
+ if (!UUID2.test(v.runId)) throw new Error("Invalid mailbox UUID");
735
+ if (!Array.isArray(v.participants) || !v.participants.length)
736
+ throw new Error("Invalid bootstrap participants");
737
+ const ids = /* @__PURE__ */ new Set();
738
+ for (const participant of v.participants) {
739
+ if (!participant || typeof participant !== "object" || Object.keys(participant).some(
740
+ (key) => !["id", "payload"].includes(key)
741
+ ) || typeof participant.id !== "string" || !BOOTSTRAP_ID.test(participant.id) || ids.has(participant.id) || typeof participant.payload !== "string")
742
+ throw new Error("Invalid or duplicate bootstrap participant");
743
+ ids.add(participant.id);
744
+ }
745
+ return;
746
+ }
702
747
  const allowed = kind === "state" ? [
703
748
  "version",
704
749
  "build",
@@ -975,6 +1020,7 @@ function resetAgentMailbox(path) {
975
1020
  for (const name of [
976
1021
  "state.json",
977
1022
  "ask.json",
1023
+ "bootstrap.json",
978
1024
  ...readdirSync3(path).filter(
979
1025
  (x) => /^(request|result)-.*\.json$/.test(x)
980
1026
  )
@@ -986,6 +1032,24 @@ function resetAgentMailbox(path) {
986
1032
  }
987
1033
  }
988
1034
  }
1035
+ function writeAgentBootstrap(path, record) {
1036
+ atomic(file(path, "bootstrap.json"), record, "bootstrap");
1037
+ }
1038
+ function readAgentBootstrap(path) {
1039
+ const value2 = read(
1040
+ file(path, "bootstrap.json"),
1041
+ "bootstrap"
1042
+ );
1043
+ if (value2) validate(value2, "bootstrap");
1044
+ return value2;
1045
+ }
1046
+ function removeAgentBootstrap(path) {
1047
+ try {
1048
+ unlinkSync2(file(path, "bootstrap.json"));
1049
+ } catch (error2) {
1050
+ if (error2.code !== "ENOENT") throw error2;
1051
+ }
1052
+ }
989
1053
  function removeAgentMailbox(path) {
990
1054
  let names2;
991
1055
  try {
@@ -1189,6 +1253,7 @@ function currentTurnIsSoleToolCall(ctx, name) {
1189
1253
  const calls = turn.content.filter((part) => part?.type === "toolCall");
1190
1254
  return calls.length === 1 && calls[0]?.name === name;
1191
1255
  }
1256
+ var MANAGED_AGENT_BOOTSTRAP_EVENT = "pi-herdsman:managed-agent-bootstrap";
1192
1257
  var FILE_HANDOFF_GUIDANCE = "Pass every user-supplied or already-available artifact relevant to the recipient's assignment, decisions, integration, validation, or onward handoff through `files`; do not assume the recipient inherits the sender's conversation or attachments. Preserve exact supplied result refs when forwarding them and omit unrelated evidence. `files` carries evidence, not runtime capability.";
1193
1258
  function failUnknownResultRef(input, operation) {
1194
1259
  fail(
@@ -4350,6 +4415,9 @@ function layoutStatusRows(rows, width, options = {}) {
4350
4415
  }) ?? layouts.at(-1);
4351
4416
  return rows.map((row) => ({
4352
4417
  label: row.label,
4418
+ labelStart: visibleWidth(
4419
+ `${row.tree}${row.spinner} ${padVisible(row.definition, columns.definitionWidth)} `
4420
+ ),
4353
4421
  ...row.paneId ? { paneId: row.paneId } : {},
4354
4422
  ...row.sessionId ? { sessionId: row.sessionId } : {},
4355
4423
  text: renderStatusLine(row, columns, layout, available, options.theme)
@@ -4386,6 +4454,8 @@ function managerPresentationSnapshot(manager) {
4386
4454
  displayName: manager.displayName,
4387
4455
  project: manager.project,
4388
4456
  workspaceId: manager.workspaceId,
4457
+ paneId: manager.paneId,
4458
+ tabId: manager.tabId,
4389
4459
  runtimeState: manager.runtimeState,
4390
4460
  agentCounts: manager.agentCounts,
4391
4461
  leadCounts: manager.leadCounts,
@@ -4574,15 +4644,23 @@ var MANAGER_METADATA_LAYOUTS = [
4574
4644
  { elapsed: false, context: true },
4575
4645
  { elapsed: false, context: false }
4576
4646
  ];
4577
- function renderSupervisionLeads(reports, width, options = {}, selectedLead) {
4647
+ function identifyingLabelVisible(text, label, width, expectedStart) {
4648
+ if (expectedStart !== void 0)
4649
+ return label.length > 0 && expectedStart + visibleWidth(label) <= width;
4650
+ const start = text.lastIndexOf(label);
4651
+ return label.length > 0 && start >= 0 && visibleWidth(text.slice(0, start + label.length)) <= width;
4652
+ }
4653
+ function renderSupervisionRows(reports, width, options = {}, selectedLead) {
4578
4654
  const status = options.status ?? "fresh";
4579
4655
  const role2 = options.role ?? "chief";
4580
4656
  if (status === "unavailable")
4581
4657
  return [
4582
- safeLine(
4583
- `${themed(options.theme, "success", "\u25CF")} ${themed(options.theme, "muted", `${role2} \xB7 unavailable`)}`,
4584
- width
4585
- )
4658
+ {
4659
+ text: safeLine(
4660
+ `${themed(options.theme, "success", "\u25CF")} ${themed(options.theme, "muted", `${role2} \xB7 unavailable`)}`,
4661
+ width
4662
+ )
4663
+ }
4586
4664
  ];
4587
4665
  const displays = orderedSupervisionLeads(presentationReports(reports));
4588
4666
  const groups = groupOrderedSupervisedLeads(displays);
@@ -4630,16 +4708,25 @@ function renderSupervisionLeads(reports, width, options = {}, selectedLead) {
4630
4708
  })
4631
4709
  ) ?? MANAGER_METADATA_LAYOUTS.at(-1);
4632
4710
  return [
4633
- safeLine(header, width),
4711
+ { text: safeLine(header, width) },
4634
4712
  ...visible.map((item, index) => {
4635
4713
  const branch = index === visible.length - 1 && !remaining ? "\u2514\u2500" : "\u251C\u2500";
4636
4714
  const lead = item.lead;
4637
4715
  const navigation = lead && lead.lead === selectedLead ? ">" : "";
4638
4716
  if (item.kind === "lead")
4639
- return safeLine(
4640
- `${themed(options.theme, lifecycleColor(item.lead.runtimeState), `${branch} ${navigation}${supervisionLeadMarker(item.lead)}`)} ${themed(options.theme, "muted", `${item.lead.branch ?? item.lead.displayName} \xB7 Lead`)}`,
4641
- width
4642
- );
4717
+ return {
4718
+ text: safeLine(
4719
+ `${themed(options.theme, lifecycleColor(item.lead.runtimeState), `${branch} ${navigation}${supervisionLeadMarker(item.lead)}`)} ${themed(options.theme, "muted", `${item.lead.branch ?? item.lead.displayName} \xB7 Lead`)}`,
4720
+ width
4721
+ ),
4722
+ ...status === "fresh" ? {
4723
+ label: item.lead.branch ?? item.lead.displayName,
4724
+ intent: {
4725
+ kind: "session",
4726
+ sessionId: item.lead.lead
4727
+ }
4728
+ } : {}
4729
+ };
4643
4730
  const marker = item.work.status === "active" && lead ? `${lead.runtimeState === "blocked" ? "!" : ""}${supervisionLeadMarker(lead)}` : workMarkers[item.work.status];
4644
4731
  const prefix = `${branch} ${navigation}${marker} `;
4645
4732
  const suffix = ` \xB7 ${item.work.status}`;
@@ -4653,48 +4740,64 @@ function renderSupervisionLeads(reports, width, options = {}, selectedLead) {
4653
4740
  );
4654
4741
  const markerColor = item.work.status === "active" && lead ? lifecycleColor(lead.runtimeState) : item.work.status === "active" ? "success" : item.work.status === "conflict" ? "warning" : "muted";
4655
4742
  const markerText = themed(options.theme, markerColor, marker);
4656
- const branchText = themed(
4657
- options.theme,
4658
- "muted",
4659
- safeLine(
4660
- item.work.branch,
4661
- width - visibleWidth(prefix) - visibleWidth(suffix) - telemetryWidth
4662
- )
4663
- );
4664
- return safeLine(
4665
- `${themed(options.theme, "muted", `${branch} ${navigation}`)}${markerText} ${branchText}${themed(options.theme, "muted", suffix)}${telemetry.map((value2) => themed(options.theme, "muted", ` ${value2}`)).join("")}`,
4666
- width
4667
- );
4743
+ const branchWidth = width - visibleWidth(prefix) - visibleWidth(suffix) - telemetryWidth;
4744
+ const displayedBranch = safeLine(item.work.branch, branchWidth);
4745
+ const branchText = themed(options.theme, "muted", displayedBranch);
4746
+ return {
4747
+ text: safeLine(
4748
+ `${themed(options.theme, "muted", `${branch} ${navigation}`)}${markerText} ${branchText}${themed(options.theme, "muted", suffix)}${telemetry.map((value2) => themed(options.theme, "muted", ` ${value2}`)).join("")}`,
4749
+ width
4750
+ ),
4751
+ ...status === "fresh" && item.work.status === "active" && lead && displayedBranch === item.work.branch ? {
4752
+ label: item.work.branch,
4753
+ intent: { kind: "session", sessionId: lead.lead }
4754
+ } : {}
4755
+ };
4668
4756
  }),
4669
- ...remaining ? [safeLine(`\u2514\u2500 \u2026 ${remaining} more \xB7 /manager`, width)] : []
4757
+ ...remaining ? [{ text: safeLine(`\u2514\u2500 \u2026 ${remaining} more \xB7 /manager`, width) }] : []
4670
4758
  ];
4671
4759
  }
4672
4760
  return [
4673
- safeLine(header, width),
4761
+ { text: safeLine(header, width) },
4674
4762
  ...shown.flatMap((lead, index) => {
4675
4763
  const branch = index === shown.length - 1 && hidden === 0 ? "\u2514\u2500" : "\u251C\u2500";
4676
4764
  const marker = supervisionLeadMarker(lead);
4677
4765
  const navigation = lead.lead === selectedLead ? ">" : "";
4678
4766
  const indicators = navigation;
4679
4767
  const counts = lead.leadCounts;
4680
- const row = safeLine(
4681
- `${themed(options.theme, lifecycleColor(lead.runtimeState), `${branch} ${indicators}${marker}`)} ${themed(options.theme, "muted", `${lead.displayName}${counts?.total ? ` \xB7 ${counts.total} lead${counts.total === 1 ? "" : "s"}` : ""}${lead.agentCounts?.total ? ` \xB7 ${lead.agentCounts.total} agent${lead.agentCounts.total === 1 ? "" : "s"}` : ""}`)}`,
4682
- width
4683
- );
4768
+ const intent = status === "fresh" ? { kind: "session", sessionId: lead.lead } : void 0;
4769
+ const row = {
4770
+ text: safeLine(
4771
+ `${themed(options.theme, lifecycleColor(lead.runtimeState), `${branch} ${indicators}${marker}`)} ${themed(options.theme, "muted", `${lead.displayName}${counts?.total ? ` \xB7 ${counts.total} lead${counts.total === 1 ? "" : "s"}` : ""}${lead.agentCounts?.total ? ` \xB7 ${lead.agentCounts.total} agent${lead.agentCounts.total === 1 ? "" : "s"}` : ""}`)}`,
4772
+ width
4773
+ ),
4774
+ ...intent ? { intent } : {},
4775
+ ...intent ? { label: lead.displayName } : {}
4776
+ };
4684
4777
  const children = (lead.leads ?? []).slice(0, 3);
4685
4778
  const childIndent = branch === "\u2514\u2500" ? " " : "\u2502 ";
4686
4779
  return [
4687
4780
  row,
4688
4781
  ...children.map((child, childIndex) => {
4689
4782
  const childBranch = childIndex === children.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
4690
- return safeLine(
4691
- `${themed(options.theme, "muted", `${childIndent}${childBranch} ${child.branch ?? child.display_name} \xB7 `)}${themed(options.theme, lifecycleColor(child.runtime_state), child.runtime_state)}${themed(options.theme, "muted", `${child.agent_counts.total ? ` \xB7 ${child.agent_counts.total} agent${child.agent_counts.total === 1 ? "" : "s"}` : ""}`)}`,
4692
- width
4693
- );
4783
+ return {
4784
+ text: safeLine(
4785
+ `${themed(options.theme, "muted", `${childIndent}${childBranch} ${child.branch ?? child.display_name} \xB7 `)}${themed(options.theme, lifecycleColor(child.runtime_state), child.runtime_state)}${themed(options.theme, "muted", `${child.agent_counts.total ? ` \xB7 ${child.agent_counts.total} agent${child.agent_counts.total === 1 ? "" : "s"}` : ""}`)}`,
4786
+ width
4787
+ ),
4788
+ ...status === "fresh" ? {
4789
+ label: child.branch ?? child.display_name,
4790
+ intent: {
4791
+ kind: "session",
4792
+ sessionId: child.session,
4793
+ parentSessionId: lead.lead
4794
+ }
4795
+ } : {}
4796
+ };
4694
4797
  })
4695
4798
  ];
4696
4799
  }),
4697
- ...hidden > 0 ? [safeLine(`\u2514\u2500 \u2026 ${hidden} more \xB7 /${role2}`, width)] : []
4800
+ ...hidden > 0 ? [{ text: safeLine(`\u2514\u2500 \u2026 ${hidden} more \xB7 /${role2}`, width) }] : []
4698
4801
  ];
4699
4802
  }
4700
4803
  function formatSupervisionNotification(reports, status = "fresh") {
@@ -4855,15 +4958,39 @@ function formatSupervisionContext(snapshot, options) {
4855
4958
  }
4856
4959
  return [...included, closing].join("\n");
4857
4960
  }
4858
- function createSupervisionWidget(getLeads, getStatus, role2 = "chief", theme) {
4961
+ function createSupervisionWidget(getLeads, getStatus, role2 = "chief", theme, onFocus) {
4962
+ let renderedWidth = -1;
4963
+ let hits = [];
4859
4964
  return {
4860
4965
  render(width) {
4861
- return renderSupervisionLeads(getLeads(), width, {
4966
+ renderedWidth = width;
4967
+ const rows = renderSupervisionRows(getLeads(), width, {
4862
4968
  status: getStatus(),
4863
4969
  ordinaryCap: 6,
4864
4970
  role: role2,
4865
4971
  theme
4866
- }).filter((line) => line.length > 0);
4972
+ }).filter(({ text }) => text.length > 0);
4973
+ hits = [];
4974
+ rows.forEach((row, index) => {
4975
+ if (row.intent && row.label && identifyingLabelVisible(row.text, row.label, width))
4976
+ hits.push({
4977
+ row: index,
4978
+ from: 0,
4979
+ to: Math.min(width, visibleWidth(row.text)),
4980
+ intent: row.intent
4981
+ });
4982
+ });
4983
+ return rows.map(({ text }) => text);
4984
+ },
4985
+ handleMouse(event) {
4986
+ if (event.type !== "click" || event.button !== "left" || event.width !== renderedWidth)
4987
+ return;
4988
+ const hit = hits.find(
4989
+ ({ row, from, to }) => row === event.y && event.x >= from && event.x < to
4990
+ );
4991
+ if (!hit || !onFocus) return;
4992
+ onFocus(hit.intent);
4993
+ return { handled: true, render: false };
4867
4994
  },
4868
4995
  invalidate() {
4869
4996
  }
@@ -4936,22 +5063,38 @@ function safeDisplaySegment(value2) {
4936
5063
  return value2.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/gu, "").replace(/\u001b\[[0-?]*[ -/]*[@-~]/gu, "").replace(/[\u0000-\u001f\u007f]/gu, " ").replace(/\s+/gu, " ").trim();
4937
5064
  }
4938
5065
  function renderBreadcrumb(segments, width) {
4939
- if (width <= 0) return "";
4940
- const names2 = segments.map(safeDisplaySegment).filter(Boolean);
4941
- const current = names2.at(-1) ?? "?";
5066
+ if (width <= 0) return { text: "", hits: [] };
5067
+ const visible = segments.map((segment) => ({ ...segment, text: safeDisplaySegment(segment.text) })).filter((segment) => segment.text);
5068
+ const current = visible.at(-1) ?? { text: "?" };
4942
5069
  const marker = "\u25CF";
4943
- if (width <= visibleWidth(marker)) return truncateToWidth(marker, width, "");
5070
+ if (width <= visibleWidth(marker))
5071
+ return { text: truncateToWidth(marker, width, ""), hits: [] };
4944
5072
  const prefix = `${marker} `;
4945
5073
  const available = width - visibleWidth(prefix);
4946
- if (available <= 0) return marker;
4947
- const currentText = tailTruncate(current, available);
4948
- let result = `${prefix}${currentText}`;
4949
- for (let index = names2.length - 2; index >= 0; index--) {
4950
- const candidate = `${prefix}${names2[index]} \u2192 ${result.slice(prefix.length)}`;
4951
- if (visibleWidth(candidate) <= width) result = candidate;
5074
+ if (available <= 0) return { text: marker, hits: [] };
5075
+ const currentText = tailTruncate(current.text, available);
5076
+ const included = [{ ...current, text: currentText }];
5077
+ for (let index = visible.length - 2; index >= 0; index--) {
5078
+ const candidate = [visible[index], ...included];
5079
+ const candidateText = `${prefix}${candidate.map(({ text: text2 }) => text2).join(" \u2192 ")}`;
5080
+ if (visibleWidth(candidateText) <= width) included.unshift(visible[index]);
4952
5081
  else break;
4953
5082
  }
4954
- return result;
5083
+ const text = `${prefix}${included.map(({ text: text2 }) => text2).join(" \u2192 ")}`;
5084
+ const hits = [];
5085
+ let x = visibleWidth(prefix);
5086
+ included.forEach((segment, index) => {
5087
+ const segmentWidth = visibleWidth(segment.text);
5088
+ if (index < included.length - 1 && segment.sessionId)
5089
+ hits.push({
5090
+ row: 0,
5091
+ from: x,
5092
+ to: x + segmentWidth,
5093
+ intent: { kind: "session", sessionId: segment.sessionId }
5094
+ });
5095
+ x += segmentWidth + (index < included.length - 1 ? visibleWidth(" \u2192 ") : 0);
5096
+ });
5097
+ return { text, hits };
4955
5098
  }
4956
5099
  function renderToolMetadata(tools, width) {
4957
5100
  if (!tools?.length || width <= 0) return "";
@@ -6347,8 +6490,12 @@ var StatusWidget = class {
6347
6490
  timer;
6348
6491
  invalidateUI;
6349
6492
  theme;
6350
- constructor(invalidateUI, theme) {
6493
+ onFocus;
6494
+ renderedWidth = -1;
6495
+ hits = [];
6496
+ constructor(invalidateUI, theme, onFocus) {
6351
6497
  this.invalidateUI = invalidateUI;
6498
+ this.onFocus = onFocus;
6352
6499
  this.theme = theme ?? {
6353
6500
  fg: (_color, text) => text,
6354
6501
  bold: (text) => text
@@ -6356,6 +6503,8 @@ var StatusWidget = class {
6356
6503
  }
6357
6504
  setSnapshot(snapshot) {
6358
6505
  this.snapshot = snapshot;
6506
+ this.hits = [];
6507
+ this.renderedWidth = -1;
6359
6508
  const animated = snapshot.agents.some(
6360
6509
  (agent) => agent.state === "working" || agent.state === "settling" || agent.state === "starting"
6361
6510
  );
@@ -6375,20 +6524,36 @@ var StatusWidget = class {
6375
6524
  }
6376
6525
  render(width) {
6377
6526
  const s = this.snapshot;
6527
+ this.renderedWidth = width;
6528
+ this.hits = [];
6378
6529
  const suffix = s.unavailable ? "unavailable" : `${formatStatusCounts(s.agents)}${s.stale ? " \xB7 stale" : ""}`;
6379
6530
  const availableWidth = Math.max(0, width);
6380
6531
  const execution = s.execution ? ` \xB7 ${s.execution.kind === "managed" ? "managed" : s.execution.mode}` : "";
6381
6532
  const elapsed = formatElapsed(s.herdRunStartedAt, Date.now());
6382
6533
  let run = !s.identityOnly && elapsed ? ` \xB7 ${elapsed}` : "";
6383
6534
  const suffixText = s.identityOnly || !suffix ? "" : ` ${suffix}`;
6384
- const breadcrumbSegments = s.breadcrumb ?? ["?"];
6385
- const breadcrumb = renderBreadcrumb(breadcrumbSegments, availableWidth);
6535
+ const breadcrumbSegments = s.breadcrumb ?? [{ text: "?" }];
6536
+ const renderedBreadcrumb = renderBreadcrumb(
6537
+ breadcrumbSegments,
6538
+ availableWidth
6539
+ );
6540
+ const breadcrumb = renderedBreadcrumb.text;
6541
+ if (!s.stale && !s.unavailable) this.hits.push(...renderedBreadcrumb.hits);
6386
6542
  const remainingWidth = Math.max(
6387
6543
  0,
6388
6544
  availableWidth - visibleWidth(breadcrumb)
6389
6545
  );
6390
6546
  const executionFits = !!execution && visibleWidth(execution) + visibleWidth(suffixText) <= remainingWidth;
6391
6547
  const shownExecution = executionFits ? execution : "";
6548
+ if (!s.stale && !s.unavailable && executionFits && s.execution?.kind === "managed") {
6549
+ const from = visibleWidth(breadcrumb) + visibleWidth(" \xB7 ");
6550
+ this.hits.push({
6551
+ row: 0,
6552
+ from,
6553
+ to: from + visibleWidth("managed"),
6554
+ intent: { kind: "assigned-manager" }
6555
+ });
6556
+ }
6392
6557
  const tools = renderToolMetadata(
6393
6558
  s.ownTools,
6394
6559
  Math.max(
@@ -6406,24 +6571,55 @@ var StatusWidget = class {
6406
6571
  const header = `${styledBreadcrumb}${styledExecution}${styledTools}${styledRun}${styledSuffix}`;
6407
6572
  const out = [truncateToWidth(header, availableWidth, "\u2026")];
6408
6573
  if (s.identityOnly) return out;
6409
- out.push(
6410
- ...renderStatusRows(s.agents, {
6411
- now: Date.now(),
6412
- frame: this.frame,
6413
- width: Math.max(0, width),
6414
- theme: this.theme
6415
- }).map(({ text }) => text)
6416
- );
6574
+ const rows = renderStatusRows(s.agents, {
6575
+ now: Date.now(),
6576
+ frame: this.frame,
6577
+ width: Math.max(0, width),
6578
+ theme: this.theme
6579
+ });
6580
+ out.push(...rows.map(({ text }) => text));
6581
+ if (!s.stale && !s.unavailable)
6582
+ rows.forEach((row, index) => {
6583
+ const agent = s.agents.find(
6584
+ ({ label, sessionId }) => label === row.label && sessionId === row.sessionId
6585
+ );
6586
+ if (!row.sessionId || !identifyingLabelVisible(
6587
+ row.text,
6588
+ row.label,
6589
+ availableWidth,
6590
+ row.labelStart
6591
+ ) || s.agents.filter(({ sessionId }) => sessionId === row.sessionId).length !== 1 || !agent || agent.state === "unknown" || agent.state === "lost" || agent.state === "starting")
6592
+ return;
6593
+ this.hits.push({
6594
+ row: index + 1,
6595
+ from: 0,
6596
+ to: Math.min(availableWidth, visibleWidth(row.text)),
6597
+ intent: { kind: "session", sessionId: row.sessionId }
6598
+ });
6599
+ });
6417
6600
  return out;
6418
6601
  }
6602
+ handleMouse(event) {
6603
+ if (event.type !== "click" || event.button !== "left" || event.width !== this.renderedWidth)
6604
+ return;
6605
+ const hit = this.hits.find(
6606
+ ({ row, from, to }) => row === event.y && event.x >= from && event.x < to
6607
+ );
6608
+ if (!hit || !this.onFocus) return;
6609
+ this.onFocus(hit.intent);
6610
+ return { handled: true, render: false };
6611
+ }
6419
6612
  dispose() {
6420
6613
  if (this.timer) clearInterval(this.timer);
6421
6614
  this.timer = void 0;
6422
6615
  this.invalidateUI = void 0;
6616
+ this.onFocus = void 0;
6617
+ this.hits = [];
6618
+ this.renderedWidth = -1;
6423
6619
  }
6424
6620
  };
6425
- function createStatusWidget(invalidate, theme) {
6426
- return new StatusWidget(invalidate, theme);
6621
+ function createStatusWidget(invalidate, theme, onFocus) {
6622
+ return new StatusWidget(invalidate, theme, onFocus);
6427
6623
  }
6428
6624
 
6429
6625
  // extension/config.ts
@@ -7051,8 +7247,6 @@ async function drainCoordinationInbox(options) {
7051
7247
  }
7052
7248
  continue;
7053
7249
  }
7054
- const deliverAs = options.deliveryMode ? options.deliveryMode(record) : "followUp";
7055
- if (!deliverAs) continue;
7056
7250
  let token;
7057
7251
  const clearTransaction = async () => {
7058
7252
  if (token === void 0) return;
@@ -7068,6 +7262,12 @@ async function drainCoordinationInbox(options) {
7068
7262
  await clearTransaction();
7069
7263
  continue;
7070
7264
  }
7265
+ const deliverAs = options.deliveryMode ? options.deliveryMode(record) : "followUp";
7266
+ const wasAlreadyDelivered = options.isDelivered(record.id);
7267
+ if (!deliverAs && !wasAlreadyDelivered) {
7268
+ await clearTransaction();
7269
+ continue;
7270
+ }
7071
7271
  let authorized;
7072
7272
  try {
7073
7273
  authorized = await options.isAuthorized(record);
@@ -7115,29 +7315,35 @@ async function drainCoordinationInbox(options) {
7115
7315
  options.sessionId,
7116
7316
  record.id
7117
7317
  );
7118
- await options.sendMessage(
7119
- {
7120
- customType: `pi-herdsman-${record.kind}`,
7121
- content: deliveredMessageContent(record),
7122
- display: true,
7123
- details: {
7124
- id: record.id,
7125
- leaseId: record.leaseId,
7126
- fromSessionId: record.fromSessionId,
7127
- toSessionId: record.toSessionId,
7128
- leadSessionId: record.leadSessionId,
7129
- ...record.branch ? { branch: record.branch } : {}
7130
- }
7131
- },
7132
- { deliverAs, triggerTurn: true },
7133
- record
7134
- );
7135
- await options.transaction?.revalidate(token, "after-send");
7318
+ if (!options.isDelivered(record.id)) {
7319
+ await options.sendMessage(
7320
+ {
7321
+ customType: `pi-herdsman-${record.kind}`,
7322
+ content: deliveredMessageContent(record),
7323
+ display: true,
7324
+ details: {
7325
+ id: record.id,
7326
+ leaseId: record.leaseId,
7327
+ fromSessionId: record.fromSessionId,
7328
+ toSessionId: record.toSessionId,
7329
+ leadSessionId: record.leadSessionId,
7330
+ ...record.branch ? { branch: record.branch } : {}
7331
+ }
7332
+ },
7333
+ { deliverAs: deliverAs ?? "followUp", triggerTurn: true },
7334
+ record
7335
+ );
7336
+ await options.transaction?.revalidate(token, "after-send");
7337
+ }
7136
7338
  } catch {
7137
7339
  await clearTransaction();
7138
7340
  continue;
7139
7341
  }
7140
- delivered += 1;
7342
+ if (!options.isDelivered(record.id)) {
7343
+ await clearTransaction();
7344
+ continue;
7345
+ }
7346
+ if (!wasAlreadyDelivered) delivered += 1;
7141
7347
  try {
7142
7348
  await options.transaction?.revalidate(token, "before-accepted");
7143
7349
  assertChiefMessageNotQuarantined(
@@ -7153,6 +7359,7 @@ async function drainCoordinationInbox(options) {
7153
7359
  }
7154
7360
  } else {
7155
7361
  try {
7362
+ if (!wasAlreadyDelivered) delivered += 1;
7156
7363
  await options.transaction?.revalidate(token, "already-delivered");
7157
7364
  assertChiefMessageNotQuarantined(
7158
7365
  options.runtime,
@@ -8268,7 +8475,7 @@ function buildAgentStatusSnapshot(view, ctx, host) {
8268
8475
  stale: false,
8269
8476
  unavailable: false,
8270
8477
  ...herdStartedAt !== void 0 ? { herdRunStartedAt: herdStartedAt } : {},
8271
- breadcrumb: host.scope.kind === "lead" ? ["lead"] : statusBreadcrumb(
8478
+ breadcrumb: host.scope.kind === "lead" ? [{ text: "lead" }] : statusBreadcrumb(
8272
8479
  view,
8273
8480
  host.environmentIdentity(ctx),
8274
8481
  host.identityFromEnvironment(),
@@ -8289,6 +8496,7 @@ function createAgentStatusRuntime() {
8289
8496
  let statusInFlight = false;
8290
8497
  let requestActive = false;
8291
8498
  let statusPrimed = false;
8499
+ let focusInFlight = false;
8292
8500
  let lastValidStatus = {
8293
8501
  agents: [],
8294
8502
  stale: false,
@@ -8403,7 +8611,28 @@ function createAgentStatusRuntime() {
8403
8611
  clearTimer();
8404
8612
  statusContext = ctx;
8405
8613
  ctx.ui.setWidget("pi-herdsman", (tui, theme) => {
8406
- const widget = createStatusWidget(() => tui.requestRender(), theme);
8614
+ let widget;
8615
+ widget = createStatusWidget(
8616
+ () => tui.requestRender(),
8617
+ theme,
8618
+ (intent) => {
8619
+ if (generation !== statusGeneration || ctx !== statusContext || statusWidgetGeneration !== generation || statusWidget !== widget)
8620
+ return;
8621
+ const focus = options?.focusTarget;
8622
+ if (!focus || focusInFlight) return;
8623
+ const isCurrent = () => generation === statusGeneration && ctx === statusContext && statusWidgetGeneration === generation && statusWidget === widget;
8624
+ focusInFlight = true;
8625
+ void focus(ctx, intent, isCurrent).catch((error2) => {
8626
+ if (generation === statusGeneration && ctx === statusContext)
8627
+ ctx.ui.notify(
8628
+ String(error2).replace(/^Error: /u, ""),
8629
+ "warning"
8630
+ );
8631
+ }).finally(() => {
8632
+ if (generation === statusGeneration) focusInFlight = false;
8633
+ });
8634
+ }
8635
+ );
8407
8636
  widget.setSnapshot(widgetSnapshot(lastValidStatus));
8408
8637
  if (generation === statusGeneration && ctx === statusContext) {
8409
8638
  statusWidget = widget;
@@ -8423,12 +8652,14 @@ function createAgentStatusRuntime() {
8423
8652
  statusWidget?.dispose();
8424
8653
  statusWidget = void 0;
8425
8654
  requestActive = false;
8655
+ focusInFlight = false;
8426
8656
  statusPrimed = false;
8427
8657
  },
8428
8658
  prepareSession: (ctx, beforeActivate) => {
8429
8659
  clearTimer();
8430
8660
  statusRefresh = false;
8431
8661
  statusInFlight = false;
8662
+ focusInFlight = false;
8432
8663
  removeWidget();
8433
8664
  statusWidgetGeneration = 0;
8434
8665
  statusContext = void 0;
@@ -8451,6 +8682,7 @@ function createAgentStatusRuntime() {
8451
8682
  clearTimer();
8452
8683
  statusRefresh = false;
8453
8684
  statusInFlight = false;
8685
+ focusInFlight = false;
8454
8686
  removeWidget();
8455
8687
  statusWidgetGeneration = 0;
8456
8688
  statusContext = void 0;
@@ -8796,39 +9028,50 @@ function visibleAgentSnapshots(snapshot, scope, ownerSessionId) {
8796
9028
  return visible;
8797
9029
  }
8798
9030
  function statusBreadcrumb(snapshot, candidate, identity, sameIdentity) {
8799
- if (!candidate) return ["?"];
8800
- const current = snapshot.agents.find(
9031
+ if (!candidate) return [{ text: "?" }];
9032
+ const matches = snapshot.agents.filter(
8801
9033
  ({ state }) => sameIdentity(state, candidate)
8802
9034
  );
9035
+ const current = matches.length === 1 ? matches[0] : void 0;
8803
9036
  if (!current)
8804
9037
  return [
8805
- "?",
8806
- identity.definition && identity.label ? displayIdentity(identity.definition, identity.label) : identity.definition ?? "?"
9038
+ { text: "?" },
9039
+ {
9040
+ text: identity.definition && identity.label ? displayIdentity(identity.definition, identity.label) : identity.definition ?? "?"
9041
+ }
8807
9042
  ];
8808
9043
  const definitions = [
8809
- displayIdentity(current.agentDefinition, current.state.agentLabel)
9044
+ {
9045
+ text: displayIdentity(current.agentDefinition, current.state.agentLabel),
9046
+ sessionId: current.state.piSessionId
9047
+ }
8810
9048
  ];
8811
9049
  const visited = /* @__PURE__ */ new Set([current.state.piSessionId]);
8812
9050
  let ownerSessionId = current.state.ownerSessionId;
8813
9051
  while (true) {
8814
- const parent = snapshot.agents.find(
9052
+ const parents = snapshot.agents.filter(
8815
9053
  ({ state }) => state.piSessionId === ownerSessionId
8816
9054
  );
9055
+ if (parents.length > 1) return [{ text: "?" }, ...definitions.reverse()];
9056
+ const parent = parents[0];
8817
9057
  if (parent) {
8818
9058
  if (visited.has(parent.state.piSessionId))
8819
- return ["?", ...definitions.reverse()];
9059
+ return [{ text: "?" }, ...definitions.reverse()];
8820
9060
  visited.add(parent.state.piSessionId);
8821
- definitions.push(
8822
- displayIdentity(parent.agentDefinition, parent.state.agentLabel)
8823
- );
9061
+ definitions.push({
9062
+ text: displayIdentity(parent.agentDefinition, parent.state.agentLabel),
9063
+ sessionId: parent.state.piSessionId
9064
+ });
8824
9065
  ownerSessionId = parent.state.ownerSessionId;
8825
9066
  continue;
8826
9067
  }
8827
9068
  if (snapshot.mailboxes.some(
8828
9069
  ({ state }) => state.piSessionId === ownerSessionId
8829
9070
  ))
8830
- return ["?", ...definitions.reverse()];
8831
- return snapshot.leadSessionIds.includes(ownerSessionId) ? ["lead", ...definitions.reverse()] : ["?", ...definitions.reverse()];
9071
+ return [{ text: "?" }, ...definitions.reverse()];
9072
+ return snapshot.leadSessionIds.filter(
9073
+ (sessionId) => sessionId === ownerSessionId
9074
+ ).length === 1 ? [{ text: "lead", sessionId: ownerSessionId }, ...definitions.reverse()] : [{ text: "?" }, ...definitions.reverse()];
8832
9075
  }
8833
9076
  }
8834
9077
  function sessionAgentIdentity(entries, sessionId) {
@@ -12144,6 +12387,7 @@ function createAgentController(pi, options) {
12144
12387
  let promptWriteFailed = false;
12145
12388
  let started;
12146
12389
  let accepted = false;
12390
+ const preparedBootstrap = [];
12147
12391
  try {
12148
12392
  try {
12149
12393
  promptPaths = writePrivatePromptSnapshots([
@@ -12171,6 +12415,69 @@ function createAgentController(pi, options) {
12171
12415
  );
12172
12416
  ownerDisplay = displayIdentity(definition2, parentLabel);
12173
12417
  }
12418
+ if (p.action === "delegate") {
12419
+ const registrations = /* @__PURE__ */ new Map();
12420
+ let open = true;
12421
+ let registrationError;
12422
+ pi.events.emit(MANAGED_AGENT_BOOTSTRAP_EVENT, {
12423
+ protocol: 1,
12424
+ phase: "prepare",
12425
+ context: ctx,
12426
+ agent: label,
12427
+ register(id, prepare) {
12428
+ if (!open)
12429
+ throw new Error(
12430
+ "Managed-agent bootstrap registration is closed"
12431
+ );
12432
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(id)) {
12433
+ registrationError ??= new Error(
12434
+ "Invalid managed-agent bootstrap participant ID"
12435
+ );
12436
+ return;
12437
+ }
12438
+ if (registrations.has(id)) {
12439
+ registrationError ??= new Error(
12440
+ `Duplicate managed-agent bootstrap participant ID: ${id}`
12441
+ );
12442
+ return;
12443
+ }
12444
+ if (typeof prepare !== "function") {
12445
+ registrationError ??= new Error(
12446
+ "Invalid managed-agent bootstrap prepare callback"
12447
+ );
12448
+ return;
12449
+ }
12450
+ registrations.set(id, prepare);
12451
+ }
12452
+ });
12453
+ open = false;
12454
+ if (registrationError) throw registrationError;
12455
+ const participants = [];
12456
+ for (const [id, prepare] of registrations) {
12457
+ const result = await prepare();
12458
+ if (result !== void 0) {
12459
+ if (!result || typeof result.payload !== "string" || result.commit !== void 0 && typeof result.commit !== "function")
12460
+ throw new Error(
12461
+ `Invalid preparation from managed-agent bootstrap participant "${id}"`
12462
+ );
12463
+ participants.push({ id, payload: result.payload });
12464
+ preparedBootstrap.push({
12465
+ id,
12466
+ ...result.commit ? { commit: result.commit } : {}
12467
+ });
12468
+ }
12469
+ }
12470
+ if (participants.length)
12471
+ writeAgentBootstrap(mailbox, {
12472
+ version: 5,
12473
+ build: options.build,
12474
+ runId,
12475
+ ownerSessionId: owner,
12476
+ workspaceId,
12477
+ agentLabel: label,
12478
+ participants
12479
+ });
12480
+ }
12174
12481
  const env = [
12175
12482
  `PI_HERDSMAN_MAILBOX=${mailbox}`,
12176
12483
  `PI_HERDSMAN_RUN_ID=${runId}`,
@@ -12360,6 +12667,8 @@ ${result.stderr ?? ""}`.trim(),
12360
12667
  signal,
12361
12668
  waitForSession: true
12362
12669
  });
12670
+ for (const participant of preparedBootstrap)
12671
+ await participant.commit?.();
12363
12672
  runtimes.set(label, runtime);
12364
12673
  options.onChanged();
12365
12674
  const requestId = await submit(
@@ -14223,6 +14532,7 @@ function createManagedAgentLeafStatus(options) {
14223
14532
  let context;
14224
14533
  let generation = 0;
14225
14534
  let inFlight = false;
14535
+ let focusInFlight = false;
14226
14536
  let breadcrumb;
14227
14537
  let ownTools;
14228
14538
  const ownToolsSnapshot = () => ownTools ? { ownTools } : {};
@@ -14237,6 +14547,7 @@ function createManagedAgentLeafStatus(options) {
14237
14547
  }
14238
14548
  context = void 0;
14239
14549
  inFlight = false;
14550
+ focusInFlight = false;
14240
14551
  };
14241
14552
  const refresh = async (ctx, activeGeneration) => {
14242
14553
  if (activeGeneration !== generation || ctx !== context || inFlight) return;
@@ -14269,11 +14580,13 @@ function createManagedAgentLeafStatus(options) {
14269
14580
  stale: false,
14270
14581
  unavailable: true,
14271
14582
  breadcrumb: breadcrumb ?? [
14272
- "?",
14273
- process.env.PI_HERDSMAN_AGENT_DEFINITION && process.env.PI_HERDSMAN_LABEL ? displayIdentity(
14274
- process.env.PI_HERDSMAN_AGENT_DEFINITION,
14275
- process.env.PI_HERDSMAN_LABEL
14276
- ) : process.env.PI_HERDSMAN_AGENT_DEFINITION ?? "?"
14583
+ { text: "?" },
14584
+ {
14585
+ text: process.env.PI_HERDSMAN_AGENT_DEFINITION && process.env.PI_HERDSMAN_LABEL ? displayIdentity(
14586
+ process.env.PI_HERDSMAN_AGENT_DEFINITION,
14587
+ process.env.PI_HERDSMAN_LABEL
14588
+ ) : process.env.PI_HERDSMAN_AGENT_DEFINITION ?? "?"
14589
+ }
14277
14590
  ],
14278
14591
  ...ownToolsSnapshot(),
14279
14592
  identityOnly: true
@@ -14291,17 +14604,39 @@ function createManagedAgentLeafStatus(options) {
14291
14604
  const activeGeneration = generation;
14292
14605
  context = ctx;
14293
14606
  ctx.ui.setWidget("pi-herdsman", (tui, theme) => {
14294
- const next = createStatusWidget(() => tui.requestRender(), theme);
14607
+ let next;
14608
+ next = createStatusWidget(
14609
+ () => tui.requestRender(),
14610
+ theme,
14611
+ (intent) => {
14612
+ if (activeGeneration !== generation || ctx !== context || widget !== next || focusInFlight)
14613
+ return;
14614
+ if (!options.focusTarget) return;
14615
+ focusInFlight = true;
14616
+ const isCurrent = () => activeGeneration === generation && ctx === context && widget === next;
14617
+ void options.focusTarget(ctx, intent, isCurrent).catch((error2) => {
14618
+ if (activeGeneration === generation && ctx === context)
14619
+ ctx.ui.notify(
14620
+ String(error2).replace(/^Error: /u, ""),
14621
+ "warning"
14622
+ );
14623
+ }).finally(() => {
14624
+ if (activeGeneration === generation) focusInFlight = false;
14625
+ });
14626
+ }
14627
+ );
14295
14628
  next.setSnapshot({
14296
14629
  agents: [],
14297
14630
  stale: false,
14298
14631
  unavailable: true,
14299
14632
  breadcrumb: [
14300
- "?",
14301
- process.env.PI_HERDSMAN_AGENT_DEFINITION && process.env.PI_HERDSMAN_LABEL ? displayIdentity(
14302
- process.env.PI_HERDSMAN_AGENT_DEFINITION,
14303
- process.env.PI_HERDSMAN_LABEL
14304
- ) : process.env.PI_HERDSMAN_AGENT_DEFINITION ?? "?"
14633
+ { text: "?" },
14634
+ {
14635
+ text: process.env.PI_HERDSMAN_AGENT_DEFINITION && process.env.PI_HERDSMAN_LABEL ? displayIdentity(
14636
+ process.env.PI_HERDSMAN_AGENT_DEFINITION,
14637
+ process.env.PI_HERDSMAN_LABEL
14638
+ ) : process.env.PI_HERDSMAN_AGENT_DEFINITION ?? "?"
14639
+ }
14305
14640
  ],
14306
14641
  ...ownToolsSnapshot(),
14307
14642
  identityOnly: true
@@ -15000,6 +15335,72 @@ function registerManagedAgentRuntime(pi, options) {
15000
15335
  clearPendingStart: (label, expected) => controller.clearPendingStart(label, expected),
15001
15336
  runtimeForLabel: (label) => controller.runtimeForLabel(label),
15002
15337
  ownToolsSnapshot: () => ({}),
15338
+ focusTarget: async (ctx, intent, isCurrent) => {
15339
+ if (intent.kind !== "session")
15340
+ throw new Error("Manager navigation is unavailable from an Agent.");
15341
+ const signal = controller.sessionSignal();
15342
+ if (!signal) throw new Error("Agent session is no longer active.");
15343
+ const ownerSessionId = ctx.sessionManager.getSessionId();
15344
+ const view = await controller.agentSnapshotView(
15345
+ ctx,
15346
+ scope,
15347
+ signal,
15348
+ true,
15349
+ false
15350
+ );
15351
+ const fresh = buildAgentStatusSnapshot(view, ctx, {
15352
+ scope,
15353
+ listedAgentRecord: controller.listedAgentRecord,
15354
+ runtimeForLabel: controller.runtimeForLabel,
15355
+ environmentIdentity: (context) => managedAgentEnvironmentIdentity(context, options.build),
15356
+ identityFromEnvironment: () => ({
15357
+ definition: process.env.PI_HERDSMAN_AGENT_DEFINITION,
15358
+ label: process.env.PI_HERDSMAN_LABEL
15359
+ }),
15360
+ sameIdentity: sameManagedAgentIdentity,
15361
+ ownToolsSnapshot: () => ({})
15362
+ });
15363
+ const currentIdentity = managedAgentEnvironmentIdentity(
15364
+ ctx,
15365
+ options.build
15366
+ );
15367
+ const breadcrumb = currentIdentity ? statusBreadcrumb(
15368
+ view,
15369
+ currentIdentity,
15370
+ {
15371
+ definition: process.env.PI_HERDSMAN_AGENT_DEFINITION,
15372
+ label: process.env.PI_HERDSMAN_LABEL
15373
+ },
15374
+ sameManagedAgentIdentity
15375
+ ) : [];
15376
+ const statusTarget = fresh.agents.filter(
15377
+ (agent) => agent.sessionId === intent.sessionId
15378
+ );
15379
+ const isAncestor = breadcrumb.some(
15380
+ (segment) => segment.sessionId === intent.sessionId
15381
+ );
15382
+ if (!isCurrent() || signal.aborted || ctx.sessionManager.getSessionId() !== ownerSessionId || statusTarget.length !== 1 && !isAncestor)
15383
+ throw new Error("Agent changed; reopen status.");
15384
+ if (statusTarget.length === 1 && (statusTarget[0].state === "unknown" || statusTarget[0].state === "lost" || statusTarget[0].state === "starting" || !statusTarget[0].paneId))
15385
+ throw new Error("Agent changed; reopen status.");
15386
+ const { agents } = await listAllHerdrAgents(pi, ctx, signal);
15387
+ const live = agents.filter(
15388
+ (agent) => herdrSessionId(agent) === intent.sessionId && typeof agent.pane_id === "string"
15389
+ );
15390
+ if (signal.aborted || !isCurrent() || live.length !== 1 || statusTarget.length === 1 && live[0].pane_id !== statusTarget[0].paneId)
15391
+ throw new Error("Agent changed; reopen status.");
15392
+ const current = await runHerdr(
15393
+ pi,
15394
+ ctx,
15395
+ ["agent", "get", live[0].pane_id],
15396
+ { signal }
15397
+ );
15398
+ if (signal.aborted || !isCurrent() || herdrSessionId(current?.agent) !== intent.sessionId || current?.agent?.pane_id !== live[0].pane_id || current?.agent?.workspace_id !== live[0].workspace_id || current?.agent?.tab_id !== live[0].tab_id)
15399
+ throw new Error("Agent changed; reopen status.");
15400
+ await runHerdr(pi, ctx, ["agent", "focus", current.agent.pane_id], {
15401
+ signal
15402
+ });
15403
+ },
15003
15404
  loadSnapshot: async (ctx) => {
15004
15405
  const view = await controller.agentSnapshotView(
15005
15406
  ctx,
@@ -15056,7 +15457,52 @@ This is the session-start definition snapshot. Use list_agents for live Agent st
15056
15457
  ),
15057
15458
  identity: (ctx) => managedAgentEnvironmentIdentity(ctx, options.build),
15058
15459
  sameIdentity: sameManagedAgentIdentity,
15059
- signal: () => metadataPublisher.signal
15460
+ signal: () => metadataPublisher.signal,
15461
+ focusTarget: async (ctx, intent, isCurrent) => {
15462
+ if (intent.kind !== "session")
15463
+ throw new Error("Manager navigation is unavailable from an Agent.");
15464
+ const signal = metadataPublisher.signal;
15465
+ const sessionId = ctx.sessionManager.getSessionId();
15466
+ const snapshot = await managedAgentSnapshots(
15467
+ pi,
15468
+ ctx,
15469
+ options.controllerOptions.snapshotDependencies,
15470
+ signal,
15471
+ true,
15472
+ false,
15473
+ void 0,
15474
+ false
15475
+ );
15476
+ const identity = managedAgentEnvironmentIdentity(ctx, options.build);
15477
+ const breadcrumb = identity ? statusBreadcrumb(
15478
+ snapshot,
15479
+ identity,
15480
+ {
15481
+ definition: process.env.PI_HERDSMAN_AGENT_DEFINITION,
15482
+ label: process.env.PI_HERDSMAN_LABEL
15483
+ },
15484
+ sameManagedAgentIdentity
15485
+ ) : [];
15486
+ if (signal?.aborted || !isCurrent() || ctx.sessionManager.getSessionId() !== sessionId || !breadcrumb.some((segment) => segment.sessionId === intent.sessionId))
15487
+ throw new Error("Agent ancestry changed; reopen status.");
15488
+ const { agents } = await listAllHerdrAgents(pi, ctx, signal);
15489
+ const matches = agents.filter(
15490
+ (agent) => herdrSessionId(agent) === intent.sessionId && typeof agent.pane_id === "string"
15491
+ );
15492
+ if (signal?.aborted || !isCurrent() || matches.length !== 1)
15493
+ throw new Error("Agent changed; reopen status.");
15494
+ const current = await runHerdr(
15495
+ pi,
15496
+ ctx,
15497
+ ["agent", "get", matches[0].pane_id],
15498
+ { signal }
15499
+ );
15500
+ if (signal?.aborted || !isCurrent() || herdrSessionId(current?.agent) !== intent.sessionId || current?.agent?.pane_id !== matches[0].pane_id || current?.agent?.workspace_id !== matches[0].workspace_id || current?.agent?.tab_id !== matches[0].tab_id)
15501
+ throw new Error("Agent changed; reopen status.");
15502
+ await runHerdr(pi, ctx, ["agent", "focus", current.agent.pane_id], {
15503
+ signal
15504
+ });
15505
+ }
15060
15506
  });
15061
15507
  const mutateAgentState = (update) => {
15062
15508
  const state = execution.assignment;
@@ -15644,8 +16090,53 @@ function registerManagedAgentSessionStartHandler(pi, execution, options) {
15644
16090
  }
15645
16091
  }
15646
16092
  }
15647
- if (!existing)
16093
+ if (!existing) {
15648
16094
  validateManagedAgentDefinition(await options.getAgentDefinitions(ctx));
16095
+ const bootstrap = readAgentBootstrap(mailbox);
16096
+ if (bootstrap) {
16097
+ if (bootstrap.runId !== candidate.runId || bootstrap.ownerSessionId !== candidate.ownerSessionId || bootstrap.workspaceId !== candidate.workspaceId || bootstrap.agentLabel !== candidate.agentLabel)
16098
+ throw new Error("Managed-agent bootstrap identity mismatch");
16099
+ if (!sameRuntimeBuild(options.build, bootstrap.build))
16100
+ throw new Error("Managed-agent bootstrap Herdsman build mismatch");
16101
+ for (const participant of bootstrap.participants) {
16102
+ let initializer;
16103
+ let acceptCount = 0;
16104
+ let invalidInitializer = false;
16105
+ pi.events.emit(MANAGED_AGENT_BOOTSTRAP_EVENT, {
16106
+ protocol: 1,
16107
+ phase: "initialize",
16108
+ context: ctx,
16109
+ agent: candidate.agentLabel,
16110
+ participant: { id: participant.id, payload: participant.payload },
16111
+ accept(callback) {
16112
+ acceptCount++;
16113
+ if (typeof callback !== "function") {
16114
+ invalidInitializer = true;
16115
+ return;
16116
+ }
16117
+ if (acceptCount === 1) initializer = callback;
16118
+ }
16119
+ });
16120
+ if (acceptCount === 0 || invalidInitializer)
16121
+ throw new Error(
16122
+ `Managed-agent bootstrap participant "${participant.id}" is unavailable or invalid`
16123
+ );
16124
+ if (acceptCount > 1)
16125
+ throw new Error(
16126
+ `Managed-agent bootstrap participant "${participant.id}" has multiple initializers`
16127
+ );
16128
+ try {
16129
+ await initializer();
16130
+ } catch (error2) {
16131
+ throw new Error(
16132
+ `Managed-agent bootstrap participant "${participant.id}" failed: ${String(error2)}`,
16133
+ { cause: error2 }
16134
+ );
16135
+ }
16136
+ }
16137
+ removeAgentBootstrap(mailbox);
16138
+ }
16139
+ }
15649
16140
  const release = options.claimAssignmentLock(mailbox, "session_start", {
15650
16141
  label: execution.assignment.agentLabel,
15651
16142
  paneId: execution.assignment.paneId
@@ -16020,6 +16511,12 @@ function createLeadSessionStartRuntime(host) {
16020
16511
  }
16021
16512
  }
16022
16513
  if (sessionSignal.aborted) return;
16514
+ if (host.roleTransitions.activeManager())
16515
+ await host.publishLeadRole(
16516
+ ctx,
16517
+ "inactive",
16518
+ host.roleTransitions.currentRoleGeneration()
16519
+ );
16023
16520
  if (needsLeadRoster) {
16024
16521
  try {
16025
16522
  const definitions = await host.visibleAgentDefinitionMetadata(
@@ -16090,6 +16587,7 @@ This is the session-start definition snapshot. Use list_agents for live Agent st
16090
16587
  host.herdRun.agentStarted();
16091
16588
  },
16092
16589
  async agentSettled(ctx, aborted) {
16590
+ host.leadInboxRuntime.agentSettled();
16093
16591
  host.controller.settleAsks(ctx, host.controller.sessionSignal());
16094
16592
  host.herdRun.agentSettled(ctx, aborted);
16095
16593
  await host.controller.settleResults(ctx, host.controller.sessionSignal()).catch(() => {
@@ -16969,41 +17467,67 @@ function registerLeadRuntime(pi, options) {
16969
17467
  };
16970
17468
  const roleHost = options.roleTransitionHost;
16971
17469
  let leadMetadataQueue = Promise.resolve();
16972
- const publishLeadRole = (ctx, mode, generation) => {
16973
- const paneId = roleHost.paneId();
16974
- if (!paneId) return Promise.resolve();
16975
- const activeRole = () => activeLeadRole(leadRuntime);
16976
- const leadName = mode === "inactive" && activeRole() === "lead" ? roleHost.getSessionName()?.trim() : void 0;
16977
- const args = [
16978
- "pane",
16979
- "report-metadata",
16980
- paneId,
16981
- "--source",
16982
- "pi-herdsman:lead",
16983
- "--title",
16984
- mode === "active" ? "chief" : activeRole() === "manager" ? "Pi Herdsman manager" : leadName || "Pi Herdsman lead",
16985
- ...mode === "active" ? ["--token", "pi_herdsman_role=chief"] : mode === "inactive" ? ["--token", `pi_herdsman_role=${activeRole()}`] : ["--clear-token", "pi_herdsman_role"]
16986
- ];
16987
- if (mode === "inactive" && activeRole() === "lead")
16988
- args.push(
16989
- leadName ? "--token" : "--clear-token",
16990
- leadName ? `pi_herdsman_name=${leadName}` : "pi_herdsman_name"
16991
- );
16992
- if (mode !== "inactive" || activeRole() !== "lead")
16993
- args.push(
16994
- "--clear-token",
16995
- "pi_herdsman_herd_run_started_at",
16996
- "--clear-token",
16997
- "pi_herdsman_context_percent"
16998
- );
17470
+ let reportedRoleMetadataFailure;
17471
+ const publishLeadRole = (ctx, _mode, generation) => {
17472
+ const sessionId = ctx.sessionManager.getSessionId();
17473
+ const sessionGeneration = leadRuntime.sessionGeneration;
17474
+ const failureKey = `${sessionId}:${sessionGeneration}:${generation}`;
16999
17475
  leadMetadataQueue = leadMetadataQueue.catch(() => {
17000
17476
  }).then(async () => {
17001
- if (generation !== leadRuntime.chiefModeGeneration) return;
17477
+ if (generation !== leadRuntime.chiefModeGeneration || sessionGeneration !== leadRuntime.sessionGeneration || sessionId !== ctx.sessionManager.getSessionId())
17478
+ return;
17479
+ const paneId = roleHost.paneId();
17480
+ if (!paneId) return;
17481
+ const role2 = leadRuntime.chiefMode === "active" ? "chief" : leadRuntime.controllerRole === "manager" && !leadRuntime.roleSuspended ? "manager" : leadRuntime.roleSuspended ? void 0 : "lead";
17482
+ const leadName = role2 === "lead" ? roleHost.getSessionName()?.trim() : void 0;
17483
+ const args = [
17484
+ "pane",
17485
+ "report-metadata",
17486
+ paneId,
17487
+ "--source",
17488
+ "pi-herdsman:lead",
17489
+ "--title",
17490
+ role2 === "chief" ? "chief" : role2 === "manager" ? "Pi Herdsman manager" : leadName || "Pi Herdsman lead",
17491
+ ...role2 ? ["--token", `pi_herdsman_role=${role2}`] : ["--clear-token", "pi_herdsman_role"]
17492
+ ];
17493
+ if (role2 === "lead")
17494
+ args.push(
17495
+ leadName ? "--token" : "--clear-token",
17496
+ leadName ? `pi_herdsman_name=${leadName}` : "pi_herdsman_name"
17497
+ );
17498
+ else
17499
+ args.push(
17500
+ "--clear-token",
17501
+ "pi_herdsman_name",
17502
+ "--clear-token",
17503
+ "pi_herdsman_ask"
17504
+ );
17505
+ if (role2 !== "lead")
17506
+ args.push(
17507
+ "--clear-token",
17508
+ "pi_herdsman_herd_run_started_at",
17509
+ "--clear-token",
17510
+ "pi_herdsman_context_percent"
17511
+ );
17002
17512
  await roleHost.runHerdr(pi, ctx, args, {
17003
17513
  noResult: true,
17004
17514
  timeout: 1e4
17005
17515
  });
17006
- }).catch(() => {
17516
+ reportedRoleMetadataFailure = void 0;
17517
+ }).catch((error2) => {
17518
+ if (sessionGeneration !== leadRuntime.sessionGeneration || sessionId !== ctx.sessionManager.getSessionId())
17519
+ return;
17520
+ if (reportedRoleMetadataFailure === failureKey) return;
17521
+ reportedRoleMetadataFailure = failureKey;
17522
+ try {
17523
+ roleHost.appendDurableError(
17524
+ pi,
17525
+ ctx,
17526
+ "pi_herdsman_state_error",
17527
+ error2
17528
+ );
17529
+ } catch {
17530
+ }
17007
17531
  });
17008
17532
  return leadMetadataQueue;
17009
17533
  };
@@ -17016,8 +17540,30 @@ function registerLeadRuntime(pi, options) {
17016
17540
  );
17017
17541
  return;
17018
17542
  }
17543
+ const sessionId = ctx.sessionManager.getSessionId();
17544
+ const sessionGeneration = leadRuntime.sessionGeneration;
17019
17545
  leadMetadataQueue = leadMetadataQueue.catch(() => {
17020
- }).then(() => roleHost.reportLeadMetadata(pi, ctx, metadata)).catch(() => {
17546
+ }).then(() => {
17547
+ if (sessionGeneration !== leadRuntime.sessionGeneration || sessionId !== ctx.sessionManager.getSessionId() || leadRuntime.controllerRole !== "lead" || leadRuntime.roleSuspended)
17548
+ return;
17549
+ return roleHost.reportLeadMetadata(pi, ctx, metadata).then(() => {
17550
+ reportedRoleMetadataFailure = void 0;
17551
+ });
17552
+ }).catch((error2) => {
17553
+ if (sessionGeneration !== leadRuntime.sessionGeneration || sessionId !== ctx.sessionManager.getSessionId())
17554
+ return;
17555
+ const failureKey = `${sessionId}:${sessionGeneration}:${leadRuntime.chiefModeGeneration}`;
17556
+ if (reportedRoleMetadataFailure === failureKey) return;
17557
+ reportedRoleMetadataFailure = failureKey;
17558
+ try {
17559
+ roleHost.appendDurableError(
17560
+ pi,
17561
+ ctx,
17562
+ "pi_herdsman_state_error",
17563
+ error2
17564
+ );
17565
+ } catch {
17566
+ }
17021
17567
  });
17022
17568
  };
17023
17569
  const persistRole = (role2) => {
@@ -17232,13 +17778,88 @@ function registerLeadRuntime(pi, options) {
17232
17778
  const execution = isLead && assignmentVerified ? assignment ? { kind: "managed" } : leadExecution?.valid ? { kind: "ordinary", mode: leadExecution.mode } : void 0 : void 0;
17233
17779
  return { ...snapshot, ...execution ? { execution } : {} };
17234
17780
  };
17781
+ const focusTarget = async (ctx, intent, isCurrent) => {
17782
+ const sessionId = ctx.sessionManager.getSessionId();
17783
+ const signal = controller.sessionSignal();
17784
+ const current = () => !!signal && !signal.aborted && isCurrent() && ctx.sessionManager.getSessionId() === sessionId && activeLeadRole(options.leadRuntime) === "lead" && !options.leadRuntime.roleSuspended;
17785
+ if (!current()) throw new Error("Navigation is no longer available.");
17786
+ if (intent.kind === "assigned-manager") {
17787
+ const scope = await roleTransitions.currentWorktreeScope(ctx);
17788
+ if (!current() || !scope)
17789
+ throw new Error("Managed project assignment changed.");
17790
+ const assignment = roleTransitions.projectAssignmentForScope(
17791
+ scope,
17792
+ sessionId
17793
+ );
17794
+ const manager = await roleTransitions.currentManager(ctx);
17795
+ if (!current() || !assignment || !manager || manager.repoKey !== assignment.repoKey)
17796
+ throw new Error("Managed project Manager is unavailable.");
17797
+ const target2 = await verifiedHerdrAgent(manager, {
17798
+ listAgents: async () => (await options.identityHost.listAgents(ctx)).agents,
17799
+ getAgent: async (paneId) => (await options.identityHost.getAgent(ctx, paneId))?.agent,
17800
+ expectedSession: options.identityHost.expectedSession,
17801
+ isPiAgent: options.identityHost.isPiAgent,
17802
+ matchesExpectedSession: options.identityHost.matchesExpectedSession
17803
+ });
17804
+ const currentManager = await roleTransitions.currentManager(ctx);
17805
+ const currentScope = await roleTransitions.currentWorktreeScope(ctx);
17806
+ const currentAssignment = currentScope ? roleTransitions.projectAssignmentForScope(currentScope, sessionId) : void 0;
17807
+ if (!current() || !target2 || !currentAssignment || currentAssignment.id !== assignment.id || currentAssignment.repoKey !== assignment.repoKey || currentAssignment.branch !== assignment.branch || !currentManager || currentManager.repoKey !== assignment.repoKey || !options.projectHost.sameManagerDescriptor(currentManager, manager))
17808
+ throw new Error("Managed project Manager changed.");
17809
+ await options.commandHost.runHerdr(
17810
+ pi,
17811
+ ctx,
17812
+ ["agent", "focus", target2.pane_id],
17813
+ {
17814
+ signal
17815
+ }
17816
+ );
17817
+ return;
17818
+ }
17819
+ const fresh = await loadStatusSnapshot(ctx, signal, false);
17820
+ const matches = fresh.agents.filter(
17821
+ (agent) => agent.sessionId === intent.sessionId && agent.state !== "lost" && agent.state !== "unknown" && agent.state !== "starting" && !!agent.paneId
17822
+ );
17823
+ if (!current() || fresh.stale || fresh.unavailable || matches.length !== 1)
17824
+ throw new Error("Agent changed; reopen Running.");
17825
+ const target = matches[0];
17826
+ const live = await identityRuntime.liveAgent(ctx, target.sessionId);
17827
+ if (!current() || live.length !== 1 || live[0].pane_id !== target.paneId)
17828
+ throw new Error("Agent changed; reopen Running.");
17829
+ const verified = await verifiedHerdrAgent(
17830
+ {
17831
+ piSessionId: target.sessionId,
17832
+ paneId: target.paneId,
17833
+ tabId: live[0].tab_id,
17834
+ workspaceId: live[0].workspace_id
17835
+ },
17836
+ {
17837
+ listAgents: async () => (await options.identityHost.listAgents(ctx)).agents,
17838
+ getAgent: async (paneId) => (await options.identityHost.getAgent(ctx, paneId))?.agent,
17839
+ expectedSession: options.identityHost.expectedSession,
17840
+ isPiAgent: options.identityHost.isPiAgent,
17841
+ matchesExpectedSession: options.identityHost.matchesExpectedSession
17842
+ }
17843
+ );
17844
+ if (!current() || !verified)
17845
+ throw new Error("Agent changed; reopen Running.");
17846
+ await options.commandHost.runHerdr(
17847
+ pi,
17848
+ ctx,
17849
+ ["agent", "focus", verified.pane_id],
17850
+ {
17851
+ signal
17852
+ }
17853
+ );
17854
+ };
17235
17855
  statusRuntime.configure({
17236
17856
  ...options.statusSnapshotHost,
17237
17857
  pendingStartEntries: () => controller.pendingStartEntries(),
17238
17858
  hasPendingStart: (label) => controller.hasPendingStart(label),
17239
17859
  clearPendingStart: (label, expected) => controller.clearPendingStart(label, expected),
17240
17860
  loadSnapshot: (ctx) => loadStatusSnapshot(ctx, controller.sessionSignal(), false),
17241
- runtimeForLabel: (label) => controller.runtimeForLabel(label)
17861
+ runtimeForLabel: (label) => controller.runtimeForLabel(label),
17862
+ focusTarget
17242
17863
  });
17243
17864
  let projectRuntime;
17244
17865
  const projectHost = options.projectHost;
@@ -18042,6 +18663,8 @@ function createLeadSupervisionRuntime(host) {
18042
18663
  let overviewGeneration = 0;
18043
18664
  let renderOverview;
18044
18665
  let renderWidget;
18666
+ let widgetFocusInFlight = false;
18667
+ let supervisionWidgetGeneration = 0;
18045
18668
  const generation = (ctx) => [
18046
18669
  host.state.chiefModeGeneration,
18047
18670
  host.state.sessionGeneration,
@@ -18110,6 +18733,108 @@ function createLeadSupervisionRuntime(host) {
18110
18733
  });
18111
18734
  return inFlight;
18112
18735
  };
18736
+ const focusLead = async (ctx, leadId, parentManagerSessionId, isCurrent = () => true) => {
18737
+ const currentGeneration = generation(ctx);
18738
+ const fresh = await loadSnapshot(ctx);
18739
+ if (!isCurrent() || currentGeneration !== generation(ctx))
18740
+ throw new Error("Lead changed; reopen staff.");
18741
+ if (host.activeRole() === "manager" && host.managerSupervisionItems(fresh).filter(
18742
+ (item) => item.kind === "lead" ? item.lead.lead === leadId : item.work.status === "active" && item.lead?.lead === leadId
18743
+ ).length !== 1)
18744
+ throw new Error("Lead is no longer available.");
18745
+ const managers = host.listManagerDescriptors(host.supervisionRuntime());
18746
+ const verifyManager = async (sessionId, report) => {
18747
+ const candidates = managers.filter(
18748
+ (candidate) => candidate.piSessionId === sessionId
18749
+ );
18750
+ if (candidates.length !== 1) return void 0;
18751
+ const descriptor = candidates[0];
18752
+ const agents = await host.listAllHerdrAgents(host.pi, ctx, ctx.signal);
18753
+ const placements = agents.agents.filter(
18754
+ (candidate) => host.isPiAgent(candidate) && host.herdrSessionId(candidate) === sessionId && candidate.pane_id === descriptor.paneId && candidate.tab_id === descriptor.tabId && candidate.workspace_id === descriptor.workspaceId && (!report || report.paneId === descriptor.paneId && report.tabId === descriptor.tabId && report.workspaceId === descriptor.workspaceId)
18755
+ );
18756
+ return placements.length === 1 ? { descriptor, paneId: descriptor.paneId } : void 0;
18757
+ };
18758
+ let lead;
18759
+ let parent;
18760
+ let verifiedParent;
18761
+ if (parentManagerSessionId) {
18762
+ parent = host.presentationReports(fresh).find(
18763
+ (candidate) => candidate.role === "manager" && candidate.lead === parentManagerSessionId
18764
+ );
18765
+ const children = parent?.leads?.filter(
18766
+ (candidate) => candidate.session === leadId
18767
+ );
18768
+ if (!parent || children?.length !== 1)
18769
+ throw new Error("Lead changed; reopen staff.");
18770
+ lead = { lead: leadId };
18771
+ } else {
18772
+ const matches2 = host.presentationReports(fresh).filter((candidate) => candidate.lead === leadId);
18773
+ if (matches2.length !== 1) throw new Error("Lead changed; reopen staff.");
18774
+ lead = matches2[0];
18775
+ }
18776
+ if (parent || lead.role === "manager") {
18777
+ const managerSessionId = parent?.lead ?? lead.lead;
18778
+ const report = parent ?? lead;
18779
+ const manager = await verifyManager(managerSessionId, report);
18780
+ if (!manager || !isCurrent() || currentGeneration !== generation(ctx))
18781
+ throw new Error("Manager changed; reopen staff.");
18782
+ verifiedParent = manager.descriptor;
18783
+ if (!parent) {
18784
+ const currentManagers = host.listManagerDescriptors(host.supervisionRuntime()).filter((descriptor) => descriptor.piSessionId === managerSessionId);
18785
+ if (!isCurrent() || currentManagers.length !== 1 || !host.sameManagerDescriptor(currentManagers[0], manager.descriptor))
18786
+ throw new Error("Manager changed; reopen staff.");
18787
+ const currentAgent2 = await host.runHerdr(
18788
+ host.pi,
18789
+ ctx,
18790
+ ["agent", "get", manager.paneId],
18791
+ { signal: ctx.signal }
18792
+ );
18793
+ if (!isCurrent() || host.herdrSessionId(currentAgent2?.agent) !== managerSessionId || currentAgent2?.agent?.pane_id !== manager.paneId || currentAgent2?.agent?.tab_id !== manager.descriptor.tabId || currentAgent2?.agent?.workspace_id !== manager.descriptor.workspaceId)
18794
+ throw new Error("Manager changed; reopen staff.");
18795
+ const finalManagers = host.listManagerDescriptors(host.supervisionRuntime()).filter((descriptor) => descriptor.piSessionId === managerSessionId);
18796
+ if (!isCurrent() || finalManagers.length !== 1 || !host.sameManagerDescriptor(finalManagers[0], manager.descriptor))
18797
+ throw new Error("Manager changed; reopen staff.");
18798
+ await host.runHerdr(host.pi, ctx, ["agent", "focus", manager.paneId], {
18799
+ signal: ctx.signal
18800
+ });
18801
+ return;
18802
+ }
18803
+ }
18804
+ const coordination = host.readLeadCoordinationState(
18805
+ host.supervisionRuntime(),
18806
+ leadId
18807
+ );
18808
+ if (!coordination || coordination.piSessionId !== leadId || lead.instanceId !== void 0 && coordination.instanceId !== lead.instanceId)
18809
+ throw new Error("Lead changed; reopen staff.");
18810
+ const verified = await host.listAllHerdrAgents(host.pi, ctx, ctx.signal);
18811
+ if (!isCurrent() || currentGeneration !== generation(ctx))
18812
+ throw new Error("Lead changed; reopen staff.");
18813
+ const matches = verified.agents.filter(
18814
+ (candidate) => host.isPiAgent(candidate) && host.herdrSessionId(candidate) === leadId && typeof candidate?.pane_id === "string" && (parentManagerSessionId || candidate?.pane_id === lead.paneId && candidate?.tab_id === lead.tabId && candidate?.workspace_id === lead.workspaceId)
18815
+ );
18816
+ if (matches.length !== 1) throw new Error("Lead changed; reopen staff.");
18817
+ if (currentGeneration !== generation(ctx) || !isCurrent() || verifiedParent && !host.listManagerDescriptors(host.supervisionRuntime()).some(
18818
+ (descriptor) => host.sameManagerDescriptor(descriptor, verifiedParent)
18819
+ ))
18820
+ throw new Error("Manager changed; reopen staff.");
18821
+ const currentAgent = await host.runHerdr(
18822
+ host.pi,
18823
+ ctx,
18824
+ ["agent", "get", matches[0].pane_id],
18825
+ { signal: ctx.signal }
18826
+ );
18827
+ if (!isCurrent() || host.herdrSessionId(currentAgent?.agent) !== leadId || currentAgent?.agent?.pane_id !== matches[0].pane_id || currentAgent?.agent?.workspace_id !== matches[0].workspace_id || currentAgent?.agent?.tab_id !== matches[0].tab_id)
18828
+ throw new Error("Lead changed; reopen staff.");
18829
+ await host.runHerdr(
18830
+ host.pi,
18831
+ ctx,
18832
+ ["agent", "focus", currentAgent.agent.pane_id],
18833
+ {
18834
+ signal: ctx.signal
18835
+ }
18836
+ );
18837
+ };
18113
18838
  const nextOverviewGeneration = () => ++overviewGeneration;
18114
18839
  const openOverview = async (ctx, uiActions) => {
18115
18840
  if (ctx.mode !== "tui") {
@@ -18415,25 +19140,7 @@ function createLeadSupervisionRuntime(host) {
18415
19140
  openOverview,
18416
19141
  loadSnapshot: loadSupervisionSnapshot,
18417
19142
  directReports,
18418
- focusLead: async (ctx, leadId) => {
18419
- const fresh = await loadSnapshot(ctx);
18420
- const lead = host.presentationReports(fresh).find((candidate) => candidate.lead === leadId);
18421
- if (!lead) throw new Error("Lead changed; reopen staff.");
18422
- const coordination = host.readLeadCoordinationState(
18423
- host.supervisionRuntime(),
18424
- lead.lead
18425
- );
18426
- if (!coordination || coordination.piSessionId !== lead.lead || lead.instanceId !== void 0 && coordination.instanceId !== lead.instanceId)
18427
- throw new Error("Lead changed; reopen staff.");
18428
- const verified = await host.listAllHerdrAgents(host.pi, ctx, ctx.signal);
18429
- const matches = verified.agents.filter(
18430
- (candidate) => candidate?.pane_id === lead.paneId && candidate?.tab_id === lead.tabId && candidate?.workspace_id === lead.workspaceId && host.isPiAgent(candidate) && host.herdrSessionId(candidate) === lead.lead
18431
- );
18432
- if (matches.length !== 1) throw new Error("Lead changed; reopen staff.");
18433
- await host.runHerdr(host.pi, ctx, ["agent", "focus", lead.paneId], {
18434
- signal: ctx.signal
18435
- });
18436
- },
19143
+ focusLead,
18437
19144
  focusChief: async (ctx) => {
18438
19145
  const confirmed = await ctx.ui.confirm(
18439
19146
  "Focus Chief?",
@@ -18479,13 +19186,35 @@ function createLeadSupervisionRuntime(host) {
18479
19186
  registerWidget: (ctx, role2) => {
18480
19187
  if (ctx.mode !== "tui" || !ctx.hasUI) return;
18481
19188
  try {
19189
+ const widgetGeneration = ++supervisionWidgetGeneration;
18482
19190
  ctx.ui.setWidget("pi-herdsman-staff", (tui, theme) => {
18483
19191
  renderWidget = () => tui.requestRender();
18484
19192
  return createSupervisionWidget(
18485
19193
  () => snapshot,
18486
19194
  () => status(ctx),
18487
19195
  role2,
18488
- theme
19196
+ theme,
19197
+ (intent) => {
19198
+ if (widgetFocusInFlight || widgetGeneration !== supervisionWidgetGeneration || generation(ctx) !== generationId || status(ctx) !== "fresh")
19199
+ return;
19200
+ if (intent.kind !== "session") return;
19201
+ widgetFocusInFlight = true;
19202
+ const isCurrent = () => widgetGeneration === supervisionWidgetGeneration && uiContext === ctx && generation(ctx) === generationId;
19203
+ void focusLead(
19204
+ ctx,
19205
+ intent.sessionId,
19206
+ intent.parentSessionId,
19207
+ isCurrent
19208
+ ).catch((error2) => {
19209
+ if (isCurrent())
19210
+ ctx.ui.notify(
19211
+ String(error2).replace(/^Error: /u, ""),
19212
+ "warning"
19213
+ );
19214
+ }).finally(() => {
19215
+ widgetFocusInFlight = false;
19216
+ });
19217
+ }
18489
19218
  );
18490
19219
  });
18491
19220
  } catch {
@@ -18494,6 +19223,8 @@ function createLeadSupervisionRuntime(host) {
18494
19223
  }
18495
19224
  },
18496
19225
  removeWidget: (ctx) => {
19226
+ supervisionWidgetGeneration++;
19227
+ widgetFocusInFlight = false;
18497
19228
  try {
18498
19229
  ctx.ui.setWidget("pi-herdsman-staff", void 0);
18499
19230
  } catch {
@@ -20417,6 +21148,8 @@ function createLeadInboxRuntime(host) {
20417
21148
  let chiefInboxTimer;
20418
21149
  let chiefInboxGeneration = 0;
20419
21150
  let chiefInboxAbortController;
21151
+ let currentSubmittedAt;
21152
+ let settledSubmissions = /* @__PURE__ */ new Set();
20420
21153
  const chiefStartPreflights = [];
20421
21154
  const {
20422
21155
  leadRuntime,
@@ -20475,7 +21208,7 @@ function createLeadInboxRuntime(host) {
20475
21208
  );
20476
21209
  if (index >= 0) chiefStartPreflights.splice(index, 1);
20477
21210
  };
20478
- const messageDelivered = (ctx, record) => record.toSessionId === ctx.sessionManager.getSessionId() && ctx.sessionManager.getBranch().some(
21211
+ const messageDelivered = (ctx, record) => record.toSessionId === ctx.sessionManager.getSessionId() && ctx.sessionManager.getEntries().some(
20479
21212
  (entry) => entry?.customType === `pi-herdsman-${record.kind}` && entry?.details?.id === record.id && entry?.details?.leaseId === record.leaseId && entry?.details?.fromSessionId === record.fromSessionId && entry?.details?.leadSessionId === record.leadSessionId && entry?.details?.branch === record.branch
20480
21213
  );
20481
21214
  const projectAssignmentAuthorized = (authorized, reason) => {
@@ -20614,11 +21347,25 @@ function createLeadInboxRuntime(host) {
20614
21347
  return !!chief && !!state2 && chief.piSessionId === record.fromSessionId && chief.leaseId === record.leaseId && record.leadSessionId === sessionId;
20615
21348
  }
20616
21349
  const scope = await host.currentWorktreeScope(ctx);
20617
- if (!scope || !host.projectAssignmentForScope(scope, sessionId))
20618
- return false;
21350
+ if (!scope) throw new Error("Project scope cannot be verified");
21351
+ if (!host.projectAssignmentForScope(scope, sessionId)) return false;
20619
21352
  const manager = await currentManager(ctx, scope);
20620
- const state = readLeadCoordinationState2(supervisionRuntime2(), sessionId);
20621
- return !!manager && !!state && record.leaseId === manager.leaseId && record.fromSessionId === manager.piSessionId && record.leadSessionId === sessionId && record.kind === "manager_message";
21353
+ let state;
21354
+ try {
21355
+ state = readLeadCoordinationState2(supervisionRuntime2(), sessionId);
21356
+ } catch (error2) {
21357
+ const cause = error2 instanceof Error ? error2.cause : void 0;
21358
+ if (cause instanceof SyntaxError || cause instanceof Error && ["invalid lead state", "lead state too large"].includes(
21359
+ cause.message
21360
+ ))
21361
+ return false;
21362
+ throw error2;
21363
+ }
21364
+ if (!manager)
21365
+ throw new Error("Manager authority is temporarily unavailable");
21366
+ if (!state)
21367
+ throw new Error("Lead coordination state is temporarily unavailable");
21368
+ return (state.role ?? "lead") === "lead" && record.leaseId === manager.leaseId && record.fromSessionId === manager.piSessionId && record.leadSessionId === sessionId && record.kind === "manager_message";
20622
21369
  };
20623
21370
  const startChiefInbox = (ctx) => {
20624
21371
  const generation = ++chiefInboxGeneration;
@@ -20629,6 +21376,9 @@ function createLeadInboxRuntime(host) {
20629
21376
  const isCurrent = () => generation === chiefInboxGeneration && !chiefInboxAbortController?.signal.aborted;
20630
21377
  if (chiefInboxTimer) clearTimeout(chiefInboxTimer);
20631
21378
  const transactions = /* @__PURE__ */ new Map();
21379
+ const submittedAt = /* @__PURE__ */ new Map();
21380
+ currentSubmittedAt = submittedAt;
21381
+ settledSubmissions = /* @__PURE__ */ new Set();
20632
21382
  const revalidateTransaction = (token, phase) => {
20633
21383
  const transaction = token;
20634
21384
  if (transaction?.generation !== chiefInboxGeneration || transaction.sessionId !== ctx.sessionManager.getSessionId() || transaction.instanceId !== getLeadInstanceId() || chiefInboxAbortController?.signal.aborted || chiefInboxAbortController?.signal !== transaction.signal || leadRuntime.chiefMode !== transaction.role || transaction.role === "active" && chiefStartPreflightHeld(ctx) || transaction.role === "inactive" && (!coordinationHealthy() || leadRuntime.roleSuspended)) {
@@ -20647,6 +21397,13 @@ function createLeadInboxRuntime(host) {
20647
21397
  appendDurableError2(pi, ctx, "pi_herdsman_state_error", error2);
20648
21398
  },
20649
21399
  deliveryMode: (record) => {
21400
+ const attempted = submittedAt.get(record.id);
21401
+ if (attempted !== void 0 && !messageDelivered(ctx, record)) {
21402
+ const elapsed = Date.now() - attempted;
21403
+ const settled = settledSubmissions.has(record.id);
21404
+ if (!ctx.isIdle() || ctx.hasPendingMessages() || elapsed < (settled ? 1e3 : 3e4))
21405
+ return void 0;
21406
+ }
20650
21407
  const role2 = activeRole();
20651
21408
  const supervisorDownlink = role2 === "lead" && (record.kind === "chief_message" || record.kind === "manager_message") || role2 === "manager" && record.kind === "chief_message";
20652
21409
  if (supervisorDownlink) return "steer";
@@ -20745,7 +21502,17 @@ function createLeadInboxRuntime(host) {
20745
21502
  };
20746
21503
  }
20747
21504
  importResultBindings2(pi, ctx, resultBindings, record.kind);
20748
- const result = await pi.sendMessage(payload, options);
21505
+ if (messageDelivered(ctx, record)) return;
21506
+ let result;
21507
+ try {
21508
+ result = pi.sendMessage(payload, options);
21509
+ } catch (error2) {
21510
+ submittedAt.delete(record.id);
21511
+ settledSubmissions.delete(record.id);
21512
+ throw error2;
21513
+ }
21514
+ submittedAt.set(record.id, Date.now());
21515
+ settledSubmissions.delete(record.id);
20749
21516
  if (projectAssignment)
20750
21517
  managerDiagnostic("project_assignment_send", {
20751
21518
  outcome: "resolved",
@@ -20791,9 +21558,13 @@ function createLeadInboxRuntime(host) {
20791
21558
  }
20792
21559
  }
20793
21560
  },
20794
- accepted: async (_record) => {
21561
+ accepted: async (record) => {
21562
+ submittedAt.delete(record.id);
21563
+ settledSubmissions.delete(record.id);
20795
21564
  },
20796
21565
  rejected: (record) => {
21566
+ submittedAt.delete(record.id);
21567
+ settledSubmissions.delete(record.id);
20797
21568
  if (!record.build || !sameRuntimeBuild2(record.build, HERDSMAN_BUILD2))
20798
21569
  ctx.ui.notify(
20799
21570
  record.build ? `Rejected Pi Herdsman coordination from ${formatRuntimeBuild2(record.build)}; local build is ${formatRuntimeBuild2(HERDSMAN_BUILD2)}. Restart the stale Pi session.` : "Rejected coordination from a Pi Herdsman runtime without build identity. Restart the stale Pi session.",
@@ -20908,9 +21679,15 @@ function createLeadInboxRuntime(host) {
20908
21679
  },
20909
21680
  clearTimer,
20910
21681
  beginSession: () => {
21682
+ currentSubmittedAt?.clear();
21683
+ settledSubmissions.clear();
20911
21684
  chiefInboxAbortController?.abort();
20912
21685
  chiefInboxAbortController = new AbortController();
20913
21686
  },
21687
+ agentSettled: () => {
21688
+ for (const id of currentSubmittedAt?.keys() ?? [])
21689
+ settledSubmissions.add(id);
21690
+ },
20914
21691
  beginShutdown: () => {
20915
21692
  chiefInboxGeneration++;
20916
21693
  clearTimer();
@@ -22250,6 +23027,7 @@ function createLeadRoleTransitions(state, host) {
22250
23027
  suspendMalformedRole,
22251
23028
  shouldRestoreChief: (persistedRole) => persistedRole === "chief" && state.controllerRole === "lead" && !state.roleSuspended,
22252
23029
  activeChiefGeneration: () => state.chiefMode === "active" ? state.chiefModeGeneration : void 0,
23030
+ currentRoleGeneration: () => state.chiefModeGeneration,
22253
23031
  mayPublishLeadPresence: () => state.chiefMode === "inactive" && !state.roleSuspended && host.coordinationHealthy(),
22254
23032
  canStartChiefInbox: () => !state.roleSuspended && (state.chiefMode === "inactive" || state.chiefMode === "active") && (state.chiefMode === "active" || host.coordinationHealthy()),
22255
23033
  activeManager: () => state.controllerRole === "manager" && !state.roleSuspended,
@@ -22570,7 +23348,7 @@ async function workspacePresentationProvenance(pi, ctx, workspaceIds, workspaceC
22570
23348
  );
22571
23349
  return new Map(entries);
22572
23350
  }
22573
- function index_default(pi) {
23351
+ function extension_default(pi) {
22574
23352
  let leadRuntimes;
22575
23353
  let leadStatusRuntime;
22576
23354
  let leadHerdRunRuntime;
@@ -23122,12 +23900,14 @@ ${globalInstructions}
23122
23900
  definitionRoster: () => startupDefinitionRoster,
23123
23901
  isStaffTool: (name) => STAFF_TOOLS.includes(name)
23124
23902
  };
23125
- const initialStatusBreadcrumb = controllerScope.kind === "lead" ? ["lead"] : [
23126
- "?",
23127
- process.env.PI_HERDSMAN_AGENT_DEFINITION && process.env.PI_HERDSMAN_LABEL ? displayIdentity(
23128
- process.env.PI_HERDSMAN_AGENT_DEFINITION,
23129
- process.env.PI_HERDSMAN_LABEL
23130
- ) : process.env.PI_HERDSMAN_AGENT_DEFINITION ?? "?"
23903
+ const initialStatusBreadcrumb = controllerScope.kind === "lead" ? [{ text: "lead" }] : [
23904
+ { text: "?" },
23905
+ {
23906
+ text: process.env.PI_HERDSMAN_AGENT_DEFINITION && process.env.PI_HERDSMAN_LABEL ? displayIdentity(
23907
+ process.env.PI_HERDSMAN_AGENT_DEFINITION,
23908
+ process.env.PI_HERDSMAN_LABEL
23909
+ ) : process.env.PI_HERDSMAN_AGENT_DEFINITION ?? "?"
23910
+ }
23131
23911
  ];
23132
23912
  let ownTools;
23133
23913
  const ownToolsSnapshot = () => ownTools ? { ownTools } : {};
@@ -23799,6 +24579,6 @@ ${summary}`,
23799
24579
  });
23800
24580
  }
23801
24581
  export {
23802
- index_default as default
24582
+ extension_default as default
23803
24583
  };
23804
24584
  //# sourceMappingURL=index.js.map