@fitchmultz/pi-subagents 0.52.1 → 0.52.2
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 +6 -0
- package/dist/agent/parking.js +10 -4
- package/dist/agent/process.js +10 -3
- package/dist/build-info.js +1 -1
- package/docs/performance.md +21 -0
- package/package.json +1 -1
- package/src/agent/parking.ts +10 -6
- package/src/agent/process.ts +15 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.52.2] - 2026-10-29
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Avoid rereading historical shell records on every idle parking check while a live shell still blocks parking. Retain one publication path and revalidate its current record, owner token and process birth. Read the full inventory again when that witness disappears or dies. Stop and cold continuation keep their complete inventory checks. No records are pruned and live jobs are not interrupted.
|
|
10
|
+
|
|
5
11
|
## [0.52.1] - 2026-10-29
|
|
6
12
|
|
|
7
13
|
### Fixed
|
package/dist/agent/parking.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import { parkedSchema } from "./contracts.js";
|
|
5
5
|
import { errorText, loadSDK } from "./host.js";
|
|
6
6
|
import { conversation, latest, launchAt, list, read, runDirectory, write } from "./storage.js";
|
|
7
|
-
import { alive, shells, wakeActor } from "./process.js";
|
|
7
|
+
import { alive, shell, shells, wakeActor } from "./process.js";
|
|
8
8
|
import { isHistoryRequest, PARK, PARKED_HISTORY, WAKE, } from "../shared/parking.js";
|
|
9
9
|
import { nativePeerId } from "../pi-intercom/session-targets.js";
|
|
10
10
|
function parkable(session) {
|
|
@@ -74,6 +74,7 @@ export class Parking {
|
|
|
74
74
|
restored;
|
|
75
75
|
since = 0;
|
|
76
76
|
leaf;
|
|
77
|
+
shell;
|
|
77
78
|
base;
|
|
78
79
|
launch;
|
|
79
80
|
token;
|
|
@@ -111,9 +112,7 @@ export class Parking {
|
|
|
111
112
|
}
|
|
112
113
|
/** True once this generation has exited its runtime with or without parked authority. */
|
|
113
114
|
async park(runtime, actor) {
|
|
114
|
-
if (!actor.pi ||
|
|
115
|
-
!this.due(runtime.session, actor.busy) ||
|
|
116
|
-
shells(this.base, this.launch, this.token).some(alive)) {
|
|
115
|
+
if (!actor.pi || !this.due(runtime.session, actor.busy) || this.hasShell()) {
|
|
117
116
|
return false;
|
|
118
117
|
}
|
|
119
118
|
const hold = await this.prepare(runtime, actor.pi, actor);
|
|
@@ -122,6 +121,13 @@ export class Parking {
|
|
|
122
121
|
}
|
|
123
122
|
return hold !== undefined;
|
|
124
123
|
}
|
|
124
|
+
hasShell() {
|
|
125
|
+
const witness = this.shell === undefined ? undefined : shell(this.shell, this.token);
|
|
126
|
+
if (!witness || !alive(witness)) {
|
|
127
|
+
this.shell = shells(this.base, this.launch, this.token).find(alive)?.file;
|
|
128
|
+
}
|
|
129
|
+
return this.shell !== undefined;
|
|
130
|
+
}
|
|
125
131
|
/** Final idle: no run, turn, journal change or viewer since the period began. */
|
|
126
132
|
due(session, busy) {
|
|
127
133
|
const leaf = session.sessionManager.getLeafId();
|
package/dist/agent/process.js
CHANGED
|
@@ -346,6 +346,13 @@ async function waitForExit(targets, timeout) {
|
|
|
346
346
|
await delay(25);
|
|
347
347
|
}
|
|
348
348
|
}
|
|
349
|
+
export function shell(file, token) {
|
|
350
|
+
const identity = published(file, read(file, processSchema));
|
|
351
|
+
if (identity && identity.token !== token) {
|
|
352
|
+
throw new Error("Foreign or missing native shell identity.");
|
|
353
|
+
}
|
|
354
|
+
return identity;
|
|
355
|
+
}
|
|
349
356
|
export function shells(base, launch, token) {
|
|
350
357
|
const dir = path.join(conversation(base, launch), "shells", token);
|
|
351
358
|
if (!fs.existsSync(dir)) {
|
|
@@ -356,11 +363,11 @@ export function shells(base, launch, token) {
|
|
|
356
363
|
.filter((name) => name.endsWith(".json"))
|
|
357
364
|
.map((name) => {
|
|
358
365
|
const file = path.join(dir, name);
|
|
359
|
-
const identity =
|
|
360
|
-
if (!identity
|
|
366
|
+
const identity = shell(file, token);
|
|
367
|
+
if (!identity) {
|
|
361
368
|
throw new Error("Foreign or missing native shell identity.");
|
|
362
369
|
}
|
|
363
|
-
return identity;
|
|
370
|
+
return Object.assign(identity, { file });
|
|
364
371
|
});
|
|
365
372
|
}
|
|
366
373
|
function quote(value) {
|
package/dist/build-info.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const EXTENSION_BUILD = Object.freeze({"version":"0.52.
|
|
1
|
+
export const EXTENSION_BUILD = Object.freeze({"version":"0.52.2","sha256":"cb74b5f47c1e4f8a3a5f27aa2ce24dcb7c240b43aba9472b276a9a815ec30b2a"});
|
package/docs/performance.md
CHANGED
|
@@ -165,6 +165,27 @@ is not an indefinite-idle bound. The
|
|
|
165
165
|
[issue 238 evidence](evidence/issue-238-idle/README.md) has the samples, host
|
|
166
166
|
hashes, native causal chain and limits.
|
|
167
167
|
|
|
168
|
+
### Idle parking shell records
|
|
169
|
+
|
|
170
|
+
An idle child with a live shell now retains one shell publication path. Each
|
|
171
|
+
parking check rereads that record and uses the existing token and process-birth
|
|
172
|
+
validation. A missing or dead witness causes a full inventory read. Stop and cold
|
|
173
|
+
continuation still read the complete inventory.
|
|
174
|
+
|
|
175
|
+
On macOS arm64 with Node 24.22.1, five alternating before/after pairs exercised
|
|
176
|
+
`Parking.park` against one real actor's 615 shell records. Both versions refused
|
|
177
|
+
to park on every check. After one untimed initial inventory, each recurring check
|
|
178
|
+
opened 615 records before and one record after. Median wall time changed from
|
|
179
|
+
80.96 ms to 4.30 ms. Observed ranges were 73.40 to 84.01 ms before and 4.19 to
|
|
180
|
+
4.43 ms after. The baseline was release 0.52.1 at `3650e11`.
|
|
181
|
+
|
|
182
|
+
This isolated helper measurement advanced the idle clock and supplied an idle
|
|
183
|
+
session stub. It read the real records without changing them or the active actor.
|
|
184
|
+
The initial scan and witness-loss scan still read the full inventory. This is not
|
|
185
|
+
an end-to-end model, TUI or fleet CPU speedup claim. The native `park-shells` test
|
|
186
|
+
separately exercises repeated checks, witness death, missing and replaced
|
|
187
|
+
publications, and continuation on official Pi and the maintained fork.
|
|
188
|
+
|
|
168
189
|
## Necessary costs
|
|
169
190
|
|
|
170
191
|
- Native Pi owns context loading and model execution. The extension cannot remove
|
package/package.json
CHANGED
package/src/agent/parking.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { AgentSession, ExtensionAPI } from "@earendil-works/pi-coding-agent
|
|
|
5
5
|
import { parkedSchema, type Launch, type Parked } from "./contracts.ts";
|
|
6
6
|
import { errorText, loadSDK, type WorkingState } from "./host.ts";
|
|
7
7
|
import { conversation, latest, launchAt, list, read, runDirectory, write } from "./storage.ts";
|
|
8
|
-
import { alive, shells, wakeActor } from "./process.ts";
|
|
8
|
+
import { alive, shell, shells, wakeActor } from "./process.ts";
|
|
9
9
|
import {
|
|
10
10
|
isHistoryRequest,
|
|
11
11
|
PARK,
|
|
@@ -138,6 +138,7 @@ export class Parking {
|
|
|
138
138
|
private restored?: string;
|
|
139
139
|
private since = 0;
|
|
140
140
|
private leaf?: string | null;
|
|
141
|
+
private shell?: string;
|
|
141
142
|
private readonly base: string;
|
|
142
143
|
private readonly launch: Launch;
|
|
143
144
|
private readonly token: string;
|
|
@@ -175,11 +176,7 @@ export class Parking {
|
|
|
175
176
|
}
|
|
176
177
|
/** True once this generation has exited its runtime with or without parked authority. */
|
|
177
178
|
async park(runtime: Runtime, actor: Resident): Promise<boolean> {
|
|
178
|
-
if (
|
|
179
|
-
!actor.pi ||
|
|
180
|
-
!this.due(runtime.session, actor.busy) ||
|
|
181
|
-
shells(this.base, this.launch, this.token).some(alive)
|
|
182
|
-
) {
|
|
179
|
+
if (!actor.pi || !this.due(runtime.session, actor.busy) || this.hasShell()) {
|
|
183
180
|
return false;
|
|
184
181
|
}
|
|
185
182
|
const hold = await this.prepare(runtime, actor.pi, actor);
|
|
@@ -188,6 +185,13 @@ export class Parking {
|
|
|
188
185
|
}
|
|
189
186
|
return hold !== undefined;
|
|
190
187
|
}
|
|
188
|
+
private hasShell(): boolean {
|
|
189
|
+
const witness = this.shell === undefined ? undefined : shell(this.shell, this.token);
|
|
190
|
+
if (!witness || !alive(witness)) {
|
|
191
|
+
this.shell = shells(this.base, this.launch, this.token).find(alive)?.file;
|
|
192
|
+
}
|
|
193
|
+
return this.shell !== undefined;
|
|
194
|
+
}
|
|
191
195
|
/** Final idle: no run, turn, journal change or viewer since the period began. */
|
|
192
196
|
private due(session: AgentSession, busy: boolean): boolean {
|
|
193
197
|
const leaf = session.sessionManager.getLeafId();
|
package/src/agent/process.ts
CHANGED
|
@@ -393,7 +393,18 @@ async function waitForExit(targets: readonly OwnedProcess[], timeout: number): P
|
|
|
393
393
|
await delay(25);
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
|
-
export function
|
|
396
|
+
export function shell(file: string, token: string): OwnedProcess | undefined {
|
|
397
|
+
const identity = published(file, read(file, processSchema));
|
|
398
|
+
if (identity && identity.token !== token) {
|
|
399
|
+
throw new Error("Foreign or missing native shell identity.");
|
|
400
|
+
}
|
|
401
|
+
return identity;
|
|
402
|
+
}
|
|
403
|
+
export function shells(
|
|
404
|
+
base: string,
|
|
405
|
+
launch: Launch,
|
|
406
|
+
token: string,
|
|
407
|
+
): (OwnedProcess & { readonly file: string })[] {
|
|
397
408
|
const dir = path.join(conversation(base, launch), "shells", token);
|
|
398
409
|
if (!fs.existsSync(dir)) {
|
|
399
410
|
return [];
|
|
@@ -403,11 +414,11 @@ export function shells(base: string, launch: Launch, token: string): OwnedProces
|
|
|
403
414
|
.filter((name) => name.endsWith(".json"))
|
|
404
415
|
.map((name) => {
|
|
405
416
|
const file = path.join(dir, name);
|
|
406
|
-
const identity =
|
|
407
|
-
if (!identity
|
|
417
|
+
const identity = shell(file, token);
|
|
418
|
+
if (!identity) {
|
|
408
419
|
throw new Error("Foreign or missing native shell identity.");
|
|
409
420
|
}
|
|
410
|
-
return identity;
|
|
421
|
+
return Object.assign(identity, { file });
|
|
411
422
|
});
|
|
412
423
|
}
|
|
413
424
|
function quote(value: string): string {
|