@maxiaochao/pi-toolkit 0.10.22 → 0.10.24
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## 2026-10-12 - v0.10.24
|
|
2
|
+
|
|
3
|
+
- bundle herdr-subagents v0.4.10: Stabilize concurrency checks and cancellation synchronization on slower CI runners.
|
|
4
|
+
|
|
5
|
+
## 2026-10-12 - v0.10.23
|
|
6
|
+
|
|
7
|
+
- bundle herdr-subagents v0.4.9: Stabilize concurrency checks on slower CI runners.
|
|
1
8
|
## 2026-10-12 - v0.10.22
|
|
2
9
|
|
|
3
10
|
- bundle herdr-subagents v0.4.8: make blocking behave identically in serial and parallel runs and let respond continue a named failed task from its saved session
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maxiaochao/pi-toolkit",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.24",
|
|
4
4
|
"description": "Personal pi coding agent toolkit: scheduler, /btw, /cache_export, apply_patch, browser automation, and bundled theme/skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## 2026-10-12 - v0.4.10
|
|
2
|
+
|
|
3
|
+
- Stabilize concurrency checks and cancellation synchronization on slower CI runners.
|
|
4
|
+
|
|
5
|
+
## 2026-10-12 - v0.4.9
|
|
6
|
+
|
|
7
|
+
- Stabilize concurrency checks on slower CI runners.
|
|
1
8
|
## 2026-10-12 - v0.4.8
|
|
2
9
|
|
|
3
10
|
- make blocking behave identically in serial and parallel runs and let respond continue a named failed task from its saved session
|
|
@@ -75,6 +75,7 @@ function fakeHerdr(mode = "completed") {
|
|
|
75
75
|
let active = 0;
|
|
76
76
|
let maxActive = 0;
|
|
77
77
|
let taskRuns = 0;
|
|
78
|
+
let promptBarrier;
|
|
78
79
|
const agents = new Map();
|
|
79
80
|
const writeAttemptReport = (directory, report) => {
|
|
80
81
|
const task = JSON.parse(readFileSync(join(directory, "task.json"), "utf8"));
|
|
@@ -87,6 +88,18 @@ function fakeHerdr(mode = "completed") {
|
|
|
87
88
|
return {
|
|
88
89
|
calls,
|
|
89
90
|
get maxActive() { return maxActive; },
|
|
91
|
+
expectConcurrentPrompts(count) {
|
|
92
|
+
let release;
|
|
93
|
+
const barrier = { count, arrivals: 0, promise: new Promise((resolve) => { release = resolve; }) };
|
|
94
|
+
const finish = () => {
|
|
95
|
+
if (promptBarrier !== barrier) return;
|
|
96
|
+
promptBarrier = undefined;
|
|
97
|
+
clearTimeout(barrier.timeout);
|
|
98
|
+
release();
|
|
99
|
+
};
|
|
100
|
+
barrier.finish = finish;
|
|
101
|
+
promptBarrier = barrier;
|
|
102
|
+
},
|
|
90
103
|
async createTab(request) {
|
|
91
104
|
calls.push(["createTab", request]);
|
|
92
105
|
taskDirectory = request.env.PI_HERDR_SUBAGENTS_TASK_DIR;
|
|
@@ -104,6 +117,13 @@ function fakeHerdr(mode = "completed") {
|
|
|
104
117
|
const runNumber = ++taskRuns;
|
|
105
118
|
active += 1;
|
|
106
119
|
maxActive = Math.max(maxActive, active);
|
|
120
|
+
const barrier = promptBarrier;
|
|
121
|
+
if (barrier) {
|
|
122
|
+
barrier.timeout ??= setTimeout(barrier.finish, 10_000);
|
|
123
|
+
barrier.arrivals += 1;
|
|
124
|
+
if (barrier.arrivals >= barrier.count) barrier.finish();
|
|
125
|
+
await barrier.promise;
|
|
126
|
+
}
|
|
107
127
|
await new Promise((resolve) => setTimeout(resolve, mode === "ui-blocked" ? 30 : request.prompt.includes("slow") ? 15 : 2));
|
|
108
128
|
if (mode === "delivery-unknown-late") {
|
|
109
129
|
const task = JSON.parse(readFileSync(join(directory, "task.json"), "utf8"));
|
|
@@ -599,6 +619,7 @@ try {
|
|
|
599
619
|
check("configured persona uses displayed effective values", !configuredRun.isError && configuredStart.args.includes("task/model") && configuredStart.args.includes("high") && configuredStart.args.includes("--skill") && configuredStart.args.includes("project-skill"));
|
|
600
620
|
|
|
601
621
|
const analystHerdr = fakeHerdr();
|
|
622
|
+
analystHerdr.expectConcurrentPrompts(3);
|
|
602
623
|
const analystBatch = await executeHerdrSubagents({ label: "independent-analysis", concurrency: 3, tasks: [
|
|
603
624
|
{ name: "sol-audit", prompt: "slow audit", agent: "analyst", model: "vendor/sol" },
|
|
604
625
|
{ name: "grok-replicate", prompt: "slow replicate", agent: "analyst", model: "vendor/grok" },
|
|
@@ -611,6 +632,7 @@ try {
|
|
|
611
632
|
check("completed analyst batch closes its run tab", analystHerdr.calls.at(-1)?.[0] === "closeTab");
|
|
612
633
|
|
|
613
634
|
const batchHerdr = fakeHerdr();
|
|
635
|
+
batchHerdr.expectConcurrentPrompts(2);
|
|
614
636
|
const batchUpdates = [];
|
|
615
637
|
const batch = await executeHerdrSubagents({ label: "reviews", concurrency: 2, tasks: [
|
|
616
638
|
{ name: "one", prompt: "one slow", agent: "explorer" },
|
|
@@ -649,10 +671,32 @@ try {
|
|
|
649
671
|
check("the parallel run closes only after its last blocked lane is answered", !secondOfBoth.isError && secondOfBoth.details.status === "completed" && secondOfBoth.details.tasks.every((entry) => entry.status === "completed") && bothBlockedHerdr.calls.filter(([name]) => name === "closeTab").length === 1);
|
|
650
672
|
const mixedCancelHerdr = fakeHerdr();
|
|
651
673
|
const mixedCancelPrompt = mixedCancelHerdr.promptAgent.bind(mixedCancelHerdr);
|
|
652
|
-
|
|
674
|
+
let markMixedFailureSettled;
|
|
675
|
+
let markMixedWaitStarted;
|
|
676
|
+
const mixedFailureSettled = new Promise((resolve) => { markMixedFailureSettled = resolve; });
|
|
677
|
+
const mixedWaitStarted = new Promise((resolve) => { markMixedWaitStarted = resolve; });
|
|
678
|
+
mixedCancelHerdr.promptAgent = async (request) => {
|
|
679
|
+
if (request.prompt.includes("WAIT")) {
|
|
680
|
+
markMixedWaitStarted();
|
|
681
|
+
if (!request.signal) throw new Error("WAIT prompt requires an AbortSignal");
|
|
682
|
+
return await new Promise((_resolve, reject) => {
|
|
683
|
+
if (request.signal.aborted) reject(request.signal.reason);
|
|
684
|
+
else request.signal.addEventListener("abort", () => reject(request.signal.reason), { once: true });
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
return mixedCancelPrompt(request);
|
|
688
|
+
};
|
|
653
689
|
const mixedCancelController = new AbortController();
|
|
654
|
-
const mixedCancelPromise = executeHerdrSubagents(
|
|
655
|
-
|
|
690
|
+
const mixedCancelPromise = executeHerdrSubagents(
|
|
691
|
+
{ concurrency: 2, tasks: [{ name: "fail-first", prompt: "FAIL", agent: "explorer" }, { name: "cancel-second", prompt: "WAIT", agent: "reviewer" }] },
|
|
692
|
+
mixedCancelController.signal,
|
|
693
|
+
(update) => {
|
|
694
|
+
if (update.details?.tasks?.some((task) => task.status === "failed")) markMixedFailureSettled();
|
|
695
|
+
},
|
|
696
|
+
{ cwd: root },
|
|
697
|
+
{ agentsDirectory: join(process.cwd(), "agents"), herdr: mixedCancelHerdr },
|
|
698
|
+
);
|
|
699
|
+
await Promise.all([mixedFailureSettled, mixedWaitStarted]);
|
|
656
700
|
mixedCancelController.abort(new Error("Parent Pi session closed."));
|
|
657
701
|
const mixedCancel = await mixedCancelPromise;
|
|
658
702
|
check("failed and cancelled production tasks aggregate to partial", mixedCancel.details.status === "partial" && mixedCancel.details.tasks.some((task) => task.status === "failed") && mixedCancel.details.tasks.some((task) => task.status === "cancelled"));
|
|
@@ -1724,6 +1768,7 @@ try {
|
|
|
1724
1768
|
const queuedRecoveryHerdr = fakeHerdr();
|
|
1725
1769
|
await reconcileSubagentRuns(queuedRecoveryRoot, queuedRecoveryHerdr);
|
|
1726
1770
|
check("reconciliation preserves unstarted task records as queued", JSON.parse(readFileSync(join(secondRecoveryTask, "task.json"), "utf8")).status === "queued" && JSON.parse(readFileSync(join(thirdRecoveryTask, "task.json"), "utf8")).status === "queued");
|
|
1771
|
+
queuedRecoveryHerdr.expectConcurrentPrompts(2);
|
|
1727
1772
|
await continueQueuedRun(queuedRunDirectory, queuedRecoveryHerdr);
|
|
1728
1773
|
check("startup continuation restores bounded concurrency", queuedRecoveryHerdr.maxActive === 2 && JSON.parse(readFileSync(join(secondRecoveryTask, "task.json"), "utf8")).status === "completed" && JSON.parse(readFileSync(join(thirdRecoveryTask, "task.json"), "utf8")).status === "completed" && JSON.parse(readFileSync(join(queuedRunDirectory, "run.json"), "utf8")).status === "completed");
|
|
1729
1774
|
const staleContinuationRoot = join(root, "stale-continuation");
|