@afokapu/atdd-flow 0.3.49 → 0.3.50
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 +33 -0
- package/package.json +1 -1
package/extensions/pi/index.ts
CHANGED
|
@@ -318,6 +318,28 @@ export async function awaitLaunchActivation({ root, seat, piSession, herdrSessio
|
|
|
318
318
|
throw new Error("Timed out waiting for verified Flow launch receipt and binding.");
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Host-attached Pi has no launcher variables; it may activate only when Herdr's injected pane identity exactly
|
|
323
|
+
* matches the Desk seat's native scoped binding. This reads the seat and never writes or creates a binding.
|
|
324
|
+
*/
|
|
325
|
+
export async function hostAttachedActivation({ root, seat, herdrSession, pane, piSession, piSessionPath }: { root: string; seat: string; herdrSession?: string; pane?: string; piSession?: string; piSessionPath?: string }) {
|
|
326
|
+
if (!herdrSession || !pane) return false;
|
|
327
|
+
try {
|
|
328
|
+
const runtime = (await readYaml<Seat>(paths(root).seatFile(seat))).runtime;
|
|
329
|
+
return nativeScopedTo(runtime, herdrSession, pane) && sessionUnsetOrEqual(runtime, piSession, piSessionPath);
|
|
330
|
+
} catch { return false; }
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function nativeScopedTo(runtime: Seat["runtime"], herdrSession: string, pane: string) {
|
|
334
|
+
const locator = runtime?.addresses.herdr;
|
|
335
|
+
return runtime?.application === "herdr" && runtime.wake === "native" && !runtime.launch_receipt
|
|
336
|
+
&& typeof locator === "object" && locator.session === herdrSession && locator.pane === pane;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const unsetOrEqual = (recorded: string | undefined, live: string | undefined) => !recorded || recorded === live;
|
|
340
|
+
const sessionUnsetOrEqual = (runtime: Seat["runtime"], piSession?: string, piSessionPath?: string) =>
|
|
341
|
+
unsetOrEqual(runtime?.pi_session, piSession) && unsetOrEqual(runtime?.pi_session_path, piSessionPath);
|
|
342
|
+
|
|
321
343
|
/** Injects the current task-derived action; failure to derive never blocks mail or startup. */
|
|
322
344
|
async function sendTaskDirective(pi: ExtensionAPI, root: string, seat: string) {
|
|
323
345
|
try {
|
|
@@ -337,6 +359,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
337
359
|
if (!root || !seat) return;
|
|
338
360
|
let runtime: ReturnType<typeof createPiRuntime> | undefined;
|
|
339
361
|
let handoverPrompted = false;
|
|
362
|
+
// Host-attach only: mail older than this activation is consumed without a prompt, so a long backlog cannot trigger one turn per message.
|
|
363
|
+
let hostActivatedAt: number | undefined;
|
|
340
364
|
pi.on("turn_end", async (_event, ctx) => {
|
|
341
365
|
if (handoverPrompted) return;
|
|
342
366
|
const percent = ctx.getContextUsage()?.percent;
|
|
@@ -369,6 +393,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
369
393
|
pi.on("session_start", async (_event, ctx) => {
|
|
370
394
|
runtime = createPiRuntime({ root, seat, deferActivation: true, deliver: async (mail, thread, path, sendIfOwned) => {
|
|
371
395
|
if (!sendIfOwned) return false;
|
|
396
|
+
if (hostActivatedAt !== undefined && Date.parse(typeof mail.created_at === "string" ? mail.created_at : "") < hostActivatedAt) return sendIfOwned(async () => undefined);
|
|
372
397
|
return sendIfOwned(async () => {
|
|
373
398
|
// Task state is re-read at every wake, so queued mail cannot carry a stale action.
|
|
374
399
|
await sendTaskDirective(pi, root, seat);
|
|
@@ -383,6 +408,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
383
408
|
const piSession = process.env.ATDD_FLOW_PI_SESSION;
|
|
384
409
|
const herdrSession = process.env.ATDD_FLOW_HERDR_SESSION;
|
|
385
410
|
const pane = process.env.ATDD_FLOW_HERDR_PANE;
|
|
411
|
+
if (!piSession && !herdrSession && !pane) {
|
|
412
|
+
const sessionManager = (ctx as { sessionManager?: { getSessionId?: () => string; getSessionFile?: () => string | undefined } }).sessionManager;
|
|
413
|
+
if (await hostAttachedActivation({ root, seat, herdrSession: process.env.HERDR_SESSION, pane: process.env.HERDR_PANE_ID, piSession: sessionManager?.getSessionId?.(), piSessionPath: sessionManager?.getSessionFile?.() })) {
|
|
414
|
+
hostActivatedAt = Date.now();
|
|
415
|
+
if (await runtime.activate()) pi.sendMessage({ customType: "atdd-flow-start", content: `Native mail active for ${seat} (host-attached). Notices created before this session were not replayed; run atdd-flow status seat ${seat} for unanswered mail.`, display: true, details: { seat, root } }, { triggerTurn: true, deliverAs: "followUp" });
|
|
416
|
+
}
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
386
419
|
if (!piSession || !herdrSession || !pane) return;
|
|
387
420
|
const current = runtime;
|
|
388
421
|
void awaitLaunchActivation({
|