pantheon-opencode 1.5.0-beta.18 → 1.5.0-beta.19
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 +14 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/pyproject.toml +1 -1
- package/src/plugins/tui/dist/tui.d.ts +47 -12
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +64 -24
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +1 -1
- package/src/plugins/tui/src/index.tsx +84 -31
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
18
18
|
|
|
19
19
|
## ✅ Closed Issues
|
|
20
20
|
|
|
21
|
+
## [v1.5.0-beta.19] - 2026-09-16
|
|
22
|
+
|
|
23
|
+
<!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
|
|
24
|
+
move this section to a versioned entry and reset the template below. -->
|
|
25
|
+
|
|
26
|
+
## 🆕 What's New
|
|
27
|
+
|
|
28
|
+
- **Improved — painel Delegations**: status ausente de children antigas agora é
|
|
29
|
+
`done` (não `running`); grace 60s para sessões recém-criadas; janela de
|
|
30
|
+
recência 24h (filtra histórico); ordenação newest-first; ceiling esconde
|
|
31
|
+
antigos.
|
|
32
|
+
- **Improved — animação**: spinner ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ agora tem timer
|
|
33
|
+
independente (80ms/frame), separado do poll de dados — fluido, não mais
|
|
34
|
+
"travado".
|
|
21
35
|
## [v1.5.0-beta.18] - 2026-09-16
|
|
22
36
|
|
|
23
37
|
<!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pantheon",
|
|
3
3
|
"publisher": "ils15",
|
|
4
|
-
"version": "1.5.0-beta.
|
|
4
|
+
"version": "1.5.0-beta.19",
|
|
5
5
|
"displayName": "Pantheon",
|
|
6
6
|
"description": "Conductor-Delegate orchestration platform with 14 specialized agents, 21 skills, and OpenCode support.",
|
|
7
7
|
"agents": "./src/agents",
|
package/pyproject.toml
CHANGED
|
@@ -104,6 +104,24 @@ declare const IDLE_SILENCE_MS: number;
|
|
|
104
104
|
* constants only control which rows enter the TUI window. */
|
|
105
105
|
declare const DELEGATION_DONE_RETENTION_MS: number;
|
|
106
106
|
declare const DELEGATION_FAILED_RETENTION_MS: number;
|
|
107
|
+
/** Grace window for an ABSENT child status. `api.state.session.status()` only
|
|
108
|
+
* carries the sessions that are currently ALIVE; every finished child of a
|
|
109
|
+
* long-lived session is missing from that map. A child with no status is
|
|
110
|
+
* therefore treated as terminal (done) — treating it as running made every
|
|
111
|
+
* historical child of the session show up as "active" (the "197 children,
|
|
112
|
+
* all active" regression). The one exception is a freshly-spawned child that
|
|
113
|
+
* has not been registered by the status API yet: within this window of its
|
|
114
|
+
* last activity (`time.updated`, falling back to `time.created`) it still
|
|
115
|
+
* reads as running. */
|
|
116
|
+
declare const DELEGATION_CHILD_STATUS_GRACE_MS: number;
|
|
117
|
+
/** Recency window for the children channel. `session.children` returns EVERY
|
|
118
|
+
* child the focused session ever spawned, so a long session accumulates
|
|
119
|
+
* hundreds of historical rows that would inflate the panel. A child whose
|
|
120
|
+
* last activity (its own time, or its matched report's Finalized timestamp)
|
|
121
|
+
* is older than this window is dropped BEFORE it can enter the list. 24h
|
|
122
|
+
* keeps the sidebar scoped to current work while still covering long
|
|
123
|
+
* delegations. A child with no timestamp at all is kept (fail-open). */
|
|
124
|
+
declare const DELEGATION_CHILDREN_RECENCY_MS: number;
|
|
107
125
|
/** Alias-less NATIVE task() live entries never receive a report alias (the
|
|
108
126
|
* task tool output carries none), so the 30s alias-less prune in
|
|
109
127
|
* mergeChildDelegationSources must not apply to them — 5 minutes covers a
|
|
@@ -130,9 +148,10 @@ declare function delegationElapsed(entry: DelegationEntry, now: number): string;
|
|
|
130
148
|
type DelegationActivity = 'delegating' | 'working' | 'reading' | 'completed' | 'error' | 'cancelled';
|
|
131
149
|
declare function delegationActivity(entry: DelegationEntry): DelegationActivity;
|
|
132
150
|
declare function delegationActivityLabel(entry: DelegationEntry): string;
|
|
133
|
-
/** Return
|
|
134
|
-
*
|
|
135
|
-
|
|
151
|
+
/** Return the spinner glyph for the given frame counter. The frame counter
|
|
152
|
+
* is incremented every ~80ms by a dedicated animation timer, decoupled from
|
|
153
|
+
* the data poll cycle. Pure — no side effects. */
|
|
154
|
+
declare function delegationSpinnerFrame(frame: number): string;
|
|
136
155
|
/** Every state the row knows how to draw: the delegation lifecycle states
|
|
137
156
|
* derived from the children status + md reports (`completed`, `error`,
|
|
138
157
|
* `cancelled`, `startup_failed`, `startup_unknown`) plus the TUI-only
|
|
@@ -364,11 +383,27 @@ type ChildDelegationLike = {
|
|
|
364
383
|
updated?: number;
|
|
365
384
|
};
|
|
366
385
|
};
|
|
367
|
-
/** Map a child status type to a display state.
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
|
|
386
|
+
/** Map a child status type to a display state.
|
|
387
|
+
*
|
|
388
|
+
* Only `busy`/`retry` are EXPLICIT live states. `idle` is terminal and so is
|
|
389
|
+
* any other/unknown status — a status the panel does not recognise is not
|
|
390
|
+
* evidence of activity. An ABSENT status is the historical case: the status
|
|
391
|
+
* map (`api.state.session.status`) contains only currently-alive sessions, so
|
|
392
|
+
* every finished child of a long session is missing from it. Defaulting that
|
|
393
|
+
* to running made the whole session history render as "active" (the
|
|
394
|
+
* "197 children, all active" regression), so an absent status is terminal
|
|
395
|
+
* (done) UNLESS the child was active within `graceMs` — a just-spawned child
|
|
396
|
+
* that the status API has not registered yet must still render as running.
|
|
397
|
+
*
|
|
398
|
+
* @param status the `api.state.session.status(id)?.type`, or undefined
|
|
399
|
+
* @param time the child session time fields (`created`/`updated`)
|
|
400
|
+
* @param now current wall-clock ms (injectable for tests)
|
|
401
|
+
* @param graceMs recency window for the absent-status running assumption
|
|
402
|
+
* Pure — no I/O. */
|
|
403
|
+
declare function childStatusToState(status: string | undefined, time?: {
|
|
404
|
+
created?: number;
|
|
405
|
+
updated?: number;
|
|
406
|
+
}, now?: number, graceMs?: number): 'running' | 'completed' | 'retry';
|
|
372
407
|
/** Status-only row model: ONE glyph + ONE identity per row.
|
|
373
408
|
*
|
|
374
409
|
* A row is
|
|
@@ -386,9 +421,9 @@ declare function delegationRowStatus(state: DelegationDisplayState): DelegationR
|
|
|
386
421
|
declare const DELEGATION_ROW_GLYPHS: Readonly<Record<DelegationRowStatus, string>>;
|
|
387
422
|
declare function delegationRowGlyph(status: DelegationRowStatus): string;
|
|
388
423
|
/** Row marker (`<glyph> `) — the single state channel. `active` animates
|
|
389
|
-
* through the
|
|
390
|
-
* Pure. */
|
|
391
|
-
declare function delegationRowMarker(state: DelegationDisplayState,
|
|
424
|
+
* through the spinner for the given frame counter (incremented every ~80ms);
|
|
425
|
+
* every other kind is static. Pure. */
|
|
426
|
+
declare function delegationRowMarker(state: DelegationDisplayState, frame?: number): string;
|
|
392
427
|
/** ONE identity per row: the short report alias (`apo-1`); a row without a
|
|
393
428
|
* report alias (native task() child `native-task` / `native-<id>`, or an
|
|
394
429
|
* alias-less native live row `live-<callID>` kept as children-only) shows
|
|
@@ -473,5 +508,5 @@ declare const plugin: TuiPluginModule & {
|
|
|
473
508
|
setup: () => Promise<void>;
|
|
474
509
|
};
|
|
475
510
|
//#endregion
|
|
476
|
-
export { ChildDelegationLike, DELEGATION_ALIAS_WIDTH, DELEGATION_DESCRIPTION_MAX, DELEGATION_DONE_RETENTION_MS, DELEGATION_ELAPSED_WIDTH, DELEGATION_FAILED_RETENTION_MS, DELEGATION_ROW_GLYPHS, DELEGATION_VISIBLE_CEILING, DelegationActivity, DelegationDisplayState, DelegationEntry, DelegationRowStatus, DelegationStateTone, DelegationToolPart, IDLE_SILENCE_MS, LiveDelegationEntry, LiveDelegationStore, NATIVE_LIVE_ALIASLESS_TTL_MS, ParsedDelegationToolPart, STALE_RUNNING_THRESHOLD_MS, ToolActivity, TuiSessionSources, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, createDelegationRowOpenHandler, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationRowGlyph, delegationRowIdentity, delegationRowMarker, delegationRowStatus, delegationSpinnerFrame, delegationStateTone, extractToolActivity, filterDelegationsToSession, fmtElapsed, formatDelegationAlias, formatDelegationElapsed, formatDelegationHeader, formatDelegationRowLead, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
|
|
511
|
+
export { ChildDelegationLike, DELEGATION_ALIAS_WIDTH, DELEGATION_CHILDREN_RECENCY_MS, DELEGATION_CHILD_STATUS_GRACE_MS, DELEGATION_DESCRIPTION_MAX, DELEGATION_DONE_RETENTION_MS, DELEGATION_ELAPSED_WIDTH, DELEGATION_FAILED_RETENTION_MS, DELEGATION_ROW_GLYPHS, DELEGATION_VISIBLE_CEILING, DelegationActivity, DelegationDisplayState, DelegationEntry, DelegationRowStatus, DelegationStateTone, DelegationToolPart, IDLE_SILENCE_MS, LiveDelegationEntry, LiveDelegationStore, NATIVE_LIVE_ALIASLESS_TTL_MS, ParsedDelegationToolPart, STALE_RUNNING_THRESHOLD_MS, ToolActivity, TuiSessionSources, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, createDelegationRowOpenHandler, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationRowGlyph, delegationRowIdentity, delegationRowMarker, delegationRowStatus, delegationSpinnerFrame, delegationStateTone, extractToolActivity, filterDelegationsToSession, fmtElapsed, formatDelegationAlias, formatDelegationElapsed, formatDelegationHeader, formatDelegationRowLead, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
|
|
477
512
|
//# sourceMappingURL=tui.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;KA07BY;;EAEV;;EAEA;;;;EAIA;;EAEA;EACA;;EAUA;;EAEA;EACA;EACA;;EAEA;;;;;;EAUA;;;;;;;;;;;iBAsBc,wBACd,aACA,oBACA,qBACC;;;;;;iBAkHmB,sBAAsB,cAAc,QAAQ;;;;;;;;;iBAyC5C,yBAAyB,eAAe,QAAQ;;;;;;;iBAUtD,oBACd;EAAS;EAAoB;eAC7B;;;;iBAUc,sBACd;EAAS;EAAoB;eAC7B;;;;;;iBAUc,YAAY;;;iBAMZ,WAAW;;;;iBAOX,yBAAyB,GAAG,iBAAiB,GAAG;;;;;iBAahD,oBACd,cAAc,mBACd,oBACA,cACA;EACG,QAAQ;EAAmB,QAAQ;;;;;iBAmCxB,sBACd,cAAc,mBACd,sBACA,cACA,4BACC;;cAMU;;;;cAKA;;;cAIA;cACA;;;;;cAMA;;;;;;;;;iBAUG,mBACd,OAAO,iBACP,aACA,uBACC;;;iBAWa,WAAW;;;;iBAeX,kBAAkB,OAAO,iBAAiB;;;KAQ9C;iBAQI,mBAAmB,OAAO,kBAAkB;iBAc5C,wBAAwB,OAAO
|
|
1
|
+
{"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":"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"}
|
|
@@ -165,7 +165,7 @@ async function detectVersion(api) {
|
|
|
165
165
|
if (ver) return ver;
|
|
166
166
|
}
|
|
167
167
|
} catch {}
|
|
168
|
-
return "1.5.0-beta.
|
|
168
|
+
return "1.5.0-beta.19";
|
|
169
169
|
}
|
|
170
170
|
/**
|
|
171
171
|
* usage-bar — AI subscription usage gauge for the opencode TUI.
|
|
@@ -914,6 +914,24 @@ const IDLE_SILENCE_MS = 60 * 1e3;
|
|
|
914
914
|
* constants only control which rows enter the TUI window. */
|
|
915
915
|
const DELEGATION_DONE_RETENTION_MS = 120 * 1e3;
|
|
916
916
|
const DELEGATION_FAILED_RETENTION_MS = 600 * 1e3;
|
|
917
|
+
/** Grace window for an ABSENT child status. `api.state.session.status()` only
|
|
918
|
+
* carries the sessions that are currently ALIVE; every finished child of a
|
|
919
|
+
* long-lived session is missing from that map. A child with no status is
|
|
920
|
+
* therefore treated as terminal (done) — treating it as running made every
|
|
921
|
+
* historical child of the session show up as "active" (the "197 children,
|
|
922
|
+
* all active" regression). The one exception is a freshly-spawned child that
|
|
923
|
+
* has not been registered by the status API yet: within this window of its
|
|
924
|
+
* last activity (`time.updated`, falling back to `time.created`) it still
|
|
925
|
+
* reads as running. */
|
|
926
|
+
const DELEGATION_CHILD_STATUS_GRACE_MS = 60 * 1e3;
|
|
927
|
+
/** Recency window for the children channel. `session.children` returns EVERY
|
|
928
|
+
* child the focused session ever spawned, so a long session accumulates
|
|
929
|
+
* hundreds of historical rows that would inflate the panel. A child whose
|
|
930
|
+
* last activity (its own time, or its matched report's Finalized timestamp)
|
|
931
|
+
* is older than this window is dropped BEFORE it can enter the list. 24h
|
|
932
|
+
* keeps the sidebar scoped to current work while still covering long
|
|
933
|
+
* delegations. A child with no timestamp at all is kept (fail-open). */
|
|
934
|
+
const DELEGATION_CHILDREN_RECENCY_MS = 1440 * 60 * 1e3;
|
|
917
935
|
/** Alias-less NATIVE task() live entries never receive a report alias (the
|
|
918
936
|
* task tool output carries none), so the 30s alias-less prune in
|
|
919
937
|
* mergeChildDelegationSources must not apply to them — 5 minutes covers a
|
|
@@ -989,10 +1007,11 @@ const DELEGATION_SPINNER_FRAMES = [
|
|
|
989
1007
|
"⠇",
|
|
990
1008
|
"⠏"
|
|
991
1009
|
];
|
|
992
|
-
/** Return
|
|
993
|
-
*
|
|
994
|
-
|
|
995
|
-
|
|
1010
|
+
/** Return the spinner glyph for the given frame counter. The frame counter
|
|
1011
|
+
* is incremented every ~80ms by a dedicated animation timer, decoupled from
|
|
1012
|
+
* the data poll cycle. Pure — no side effects. */
|
|
1013
|
+
function delegationSpinnerFrame(frame) {
|
|
1014
|
+
const index = Math.abs(Math.floor(frame)) % DELEGATION_SPINNER_FRAMES.length;
|
|
996
1015
|
return DELEGATION_SPINNER_FRAMES[index] ?? DELEGATION_SPINNER_FRAMES[0];
|
|
997
1016
|
}
|
|
998
1017
|
/** Every state the row knows how to draw: the delegation lifecycle states
|
|
@@ -1451,14 +1470,30 @@ function buildChildrenPath(id) {
|
|
|
1451
1470
|
return safeSessionPath(id);
|
|
1452
1471
|
}
|
|
1453
1472
|
/** Duck-typed subset of a child Session (+ its live status type). */
|
|
1454
|
-
/** Map a child status type to a display state.
|
|
1455
|
-
*
|
|
1456
|
-
*
|
|
1457
|
-
*
|
|
1458
|
-
|
|
1459
|
-
|
|
1473
|
+
/** Map a child status type to a display state.
|
|
1474
|
+
*
|
|
1475
|
+
* Only `busy`/`retry` are EXPLICIT live states. `idle` is terminal and so is
|
|
1476
|
+
* any other/unknown status — a status the panel does not recognise is not
|
|
1477
|
+
* evidence of activity. An ABSENT status is the historical case: the status
|
|
1478
|
+
* map (`api.state.session.status`) contains only currently-alive sessions, so
|
|
1479
|
+
* every finished child of a long session is missing from it. Defaulting that
|
|
1480
|
+
* to running made the whole session history render as "active" (the
|
|
1481
|
+
* "197 children, all active" regression), so an absent status is terminal
|
|
1482
|
+
* (done) UNLESS the child was active within `graceMs` — a just-spawned child
|
|
1483
|
+
* that the status API has not registered yet must still render as running.
|
|
1484
|
+
*
|
|
1485
|
+
* @param status the `api.state.session.status(id)?.type`, or undefined
|
|
1486
|
+
* @param time the child session time fields (`created`/`updated`)
|
|
1487
|
+
* @param now current wall-clock ms (injectable for tests)
|
|
1488
|
+
* @param graceMs recency window for the absent-status running assumption
|
|
1489
|
+
* Pure — no I/O. */
|
|
1490
|
+
function childStatusToState(status, time, now = Date.now(), graceMs = DELEGATION_CHILD_STATUS_GRACE_MS) {
|
|
1460
1491
|
if (status === "retry") return "retry";
|
|
1461
|
-
return "running";
|
|
1492
|
+
if (status === "busy") return "running";
|
|
1493
|
+
if (status !== void 0) return "completed";
|
|
1494
|
+
const lastActivity = Math.max(time?.updated ?? 0, time?.created ?? 0);
|
|
1495
|
+
if (lastActivity > 0 && now - lastActivity < graceMs) return "running";
|
|
1496
|
+
return "completed";
|
|
1462
1497
|
}
|
|
1463
1498
|
/** Status-only row model: ONE glyph + ONE identity per row.
|
|
1464
1499
|
*
|
|
@@ -1494,11 +1529,11 @@ function delegationRowGlyph(status) {
|
|
|
1494
1529
|
return DELEGATION_ROW_GLYPHS[status];
|
|
1495
1530
|
}
|
|
1496
1531
|
/** Row marker (`<glyph> `) — the single state channel. `active` animates
|
|
1497
|
-
* through the
|
|
1498
|
-
* Pure. */
|
|
1499
|
-
function delegationRowMarker(state,
|
|
1532
|
+
* through the spinner for the given frame counter (incremented every ~80ms);
|
|
1533
|
+
* every other kind is static. Pure. */
|
|
1534
|
+
function delegationRowMarker(state, frame = 0) {
|
|
1500
1535
|
const status = delegationRowStatus(state);
|
|
1501
|
-
return `${status === "active" ? delegationSpinnerFrame(
|
|
1536
|
+
return `${status === "active" ? delegationSpinnerFrame(frame) : delegationRowGlyph(status)} `;
|
|
1502
1537
|
}
|
|
1503
1538
|
/** ONE identity per row: the short report alias (`apo-1`); a row without a
|
|
1504
1539
|
* report alias (native task() child `native-task` / `native-<id>`, or an
|
|
@@ -1637,7 +1672,10 @@ function formatPanelLogLine(children, pantheon, native, md, events) {
|
|
|
1637
1672
|
* `parentSessionID` (the focused session id) is stamped on report-less
|
|
1638
1673
|
* children so the row carries its origin session; omit it and they stay
|
|
1639
1674
|
* unscoped ('').
|
|
1640
|
-
*
|
|
1675
|
+
* Children outside {@link DELEGATION_CHILDREN_RECENCY_MS} are dropped before
|
|
1676
|
+
* they can enter the list (the per-session child list grows for the whole
|
|
1677
|
+
* session lifetime); the survivors are sorted running-first, most recent
|
|
1678
|
+
* first (compareDelegationEntries), so a ceiling hides the OLDEST rows.
|
|
1641
1679
|
* Pure — no I/O. */
|
|
1642
1680
|
/** Temporal guard for the children channel (same recycled-alias class as the
|
|
1643
1681
|
* md merge): an md terminal report matched by taskID may still belong to
|
|
@@ -1662,7 +1700,9 @@ function childrenToDelegationEntries(children, md, now = Date.now(), parentSessi
|
|
|
1662
1700
|
if (child.id === "" || seen.has(child.id)) continue;
|
|
1663
1701
|
seen.add(child.id);
|
|
1664
1702
|
const mdEntry = byTaskID.get(child.id);
|
|
1665
|
-
const
|
|
1703
|
+
const lastActivity = Math.max(child.time?.updated ?? 0, child.time?.created ?? 0, mdEntry?.updatedAt ?? 0);
|
|
1704
|
+
if (lastActivity > 0 && now - lastActivity > 864e5) continue;
|
|
1705
|
+
const state = mdEntry !== void 0 && mdEntry.state !== "running" && mdTerminalCoversChild(mdEntry, child) ? mdEntry.state : childStatusToState(child.status, child.time, now);
|
|
1666
1706
|
out.push({
|
|
1667
1707
|
alias: mdEntry?.alias ?? `native-${child.id.slice(-4)}`,
|
|
1668
1708
|
sessionID: mdEntry?.sessionID ?? parentSessionID,
|
|
@@ -1771,7 +1811,7 @@ function DelegationRow(props) {
|
|
|
1771
1811
|
default: return t.textMuted;
|
|
1772
1812
|
}
|
|
1773
1813
|
});
|
|
1774
|
-
const marker = createMemo(() => delegationRowMarker(props.job.state, props.
|
|
1814
|
+
const marker = createMemo(() => delegationRowMarker(props.job.state, props.frame));
|
|
1775
1815
|
const lead = createMemo(() => formatDelegationRowLead(props.job, marker()));
|
|
1776
1816
|
const elapsed = createMemo(() => formatDelegationElapsed(props.job, props.now));
|
|
1777
1817
|
const description = createMemo(() => truncateDelegationDescription(props.job.description));
|
|
@@ -1878,7 +1918,7 @@ function View(props) {
|
|
|
1878
1918
|
const panelLog = createTuiLogger(projectRoot());
|
|
1879
1919
|
const [childDelegations, setChildDelegations] = createSignal([]);
|
|
1880
1920
|
const [now, setNow] = createSignal(Date.now());
|
|
1881
|
-
const [
|
|
1921
|
+
const [frame, setFrame] = createSignal(0);
|
|
1882
1922
|
let eventRefreshCount = 0;
|
|
1883
1923
|
let delegationsInflight = null;
|
|
1884
1924
|
const refreshDelegations = () => {
|
|
@@ -1964,7 +2004,7 @@ function View(props) {
|
|
|
1964
2004
|
refreshDelegations();
|
|
1965
2005
|
}, 1e3);
|
|
1966
2006
|
cleanup.push(() => clearInterval(poll));
|
|
1967
|
-
const animation = setInterval(() =>
|
|
2007
|
+
const animation = setInterval(() => setFrame((f) => f + 1), 80);
|
|
1968
2008
|
cleanup.push(() => clearInterval(animation));
|
|
1969
2009
|
try {
|
|
1970
2010
|
const sdk = props.api.state?.session;
|
|
@@ -2138,8 +2178,8 @@ function View(props) {
|
|
|
2138
2178
|
get now() {
|
|
2139
2179
|
return now();
|
|
2140
2180
|
},
|
|
2141
|
-
get
|
|
2142
|
-
return
|
|
2181
|
+
get frame() {
|
|
2182
|
+
return frame();
|
|
2143
2183
|
}
|
|
2144
2184
|
})
|
|
2145
2185
|
}), null);
|
|
@@ -2237,6 +2277,6 @@ const plugin = {
|
|
|
2237
2277
|
setup: async () => {}
|
|
2238
2278
|
};
|
|
2239
2279
|
//#endregion
|
|
2240
|
-
export { DELEGATION_ALIAS_WIDTH, DELEGATION_DESCRIPTION_MAX, DELEGATION_DONE_RETENTION_MS, DELEGATION_ELAPSED_WIDTH, DELEGATION_FAILED_RETENTION_MS, DELEGATION_ROW_GLYPHS, DELEGATION_VISIBLE_CEILING, IDLE_SILENCE_MS, NATIVE_LIVE_ALIASLESS_TTL_MS, STALE_RUNNING_THRESHOLD_MS, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, createDelegationRowOpenHandler, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationRowGlyph, delegationRowIdentity, delegationRowMarker, delegationRowStatus, delegationSpinnerFrame, delegationStateTone, extractToolActivity, filterDelegationsToSession, fmtElapsed, formatDelegationAlias, formatDelegationElapsed, formatDelegationHeader, formatDelegationRowLead, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
|
|
2280
|
+
export { DELEGATION_ALIAS_WIDTH, DELEGATION_CHILDREN_RECENCY_MS, DELEGATION_CHILD_STATUS_GRACE_MS, DELEGATION_DESCRIPTION_MAX, DELEGATION_DONE_RETENTION_MS, DELEGATION_ELAPSED_WIDTH, DELEGATION_FAILED_RETENTION_MS, DELEGATION_ROW_GLYPHS, DELEGATION_VISIBLE_CEILING, IDLE_SILENCE_MS, NATIVE_LIVE_ALIASLESS_TTL_MS, STALE_RUNNING_THRESHOLD_MS, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, createDelegationRowOpenHandler, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationRowGlyph, delegationRowIdentity, delegationRowMarker, delegationRowStatus, delegationSpinnerFrame, delegationStateTone, extractToolActivity, filterDelegationsToSession, fmtElapsed, formatDelegationAlias, formatDelegationElapsed, formatDelegationHeader, formatDelegationRowLead, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
|
|
2241
2281
|
|
|
2242
2282
|
//# sourceMappingURL=tui.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pantheon-tui",
|
|
3
|
-
"version": "1.5.0-beta.
|
|
3
|
+
"version": "1.5.0-beta.19",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pantheon-tui",
|
|
9
|
-
"version": "1.5.0-beta.
|
|
9
|
+
"version": "1.5.0-beta.19",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@opencode-ai/plugin": ">=1.14.0",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pantheon-tui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.5.0-beta.
|
|
4
|
+
"version": "1.5.0-beta.19",
|
|
5
5
|
"description": "Pantheon TUI plugin — sidebar (version, preset, sessions, real-time delegations) + AI subscription usage gauges incl. OpenCode Go/Zen dollar limits (vendored, MIT)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
@@ -1293,6 +1293,26 @@ export const IDLE_SILENCE_MS = 60 * 1000
|
|
|
1293
1293
|
export const DELEGATION_DONE_RETENTION_MS = 2 * 60 * 1000
|
|
1294
1294
|
export const DELEGATION_FAILED_RETENTION_MS = 10 * 60 * 1000
|
|
1295
1295
|
|
|
1296
|
+
/** Grace window for an ABSENT child status. `api.state.session.status()` only
|
|
1297
|
+
* carries the sessions that are currently ALIVE; every finished child of a
|
|
1298
|
+
* long-lived session is missing from that map. A child with no status is
|
|
1299
|
+
* therefore treated as terminal (done) — treating it as running made every
|
|
1300
|
+
* historical child of the session show up as "active" (the "197 children,
|
|
1301
|
+
* all active" regression). The one exception is a freshly-spawned child that
|
|
1302
|
+
* has not been registered by the status API yet: within this window of its
|
|
1303
|
+
* last activity (`time.updated`, falling back to `time.created`) it still
|
|
1304
|
+
* reads as running. */
|
|
1305
|
+
export const DELEGATION_CHILD_STATUS_GRACE_MS = 60 * 1000
|
|
1306
|
+
|
|
1307
|
+
/** Recency window for the children channel. `session.children` returns EVERY
|
|
1308
|
+
* child the focused session ever spawned, so a long session accumulates
|
|
1309
|
+
* hundreds of historical rows that would inflate the panel. A child whose
|
|
1310
|
+
* last activity (its own time, or its matched report's Finalized timestamp)
|
|
1311
|
+
* is older than this window is dropped BEFORE it can enter the list. 24h
|
|
1312
|
+
* keeps the sidebar scoped to current work while still covering long
|
|
1313
|
+
* delegations. A child with no timestamp at all is kept (fail-open). */
|
|
1314
|
+
export const DELEGATION_CHILDREN_RECENCY_MS = 24 * 60 * 60 * 1000
|
|
1315
|
+
|
|
1296
1316
|
/** Alias-less NATIVE task() live entries never receive a report alias (the
|
|
1297
1317
|
* task tool output carries none), so the 30s alias-less prune in
|
|
1298
1318
|
* mergeChildDelegationSources must not apply to them — 5 minutes covers a
|
|
@@ -1387,10 +1407,11 @@ export function delegationActivityLabel(entry: DelegationEntry): string {
|
|
|
1387
1407
|
|
|
1388
1408
|
const DELEGATION_SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
|
1389
1409
|
|
|
1390
|
-
/** Return
|
|
1391
|
-
*
|
|
1392
|
-
|
|
1393
|
-
|
|
1410
|
+
/** Return the spinner glyph for the given frame counter. The frame counter
|
|
1411
|
+
* is incremented every ~80ms by a dedicated animation timer, decoupled from
|
|
1412
|
+
* the data poll cycle. Pure — no side effects. */
|
|
1413
|
+
export function delegationSpinnerFrame(frame: number): string {
|
|
1414
|
+
const index = Math.abs(Math.floor(frame)) % DELEGATION_SPINNER_FRAMES.length
|
|
1394
1415
|
return DELEGATION_SPINNER_FRAMES[index] ?? DELEGATION_SPINNER_FRAMES[0]
|
|
1395
1416
|
}
|
|
1396
1417
|
|
|
@@ -2140,14 +2161,36 @@ export type ChildDelegationLike = {
|
|
|
2140
2161
|
}
|
|
2141
2162
|
}
|
|
2142
2163
|
|
|
2143
|
-
/** Map a child status type to a display state.
|
|
2144
|
-
*
|
|
2145
|
-
*
|
|
2146
|
-
*
|
|
2147
|
-
|
|
2148
|
-
|
|
2164
|
+
/** Map a child status type to a display state.
|
|
2165
|
+
*
|
|
2166
|
+
* Only `busy`/`retry` are EXPLICIT live states. `idle` is terminal and so is
|
|
2167
|
+
* any other/unknown status — a status the panel does not recognise is not
|
|
2168
|
+
* evidence of activity. An ABSENT status is the historical case: the status
|
|
2169
|
+
* map (`api.state.session.status`) contains only currently-alive sessions, so
|
|
2170
|
+
* every finished child of a long session is missing from it. Defaulting that
|
|
2171
|
+
* to running made the whole session history render as "active" (the
|
|
2172
|
+
* "197 children, all active" regression), so an absent status is terminal
|
|
2173
|
+
* (done) UNLESS the child was active within `graceMs` — a just-spawned child
|
|
2174
|
+
* that the status API has not registered yet must still render as running.
|
|
2175
|
+
*
|
|
2176
|
+
* @param status the `api.state.session.status(id)?.type`, or undefined
|
|
2177
|
+
* @param time the child session time fields (`created`/`updated`)
|
|
2178
|
+
* @param now current wall-clock ms (injectable for tests)
|
|
2179
|
+
* @param graceMs recency window for the absent-status running assumption
|
|
2180
|
+
* Pure — no I/O. */
|
|
2181
|
+
export function childStatusToState(
|
|
2182
|
+
status: string | undefined,
|
|
2183
|
+
time?: { created?: number; updated?: number },
|
|
2184
|
+
now = Date.now(),
|
|
2185
|
+
graceMs = DELEGATION_CHILD_STATUS_GRACE_MS,
|
|
2186
|
+
): 'running' | 'completed' | 'retry' {
|
|
2149
2187
|
if (status === 'retry') return 'retry'
|
|
2150
|
-
|
|
2188
|
+
if (status === 'busy') return 'running'
|
|
2189
|
+
if (status !== undefined) return 'completed' // idle + any other explicit terminal
|
|
2190
|
+
// Absent status: running only while the child's last activity is fresh.
|
|
2191
|
+
const lastActivity = Math.max(time?.updated ?? 0, time?.created ?? 0)
|
|
2192
|
+
if (lastActivity > 0 && now - lastActivity < graceMs) return 'running'
|
|
2193
|
+
return 'completed'
|
|
2151
2194
|
}
|
|
2152
2195
|
|
|
2153
2196
|
/** Status-only row model: ONE glyph + ONE identity per row.
|
|
@@ -2193,11 +2236,11 @@ export function delegationRowGlyph(status: DelegationRowStatus): string {
|
|
|
2193
2236
|
}
|
|
2194
2237
|
|
|
2195
2238
|
/** Row marker (`<glyph> `) — the single state channel. `active` animates
|
|
2196
|
-
* through the
|
|
2197
|
-
* Pure. */
|
|
2198
|
-
export function delegationRowMarker(state: DelegationDisplayState,
|
|
2239
|
+
* through the spinner for the given frame counter (incremented every ~80ms);
|
|
2240
|
+
* every other kind is static. Pure. */
|
|
2241
|
+
export function delegationRowMarker(state: DelegationDisplayState, frame = 0): string {
|
|
2199
2242
|
const status = delegationRowStatus(state)
|
|
2200
|
-
const glyph = status === 'active' ? delegationSpinnerFrame(
|
|
2243
|
+
const glyph = status === 'active' ? delegationSpinnerFrame(frame) : delegationRowGlyph(status)
|
|
2201
2244
|
return `${glyph} `
|
|
2202
2245
|
}
|
|
2203
2246
|
|
|
@@ -2375,7 +2418,10 @@ export function formatPanelLogLine(
|
|
|
2375
2418
|
* `parentSessionID` (the focused session id) is stamped on report-less
|
|
2376
2419
|
* children so the row carries its origin session; omit it and they stay
|
|
2377
2420
|
* unscoped ('').
|
|
2378
|
-
*
|
|
2421
|
+
* Children outside {@link DELEGATION_CHILDREN_RECENCY_MS} are dropped before
|
|
2422
|
+
* they can enter the list (the per-session child list grows for the whole
|
|
2423
|
+
* session lifetime); the survivors are sorted running-first, most recent
|
|
2424
|
+
* first (compareDelegationEntries), so a ceiling hides the OLDEST rows.
|
|
2379
2425
|
* Pure — no I/O. */
|
|
2380
2426
|
/** Temporal guard for the children channel (same recycled-alias class as the
|
|
2381
2427
|
* md merge): an md terminal report matched by taskID may still belong to
|
|
@@ -2409,10 +2455,22 @@ export function childrenToDelegationEntries(
|
|
|
2409
2455
|
if (child.id === '' || seen.has(child.id)) continue
|
|
2410
2456
|
seen.add(child.id)
|
|
2411
2457
|
const mdEntry = byTaskID.get(child.id)
|
|
2458
|
+
// Recency window (source-level): the children API returns EVERY child the
|
|
2459
|
+
// session ever spawned, so without this the whole session history enters
|
|
2460
|
+
// the list (the "197 children" panel). Effective recency = the latest of
|
|
2461
|
+
// the child's own activity and its matched report's Finalized timestamp;
|
|
2462
|
+
// a child with NO timestamp at all is kept (fail-open — it cannot be
|
|
2463
|
+
// judged). Oldest rows are the ones dropped.
|
|
2464
|
+
const lastActivity = Math.max(
|
|
2465
|
+
child.time?.updated ?? 0,
|
|
2466
|
+
child.time?.created ?? 0,
|
|
2467
|
+
mdEntry?.updatedAt ?? 0,
|
|
2468
|
+
)
|
|
2469
|
+
if (lastActivity > 0 && now - lastActivity > DELEGATION_CHILDREN_RECENCY_MS) continue
|
|
2412
2470
|
const state =
|
|
2413
2471
|
mdEntry !== undefined && mdEntry.state !== 'running' && mdTerminalCoversChild(mdEntry, child)
|
|
2414
2472
|
? mdEntry.state
|
|
2415
|
-
: childStatusToState(child.status)
|
|
2473
|
+
: childStatusToState(child.status, child.time, now)
|
|
2416
2474
|
out.push({
|
|
2417
2475
|
alias: mdEntry?.alias ?? `native-${child.id.slice(-4)}`,
|
|
2418
2476
|
// A report-less child IS a child of the focused session
|
|
@@ -2593,7 +2651,7 @@ function DelegationRow(props: {
|
|
|
2593
2651
|
api: TuiPluginApi
|
|
2594
2652
|
job: DelegationEntry
|
|
2595
2653
|
now: number
|
|
2596
|
-
|
|
2654
|
+
frame: number
|
|
2597
2655
|
}) {
|
|
2598
2656
|
const theme = () => props.api.theme.current
|
|
2599
2657
|
|
|
@@ -2615,7 +2673,7 @@ function DelegationRow(props: {
|
|
|
2615
2673
|
}
|
|
2616
2674
|
})
|
|
2617
2675
|
|
|
2618
|
-
const marker = createMemo(() => delegationRowMarker(props.job.state, props.
|
|
2676
|
+
const marker = createMemo(() => delegationRowMarker(props.job.state, props.frame))
|
|
2619
2677
|
const lead = createMemo(() => formatDelegationRowLead(props.job, marker()))
|
|
2620
2678
|
const elapsed = createMemo(() => formatDelegationElapsed(props.job, props.now))
|
|
2621
2679
|
const description = createMemo(() => truncateDelegationDescription(props.job.description))
|
|
@@ -2775,9 +2833,9 @@ function View(props: {
|
|
|
2775
2833
|
// (oh-my-opencode-slim pattern): re-fetch children + re-read the md every
|
|
2776
2834
|
// second so the panel updates even when NO event ever fires.
|
|
2777
2835
|
const [now, setNow] = createSignal(Date.now())
|
|
2778
|
-
//
|
|
2779
|
-
//
|
|
2780
|
-
const [
|
|
2836
|
+
// Frame counter for the spinner animation — incremented every ~80ms by a
|
|
2837
|
+
// dedicated fast timer, completely decoupled from the data poll cycle.
|
|
2838
|
+
const [frame, setFrame] = createSignal(0)
|
|
2781
2839
|
// Cumulative count of event-triggered refreshes — logged with every fetch
|
|
2782
2840
|
// ("panel: children=N(pantheon=M native=K) md=N events=N") so the (0)
|
|
2783
2841
|
// symptom is diagnosable: children>0 means the source works; events=0
|
|
@@ -2952,7 +3010,9 @@ function View(props: {
|
|
|
2952
3010
|
void refreshDelegations()
|
|
2953
3011
|
}, 1_000)
|
|
2954
3012
|
cleanup.push(() => clearInterval(poll))
|
|
2955
|
-
|
|
3013
|
+
// Fast spinner animation: 80ms frame counter, decoupled from data poll.
|
|
3014
|
+
// Only the spinner glyph depends on this; elapsed + data use the 1s timer.
|
|
3015
|
+
const animation = setInterval(() => setFrame((f) => f + 1), 80)
|
|
2956
3016
|
cleanup.push(() => clearInterval(animation))
|
|
2957
3017
|
|
|
2958
3018
|
// Compaction recovery: `message.part.removed` clears live entries while
|
|
@@ -3064,14 +3124,7 @@ function View(props: {
|
|
|
3064
3124
|
>
|
|
3065
3125
|
<box marginLeft={1} flexDirection="column">
|
|
3066
3126
|
<For each={delegationCeiling().visible}>
|
|
3067
|
-
{(job) => (
|
|
3068
|
-
<DelegationRow
|
|
3069
|
-
api={props.api}
|
|
3070
|
-
job={job}
|
|
3071
|
-
now={now()}
|
|
3072
|
-
animationNow={animationNow()}
|
|
3073
|
-
/>
|
|
3074
|
-
)}
|
|
3127
|
+
{(job) => <DelegationRow api={props.api} job={job} now={now()} frame={frame()} />}
|
|
3075
3128
|
</For>
|
|
3076
3129
|
<Show when={delegationCeiling().hidden > 0}>
|
|
3077
3130
|
<text fg={theme().textMuted}>
|