@afokapu/atdd-flow 0.3.81 → 0.3.82
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/extensions/pi/index.ts +19 -1
- package/package.json +1 -1
package/extensions/pi/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import { mkdir, readFile, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
5
|
import { parse, stringify } from "yaml";
|
|
5
6
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
7
|
import { clearStaleRuntimeState, clearRuntimeState, heartbeatRuntimeState, ownsRuntimeState, recordMailActivation, registerRuntimeState, runtimeOwnerToken, withOwnedRuntimeState } from "../../src/runtime-state";
|
|
@@ -413,6 +414,23 @@ function unmatchedWakeRequest(branch: TranscriptEntry[], identity: WakeIdentity)
|
|
|
413
414
|
/** The recovery bundle is advisory; any failure falls back to the plain prompt. */
|
|
414
415
|
const recovery = (root: string, seat: string, mode: RecoveryMode, session?: string) => recoveryFor(root, seat, mode, session).catch(() => undefined);
|
|
415
416
|
|
|
417
|
+
type ReceiptWithLaunchRequest = { handover_replacement?: { request?: unknown } };
|
|
418
|
+
|
|
419
|
+
/** The request recorded in this seat's own launcher receipt, or undefined when the seat, receipt or embed is absent or unreadable. */
|
|
420
|
+
async function embeddedLaunchRequest(root: string, seat: string) {
|
|
421
|
+
try {
|
|
422
|
+
const receiptPath = (await readYaml<Seat>(paths(root).seatFile(seat))).runtime?.launch_receipt;
|
|
423
|
+
if (!receiptPath) return undefined;
|
|
424
|
+
return (parse(await readFile(receiptPath, "utf8")) as ReceiptWithLaunchRequest | null)?.handover_replacement?.request;
|
|
425
|
+
} catch { return undefined; }
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** True when this Pi was launched by the active request: its own receipt embeds exactly that request, so it is the consumed successor, not a source to shut down. */
|
|
429
|
+
async function launchedByRequest(root: string, seat: string, request: unknown) {
|
|
430
|
+
const embedded = await embeddedLaunchRequest(root, seat);
|
|
431
|
+
return embedded !== undefined && isDeepStrictEqual(embedded, request);
|
|
432
|
+
}
|
|
433
|
+
|
|
416
434
|
/** Desk mail is authoritative; the queue is a bounded delivery index and fs.watch only shortens latency. */
|
|
417
435
|
export default function (pi: ExtensionAPI, environment: NodeJS.ProcessEnv = process.env) {
|
|
418
436
|
const root = environment.ATDD_WORKFLOW_ROOT;
|
|
@@ -456,7 +474,7 @@ export default function (pi: ExtensionAPI, environment: NodeJS.ProcessEnv = proc
|
|
|
456
474
|
if (!sourceSession) return;
|
|
457
475
|
try {
|
|
458
476
|
const request = await readHandoverReplacement(root, seat);
|
|
459
|
-
if (request.source.pi_session !== sourceSession) return;
|
|
477
|
+
if (request.source.pi_session !== sourceSession || await launchedByRequest(root, seat, request)) return;
|
|
460
478
|
pi.sendMessage({
|
|
461
479
|
customType: "atdd-flow-context-handover-replacement",
|
|
462
480
|
content: `SYSTEM: Validated Path-A request for ${seat} observed. The successor must not start until this Pi has shut down and Herdr reports the exact source pane released.`,
|