pi-goal-list-loop-audit 0.38.27 → 0.38.29
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 +13 -0
- package/docs/INDEX.md +2 -2
- package/extensions/goal-loop-display.ts +130 -28
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.38.29 — compact active-card recovery and judgment details (2026-09-08)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- The active goal card now renders main-model recovery as one compact, truthful row instead of repeating the full chain, current model, pending switch, attempts, and skip details. Full recovery diagnostics remain available through `/goal status`; the status footer no longer duplicates that report.
|
|
7
|
+
- Durable-vs-defer plaques cap their glance-card prose while preserving ordering, recommendation, and selected-choice facts. The final judgment row closes the tree cleanly.
|
|
8
|
+
- Markdown emphasis/code wrappers are removed from the display-only objective projection, so `**objective**` and backtick-wrapped commands no longer add visual noise. Stored objectives remain unchanged.
|
|
9
|
+
- Added regression coverage for compact recovery, duplicate suppression, Markdown cleanup, and the closed judgment tail.
|
|
10
|
+
|
|
11
|
+
## 0.38.28 — close redundant provenance rows in the active goal card (2026-09-08)
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- The active card no longer shows a duplicate `handled turn` row when it is the same model as the primary model. When provenance is the final detail block, its last row now closes with `└─` instead of a dangling `│`/`├─`, so the card no longer implies missing rows. Failover-handled turns remain visible. Added focused regression coverage; `tsc` and the focused display suite pass.
|
|
15
|
+
|
|
3
16
|
## 0.38.27 — selective port of PRs #45/#46: /loop pause + widget subtask count (2026-09-08)
|
|
4
17
|
|
|
5
18
|
### Added
|
package/docs/INDEX.md
CHANGED
|
@@ -18,7 +18,7 @@ For shipped docs, the relevant entry points are:
|
|
|
18
18
|
failback; v0.35.9 hardened cross-version npm tarball checks; v0.35.10
|
|
19
19
|
handles multi-entry npm dry-run reports; v0.35.11 accepts both npm report
|
|
20
20
|
shapes; v0.35.12 supports npm 12's keyed pack reports; v0.35.13 fixes stale-API recovery loops.
|
|
21
|
-
v0.35.14–v0.38.
|
|
21
|
+
v0.35.14–v0.38.29 continue through the supervisor freeze (`/glla pause`),
|
|
22
22
|
load hold, auditor picker parity, Windows launch fix, zombie-watchdog
|
|
23
23
|
subagent carve-out, due-wait backstop, the `/glla agents` visibility panel,
|
|
24
24
|
durable state-root selection, blank-until-resume auditor context, frozen
|
|
@@ -28,7 +28,7 @@ For shipped docs, the relevant entry points are:
|
|
|
28
28
|
packed-artifact release verification, and the 2026-09-07 display/lifecycle/
|
|
29
29
|
settings audit pass (abort-latch send guards, ownership compare-and-swap,
|
|
30
30
|
auditor inherit/clear parity) and the v0.38.25 post-objective summary
|
|
31
|
-
(canonical approval render, persist + replay, audit-goal counts line)
|
|
31
|
+
(canonical approval render, persist + replay, audit-goal counts line), the v0.38.27 selective port of PRs #45/#46 (`/loop pause` soft-hold, widget subtask count), the v0.38.28 provenance-row closure fix, and the v0.38.29 compact active-card recovery/judgment projection — see CHANGELOG.md for the full trail.
|
|
32
32
|
- `../README.md` — what the plugin is, install, quickstart, and the
|
|
33
33
|
architectural guarantee (drafting + confirm + detached auditor).
|
|
34
34
|
- `../INSTALL.md` — source install / local development setup; the recommended
|
|
@@ -87,6 +87,17 @@ function displayPauseReason(reason: string): string {
|
|
|
87
87
|
return compactDisplayText(sanitizeProviderDisplayText(reason));
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/** Objectives are stored verbatim, but the glance card is a plain-text
|
|
91
|
+
* surface. Remove the common Markdown emphasis/code wrappers that otherwise
|
|
92
|
+
* turn a long objective into noisy `**...**` and backtick litter. This is a
|
|
93
|
+
* display-only projection; prompts, archives, and state keep the original. */
|
|
94
|
+
function displayObjective(objective: string): string {
|
|
95
|
+
return compactDisplayText(objective)
|
|
96
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
|
97
|
+
.replace(/__([^_]+)__/g, "$1")
|
|
98
|
+
.replace(/`([^`\n]*)`/g, "$1");
|
|
99
|
+
}
|
|
100
|
+
|
|
90
101
|
/** v0.33.1: painted strings measure by their terminal-cell width. */
|
|
91
102
|
function visibleLen(s: string): number {
|
|
92
103
|
return tuiVisibleWidth(s);
|
|
@@ -280,6 +291,53 @@ function modelSourceLabel(source: string): string {
|
|
|
280
291
|
return source.trim();
|
|
281
292
|
}
|
|
282
293
|
|
|
294
|
+
function compactMainModelRecoveryLine(recovery: MainModelRecovery | undefined, configuredBackups: string[] = [], now = Date.now()): string | undefined {
|
|
295
|
+
if (!recovery || !recovery.primary.trim()) return undefined;
|
|
296
|
+
const primary = recovery.primary.trim();
|
|
297
|
+
const current = (recovery.active ?? primary).trim() || primary;
|
|
298
|
+
const backups = uniqueModelRefs(configuredBackups).slice(0, 10);
|
|
299
|
+
const chain = [primary, ...backups];
|
|
300
|
+
const currentIndex = chain.findIndex((ref) => ref.toLowerCase() === current.toLowerCase());
|
|
301
|
+
const selected = currentIndex === 0
|
|
302
|
+
? "primary"
|
|
303
|
+
: currentIndex > 0
|
|
304
|
+
? `backup ${currentIndex}/${backups.length}`
|
|
305
|
+
: "fallback";
|
|
306
|
+
const retryMs = recovery.retryAt ? Date.parse(recovery.retryAt) - now : Number.NaN;
|
|
307
|
+
const probeMs = recovery.primaryProbeAt ? Date.parse(recovery.primaryProbeAt) - now : Number.NaN;
|
|
308
|
+
const phase = recovery.manualResumeRequired
|
|
309
|
+
? "manual hold"
|
|
310
|
+
: recovery.pendingModelSwitch
|
|
311
|
+
? `switching → ${truncate(recovery.pendingModelSwitch, 48)}`
|
|
312
|
+
: Number.isFinite(retryMs)
|
|
313
|
+
? retryMs <= 0 ? "retrying now" : `retrying in ${fmtElapsed(retryMs)}`
|
|
314
|
+
: recovery.primaryProbeInFlight
|
|
315
|
+
? "primary probe pending"
|
|
316
|
+
: Number.isFinite(probeMs)
|
|
317
|
+
? probeMs <= 0 ? "probing primary now" : `primary probe in ${fmtElapsed(probeMs)}`
|
|
318
|
+
: `${selected} selected`;
|
|
319
|
+
const attempted = recovery.attempted?.length ?? 0;
|
|
320
|
+
const skipped = recovery.skipped?.length ?? 0;
|
|
321
|
+
const hasEpisode = !!recovery.manualResumeRequired
|
|
322
|
+
|| !!recovery.pendingModelSwitch
|
|
323
|
+
|| Number.isFinite(retryMs)
|
|
324
|
+
|| Number.isFinite(probeMs)
|
|
325
|
+
|| !!recovery.primaryProbeInFlight
|
|
326
|
+
|| recovery.attempts > 0
|
|
327
|
+
|| attempted > 1
|
|
328
|
+
|| skipped > 0
|
|
329
|
+
|| currentIndex !== 0;
|
|
330
|
+
// A recovery object can briefly survive the successful primary selection.
|
|
331
|
+
// Do not label that normal state as an incident, but keep the selected model
|
|
332
|
+
// on the card when no separate provenance row is available.
|
|
333
|
+
if (!hasEpisode) return `model: ${truncate(current, 56)} · primary`;
|
|
334
|
+
const history = [
|
|
335
|
+
recovery.attempts > 0 ? `attempts ${recovery.attempts}` : "",
|
|
336
|
+
skipped > 0 ? `skipped ${skipped}` : "",
|
|
337
|
+
].filter(Boolean);
|
|
338
|
+
return `recovery: ${phase} · ${selected} ${truncate(current, 48)}${history.length > 0 ? ` · ${history.join(" · ")}` : ""}`;
|
|
339
|
+
}
|
|
340
|
+
|
|
283
341
|
function modelProvenanceLines(provenance: ModelProvenanceDisplay | undefined, width?: number): string[] {
|
|
284
342
|
if (!provenance) return [];
|
|
285
343
|
const budget = budgetFor(width, 3, 60);
|
|
@@ -294,7 +352,13 @@ function modelProvenanceLines(provenance: ModelProvenanceDisplay | undefined, wi
|
|
|
294
352
|
const skipped = uniqueModelRefs(provenance.skippedForbiddenRefs);
|
|
295
353
|
if (skipped.length > 0) lines.push(`skipped forbidden: ${truncate(skipped.join(", "), budget)}`);
|
|
296
354
|
const handledTurn = typeof provenance.handledTurn === "string" ? provenance.handledTurn.trim() : "";
|
|
297
|
-
|
|
355
|
+
// A normal turn is handled by the configured primary model. Repeating the
|
|
356
|
+
// same ref immediately below it adds no information and, when it is the
|
|
357
|
+
// last row, leaves a misleading continuation glyph in the card. Keep the
|
|
358
|
+
// row only when a recovery/failover actually handled the turn.
|
|
359
|
+
if (handledTurn && handledTurn.toLowerCase() !== primary.toLowerCase()) {
|
|
360
|
+
lines.push(`handled turn: ${truncate(handledTurn, budget)}`);
|
|
361
|
+
}
|
|
298
362
|
const handledAudit = typeof provenance.handledAudit === "string" ? provenance.handledAudit.trim() : "";
|
|
299
363
|
if (handledAudit) {
|
|
300
364
|
const source = provenance.handledAuditSource?.trim() ? modelSourceLabel(provenance.handledAuditSource) : "";
|
|
@@ -322,9 +386,14 @@ export function buildDurableDeferDecisionLines(input: DurableDeferRecommendation
|
|
|
322
386
|
// Reserve room for the recommendation marker and the outer tree prefix
|
|
323
387
|
// so a normal 80-column production widget cannot truncate away the
|
|
324
388
|
// fact that the durable plaque is the selected action.
|
|
389
|
+
// Recommendation bodies are durable facts, but the glance card is not
|
|
390
|
+
// the command's full report. A wide terminal used to turn these plaques
|
|
391
|
+
// into sentence-length banners and crowd out the actual work state;
|
|
392
|
+
// /goal status retains the complete text.
|
|
393
|
+
const bodyCap = plaque.kind === "durable" ? 96 : 72;
|
|
325
394
|
const bodyBudget = width && width > 0
|
|
326
|
-
? Math.max(8, width - WIDGET_HORIZONTAL_MARGIN - 3 - visibleLen(prefix) - visibleLen(marker))
|
|
327
|
-
: budgetFor(width, 3, 60);
|
|
395
|
+
? Math.max(8, Math.min(bodyCap, width - WIDGET_HORIZONTAL_MARGIN - 3 - visibleLen(prefix) - visibleLen(marker)))
|
|
396
|
+
: Math.min(bodyCap, budgetFor(width, 3, 60));
|
|
328
397
|
return `${prefix}${truncate(plaque.body, bodyBudget)}${marker}`;
|
|
329
398
|
}),
|
|
330
399
|
`selected: ${recommendation.choice}${recommendation.choice === "inline" ? " (durable fix)" : " (reversible workaround)"}`,
|
|
@@ -1157,21 +1226,21 @@ function buildStatusTextBase(state: State, audit?: AuditDisplayProgress | null,
|
|
|
1157
1226
|
return `glla: ${paint(theme, pauseIsError(g) ? "error" : "warning", label)}${pausedStatusSuffix(g, state, extras, now)}${heldSuffix}`;
|
|
1158
1227
|
}
|
|
1159
1228
|
if (g.status === "active") {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1229
|
+
// The footer is a glance/liveness surface. Recovery details belong to the
|
|
1230
|
+
// card (and the full /goal status report), not a second horizontally
|
|
1231
|
+
// concatenated copy here. This keeps exceptions readable without making
|
|
1232
|
+
// a healthy WORKING line look like an incident.
|
|
1164
1233
|
// v0.28.1 (S1/S2): a stale-handle interrupt keeps the goal ACTIVE.
|
|
1165
1234
|
// It outranks any older operational note on the same state snapshot.
|
|
1166
1235
|
if (g.interruptedAt) {
|
|
1167
1236
|
const label = interruptedForNoStart(g)
|
|
1168
1237
|
? "⚠ turn start not observed — automatic retry held"
|
|
1169
1238
|
: "⚠ interrupted — stale handle · /new (or a fresh session_start) rebinds";
|
|
1170
|
-
return
|
|
1239
|
+
return `glla: ${paint(theme, "error", label)}${heldSuffix}`;
|
|
1171
1240
|
}
|
|
1172
1241
|
const attention = activeAttention(g);
|
|
1173
1242
|
if (attention) {
|
|
1174
|
-
return
|
|
1243
|
+
return `glla: ${paint(theme, attention.color, `⚠ ${attention.label}`)}${heldSuffix}`;
|
|
1175
1244
|
}
|
|
1176
1245
|
const activity = goalDisplayActivity(g, extras, now);
|
|
1177
1246
|
// v0.34.97: while the post-compaction grace window is open, surface
|
|
@@ -1180,10 +1249,10 @@ function buildStatusTextBase(state: State, audit?: AuditDisplayProgress | null,
|
|
|
1180
1249
|
const compactAgeMs = state.lastCompactionAt ? now - state.lastCompactionAt : Number.POSITIVE_INFINITY;
|
|
1181
1250
|
const compacting = Number.isFinite(compactAgeMs) && compactAgeMs >= 0 && compactAgeMs < 180_000; // COMPACTION_GRACE_MS = 3 min
|
|
1182
1251
|
if (compacting) {
|
|
1183
|
-
return
|
|
1252
|
+
return `glla: ${paint(theme, "warning", `⏳ compacting… (${fmtElapsed(compactAgeMs)} ago)`)}${heldSuffix}`;
|
|
1184
1253
|
}
|
|
1185
1254
|
if (activity === "awaiting-first-turn") {
|
|
1186
|
-
return
|
|
1255
|
+
return `glla: ${activityStateBadge("AWAITING FIRST TURN", theme, "warning")}${heldSuffix}`;
|
|
1187
1256
|
}
|
|
1188
1257
|
if (activity === "idle") {
|
|
1189
1258
|
// Audit 2026-09-07 (DECIDED: head owns liveness): the status keeps
|
|
@@ -1194,7 +1263,7 @@ function buildStatusTextBase(state: State, audit?: AuditDisplayProgress | null,
|
|
|
1194
1263
|
goalTotalText(g, now),
|
|
1195
1264
|
(state.list?.length ?? 0) > 0 ? `${state.list!.length} queued` : "",
|
|
1196
1265
|
].filter(Boolean);
|
|
1197
|
-
return
|
|
1266
|
+
return `glla: ${activityStateBadge("IDLE", theme, "warning")}${idleDetails.length > 0 ? ` ${idleDetails.join(" · ")}` : ""}${heldSuffix}`;
|
|
1198
1267
|
}
|
|
1199
1268
|
// v0.34.39: distinguish durable state from evidence of a live host turn.
|
|
1200
1269
|
// A spinner is reserved for recent stream/tool evidence; BUSY without
|
|
@@ -1206,7 +1275,7 @@ function buildStatusTextBase(state: State, audit?: AuditDisplayProgress | null,
|
|
|
1206
1275
|
g.taskList ? `${countDone(g)}/${countTotal(g)} tasks` : "",
|
|
1207
1276
|
(state.list?.length ?? 0) > 0 ? `${state.list!.length} queued` : "",
|
|
1208
1277
|
].filter(Boolean);
|
|
1209
|
-
return
|
|
1278
|
+
return `glla: ${activityStateBadge("BUSY", theme, "warning")}${busyDetails.length > 0 ? ` ${busyDetails.join(" · ")}` : ""}${heldSuffix}`;
|
|
1210
1279
|
}
|
|
1211
1280
|
// v0.34.16: a fresh session_start owns the handoff. A cold boot still
|
|
1212
1281
|
// follows the global autoResume setting, so the widget names the actual
|
|
@@ -1252,7 +1321,7 @@ function buildStatusTextBase(state: State, audit?: AuditDisplayProgress | null,
|
|
|
1252
1321
|
monitoring ? "next check" : "",
|
|
1253
1322
|
n > 0 ? `${n} queued` : "",
|
|
1254
1323
|
].filter(Boolean);
|
|
1255
|
-
return
|
|
1324
|
+
return `glla: ${marker}${details.length > 0 ? ` ${details.join(" · ")}` : ""}${recoverySuffix}${heldSuffix}`;
|
|
1256
1325
|
}
|
|
1257
1326
|
// v0.34.65: a terminal goal names its outcome + wall duration instead of
|
|
1258
1327
|
// clearing the segment (note.md 2026-08-07: "this seems weak for a complete
|
|
@@ -1346,6 +1415,16 @@ export function buildWidgetLines(state: State, audit?: AuditDisplayProgress | nu
|
|
|
1346
1415
|
withAgents = orphanHead ? [orphanHead, ...agentLines] : [...agentLines];
|
|
1347
1416
|
}
|
|
1348
1417
|
}
|
|
1418
|
+
// Keep the final tree row visually closed when the compact judgment block
|
|
1419
|
+
// ends without a queue footer. Without this, its last `├─` reads like a
|
|
1420
|
+
// missing continuation and makes an already dense card feel unfinished.
|
|
1421
|
+
if (withAgents && inner && detailedAgents.length === 0 && extras?.durableDeferRecommendation && withAgents.length > 0) {
|
|
1422
|
+
const tailIndex = withAgents.length - 1;
|
|
1423
|
+
const tail = withAgents[tailIndex]!;
|
|
1424
|
+
if (tail.startsWith("├─ ") || tail.startsWith("│ ")) {
|
|
1425
|
+
withAgents[tailIndex] = `└─ ${tail.slice(3)}`;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1349
1428
|
// v0.28.6 (E1): a persistence failure outranks everything — first line,
|
|
1350
1429
|
// on every render, until a write lands again.
|
|
1351
1430
|
let lines: string[] | undefined = withAgents;
|
|
@@ -1367,7 +1446,7 @@ export function buildWidgetLines(state: State, audit?: AuditDisplayProgress | nu
|
|
|
1367
1446
|
function waitingListStatus(state: State, _now: number, theme?: DisplayTheme, width?: number): string {
|
|
1368
1447
|
const queue = state.list ?? [];
|
|
1369
1448
|
const head = queue[0];
|
|
1370
|
-
const objective = head?.objective?.trim() ?
|
|
1449
|
+
const objective = head?.objective?.trim() ? displayObjective(head.objective) : "unnamed queued item";
|
|
1371
1450
|
const hold = typeof state.loadHoldAt === "number" ? " · held on restore" : "";
|
|
1372
1451
|
// Audit 2026-09-06: the width budget truncates the END of the status
|
|
1373
1452
|
// line — the `/glla resume` action must survive, so the OBJECTIVE takes
|
|
@@ -1384,7 +1463,7 @@ function waitingListStatus(state: State, _now: number, theme?: DisplayTheme, wid
|
|
|
1384
1463
|
function waitingListLines(state: State, theme?: DisplayTheme, width?: number): string[] {
|
|
1385
1464
|
const queue = state.list ?? [];
|
|
1386
1465
|
const head = queue[0];
|
|
1387
|
-
const objective = head?.objective?.trim() ?
|
|
1466
|
+
const objective = head?.objective?.trim() ? displayObjective(head.objective) : "unnamed queued item";
|
|
1388
1467
|
const objectiveBudget = budgetFor(width, visibleLen("├─ up next: "), 56);
|
|
1389
1468
|
const action = typeof state.loadHoldAt === "number"
|
|
1390
1469
|
? "held on restore · /glla resume starts the queue · /list next skips/chooses"
|
|
@@ -1549,7 +1628,7 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
1549
1628
|
const headIcon = headLive
|
|
1550
1629
|
? paint(theme, headLive.band === "fresh" ? "success" : headLive.band === "aging" ? "warning" : "error", headLive.breath)
|
|
1551
1630
|
: icon;
|
|
1552
|
-
const head = `${headIcon} ${truncate(g.objective
|
|
1631
|
+
const head = `${headIcon} ${truncate(displayObjective(g.objective), objBudget)} ${paint(theme, "dim", "·")} ${segsText}`;
|
|
1553
1632
|
const lines = [head];
|
|
1554
1633
|
// v0.38.8: durable verdict tally as a first-class card row — the widget
|
|
1555
1634
|
// is the glance surface, and stored verdicts are the progress evidence
|
|
@@ -1566,20 +1645,23 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
1566
1645
|
lines.push(`├─ ${paint(theme, "dim", `Recovery: ${repairStep}`)}`);
|
|
1567
1646
|
}
|
|
1568
1647
|
// A model switch crosses an asynchronous boundary while the goal remains
|
|
1569
|
-
// active. Keep
|
|
1570
|
-
//
|
|
1571
|
-
//
|
|
1648
|
+
// active. Keep that fact visible, but do not print the full recovery report
|
|
1649
|
+
// into the glance card; it made the card taller than the space below the
|
|
1650
|
+
// editor. `/goal status` remains the detailed recovery surface.
|
|
1651
|
+
let recoveryLine: string | undefined;
|
|
1572
1652
|
if (g.status === "active" && state.mainModelRecovery) {
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
lines.push(`${i === 0 ? "├─" : "│ "} ${paint(theme, "dim", line)}`);
|
|
1576
|
-
});
|
|
1653
|
+
recoveryLine = compactMainModelRecoveryLine(state.mainModelRecovery, extras?.mainModelFallbacks, now);
|
|
1654
|
+
if (recoveryLine) lines.push(`├─ ${paint(theme, "dim", recoveryLine)}`);
|
|
1577
1655
|
}
|
|
1578
1656
|
// Model provenance is a card fact, not a notification: keep it visible
|
|
1579
|
-
// across active, interrupted, auditing, and paused branches.
|
|
1580
|
-
//
|
|
1581
|
-
//
|
|
1582
|
-
|
|
1657
|
+
// across active, interrupted, auditing, and paused branches. During an
|
|
1658
|
+
// active recovery the compact recovery row owns the model fact, so do not
|
|
1659
|
+
// print a second primary/current row beside it. The full chain and skipped
|
|
1660
|
+
// refs remain available through `/goal status`.
|
|
1661
|
+
const provenance = state.mainModelRecovery
|
|
1662
|
+
? []
|
|
1663
|
+
: modelProvenanceLines(extras?.modelProvenance, width);
|
|
1664
|
+
const provenanceStart = lines.length;
|
|
1583
1665
|
provenance.forEach((line, i) => {
|
|
1584
1666
|
lines.push(`${i === 0 ? "├─" : "│ "} ${paint(theme, "dim", line)}`);
|
|
1585
1667
|
});
|
|
@@ -1904,6 +1986,26 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
1904
1986
|
// `/goal status` / `/glla` command hints were extension meta, not task
|
|
1905
1987
|
// information — dropped. No queue, no footer at all.
|
|
1906
1988
|
if (queue > 0) lines.push(`└─ ${paint(theme, "dim", `${queue} queued`)}`);
|
|
1989
|
+
// v0.38.28: provenance rows can be the entire detail block when there is
|
|
1990
|
+
// no recent action, pending task, or queue footer. Close that block with a
|
|
1991
|
+
// terminator instead of leaving `├─`/`│` suggesting missing rows.
|
|
1992
|
+
const provenanceEnd = provenanceStart + provenance.length;
|
|
1993
|
+
const detailedAgentRows = extras?.agents?.lines ?? (extras?.agents?.line ? [extras.agents.line] : []);
|
|
1994
|
+
if (provenance.length > 0 && detailedAgentRows.length === 0 && provenanceEnd === lines.length) {
|
|
1995
|
+
const last = lines[provenanceEnd - 1]!;
|
|
1996
|
+
if (last.startsWith("├─ ") || last.startsWith("│ ")) {
|
|
1997
|
+
lines[provenanceEnd - 1] = `└─ ${last.slice(3)}`;
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
// A compact recovery/model row can be the final detail when no other card
|
|
2001
|
+
// fact follows it. Close that one row without changing the established
|
|
2002
|
+
// open-row shape for recent actions or worker details.
|
|
2003
|
+
if (recoveryLine && detailedAgentRows.length === 0 && lines.length > 0) {
|
|
2004
|
+
const last = lines[lines.length - 1]!;
|
|
2005
|
+
if (last.includes(recoveryLine) && (last.startsWith("├─ ") || last.startsWith("│ "))) {
|
|
2006
|
+
lines[lines.length - 1] = `└─ ${last.slice(3)}`;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
1907
2009
|
return lines;
|
|
1908
2010
|
}
|
|
1909
2011
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.29",
|
|
4
4
|
"description": "Mission control for autonomous pi: interview-drafted goals, an audited task queue, and forever-loops (metric, spec, project-audit) that run for hours. A detached extension-less auditor process re-verifies every completion with raw evidence without holding the main pi turn; confirmed drafts, decision pauses and consent gates keep you in charge.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"author": "dracon",
|