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