pi-herdsman 0.23.0 → 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 +776 -172
- package/dist/index.js.map +3 -3
- package/docs/reference/status-widget.md +19 -0
- package/package.json +1 -1
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.
|
|
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",
|
|
@@ -4415,6 +4415,9 @@ function layoutStatusRows(rows, width, options = {}) {
|
|
|
4415
4415
|
}) ?? layouts.at(-1);
|
|
4416
4416
|
return rows.map((row) => ({
|
|
4417
4417
|
label: row.label,
|
|
4418
|
+
labelStart: visibleWidth(
|
|
4419
|
+
`${row.tree}${row.spinner} ${padVisible(row.definition, columns.definitionWidth)} `
|
|
4420
|
+
),
|
|
4418
4421
|
...row.paneId ? { paneId: row.paneId } : {},
|
|
4419
4422
|
...row.sessionId ? { sessionId: row.sessionId } : {},
|
|
4420
4423
|
text: renderStatusLine(row, columns, layout, available, options.theme)
|
|
@@ -4451,6 +4454,8 @@ function managerPresentationSnapshot(manager) {
|
|
|
4451
4454
|
displayName: manager.displayName,
|
|
4452
4455
|
project: manager.project,
|
|
4453
4456
|
workspaceId: manager.workspaceId,
|
|
4457
|
+
paneId: manager.paneId,
|
|
4458
|
+
tabId: manager.tabId,
|
|
4454
4459
|
runtimeState: manager.runtimeState,
|
|
4455
4460
|
agentCounts: manager.agentCounts,
|
|
4456
4461
|
leadCounts: manager.leadCounts,
|
|
@@ -4639,15 +4644,23 @@ var MANAGER_METADATA_LAYOUTS = [
|
|
|
4639
4644
|
{ elapsed: false, context: true },
|
|
4640
4645
|
{ elapsed: false, context: false }
|
|
4641
4646
|
];
|
|
4642
|
-
function
|
|
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) {
|
|
4643
4654
|
const status = options.status ?? "fresh";
|
|
4644
4655
|
const role2 = options.role ?? "chief";
|
|
4645
4656
|
if (status === "unavailable")
|
|
4646
4657
|
return [
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4658
|
+
{
|
|
4659
|
+
text: safeLine(
|
|
4660
|
+
`${themed(options.theme, "success", "\u25CF")} ${themed(options.theme, "muted", `${role2} \xB7 unavailable`)}`,
|
|
4661
|
+
width
|
|
4662
|
+
)
|
|
4663
|
+
}
|
|
4651
4664
|
];
|
|
4652
4665
|
const displays = orderedSupervisionLeads(presentationReports(reports));
|
|
4653
4666
|
const groups = groupOrderedSupervisedLeads(displays);
|
|
@@ -4695,16 +4708,25 @@ function renderSupervisionLeads(reports, width, options = {}, selectedLead) {
|
|
|
4695
4708
|
})
|
|
4696
4709
|
) ?? MANAGER_METADATA_LAYOUTS.at(-1);
|
|
4697
4710
|
return [
|
|
4698
|
-
safeLine(header, width),
|
|
4711
|
+
{ text: safeLine(header, width) },
|
|
4699
4712
|
...visible.map((item, index) => {
|
|
4700
4713
|
const branch = index === visible.length - 1 && !remaining ? "\u2514\u2500" : "\u251C\u2500";
|
|
4701
4714
|
const lead = item.lead;
|
|
4702
4715
|
const navigation = lead && lead.lead === selectedLead ? ">" : "";
|
|
4703
4716
|
if (item.kind === "lead")
|
|
4704
|
-
return
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
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
|
+
};
|
|
4708
4730
|
const marker = item.work.status === "active" && lead ? `${lead.runtimeState === "blocked" ? "!" : ""}${supervisionLeadMarker(lead)}` : workMarkers[item.work.status];
|
|
4709
4731
|
const prefix = `${branch} ${navigation}${marker} `;
|
|
4710
4732
|
const suffix = ` \xB7 ${item.work.status}`;
|
|
@@ -4718,48 +4740,64 @@ function renderSupervisionLeads(reports, width, options = {}, selectedLead) {
|
|
|
4718
4740
|
);
|
|
4719
4741
|
const markerColor = item.work.status === "active" && lead ? lifecycleColor(lead.runtimeState) : item.work.status === "active" ? "success" : item.work.status === "conflict" ? "warning" : "muted";
|
|
4720
4742
|
const markerText = themed(options.theme, markerColor, marker);
|
|
4721
|
-
const
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
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
|
+
};
|
|
4733
4756
|
}),
|
|
4734
|
-
...remaining ? [safeLine(`\u2514\u2500 \u2026 ${remaining} more \xB7 /manager`, width)] : []
|
|
4757
|
+
...remaining ? [{ text: safeLine(`\u2514\u2500 \u2026 ${remaining} more \xB7 /manager`, width) }] : []
|
|
4735
4758
|
];
|
|
4736
4759
|
}
|
|
4737
4760
|
return [
|
|
4738
|
-
safeLine(header, width),
|
|
4761
|
+
{ text: safeLine(header, width) },
|
|
4739
4762
|
...shown.flatMap((lead, index) => {
|
|
4740
4763
|
const branch = index === shown.length - 1 && hidden === 0 ? "\u2514\u2500" : "\u251C\u2500";
|
|
4741
4764
|
const marker = supervisionLeadMarker(lead);
|
|
4742
4765
|
const navigation = lead.lead === selectedLead ? ">" : "";
|
|
4743
4766
|
const indicators = navigation;
|
|
4744
4767
|
const counts = lead.leadCounts;
|
|
4745
|
-
const
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
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
|
+
};
|
|
4749
4777
|
const children = (lead.leads ?? []).slice(0, 3);
|
|
4750
4778
|
const childIndent = branch === "\u2514\u2500" ? " " : "\u2502 ";
|
|
4751
4779
|
return [
|
|
4752
4780
|
row,
|
|
4753
4781
|
...children.map((child, childIndex) => {
|
|
4754
4782
|
const childBranch = childIndex === children.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
|
|
4755
|
-
return
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
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
|
+
};
|
|
4759
4797
|
})
|
|
4760
4798
|
];
|
|
4761
4799
|
}),
|
|
4762
|
-
...hidden > 0 ? [safeLine(`\u2514\u2500 \u2026 ${hidden} more \xB7 /${role2}`, width)] : []
|
|
4800
|
+
...hidden > 0 ? [{ text: safeLine(`\u2514\u2500 \u2026 ${hidden} more \xB7 /${role2}`, width) }] : []
|
|
4763
4801
|
];
|
|
4764
4802
|
}
|
|
4765
4803
|
function formatSupervisionNotification(reports, status = "fresh") {
|
|
@@ -4920,15 +4958,39 @@ function formatSupervisionContext(snapshot, options) {
|
|
|
4920
4958
|
}
|
|
4921
4959
|
return [...included, closing].join("\n");
|
|
4922
4960
|
}
|
|
4923
|
-
function createSupervisionWidget(getLeads, getStatus, role2 = "chief", theme) {
|
|
4961
|
+
function createSupervisionWidget(getLeads, getStatus, role2 = "chief", theme, onFocus) {
|
|
4962
|
+
let renderedWidth = -1;
|
|
4963
|
+
let hits = [];
|
|
4924
4964
|
return {
|
|
4925
4965
|
render(width) {
|
|
4926
|
-
|
|
4966
|
+
renderedWidth = width;
|
|
4967
|
+
const rows = renderSupervisionRows(getLeads(), width, {
|
|
4927
4968
|
status: getStatus(),
|
|
4928
4969
|
ordinaryCap: 6,
|
|
4929
4970
|
role: role2,
|
|
4930
4971
|
theme
|
|
4931
|
-
}).filter((
|
|
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 };
|
|
4932
4994
|
},
|
|
4933
4995
|
invalidate() {
|
|
4934
4996
|
}
|
|
@@ -5001,22 +5063,38 @@ function safeDisplaySegment(value2) {
|
|
|
5001
5063
|
return value2.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/gu, "").replace(/\u001b\[[0-?]*[ -/]*[@-~]/gu, "").replace(/[\u0000-\u001f\u007f]/gu, " ").replace(/\s+/gu, " ").trim();
|
|
5002
5064
|
}
|
|
5003
5065
|
function renderBreadcrumb(segments, width) {
|
|
5004
|
-
if (width <= 0) return "";
|
|
5005
|
-
const
|
|
5006
|
-
const current =
|
|
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: "?" };
|
|
5007
5069
|
const marker = "\u25CF";
|
|
5008
|
-
if (width <= visibleWidth(marker))
|
|
5070
|
+
if (width <= visibleWidth(marker))
|
|
5071
|
+
return { text: truncateToWidth(marker, width, ""), hits: [] };
|
|
5009
5072
|
const prefix = `${marker} `;
|
|
5010
5073
|
const available = width - visibleWidth(prefix);
|
|
5011
|
-
if (available <= 0) return marker;
|
|
5012
|
-
const currentText = tailTruncate(current, available);
|
|
5013
|
-
|
|
5014
|
-
for (let index =
|
|
5015
|
-
const candidate =
|
|
5016
|
-
|
|
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]);
|
|
5017
5081
|
else break;
|
|
5018
5082
|
}
|
|
5019
|
-
|
|
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 };
|
|
5020
5098
|
}
|
|
5021
5099
|
function renderToolMetadata(tools, width) {
|
|
5022
5100
|
if (!tools?.length || width <= 0) return "";
|
|
@@ -6412,8 +6490,12 @@ var StatusWidget = class {
|
|
|
6412
6490
|
timer;
|
|
6413
6491
|
invalidateUI;
|
|
6414
6492
|
theme;
|
|
6415
|
-
|
|
6493
|
+
onFocus;
|
|
6494
|
+
renderedWidth = -1;
|
|
6495
|
+
hits = [];
|
|
6496
|
+
constructor(invalidateUI, theme, onFocus) {
|
|
6416
6497
|
this.invalidateUI = invalidateUI;
|
|
6498
|
+
this.onFocus = onFocus;
|
|
6417
6499
|
this.theme = theme ?? {
|
|
6418
6500
|
fg: (_color, text) => text,
|
|
6419
6501
|
bold: (text) => text
|
|
@@ -6421,6 +6503,8 @@ var StatusWidget = class {
|
|
|
6421
6503
|
}
|
|
6422
6504
|
setSnapshot(snapshot) {
|
|
6423
6505
|
this.snapshot = snapshot;
|
|
6506
|
+
this.hits = [];
|
|
6507
|
+
this.renderedWidth = -1;
|
|
6424
6508
|
const animated = snapshot.agents.some(
|
|
6425
6509
|
(agent) => agent.state === "working" || agent.state === "settling" || agent.state === "starting"
|
|
6426
6510
|
);
|
|
@@ -6440,20 +6524,36 @@ var StatusWidget = class {
|
|
|
6440
6524
|
}
|
|
6441
6525
|
render(width) {
|
|
6442
6526
|
const s = this.snapshot;
|
|
6527
|
+
this.renderedWidth = width;
|
|
6528
|
+
this.hits = [];
|
|
6443
6529
|
const suffix = s.unavailable ? "unavailable" : `${formatStatusCounts(s.agents)}${s.stale ? " \xB7 stale" : ""}`;
|
|
6444
6530
|
const availableWidth = Math.max(0, width);
|
|
6445
6531
|
const execution = s.execution ? ` \xB7 ${s.execution.kind === "managed" ? "managed" : s.execution.mode}` : "";
|
|
6446
6532
|
const elapsed = formatElapsed(s.herdRunStartedAt, Date.now());
|
|
6447
6533
|
let run = !s.identityOnly && elapsed ? ` \xB7 ${elapsed}` : "";
|
|
6448
6534
|
const suffixText = s.identityOnly || !suffix ? "" : ` ${suffix}`;
|
|
6449
|
-
const breadcrumbSegments = s.breadcrumb ?? ["?"];
|
|
6450
|
-
const
|
|
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);
|
|
6451
6542
|
const remainingWidth = Math.max(
|
|
6452
6543
|
0,
|
|
6453
6544
|
availableWidth - visibleWidth(breadcrumb)
|
|
6454
6545
|
);
|
|
6455
6546
|
const executionFits = !!execution && visibleWidth(execution) + visibleWidth(suffixText) <= remainingWidth;
|
|
6456
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
|
+
}
|
|
6457
6557
|
const tools = renderToolMetadata(
|
|
6458
6558
|
s.ownTools,
|
|
6459
6559
|
Math.max(
|
|
@@ -6471,24 +6571,55 @@ var StatusWidget = class {
|
|
|
6471
6571
|
const header = `${styledBreadcrumb}${styledExecution}${styledTools}${styledRun}${styledSuffix}`;
|
|
6472
6572
|
const out = [truncateToWidth(header, availableWidth, "\u2026")];
|
|
6473
6573
|
if (s.identityOnly) return out;
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
)
|
|
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
|
+
});
|
|
6482
6600
|
return out;
|
|
6483
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
|
+
}
|
|
6484
6612
|
dispose() {
|
|
6485
6613
|
if (this.timer) clearInterval(this.timer);
|
|
6486
6614
|
this.timer = void 0;
|
|
6487
6615
|
this.invalidateUI = void 0;
|
|
6616
|
+
this.onFocus = void 0;
|
|
6617
|
+
this.hits = [];
|
|
6618
|
+
this.renderedWidth = -1;
|
|
6488
6619
|
}
|
|
6489
6620
|
};
|
|
6490
|
-
function createStatusWidget(invalidate, theme) {
|
|
6491
|
-
return new StatusWidget(invalidate, theme);
|
|
6621
|
+
function createStatusWidget(invalidate, theme, onFocus) {
|
|
6622
|
+
return new StatusWidget(invalidate, theme, onFocus);
|
|
6492
6623
|
}
|
|
6493
6624
|
|
|
6494
6625
|
// extension/config.ts
|
|
@@ -7116,8 +7247,6 @@ async function drainCoordinationInbox(options) {
|
|
|
7116
7247
|
}
|
|
7117
7248
|
continue;
|
|
7118
7249
|
}
|
|
7119
|
-
const deliverAs = options.deliveryMode ? options.deliveryMode(record) : "followUp";
|
|
7120
|
-
if (!deliverAs) continue;
|
|
7121
7250
|
let token;
|
|
7122
7251
|
const clearTransaction = async () => {
|
|
7123
7252
|
if (token === void 0) return;
|
|
@@ -7133,6 +7262,12 @@ async function drainCoordinationInbox(options) {
|
|
|
7133
7262
|
await clearTransaction();
|
|
7134
7263
|
continue;
|
|
7135
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
|
+
}
|
|
7136
7271
|
let authorized;
|
|
7137
7272
|
try {
|
|
7138
7273
|
authorized = await options.isAuthorized(record);
|
|
@@ -7180,29 +7315,35 @@ async function drainCoordinationInbox(options) {
|
|
|
7180
7315
|
options.sessionId,
|
|
7181
7316
|
record.id
|
|
7182
7317
|
);
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
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
|
+
}
|
|
7201
7338
|
} catch {
|
|
7202
7339
|
await clearTransaction();
|
|
7203
7340
|
continue;
|
|
7204
7341
|
}
|
|
7205
|
-
|
|
7342
|
+
if (!options.isDelivered(record.id)) {
|
|
7343
|
+
await clearTransaction();
|
|
7344
|
+
continue;
|
|
7345
|
+
}
|
|
7346
|
+
if (!wasAlreadyDelivered) delivered += 1;
|
|
7206
7347
|
try {
|
|
7207
7348
|
await options.transaction?.revalidate(token, "before-accepted");
|
|
7208
7349
|
assertChiefMessageNotQuarantined(
|
|
@@ -7218,6 +7359,7 @@ async function drainCoordinationInbox(options) {
|
|
|
7218
7359
|
}
|
|
7219
7360
|
} else {
|
|
7220
7361
|
try {
|
|
7362
|
+
if (!wasAlreadyDelivered) delivered += 1;
|
|
7221
7363
|
await options.transaction?.revalidate(token, "already-delivered");
|
|
7222
7364
|
assertChiefMessageNotQuarantined(
|
|
7223
7365
|
options.runtime,
|
|
@@ -8333,7 +8475,7 @@ function buildAgentStatusSnapshot(view, ctx, host) {
|
|
|
8333
8475
|
stale: false,
|
|
8334
8476
|
unavailable: false,
|
|
8335
8477
|
...herdStartedAt !== void 0 ? { herdRunStartedAt: herdStartedAt } : {},
|
|
8336
|
-
breadcrumb: host.scope.kind === "lead" ? ["lead"] : statusBreadcrumb(
|
|
8478
|
+
breadcrumb: host.scope.kind === "lead" ? [{ text: "lead" }] : statusBreadcrumb(
|
|
8337
8479
|
view,
|
|
8338
8480
|
host.environmentIdentity(ctx),
|
|
8339
8481
|
host.identityFromEnvironment(),
|
|
@@ -8354,6 +8496,7 @@ function createAgentStatusRuntime() {
|
|
|
8354
8496
|
let statusInFlight = false;
|
|
8355
8497
|
let requestActive = false;
|
|
8356
8498
|
let statusPrimed = false;
|
|
8499
|
+
let focusInFlight = false;
|
|
8357
8500
|
let lastValidStatus = {
|
|
8358
8501
|
agents: [],
|
|
8359
8502
|
stale: false,
|
|
@@ -8468,7 +8611,28 @@ function createAgentStatusRuntime() {
|
|
|
8468
8611
|
clearTimer();
|
|
8469
8612
|
statusContext = ctx;
|
|
8470
8613
|
ctx.ui.setWidget("pi-herdsman", (tui, theme) => {
|
|
8471
|
-
|
|
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
|
+
);
|
|
8472
8636
|
widget.setSnapshot(widgetSnapshot(lastValidStatus));
|
|
8473
8637
|
if (generation === statusGeneration && ctx === statusContext) {
|
|
8474
8638
|
statusWidget = widget;
|
|
@@ -8488,12 +8652,14 @@ function createAgentStatusRuntime() {
|
|
|
8488
8652
|
statusWidget?.dispose();
|
|
8489
8653
|
statusWidget = void 0;
|
|
8490
8654
|
requestActive = false;
|
|
8655
|
+
focusInFlight = false;
|
|
8491
8656
|
statusPrimed = false;
|
|
8492
8657
|
},
|
|
8493
8658
|
prepareSession: (ctx, beforeActivate) => {
|
|
8494
8659
|
clearTimer();
|
|
8495
8660
|
statusRefresh = false;
|
|
8496
8661
|
statusInFlight = false;
|
|
8662
|
+
focusInFlight = false;
|
|
8497
8663
|
removeWidget();
|
|
8498
8664
|
statusWidgetGeneration = 0;
|
|
8499
8665
|
statusContext = void 0;
|
|
@@ -8516,6 +8682,7 @@ function createAgentStatusRuntime() {
|
|
|
8516
8682
|
clearTimer();
|
|
8517
8683
|
statusRefresh = false;
|
|
8518
8684
|
statusInFlight = false;
|
|
8685
|
+
focusInFlight = false;
|
|
8519
8686
|
removeWidget();
|
|
8520
8687
|
statusWidgetGeneration = 0;
|
|
8521
8688
|
statusContext = void 0;
|
|
@@ -8861,39 +9028,50 @@ function visibleAgentSnapshots(snapshot, scope, ownerSessionId) {
|
|
|
8861
9028
|
return visible;
|
|
8862
9029
|
}
|
|
8863
9030
|
function statusBreadcrumb(snapshot, candidate, identity, sameIdentity) {
|
|
8864
|
-
if (!candidate) return ["?"];
|
|
8865
|
-
const
|
|
9031
|
+
if (!candidate) return [{ text: "?" }];
|
|
9032
|
+
const matches = snapshot.agents.filter(
|
|
8866
9033
|
({ state }) => sameIdentity(state, candidate)
|
|
8867
9034
|
);
|
|
9035
|
+
const current = matches.length === 1 ? matches[0] : void 0;
|
|
8868
9036
|
if (!current)
|
|
8869
9037
|
return [
|
|
8870
|
-
"?",
|
|
8871
|
-
|
|
9038
|
+
{ text: "?" },
|
|
9039
|
+
{
|
|
9040
|
+
text: identity.definition && identity.label ? displayIdentity(identity.definition, identity.label) : identity.definition ?? "?"
|
|
9041
|
+
}
|
|
8872
9042
|
];
|
|
8873
9043
|
const definitions = [
|
|
8874
|
-
|
|
9044
|
+
{
|
|
9045
|
+
text: displayIdentity(current.agentDefinition, current.state.agentLabel),
|
|
9046
|
+
sessionId: current.state.piSessionId
|
|
9047
|
+
}
|
|
8875
9048
|
];
|
|
8876
9049
|
const visited = /* @__PURE__ */ new Set([current.state.piSessionId]);
|
|
8877
9050
|
let ownerSessionId = current.state.ownerSessionId;
|
|
8878
9051
|
while (true) {
|
|
8879
|
-
const
|
|
9052
|
+
const parents = snapshot.agents.filter(
|
|
8880
9053
|
({ state }) => state.piSessionId === ownerSessionId
|
|
8881
9054
|
);
|
|
9055
|
+
if (parents.length > 1) return [{ text: "?" }, ...definitions.reverse()];
|
|
9056
|
+
const parent = parents[0];
|
|
8882
9057
|
if (parent) {
|
|
8883
9058
|
if (visited.has(parent.state.piSessionId))
|
|
8884
|
-
return ["?", ...definitions.reverse()];
|
|
9059
|
+
return [{ text: "?" }, ...definitions.reverse()];
|
|
8885
9060
|
visited.add(parent.state.piSessionId);
|
|
8886
|
-
definitions.push(
|
|
8887
|
-
displayIdentity(parent.agentDefinition, parent.state.agentLabel)
|
|
8888
|
-
|
|
9061
|
+
definitions.push({
|
|
9062
|
+
text: displayIdentity(parent.agentDefinition, parent.state.agentLabel),
|
|
9063
|
+
sessionId: parent.state.piSessionId
|
|
9064
|
+
});
|
|
8889
9065
|
ownerSessionId = parent.state.ownerSessionId;
|
|
8890
9066
|
continue;
|
|
8891
9067
|
}
|
|
8892
9068
|
if (snapshot.mailboxes.some(
|
|
8893
9069
|
({ state }) => state.piSessionId === ownerSessionId
|
|
8894
9070
|
))
|
|
8895
|
-
return ["?", ...definitions.reverse()];
|
|
8896
|
-
return snapshot.leadSessionIds.
|
|
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()];
|
|
8897
9075
|
}
|
|
8898
9076
|
}
|
|
8899
9077
|
function sessionAgentIdentity(entries, sessionId) {
|
|
@@ -14354,6 +14532,7 @@ function createManagedAgentLeafStatus(options) {
|
|
|
14354
14532
|
let context;
|
|
14355
14533
|
let generation = 0;
|
|
14356
14534
|
let inFlight = false;
|
|
14535
|
+
let focusInFlight = false;
|
|
14357
14536
|
let breadcrumb;
|
|
14358
14537
|
let ownTools;
|
|
14359
14538
|
const ownToolsSnapshot = () => ownTools ? { ownTools } : {};
|
|
@@ -14368,6 +14547,7 @@ function createManagedAgentLeafStatus(options) {
|
|
|
14368
14547
|
}
|
|
14369
14548
|
context = void 0;
|
|
14370
14549
|
inFlight = false;
|
|
14550
|
+
focusInFlight = false;
|
|
14371
14551
|
};
|
|
14372
14552
|
const refresh = async (ctx, activeGeneration) => {
|
|
14373
14553
|
if (activeGeneration !== generation || ctx !== context || inFlight) return;
|
|
@@ -14400,11 +14580,13 @@ function createManagedAgentLeafStatus(options) {
|
|
|
14400
14580
|
stale: false,
|
|
14401
14581
|
unavailable: true,
|
|
14402
14582
|
breadcrumb: breadcrumb ?? [
|
|
14403
|
-
"?",
|
|
14404
|
-
|
|
14405
|
-
process.env.PI_HERDSMAN_AGENT_DEFINITION
|
|
14406
|
-
|
|
14407
|
-
|
|
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
|
+
}
|
|
14408
14590
|
],
|
|
14409
14591
|
...ownToolsSnapshot(),
|
|
14410
14592
|
identityOnly: true
|
|
@@ -14422,17 +14604,39 @@ function createManagedAgentLeafStatus(options) {
|
|
|
14422
14604
|
const activeGeneration = generation;
|
|
14423
14605
|
context = ctx;
|
|
14424
14606
|
ctx.ui.setWidget("pi-herdsman", (tui, theme) => {
|
|
14425
|
-
|
|
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
|
+
);
|
|
14426
14628
|
next.setSnapshot({
|
|
14427
14629
|
agents: [],
|
|
14428
14630
|
stale: false,
|
|
14429
14631
|
unavailable: true,
|
|
14430
14632
|
breadcrumb: [
|
|
14431
|
-
"?",
|
|
14432
|
-
|
|
14433
|
-
process.env.PI_HERDSMAN_AGENT_DEFINITION
|
|
14434
|
-
|
|
14435
|
-
|
|
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
|
+
}
|
|
14436
14640
|
],
|
|
14437
14641
|
...ownToolsSnapshot(),
|
|
14438
14642
|
identityOnly: true
|
|
@@ -15131,6 +15335,72 @@ function registerManagedAgentRuntime(pi, options) {
|
|
|
15131
15335
|
clearPendingStart: (label, expected) => controller.clearPendingStart(label, expected),
|
|
15132
15336
|
runtimeForLabel: (label) => controller.runtimeForLabel(label),
|
|
15133
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
|
+
},
|
|
15134
15404
|
loadSnapshot: async (ctx) => {
|
|
15135
15405
|
const view = await controller.agentSnapshotView(
|
|
15136
15406
|
ctx,
|
|
@@ -15187,7 +15457,52 @@ This is the session-start definition snapshot. Use list_agents for live Agent st
|
|
|
15187
15457
|
),
|
|
15188
15458
|
identity: (ctx) => managedAgentEnvironmentIdentity(ctx, options.build),
|
|
15189
15459
|
sameIdentity: sameManagedAgentIdentity,
|
|
15190
|
-
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
|
+
}
|
|
15191
15506
|
});
|
|
15192
15507
|
const mutateAgentState = (update) => {
|
|
15193
15508
|
const state = execution.assignment;
|
|
@@ -16196,6 +16511,12 @@ function createLeadSessionStartRuntime(host) {
|
|
|
16196
16511
|
}
|
|
16197
16512
|
}
|
|
16198
16513
|
if (sessionSignal.aborted) return;
|
|
16514
|
+
if (host.roleTransitions.activeManager())
|
|
16515
|
+
await host.publishLeadRole(
|
|
16516
|
+
ctx,
|
|
16517
|
+
"inactive",
|
|
16518
|
+
host.roleTransitions.currentRoleGeneration()
|
|
16519
|
+
);
|
|
16199
16520
|
if (needsLeadRoster) {
|
|
16200
16521
|
try {
|
|
16201
16522
|
const definitions = await host.visibleAgentDefinitionMetadata(
|
|
@@ -16266,6 +16587,7 @@ This is the session-start definition snapshot. Use list_agents for live Agent st
|
|
|
16266
16587
|
host.herdRun.agentStarted();
|
|
16267
16588
|
},
|
|
16268
16589
|
async agentSettled(ctx, aborted) {
|
|
16590
|
+
host.leadInboxRuntime.agentSettled();
|
|
16269
16591
|
host.controller.settleAsks(ctx, host.controller.sessionSignal());
|
|
16270
16592
|
host.herdRun.agentSettled(ctx, aborted);
|
|
16271
16593
|
await host.controller.settleResults(ctx, host.controller.sessionSignal()).catch(() => {
|
|
@@ -17145,41 +17467,67 @@ function registerLeadRuntime(pi, options) {
|
|
|
17145
17467
|
};
|
|
17146
17468
|
const roleHost = options.roleTransitionHost;
|
|
17147
17469
|
let leadMetadataQueue = Promise.resolve();
|
|
17148
|
-
|
|
17149
|
-
|
|
17150
|
-
|
|
17151
|
-
const
|
|
17152
|
-
const
|
|
17153
|
-
const args = [
|
|
17154
|
-
"pane",
|
|
17155
|
-
"report-metadata",
|
|
17156
|
-
paneId,
|
|
17157
|
-
"--source",
|
|
17158
|
-
"pi-herdsman:lead",
|
|
17159
|
-
"--title",
|
|
17160
|
-
mode === "active" ? "chief" : activeRole() === "manager" ? "Pi Herdsman manager" : leadName || "Pi Herdsman lead",
|
|
17161
|
-
...mode === "active" ? ["--token", "pi_herdsman_role=chief"] : mode === "inactive" ? ["--token", `pi_herdsman_role=${activeRole()}`] : ["--clear-token", "pi_herdsman_role"]
|
|
17162
|
-
];
|
|
17163
|
-
if (mode === "inactive" && activeRole() === "lead")
|
|
17164
|
-
args.push(
|
|
17165
|
-
leadName ? "--token" : "--clear-token",
|
|
17166
|
-
leadName ? `pi_herdsman_name=${leadName}` : "pi_herdsman_name"
|
|
17167
|
-
);
|
|
17168
|
-
if (mode !== "inactive" || activeRole() !== "lead")
|
|
17169
|
-
args.push(
|
|
17170
|
-
"--clear-token",
|
|
17171
|
-
"pi_herdsman_herd_run_started_at",
|
|
17172
|
-
"--clear-token",
|
|
17173
|
-
"pi_herdsman_context_percent"
|
|
17174
|
-
);
|
|
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}`;
|
|
17175
17475
|
leadMetadataQueue = leadMetadataQueue.catch(() => {
|
|
17176
17476
|
}).then(async () => {
|
|
17177
|
-
if (generation !== leadRuntime.chiefModeGeneration)
|
|
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
|
+
);
|
|
17178
17512
|
await roleHost.runHerdr(pi, ctx, args, {
|
|
17179
17513
|
noResult: true,
|
|
17180
17514
|
timeout: 1e4
|
|
17181
17515
|
});
|
|
17182
|
-
|
|
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
|
+
}
|
|
17183
17531
|
});
|
|
17184
17532
|
return leadMetadataQueue;
|
|
17185
17533
|
};
|
|
@@ -17192,8 +17540,30 @@ function registerLeadRuntime(pi, options) {
|
|
|
17192
17540
|
);
|
|
17193
17541
|
return;
|
|
17194
17542
|
}
|
|
17543
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
17544
|
+
const sessionGeneration = leadRuntime.sessionGeneration;
|
|
17195
17545
|
leadMetadataQueue = leadMetadataQueue.catch(() => {
|
|
17196
|
-
}).then(() =>
|
|
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
|
+
}
|
|
17197
17567
|
});
|
|
17198
17568
|
};
|
|
17199
17569
|
const persistRole = (role2) => {
|
|
@@ -17408,13 +17778,88 @@ function registerLeadRuntime(pi, options) {
|
|
|
17408
17778
|
const execution = isLead && assignmentVerified ? assignment ? { kind: "managed" } : leadExecution?.valid ? { kind: "ordinary", mode: leadExecution.mode } : void 0 : void 0;
|
|
17409
17779
|
return { ...snapshot, ...execution ? { execution } : {} };
|
|
17410
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
|
+
};
|
|
17411
17855
|
statusRuntime.configure({
|
|
17412
17856
|
...options.statusSnapshotHost,
|
|
17413
17857
|
pendingStartEntries: () => controller.pendingStartEntries(),
|
|
17414
17858
|
hasPendingStart: (label) => controller.hasPendingStart(label),
|
|
17415
17859
|
clearPendingStart: (label, expected) => controller.clearPendingStart(label, expected),
|
|
17416
17860
|
loadSnapshot: (ctx) => loadStatusSnapshot(ctx, controller.sessionSignal(), false),
|
|
17417
|
-
runtimeForLabel: (label) => controller.runtimeForLabel(label)
|
|
17861
|
+
runtimeForLabel: (label) => controller.runtimeForLabel(label),
|
|
17862
|
+
focusTarget
|
|
17418
17863
|
});
|
|
17419
17864
|
let projectRuntime;
|
|
17420
17865
|
const projectHost = options.projectHost;
|
|
@@ -18218,6 +18663,8 @@ function createLeadSupervisionRuntime(host) {
|
|
|
18218
18663
|
let overviewGeneration = 0;
|
|
18219
18664
|
let renderOverview;
|
|
18220
18665
|
let renderWidget;
|
|
18666
|
+
let widgetFocusInFlight = false;
|
|
18667
|
+
let supervisionWidgetGeneration = 0;
|
|
18221
18668
|
const generation = (ctx) => [
|
|
18222
18669
|
host.state.chiefModeGeneration,
|
|
18223
18670
|
host.state.sessionGeneration,
|
|
@@ -18286,6 +18733,108 @@ function createLeadSupervisionRuntime(host) {
|
|
|
18286
18733
|
});
|
|
18287
18734
|
return inFlight;
|
|
18288
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
|
+
};
|
|
18289
18838
|
const nextOverviewGeneration = () => ++overviewGeneration;
|
|
18290
18839
|
const openOverview = async (ctx, uiActions) => {
|
|
18291
18840
|
if (ctx.mode !== "tui") {
|
|
@@ -18591,25 +19140,7 @@ function createLeadSupervisionRuntime(host) {
|
|
|
18591
19140
|
openOverview,
|
|
18592
19141
|
loadSnapshot: loadSupervisionSnapshot,
|
|
18593
19142
|
directReports,
|
|
18594
|
-
focusLead
|
|
18595
|
-
const fresh = await loadSnapshot(ctx);
|
|
18596
|
-
const lead = host.presentationReports(fresh).find((candidate) => candidate.lead === leadId);
|
|
18597
|
-
if (!lead) throw new Error("Lead changed; reopen staff.");
|
|
18598
|
-
const coordination = host.readLeadCoordinationState(
|
|
18599
|
-
host.supervisionRuntime(),
|
|
18600
|
-
lead.lead
|
|
18601
|
-
);
|
|
18602
|
-
if (!coordination || coordination.piSessionId !== lead.lead || lead.instanceId !== void 0 && coordination.instanceId !== lead.instanceId)
|
|
18603
|
-
throw new Error("Lead changed; reopen staff.");
|
|
18604
|
-
const verified = await host.listAllHerdrAgents(host.pi, ctx, ctx.signal);
|
|
18605
|
-
const matches = verified.agents.filter(
|
|
18606
|
-
(candidate) => candidate?.pane_id === lead.paneId && candidate?.tab_id === lead.tabId && candidate?.workspace_id === lead.workspaceId && host.isPiAgent(candidate) && host.herdrSessionId(candidate) === lead.lead
|
|
18607
|
-
);
|
|
18608
|
-
if (matches.length !== 1) throw new Error("Lead changed; reopen staff.");
|
|
18609
|
-
await host.runHerdr(host.pi, ctx, ["agent", "focus", lead.paneId], {
|
|
18610
|
-
signal: ctx.signal
|
|
18611
|
-
});
|
|
18612
|
-
},
|
|
19143
|
+
focusLead,
|
|
18613
19144
|
focusChief: async (ctx) => {
|
|
18614
19145
|
const confirmed = await ctx.ui.confirm(
|
|
18615
19146
|
"Focus Chief?",
|
|
@@ -18655,13 +19186,35 @@ function createLeadSupervisionRuntime(host) {
|
|
|
18655
19186
|
registerWidget: (ctx, role2) => {
|
|
18656
19187
|
if (ctx.mode !== "tui" || !ctx.hasUI) return;
|
|
18657
19188
|
try {
|
|
19189
|
+
const widgetGeneration = ++supervisionWidgetGeneration;
|
|
18658
19190
|
ctx.ui.setWidget("pi-herdsman-staff", (tui, theme) => {
|
|
18659
19191
|
renderWidget = () => tui.requestRender();
|
|
18660
19192
|
return createSupervisionWidget(
|
|
18661
19193
|
() => snapshot,
|
|
18662
19194
|
() => status(ctx),
|
|
18663
19195
|
role2,
|
|
18664
|
-
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
|
+
}
|
|
18665
19218
|
);
|
|
18666
19219
|
});
|
|
18667
19220
|
} catch {
|
|
@@ -18670,6 +19223,8 @@ function createLeadSupervisionRuntime(host) {
|
|
|
18670
19223
|
}
|
|
18671
19224
|
},
|
|
18672
19225
|
removeWidget: (ctx) => {
|
|
19226
|
+
supervisionWidgetGeneration++;
|
|
19227
|
+
widgetFocusInFlight = false;
|
|
18673
19228
|
try {
|
|
18674
19229
|
ctx.ui.setWidget("pi-herdsman-staff", void 0);
|
|
18675
19230
|
} catch {
|
|
@@ -20593,6 +21148,8 @@ function createLeadInboxRuntime(host) {
|
|
|
20593
21148
|
let chiefInboxTimer;
|
|
20594
21149
|
let chiefInboxGeneration = 0;
|
|
20595
21150
|
let chiefInboxAbortController;
|
|
21151
|
+
let currentSubmittedAt;
|
|
21152
|
+
let settledSubmissions = /* @__PURE__ */ new Set();
|
|
20596
21153
|
const chiefStartPreflights = [];
|
|
20597
21154
|
const {
|
|
20598
21155
|
leadRuntime,
|
|
@@ -20651,7 +21208,7 @@ function createLeadInboxRuntime(host) {
|
|
|
20651
21208
|
);
|
|
20652
21209
|
if (index >= 0) chiefStartPreflights.splice(index, 1);
|
|
20653
21210
|
};
|
|
20654
|
-
const messageDelivered = (ctx, record) => record.toSessionId === ctx.sessionManager.getSessionId() && ctx.sessionManager.
|
|
21211
|
+
const messageDelivered = (ctx, record) => record.toSessionId === ctx.sessionManager.getSessionId() && ctx.sessionManager.getEntries().some(
|
|
20655
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
|
|
20656
21213
|
);
|
|
20657
21214
|
const projectAssignmentAuthorized = (authorized, reason) => {
|
|
@@ -20790,11 +21347,25 @@ function createLeadInboxRuntime(host) {
|
|
|
20790
21347
|
return !!chief && !!state2 && chief.piSessionId === record.fromSessionId && chief.leaseId === record.leaseId && record.leadSessionId === sessionId;
|
|
20791
21348
|
}
|
|
20792
21349
|
const scope = await host.currentWorktreeScope(ctx);
|
|
20793
|
-
if (!scope
|
|
20794
|
-
|
|
21350
|
+
if (!scope) throw new Error("Project scope cannot be verified");
|
|
21351
|
+
if (!host.projectAssignmentForScope(scope, sessionId)) return false;
|
|
20795
21352
|
const manager = await currentManager(ctx, scope);
|
|
20796
|
-
|
|
20797
|
-
|
|
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";
|
|
20798
21369
|
};
|
|
20799
21370
|
const startChiefInbox = (ctx) => {
|
|
20800
21371
|
const generation = ++chiefInboxGeneration;
|
|
@@ -20805,6 +21376,9 @@ function createLeadInboxRuntime(host) {
|
|
|
20805
21376
|
const isCurrent = () => generation === chiefInboxGeneration && !chiefInboxAbortController?.signal.aborted;
|
|
20806
21377
|
if (chiefInboxTimer) clearTimeout(chiefInboxTimer);
|
|
20807
21378
|
const transactions = /* @__PURE__ */ new Map();
|
|
21379
|
+
const submittedAt = /* @__PURE__ */ new Map();
|
|
21380
|
+
currentSubmittedAt = submittedAt;
|
|
21381
|
+
settledSubmissions = /* @__PURE__ */ new Set();
|
|
20808
21382
|
const revalidateTransaction = (token, phase) => {
|
|
20809
21383
|
const transaction = token;
|
|
20810
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)) {
|
|
@@ -20823,6 +21397,13 @@ function createLeadInboxRuntime(host) {
|
|
|
20823
21397
|
appendDurableError2(pi, ctx, "pi_herdsman_state_error", error2);
|
|
20824
21398
|
},
|
|
20825
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
|
+
}
|
|
20826
21407
|
const role2 = activeRole();
|
|
20827
21408
|
const supervisorDownlink = role2 === "lead" && (record.kind === "chief_message" || record.kind === "manager_message") || role2 === "manager" && record.kind === "chief_message";
|
|
20828
21409
|
if (supervisorDownlink) return "steer";
|
|
@@ -20921,7 +21502,17 @@ function createLeadInboxRuntime(host) {
|
|
|
20921
21502
|
};
|
|
20922
21503
|
}
|
|
20923
21504
|
importResultBindings2(pi, ctx, resultBindings, record.kind);
|
|
20924
|
-
|
|
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);
|
|
20925
21516
|
if (projectAssignment)
|
|
20926
21517
|
managerDiagnostic("project_assignment_send", {
|
|
20927
21518
|
outcome: "resolved",
|
|
@@ -20967,9 +21558,13 @@ function createLeadInboxRuntime(host) {
|
|
|
20967
21558
|
}
|
|
20968
21559
|
}
|
|
20969
21560
|
},
|
|
20970
|
-
accepted: async (
|
|
21561
|
+
accepted: async (record) => {
|
|
21562
|
+
submittedAt.delete(record.id);
|
|
21563
|
+
settledSubmissions.delete(record.id);
|
|
20971
21564
|
},
|
|
20972
21565
|
rejected: (record) => {
|
|
21566
|
+
submittedAt.delete(record.id);
|
|
21567
|
+
settledSubmissions.delete(record.id);
|
|
20973
21568
|
if (!record.build || !sameRuntimeBuild2(record.build, HERDSMAN_BUILD2))
|
|
20974
21569
|
ctx.ui.notify(
|
|
20975
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.",
|
|
@@ -21084,9 +21679,15 @@ function createLeadInboxRuntime(host) {
|
|
|
21084
21679
|
},
|
|
21085
21680
|
clearTimer,
|
|
21086
21681
|
beginSession: () => {
|
|
21682
|
+
currentSubmittedAt?.clear();
|
|
21683
|
+
settledSubmissions.clear();
|
|
21087
21684
|
chiefInboxAbortController?.abort();
|
|
21088
21685
|
chiefInboxAbortController = new AbortController();
|
|
21089
21686
|
},
|
|
21687
|
+
agentSettled: () => {
|
|
21688
|
+
for (const id of currentSubmittedAt?.keys() ?? [])
|
|
21689
|
+
settledSubmissions.add(id);
|
|
21690
|
+
},
|
|
21090
21691
|
beginShutdown: () => {
|
|
21091
21692
|
chiefInboxGeneration++;
|
|
21092
21693
|
clearTimer();
|
|
@@ -22426,6 +23027,7 @@ function createLeadRoleTransitions(state, host) {
|
|
|
22426
23027
|
suspendMalformedRole,
|
|
22427
23028
|
shouldRestoreChief: (persistedRole) => persistedRole === "chief" && state.controllerRole === "lead" && !state.roleSuspended,
|
|
22428
23029
|
activeChiefGeneration: () => state.chiefMode === "active" ? state.chiefModeGeneration : void 0,
|
|
23030
|
+
currentRoleGeneration: () => state.chiefModeGeneration,
|
|
22429
23031
|
mayPublishLeadPresence: () => state.chiefMode === "inactive" && !state.roleSuspended && host.coordinationHealthy(),
|
|
22430
23032
|
canStartChiefInbox: () => !state.roleSuspended && (state.chiefMode === "inactive" || state.chiefMode === "active") && (state.chiefMode === "active" || host.coordinationHealthy()),
|
|
22431
23033
|
activeManager: () => state.controllerRole === "manager" && !state.roleSuspended,
|
|
@@ -23298,12 +23900,14 @@ ${globalInstructions}
|
|
|
23298
23900
|
definitionRoster: () => startupDefinitionRoster,
|
|
23299
23901
|
isStaffTool: (name) => STAFF_TOOLS.includes(name)
|
|
23300
23902
|
};
|
|
23301
|
-
const initialStatusBreadcrumb = controllerScope.kind === "lead" ? ["lead"] : [
|
|
23302
|
-
"?",
|
|
23303
|
-
|
|
23304
|
-
process.env.PI_HERDSMAN_AGENT_DEFINITION
|
|
23305
|
-
|
|
23306
|
-
|
|
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
|
+
}
|
|
23307
23911
|
];
|
|
23308
23912
|
let ownTools;
|
|
23309
23913
|
const ownToolsSnapshot = () => ownTools ? { ownTools } : {};
|