@aeondave/pi-persona 1.16.1 → 1.16.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/AGENTS.md +2 -0
- package/CHANGELOG.md +10 -0
- package/README.md +6 -2
- package/docs/ARCHITECTURE.md +9 -9
- package/docs/REFERENCE.md +4 -3
- package/package.json +2 -2
- package/personas/elite.md +2 -2
- package/src/core/brief.ts +2 -2
- package/src/core/config.ts +4 -4
- package/src/engine/async.ts +2 -12
- package/src/extension.ts +52 -26
- package/src/telemetry/contract.ts +1 -1
- package/src/tools/delegate-tool.ts +1 -1
package/AGENTS.md
CHANGED
|
@@ -115,6 +115,8 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
|
|
|
115
115
|
never double-reported; snapshots leave running children active. In interactive sessions `delegate`
|
|
116
116
|
is background-by-default (`sync: true` opts a call out; headless `pi -p` defaults to sync so the
|
|
117
117
|
single turn carries the result). An explicit `async` flag takes precedence over `sync` and the default.
|
|
118
|
+
Periodic async status is a durable, expandable operator-only entry, never a model wake or context
|
|
119
|
+
message; completions, asks, unread bus messages, and newly-stalled alerts keep their actionable paths.
|
|
118
120
|
Message ids and run ids are separate: `intercom message { messageId }` retrieves retained bus
|
|
119
121
|
text, `intercom result { to: runId }` retrieves a settled run. Both explicit and automatic inbox
|
|
120
122
|
drains retain bounded history (256 messages / 256,000 body characters). Ask settlement clears
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,16 @@ used in its [GitHub releases](https://github.com/AeonDave/pi-persona/releases).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.16.2](https://github.com/AeonDave/pi-persona/releases/tag/v1.16.2) - 2026-10-08
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Keep routine async check-ins visible as durable, expandable operator status entries without starting
|
|
14
|
+
model turns; completion, ask, unread-message, and newly-stalled follow-ups remain actionable wakes.
|
|
15
|
+
- Show periodic status while the supervisor is busy, without consuming pending actionable messages.
|
|
16
|
+
- Keep stall/message delivery independent of status-card persistence failures and report those failures
|
|
17
|
+
non-fatally. Align supervisor guidance and documentation with the status-only check-in contract.
|
|
18
|
+
|
|
9
19
|
## [1.16.1](https://github.com/AeonDave/pi-persona/releases/tag/v1.16.1) - 2026-10-06
|
|
10
20
|
|
|
11
21
|
### Fixed
|
package/README.md
CHANGED
|
@@ -39,9 +39,9 @@ your customizations.
|
|
|
39
39
|
| Install option | Command |
|
|
40
40
|
|---|---|
|
|
41
41
|
| This project only | `pi install -l npm:@aeondave/pi-persona` |
|
|
42
|
-
| Pin this release | `pi install npm:@aeondave/pi-persona@1.16.
|
|
42
|
+
| Pin this release | `pi install npm:@aeondave/pi-persona@1.16.2` |
|
|
43
43
|
| Update an unpinned install | `pi update npm:@aeondave/pi-persona` |
|
|
44
|
-
| Git alternative | `pi install git:github.com/AeonDave/pi-persona@v1.16.
|
|
44
|
+
| Git alternative | `pi install git:github.com/AeonDave/pi-persona@v1.16.2` |
|
|
45
45
|
|
|
46
46
|
Restart Pi after updating. For a pinned install, install the new version or tag explicitly.
|
|
47
47
|
If switching from a Git install, remove that source first with
|
|
@@ -75,6 +75,10 @@ the outcome. This is one possible workflow, not a fixed pipeline; dependent step
|
|
|
75
75
|
Interactive delegation runs in the background by default. Results return automatically when the
|
|
76
76
|
supervisor is free; you can inspect or redirect a worker at any point.
|
|
77
77
|
|
|
78
|
+
Periodic async status appears as a durable, expandable operator-only card (five minutes by default); it
|
|
79
|
+
does not start a model turn or enter model context. Completions, blocking asks, unread worker messages,
|
|
80
|
+
and newly stalled workers still use their actionable notification paths.
|
|
81
|
+
|
|
78
82
|
Launch and control cards show the worker's name. Steering receipts preview the message sent;
|
|
79
83
|
expand with `Ctrl+O` (the default binding) to read it in full. A queued message is not proof that
|
|
80
84
|
the worker has acted on it.
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -644,19 +644,19 @@ a local process able to modify that directory is already inside the trust bounda
|
|
|
644
644
|
if delayed input hooks outlast the active run; no supported post-admission ordering guarantee
|
|
645
645
|
has been established. The supervisor otherwise returns control and
|
|
646
646
|
goes idle until a wake; the **peek watchdog** fires while async children run but stays SILENT unless
|
|
647
|
-
there is something to act on — a healthy background run should not interrupt. It
|
|
648
|
-
|
|
649
|
-
`STALL_FLAG_MS` (90s) stall window (a focused *possibly stuck* alert, framed patience-first —
|
|
650
|
-
leg, don't probe its environment)
|
|
651
|
-
(`PI_PERSONA_CHECKIN_MS`, ~5 min, `0` disables)
|
|
652
|
-
full transcripts —
|
|
647
|
+
there is something to act on — a healthy background run should not interrupt. It can start a model turn
|
|
648
|
+
only for actionable events: a **fast** wakeup (`PI_PERSONA_PEEK_MS`, ~30s, `0` disables) when a child NEWLY
|
|
649
|
+
crosses the `STALL_FLAG_MS` (90s) stall window (a focused *possibly stuck* alert, framed patience-first —
|
|
650
|
+
ask the leg, don't probe its environment); messages to the supervisor also wake it. The **slow routine status card**
|
|
651
|
+
(`PI_PERSONA_CHECKIN_MS`, ~5 min, `0` disables) is a durable, expandable operator-only session entry
|
|
652
|
+
with the compact ProgressView digest — never full transcripts — and does not start a model turn. Live supervisor asks
|
|
653
653
|
are derived from the exact engine handle's optional `isWaitingForSupervisor` predicate: tree/F9/peek
|
|
654
654
|
show waiting, and the watchdog suppresses false stalls until the ask settles. Parent clocks advance
|
|
655
655
|
with child engine events (liveness, not proof of useful work); parents are waiting only when every
|
|
656
656
|
live descendant branch is waiting.
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
657
|
+
Stall alerts and live messages let an idle supervisor steer/stop a wedged child even with NO completion
|
|
658
|
+
fired; the enforcing backstop is the engines' hard wall-clock cap (above). The full digest is also on demand
|
|
659
|
+
via `/peek`.
|
|
660
660
|
Progress tokens are labelled **cumulative input/output**, not context occupancy; cache-read/write
|
|
661
661
|
accounting is separate. Do not infer a full context window from that counter. File age or line-count
|
|
662
662
|
changes alone likewise do not establish a stall, a passing test or completed work.
|
package/docs/REFERENCE.md
CHANGED
|
@@ -147,8 +147,9 @@ session entry remain authoritative. `agent_name` is unavailable while Exocom is
|
|
|
147
147
|
also rejects empty, unsafe, generic, or reserved persona/agent names.
|
|
148
148
|
|
|
149
149
|
Naming needs one tool action, not a recurring prose confirmation. A stable system-level runtime rule
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
distinguishes skill-prefetch bookkeeping from new user requests; it neither hides updates nor rewrites
|
|
151
|
+
conversation history. Periodic async status is a durable operator-only entry, not a model wake or
|
|
152
|
+
context message. After updating extension code, use Pi's `/reload` once
|
|
152
153
|
delegated work has settled (or restart/resume); `/persona reload` reloads definitions only.
|
|
153
154
|
|
|
154
155
|
With Exocom active, the corresponding tool is `exocom_name`. The first incoming peer wake can be the
|
|
@@ -446,7 +447,7 @@ from a different cwd. Pi owns `--model <provider/id>`, `--thinking
|
|
|
446
447
|
| `PI_PERSONA_SEED` | off | `on` auto-installs bundled personas on first run |
|
|
447
448
|
| `PI_PERSONA_BROKER` | on | `off` disables the cross-process comm plane for child, worktree, and MCP legs |
|
|
448
449
|
| `PI_PERSONA_PEEK_MS` | 30000 | fast async stall/message wakeup tick; `0` disables it |
|
|
449
|
-
| `PI_PERSONA_CHECKIN_MS` | 300000 |
|
|
450
|
+
| `PI_PERSONA_CHECKIN_MS` | 300000 | durable expandable operator status entry while async children run; does not start a model turn; `0` disables it |
|
|
450
451
|
| `PI_PERSONA_AGENT_MAX_MS` | off | opt-in per-agent hard wall-clock cap; unset leaves healthy work running |
|
|
451
452
|
| `PI_PERSONA_AGENT_STARTUP_MS` | 300000 | startup deadline for a child that makes no progress; `0` disables it. Like the idle window, a leg waiting on a supervisor reply is not idle: the watchdog re-arms while its ask is pending, instead of killing on a blind ceiling |
|
|
452
453
|
| `PI_PERSONA_NUDGE` | on | `off` silences delegation and persistence nudges; synchronous checkpoints stay in model-visible tool results and are mirrored to durable, expandable TUI-only cards |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aeondave/pi-persona",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Supervised multi-agent orchestration for Pi — async workers, live steering, cross-session collaboration, and switchable personas.",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"typescript": "^7.0.2"
|
|
105
105
|
},
|
|
106
106
|
"pi": {
|
|
107
|
-
"image": "https://raw.githubusercontent.com/AeonDave/pi-persona/v1.16.
|
|
107
|
+
"image": "https://raw.githubusercontent.com/AeonDave/pi-persona/v1.16.2/assets/banner.png",
|
|
108
108
|
"video": "https://cdn.jsdelivr.net/gh/AeonDave/pi-persona@6655d281ce7965a75303dd704bd29db73db04070/assets/demo1.mp4",
|
|
109
109
|
"extensions": [
|
|
110
110
|
"./src/extension.ts"
|
package/personas/elite.md
CHANGED
|
@@ -228,8 +228,8 @@ the user with the packet — don't silently swap to route around it.
|
|
|
228
228
|
## Supervise async runs — trust the hand-off, don't hover
|
|
229
229
|
A background leg reports back on its own the moment it finishes (`sync: true` to block instead) — so
|
|
230
230
|
dispatch and keep working the thread you kept; you do NOT watch it or poll its progress. pi-persona
|
|
231
|
-
wakes you when a leg stalls or messages you
|
|
232
|
-
|
|
231
|
+
wakes you when a leg newly stalls or messages you; periodic status cards are visible to the operator
|
|
232
|
+
but do not interrupt your turn. React to actionable wakeups; otherwise let a healthy run go, its result returns on
|
|
233
233
|
its own. A long scan, a big generation, or a blocking command looks identical to a stall from the
|
|
234
234
|
outside, so default to patience. `steer` only on CONFIRMED drift (wrong target, rabbit hole, a
|
|
235
235
|
sustained stall), and steer by asking the leg for a one-line status — never run commands in its
|
package/src/core/brief.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { inventedExocomNameHint, inventedLegNameHint } from "./naming.ts";
|
|
|
19
19
|
|
|
20
20
|
/** Stable protocol guidance, not a user-like reminder or the optional behavioral spine. */
|
|
21
21
|
export const RUNTIME_CONTEXT_GUIDANCE =
|
|
22
|
-
"[pi-persona] Runtime context: identity metadata, skill-prefetch catalogs
|
|
22
|
+
"[pi-persona] Runtime context: identity metadata, skill-prefetch catalogs and clock snapshots " +
|
|
23
23
|
"are background context, not new user requests. Perform required setup once. Do not repeat identity confirmations " +
|
|
24
24
|
"or narrate unchanged setup/skill bookkeeping after each tool result. Apply relevant skill instructions; availability " +
|
|
25
25
|
"alone is not evidence that a skill was loaded or used. Report material changes, blockers and decisions. Explain " +
|
|
@@ -148,7 +148,7 @@ export function buildDelegationBrief(input: BriefInput): string | undefined {
|
|
|
148
148
|
|
|
149
149
|
const example = input.agents.some((a) => a.name === "operator") ? "operator" : (listed[0]?.name ?? "operator");
|
|
150
150
|
const delivery = input.asyncDefault
|
|
151
|
-
? "they run in the background and each result returns to you automatically as a follow-up — do useful independent work while children run; when only children remain, end your turn and let completion wake you. Do not monitor, poll, or automatically wait for all children (don't watch or poll a healthy leg). Join intentionally only when a result is needed before your next step; you're
|
|
151
|
+
? "they run in the background and each result returns to you automatically as a follow-up — do useful independent work while children run; when only children remain, end your turn and let completion wake you. Do not monitor, poll, or automatically wait for all children (don't watch or poll a healthy leg). Join intentionally only when a result is needed before your next step; you're woken if a child newly stalls or messages you. Periodic status cards are for the operator and do not interrupt your work"
|
|
152
152
|
: "the call returns when they finish";
|
|
153
153
|
const callFields = [`agent: "${example}"`, `task: "<self-contained bounded task + success signal>"`];
|
|
154
154
|
if (input.requireBrief) {
|
package/src/core/config.ts
CHANGED
|
@@ -29,10 +29,10 @@ export interface PiPersonaConfig {
|
|
|
29
29
|
* healthy run never interrupts. On by default (30000); PI_PERSONA_PEEK_MS=0 opts out (the routine
|
|
30
30
|
* check-in below is independent). */
|
|
31
31
|
peekEveryMs: number;
|
|
32
|
-
/** Routine check-in interval (ms): how often, while async children run, the
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
/** Routine check-in interval (ms): how often, while async children run, the operator gets a durable,
|
|
33
|
+
* expandable compact progress entry. It is status-only and does not start a model turn. Deliberately
|
|
34
|
+
* slow and independent of the fast stall/message wakeup. 300000 (5 min) by default;
|
|
35
|
+
* PI_PERSONA_CHECKIN_MS=0 opts out. */
|
|
36
36
|
checkInEveryMs: number;
|
|
37
37
|
/** Per-agent hard wall-clock cap (ms): an OPT-IN lifetime ceiling. When set (>0) it settles even
|
|
38
38
|
* a busy-but-non-converging child the idle watchdog (reset on every event) never catches.
|
package/src/engine/async.ts
CHANGED
|
@@ -547,19 +547,9 @@ export function buildPeekAlert(stuck: AsyncRun[], opts: { now: number }): string
|
|
|
547
547
|
].join("\n");
|
|
548
548
|
}
|
|
549
549
|
|
|
550
|
-
/**
|
|
551
|
-
* The routine check-in: the full progress view ({@link buildPeekDigest}) plus a one-line framing that
|
|
552
|
-
* this is an occasional glance, not a poll — step in only if a leg is off-track or wedged, otherwise
|
|
553
|
-
* carry on. It fires on the slow check-in cadence (config.checkInEveryMs) and is the counterpart to the
|
|
554
|
-
* fast {@link buildPeekAlert} stall signal: it catches a leg going the WRONG way (not stalled, just
|
|
555
|
-
* wrong) before it burns the budget, without waking the supervisor every tick.
|
|
556
|
-
*/
|
|
550
|
+
/** The operator-only periodic status card. It carries the progress digest without model instructions. */
|
|
557
551
|
export function buildCheckIn(runs: AsyncRun[], opts: { now: number; stallMs: number; waitingForSupervisor?: ReadonlySet<string> }): string {
|
|
558
|
-
return (
|
|
559
|
-
`${buildPeekDigest(runs, opts)}\n\n` +
|
|
560
|
-
"Routine check-in — internal status update, not a new task: don't reply just to acknowledge unchanged progress. Continue your current " +
|
|
561
|
-
"work and step in only if a leg is off-track (wrong target, rabbit hole) or wedged. Each result returns to you on its own."
|
|
562
|
-
);
|
|
552
|
+
return `Async activity · routine check-in\n\n${buildPeekDigest(runs, opts)}`;
|
|
563
553
|
}
|
|
564
554
|
|
|
565
555
|
/**
|
package/src/extension.ts
CHANGED
|
@@ -181,6 +181,10 @@ interface NudgeEntry {
|
|
|
181
181
|
content: string;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
interface CheckInEntry {
|
|
185
|
+
content: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
184
188
|
/**
|
|
185
189
|
* Keep the immediate operator-facing settlement toast and the semantic completion delivery on one
|
|
186
190
|
* path. A supervisor-requested abort is terminal, but it is not an execution error: the tracker
|
|
@@ -335,9 +339,25 @@ export default function piPersona(pi: ExtensionAPI, options: PiPersonaOptions =
|
|
|
335
339
|
const hint = hasMore ? `\n${theme.fg("dim", expandDetailHint())}` : "";
|
|
336
340
|
return new Text(`${title}\n${theme.fg("toolOutput", preview.text)}${hint}`, 0, 0);
|
|
337
341
|
});
|
|
342
|
+
pi.registerEntryRenderer("pi-persona-status", (entry, { expanded }, theme) => {
|
|
343
|
+
const data = entry.data as CheckInEntry;
|
|
344
|
+
const full = sanitizeTerminalText(data.content || "Async activity · routine check-in");
|
|
345
|
+
const title = theme.fg("accent", theme.bold("pi-persona · async status"));
|
|
346
|
+
if (expanded) return new Text(`${title}\n${theme.fg("toolOutput", full)}`, 0, 0);
|
|
347
|
+
// The entry title already labels this as async status. Spend the compact preview on the
|
|
348
|
+
// aggregate and first run row so the operator sees the selected alias and usage label.
|
|
349
|
+
const digestLines = full.split("\n").slice(1).filter((line) => line.trim().length > 0);
|
|
350
|
+
const preview = compactVisibleText(digestLines.slice(0, 2).join("\n") || full, { maxLines: 2, maxLineChars: 140 });
|
|
351
|
+
const hasMore = preview.truncated || digestLines.length > 2;
|
|
352
|
+
const hint = hasMore ? `\n${theme.fg("dim", expandDetailHint())}` : "";
|
|
353
|
+
return new Text(`${title}\n${theme.fg("toolOutput", preview.text)}${hint}`, 0, 0);
|
|
354
|
+
});
|
|
338
355
|
const appendNudgeEntry = (content: string): void => {
|
|
339
356
|
pi.appendEntry("pi-persona-nudge", { content } satisfies NudgeEntry);
|
|
340
357
|
};
|
|
358
|
+
const appendCheckInEntry = (content: string): void => {
|
|
359
|
+
pi.appendEntry("pi-persona-status", { content } satisfies CheckInEntry);
|
|
360
|
+
};
|
|
341
361
|
const appendCommandResult = (
|
|
342
362
|
label: string,
|
|
343
363
|
content: string,
|
|
@@ -1176,15 +1196,11 @@ export default function piPersona(pi: ExtensionAPI, options: PiPersonaOptions =
|
|
|
1176
1196
|
});
|
|
1177
1197
|
});
|
|
1178
1198
|
|
|
1179
|
-
//
|
|
1180
|
-
//
|
|
1181
|
-
//
|
|
1182
|
-
// that NEWLY looks stalled or an unread message (fast, PI_PERSONA_PEEK_MS granularity), and a routine
|
|
1183
|
-
// direction check-in (slow, PI_PERSONA_CHECKIN_MS) to catch a leg going off-track. Bounded: unref'd,
|
|
1184
|
-
// self-stops when no runs remain.
|
|
1199
|
+
// Async-run watchdog: the fast PI_PERSONA_PEEK_MS cadence wakes the supervisor only for a newly-stalled
|
|
1200
|
+
// leg or unread message. The slower PI_PERSONA_CHECKIN_MS cadence appends a durable operator-only status
|
|
1201
|
+
// card; it does not start a model turn. Bounded: unref'd, self-stops when no runs remain.
|
|
1185
1202
|
let peekTimer: ReturnType<typeof setInterval> | undefined;
|
|
1186
|
-
//
|
|
1187
|
-
// Gates the slow check-in cadence so it counts from the last time we actually surfaced something.
|
|
1203
|
+
// Last time progress status was surfaced (a new stall alert or routine card), for the slow cadence.
|
|
1188
1204
|
let lastPeekAt = 0;
|
|
1189
1205
|
function stopPeek(): void {
|
|
1190
1206
|
if (peekTimer) {
|
|
@@ -1193,9 +1209,8 @@ export default function piPersona(pi: ExtensionAPI, options: PiPersonaOptions =
|
|
|
1193
1209
|
}
|
|
1194
1210
|
}
|
|
1195
1211
|
function startPeek(): void {
|
|
1196
|
-
//
|
|
1197
|
-
//
|
|
1198
|
-
// ticks at the faster of the two (a disabled one is Infinity, so it never drives).
|
|
1212
|
+
// Fast wakeups and routine cards opt out independently. The carrier timer runs while EITHER is on
|
|
1213
|
+
// and ticks at the faster of the two (a disabled one is Infinity, so it never drives).
|
|
1199
1214
|
const fastMs = config.peekEveryMs > 0 ? config.peekEveryMs : Number.POSITIVE_INFINITY;
|
|
1200
1215
|
const checkMs = config.checkInEveryMs > 0 ? config.checkInEveryMs : Number.POSITIVE_INFINITY;
|
|
1201
1216
|
const tickMs = Math.min(fastMs, checkMs);
|
|
@@ -1207,31 +1222,42 @@ export default function piPersona(pi: ExtensionAPI, options: PiPersonaOptions =
|
|
|
1207
1222
|
stopPeek();
|
|
1208
1223
|
return;
|
|
1209
1224
|
}
|
|
1210
|
-
// Only peek a free, unqueued supervisor: an idle delivery triggers a clean turn, while a
|
|
1211
|
-
// busy one would pile up as a sticky follow-up. Skipping is safe — the next tick re-surfaces.
|
|
1212
|
-
if (!canDeliverPersonaNotification(orchestrating, processingDeferredOrchestration, lastCtx?.isIdle?.() === true) || lastCtx?.hasPendingMessages?.() === true) return;
|
|
1213
|
-
// The peek is NOT a poll. Two signals, two cadences: (1) the FAST wakeup (PI_PERSONA_PEEK_MS) —
|
|
1214
|
-
// a leg that NEWLY crossed the stall window, or an unread sub-agent message — the "is it dead or
|
|
1215
|
-
// wedged" check; (2) the SLOW routine check-in (PI_PERSONA_CHECKIN_MS) — a progress digest that
|
|
1216
|
-
// catches a leg going off-track (not stalled, just wrong) early. A healthy, quiet run between
|
|
1217
|
-
// check-ins produces no wakeup. Completions always arrive on their own (completionNotifier), and
|
|
1218
|
-
// the full status view stays on demand via `/peek`.
|
|
1219
1225
|
const now = Date.now();
|
|
1226
|
+
const dueCheckIn = config.checkInEveryMs > 0 && now - lastPeekAt >= config.checkInEveryMs;
|
|
1227
|
+
// Routine status is durable operator history, not a model notification: append it on cadence even
|
|
1228
|
+
// while a turn is busy or human input is queued. This keeps visibility independent of wake eligibility.
|
|
1229
|
+
if (dueCheckIn) {
|
|
1230
|
+
lastPeekAt = now;
|
|
1231
|
+
try {
|
|
1232
|
+
appendCheckInEntry(buildCheckIn(runs, { now, stallMs: STALL_FLAG_MS, waitingForSupervisor: waitingAsyncRunIds() }));
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
const detail = compactInlineText(error instanceof Error ? error.message : String(error), { maxChars: 120 });
|
|
1235
|
+
const warning = `pi-persona: could not append async status entry${detail ? ` (${detail})` : ""}`;
|
|
1236
|
+
let shown = false;
|
|
1237
|
+
if (lastCtx?.hasUI) {
|
|
1238
|
+
try { lastCtx.ui.notify(warning, "warning"); shown = true; } catch { /* session may be tearing down */ }
|
|
1239
|
+
}
|
|
1240
|
+
if (!shown || process.env.PI_PERSONA_DEBUG) {
|
|
1241
|
+
try { process.stderr.write(`[pi-persona] ${warning}\n`); } catch { /* diagnostics must not block an actionable wake */ }
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
// Poll actionable model wakes only for a free supervisor. A busy/queued session keeps messages
|
|
1246
|
+
// unread and leaves stall state untouched for the next tick, rather than stacking sticky follow-ups.
|
|
1247
|
+
if (!canDeliverPersonaNotification(orchestrating, processingDeferredOrchestration, lastCtx?.isIdle?.() === true) || lastCtx?.hasPendingMessages?.() === true) return;
|
|
1248
|
+
// The watchdog is not a model polling loop: the FAST path wakes on newly-stalled legs or unread
|
|
1249
|
+
// progress messages. Completions arrive separately through completionNotifier; `/peek` remains on demand.
|
|
1220
1250
|
const fast = config.peekEveryMs > 0;
|
|
1221
1251
|
const waitingRuns = waitingAsyncRunIds();
|
|
1222
1252
|
const stuck = fast ? peekWatcher.poll(runs, now, STALL_FLAG_MS, waitingRuns) : [];
|
|
1223
1253
|
// Drain only progress messages; blocking asks (expectsReply) are surfaced by the intercom
|
|
1224
1254
|
// notifier and left for the `intercom inbox` tool — so peek never double-shows them.
|
|
1225
1255
|
const unread = fast ? bus.takeWhere(SUPERVISOR, (e) => !e.expectsReply) : [];
|
|
1226
|
-
|
|
1227
|
-
if (stuck.length === 0 && unread.length === 0 && !dueCheckIn) return; // healthy + quiet ⇒ stay silent
|
|
1256
|
+
if (stuck.length === 0 && unread.length === 0) return; // healthy + quiet ⇒ stay silent
|
|
1228
1257
|
const parts: string[] = [];
|
|
1229
1258
|
if (stuck.length > 0) parts.push(buildPeekAlert(stuck, { now }));
|
|
1230
|
-
else if (dueCheckIn) parts.push(buildCheckIn(runs, { now, stallMs: STALL_FLAG_MS, waitingForSupervisor: waitingRuns }));
|
|
1231
1259
|
if (unread.length > 0) parts.push(`📨 from sub-agents:\n${fenceUntrusted(formatInbox(unread))}`);
|
|
1232
|
-
|
|
1233
|
-
// message-only wake — else a chatty child would postpone the routine off-track glance forever.
|
|
1234
|
-
if (stuck.length > 0 || dueCheckIn) lastPeekAt = now;
|
|
1260
|
+
if (stuck.length > 0) lastPeekAt = now; // message-only wakes do not postpone the routine status cadence
|
|
1235
1261
|
try {
|
|
1236
1262
|
sendPersonaFollowUp(pi, `[pi-persona] ${parts.join("\n\n")}`);
|
|
1237
1263
|
} catch {
|
|
@@ -4,7 +4,7 @@ export const TELEMETRY_EVENT_NAME = "pi:telemetry" as const;
|
|
|
4
4
|
export const LEGACY_TELEMETRY_EVENT_NAME = "pi-persona:telemetry" as const;
|
|
5
5
|
export const LEGACY_TELEMETRY_VERSION = 1 as const;
|
|
6
6
|
export const TELEMETRY_PRODUCER_ID = "pi-persona" as const;
|
|
7
|
-
export const TELEMETRY_PRODUCER_VERSION = "1.16.
|
|
7
|
+
export const TELEMETRY_PRODUCER_VERSION = "1.16.2" as const;
|
|
8
8
|
|
|
9
9
|
export type InstanceStatus = "active" | "idle" | "stopped" | "stale" | (string & {});
|
|
10
10
|
export type AgentKind = "subagent" | "delegate" | "council" | "flow" | "phase" | (string & {});
|
|
@@ -391,7 +391,7 @@ export function registerDelegateTool(pi: ExtensionAPI, d: DelegateToolDeps): voi
|
|
|
391
391
|
...(runSpec.model ? { model: runSpec.model } : {}),
|
|
392
392
|
detail: "queued",
|
|
393
393
|
});
|
|
394
|
-
d.startPeek(); // arm the
|
|
394
|
+
d.startPeek(); // arm the async-run status/stall monitor (no-op if both cadences are disabled)
|
|
395
395
|
return id;
|
|
396
396
|
}
|
|
397
397
|
|