pantheon-opencode 1.5.0-beta.16 → 1.5.0-beta.18
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 +48 -0
- package/README.md +13 -25
- package/README.pt-BR.md +4 -5
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/mcp_persistence_server.py +1 -1
- package/src/agents/apollo.md +1 -0
- package/src/agents/gaia.md +1 -0
- package/src/agents/zeus.md +8 -29
- package/src/mcp/mcp_persistence_server.py +1 -1
- package/src/pantheon/{delegation-compaction.ts → compaction-context.ts} +10 -100
- package/src/pantheon/cost-command.ts +1 -2
- package/src/pantheon/delegation-enforce.ts +59 -14
- package/src/pantheon/goal-loop.ts +1 -1
- package/src/pantheon/goal-store.ts +1 -1
- package/src/pantheon/idle-continuation.ts +4 -6
- package/src/pantheon/model-command.ts +1 -2
- package/src/pantheon/native-task-status.ts +2 -2
- package/src/pantheon/session-id.ts +30 -0
- package/src/pantheon/task-result-guard.ts +2 -3
- package/src/pantheon/todo-enforcer.ts +3 -3
- package/src/pantheon/tool-context.ts +23 -0
- package/src/pantheon/v2-bridge.ts +1 -4
- package/src/plugin-v2.ts +4 -15
- package/src/plugin.ts +61 -245
- package/src/plugins/tui/dist/tui.d.ts +49 -79
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +48 -189
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +1 -1
- package/src/plugins/tui/src/index.tsx +97 -295
- package/src/skills/context-compression/SKILL.md +1 -1
- package/src/pantheon/delegate-manager.ts +0 -851
- package/src/pantheon/delegation-classifier.ts +0 -306
- package/src/pantheon/delegation-finalize.ts +0 -468
- package/src/pantheon/delegation-notify.ts +0 -212
- package/src/pantheon/delegation-toast.ts +0 -73
- package/src/pantheon/delegation.ts +0 -1349
- package/src/pantheon/native-probe.ts +0 -234
- package/src/pantheon/step-cap.ts +0 -110
|
@@ -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.18";
|
|
169
169
|
}
|
|
170
170
|
/**
|
|
171
171
|
* usage-bar — AI subscription usage gauge for the opencode TUI.
|
|
@@ -841,71 +841,19 @@ async function readDelegationEntries(dir) {
|
|
|
841
841
|
async function readAllDelegationEntries(root) {
|
|
842
842
|
return readDelegationEntries(join(root, ".pantheon", "delegations"));
|
|
843
843
|
}
|
|
844
|
-
/** The persisted subset of BackgroundJobRecord the panel consumes. Duck-typed
|
|
845
|
-
* so a corrupt/older record never breaks parsing. */
|
|
846
|
-
/** The FilePersistence state path: `<root>/.pantheon/board/state.json`. */
|
|
847
|
-
function boardStatePath(root) {
|
|
848
|
-
return join(root, ".pantheon", "board", "state.json");
|
|
849
|
-
}
|
|
850
|
-
/** Validate one parsed record, keeping only the fields the panel uses.
|
|
851
|
-
* Returns null for anything without a taskID/state (a corrupt entry is
|
|
852
|
-
* skipped individually — the rest of the snapshot still loads). */
|
|
853
|
-
function normalizeBoardRecord(item) {
|
|
854
|
-
if (typeof item !== "object" || item === null) return null;
|
|
855
|
-
const rec = item;
|
|
856
|
-
if (typeof rec.taskID !== "string" || rec.taskID === "") return null;
|
|
857
|
-
if (typeof rec.state !== "string" || rec.state === "") return null;
|
|
858
|
-
const record = {
|
|
859
|
-
taskID: rec.taskID,
|
|
860
|
-
parentSessionID: typeof rec.parentSessionID === "string" ? rec.parentSessionID : "",
|
|
861
|
-
agent: typeof rec.agent === "string" && rec.agent !== "" ? rec.agent : "agent",
|
|
862
|
-
state: rec.state,
|
|
863
|
-
alias: typeof rec.alias === "string" ? rec.alias : ""
|
|
864
|
-
};
|
|
865
|
-
if (typeof rec.description === "string") record.description = rec.description;
|
|
866
|
-
if (typeof rec.launchedAt === "number" && Number.isFinite(rec.launchedAt)) record.launchedAt = rec.launchedAt;
|
|
867
|
-
if (typeof rec.completedAt === "number" && Number.isFinite(rec.completedAt)) record.completedAt = rec.completedAt;
|
|
868
|
-
if (typeof rec.updatedAt === "number" && Number.isFinite(rec.updatedAt)) record.updatedAt = rec.updatedAt;
|
|
869
|
-
if (typeof rec.timedOut === "boolean") record.timedOut = rec.timedOut;
|
|
870
|
-
return record;
|
|
871
|
-
}
|
|
872
|
-
/** Read `.pantheon/board/state.json` (the cross-session job board snapshot).
|
|
873
|
-
* Fail-open: missing file, corrupt JSON, non-array payload or an unreadable
|
|
874
|
-
* path all yield [] — the panel keeps rendering from the other channels. */
|
|
875
|
-
async function readBoardState(root) {
|
|
876
|
-
let raw;
|
|
877
|
-
try {
|
|
878
|
-
raw = await readFile(boardStatePath(root), "utf8");
|
|
879
|
-
} catch {
|
|
880
|
-
return [];
|
|
881
|
-
}
|
|
882
|
-
let parsed;
|
|
883
|
-
try {
|
|
884
|
-
parsed = JSON.parse(raw);
|
|
885
|
-
} catch {
|
|
886
|
-
return [];
|
|
887
|
-
}
|
|
888
|
-
if (!Array.isArray(parsed)) return [];
|
|
889
|
-
const records = [];
|
|
890
|
-
for (const item of parsed) {
|
|
891
|
-
const record = normalizeBoardRecord(item);
|
|
892
|
-
if (record !== null) records.push(record);
|
|
893
|
-
}
|
|
894
|
-
return records;
|
|
895
|
-
}
|
|
896
844
|
/** Resolve the PROJECT ROOT used by every pantheon file channel. `directory`
|
|
897
845
|
* wins over `worktree` (the old `resolveDelegationsDir` already did this);
|
|
898
846
|
* an absent/empty root or `/` (no git — e.g. the sandbox test project) falls
|
|
899
|
-
* back to cwd. Standardised here so the delegations md
|
|
900
|
-
*
|
|
847
|
+
* back to cwd. Standardised here so the delegations md and the panel logger
|
|
848
|
+
* all read the SAME root (audit finding: the channels
|
|
901
849
|
* resolved the root independently). Pure — no I/O. */
|
|
902
850
|
function resolvePantheonRoot(state, cwd = process.cwd()) {
|
|
903
851
|
const root = state?.directory ?? state?.worktree ?? "";
|
|
904
852
|
if (root === "" || root === "/") return cwd;
|
|
905
853
|
return root;
|
|
906
854
|
}
|
|
907
|
-
/** Resolve the directory where
|
|
908
|
-
* The
|
|
855
|
+
/** Resolve the directory where delegation md reports are written.
|
|
856
|
+
* The finalizer writes `.pantheon/delegations` RELATIVE to the server cwd,
|
|
909
857
|
* which the TUI exposes as `TuiState.path.directory`. */
|
|
910
858
|
function resolveDelegationsDir(state, cwd = process.cwd()) {
|
|
911
859
|
return join(resolvePantheonRoot(state, cwd), ".pantheon", "delegations");
|
|
@@ -962,11 +910,11 @@ const STALE_RUNNING_THRESHOLD_MS = 1800 * 1e3;
|
|
|
962
910
|
* considered stale. Combined with the stale-running threshold to produce the
|
|
963
911
|
* display-only `stale-running` state. */
|
|
964
912
|
const IDLE_SILENCE_MS = 60 * 1e3;
|
|
965
|
-
/** Visual-only terminal retention windows. Reports remain on disk
|
|
966
|
-
*
|
|
913
|
+
/** Visual-only terminal retention windows. Reports remain on disk; these
|
|
914
|
+
* constants only control which rows enter the TUI window. */
|
|
967
915
|
const DELEGATION_DONE_RETENTION_MS = 120 * 1e3;
|
|
968
916
|
const DELEGATION_FAILED_RETENTION_MS = 600 * 1e3;
|
|
969
|
-
/** Alias-less NATIVE task() live entries never receive a
|
|
917
|
+
/** Alias-less NATIVE task() live entries never receive a report alias (the
|
|
970
918
|
* task tool output carries none), so the 30s alias-less prune in
|
|
971
919
|
* mergeChildDelegationSources must not apply to them — 5 minutes covers a
|
|
972
920
|
* slow child listing while still bounding the live map. */
|
|
@@ -974,7 +922,7 @@ const NATIVE_LIVE_ALIASLESS_TTL_MS = 300 * 1e3;
|
|
|
974
922
|
/**
|
|
975
923
|
* Mark a running entry as `stale-running` if it has been running longer than
|
|
976
924
|
* the threshold AND has no recent activity (no `updatedAt` change in the last
|
|
977
|
-
* `IDLE_SILENCE_MS`). This is DISPLAY-ONLY — the
|
|
925
|
+
* `IDLE_SILENCE_MS`). This is DISPLAY-ONLY — the persisted state is unchanged.
|
|
978
926
|
*
|
|
979
927
|
* A `stale-running` entry renders with a warning indicator but the underlying
|
|
980
928
|
* delegation is still treated as running by the backend.
|
|
@@ -1047,9 +995,10 @@ function delegationSpinnerFrame(now) {
|
|
|
1047
995
|
const index = Math.floor(Math.max(0, now) / 1e3) % DELEGATION_SPINNER_FRAMES.length;
|
|
1048
996
|
return DELEGATION_SPINNER_FRAMES[index] ?? DELEGATION_SPINNER_FRAMES[0];
|
|
1049
997
|
}
|
|
1050
|
-
/** Every state the row knows how to draw: the
|
|
1051
|
-
*
|
|
1052
|
-
*
|
|
998
|
+
/** Every state the row knows how to draw: the delegation lifecycle states
|
|
999
|
+
* derived from the children status + md reports (`completed`, `error`,
|
|
1000
|
+
* `cancelled`, `startup_failed`, `startup_unknown`) plus the TUI-only
|
|
1001
|
+
* states (`retry`, `stale-running`). Fase 1 deliberately omits speculative
|
|
1053
1002
|
* blocked/paused/scheduled/skipped. There is no `pending` display state: a
|
|
1054
1003
|
* pre-dispatch tool part maps to `running` in {@link reduceDelegationToolPart}. */
|
|
1055
1004
|
/** Semantic tone mapped to the TUI theme at the row ({@link DelegationRow}).
|
|
@@ -1430,108 +1379,18 @@ function mergeDelegationSources(live, md) {
|
|
|
1430
1379
|
all.sort(compareDelegationEntries);
|
|
1431
1380
|
return all;
|
|
1432
1381
|
}
|
|
1433
|
-
/** Job-board FSM → panel display state. `reconciled` is intentionally absent:
|
|
1434
|
-
* the panel parser rejects it (a reconciled job is done and folded away). */
|
|
1435
|
-
const BOARD_STATE_TO_DISPLAY = {
|
|
1436
|
-
running: "running",
|
|
1437
|
-
completed: "completed",
|
|
1438
|
-
error: "error",
|
|
1439
|
-
startup_failed: "startup_failed",
|
|
1440
|
-
startup_unknown: "startup_unknown",
|
|
1441
|
-
cancelled: "cancelled"
|
|
1442
|
-
};
|
|
1443
|
-
/** Map one persisted board record into the display shape. Returns null for
|
|
1444
|
-
* states the panel does not render (reconciled / unknown). Pure. */
|
|
1445
|
-
function boardRecordToDelegationEntry(record) {
|
|
1446
|
-
const state = BOARD_STATE_TO_DISPLAY[record.state];
|
|
1447
|
-
if (state === void 0) return null;
|
|
1448
|
-
const running = state === "running";
|
|
1449
|
-
return {
|
|
1450
|
-
alias: record.alias !== "" ? record.alias : `job-${record.taskID.slice(0, 8)}`,
|
|
1451
|
-
sessionID: record.parentSessionID,
|
|
1452
|
-
taskID: record.taskID,
|
|
1453
|
-
agent: record.agent,
|
|
1454
|
-
state,
|
|
1455
|
-
startedAt: record.launchedAt ?? record.updatedAt ?? 0,
|
|
1456
|
-
updatedAt: running ? null : record.completedAt ?? record.updatedAt ?? null,
|
|
1457
|
-
timedOut: record.timedOut === true,
|
|
1458
|
-
description: record.description ?? "",
|
|
1459
|
-
source: "board"
|
|
1460
|
-
};
|
|
1461
|
-
}
|
|
1462
|
-
/** Map a whole board snapshot, dropping unrenderable states. Sorted like every
|
|
1463
|
-
* other channel (running first, then most recent). Pure. */
|
|
1464
|
-
function boardRecordsToDelegationEntries(records) {
|
|
1465
|
-
const out = [];
|
|
1466
|
-
for (const record of records) {
|
|
1467
|
-
const entry = boardRecordToDelegationEntry(record);
|
|
1468
|
-
if (entry !== null) out.push(entry);
|
|
1469
|
-
}
|
|
1470
|
-
out.sort(compareDelegationEntries);
|
|
1471
|
-
return out;
|
|
1472
|
-
}
|
|
1473
|
-
/** Active (non-terminal) display states — a job in one of these is still live. */
|
|
1474
|
-
function isActiveDelegationState(state) {
|
|
1475
|
-
return state === "running" || state === "retry" || state === "stale-running";
|
|
1476
|
-
}
|
|
1477
|
-
/** Temporal guard for the board merge (recycled-alias regression): board
|
|
1478
|
-
* aliases recycle across jobs (apo-1 of a previous job), so a board
|
|
1479
|
-
* terminal record only closes a running entry when its Finalized timestamp
|
|
1480
|
-
* is STRICTLY NEWER than the entry it would close. Older/equal/missing
|
|
1481
|
-
* keeps the live entry — when in doubt the live job wins. Pure. */
|
|
1482
|
-
function shouldKeepRunningOverBoardTerminal(existing, incoming) {
|
|
1483
|
-
if (existing === void 0) return false;
|
|
1484
|
-
if (!isActiveDelegationState(existing.state)) return false;
|
|
1485
|
-
if (isActiveDelegationState(incoming.state)) return false;
|
|
1486
|
-
const incomingTs = incoming.updatedAt;
|
|
1487
|
-
if (incomingTs === null) return true;
|
|
1488
|
-
return incomingTs <= (existing.updatedAt ?? existing.startedAt);
|
|
1489
|
-
}
|
|
1490
|
-
/** Merge the board over any other channel. The board is AUTHORITATIVE for
|
|
1491
|
-
* state/alias/agent (it is the persisted FSM). This merge is intentionally
|
|
1492
|
-
* UNSCOPED so the board can enrich an active-session row (dedup by taskID);
|
|
1493
|
-
* the caller scopes the merged list with {@link filterDelegationsToSession},
|
|
1494
|
-
* which is what keeps other-session board jobs out of the panel. Dedup by
|
|
1495
|
-
* taskID first, then by (sessionID, alias) — aliases are per parent session. Pure. */
|
|
1496
|
-
function mergeBoardDelegationSources(base, board) {
|
|
1497
|
-
const key = (sessionID, alias) => `${sessionID}\u0000${alias}`;
|
|
1498
|
-
const byTaskID = /* @__PURE__ */ new Map();
|
|
1499
|
-
const bySessionAlias = /* @__PURE__ */ new Map();
|
|
1500
|
-
const out = [...base];
|
|
1501
|
-
out.forEach((entry, index) => {
|
|
1502
|
-
if (entry.taskID !== void 0) byTaskID.set(entry.taskID, index);
|
|
1503
|
-
bySessionAlias.set(key(entry.sessionID, entry.alias), index);
|
|
1504
|
-
});
|
|
1505
|
-
for (const incoming of board) {
|
|
1506
|
-
const index = (incoming.taskID !== void 0 ? byTaskID.get(incoming.taskID) : void 0) ?? bySessionAlias.get(key(incoming.sessionID, incoming.alias));
|
|
1507
|
-
if (index === void 0) {
|
|
1508
|
-
out.push(incoming);
|
|
1509
|
-
const next = out.length - 1;
|
|
1510
|
-
if (incoming.taskID !== void 0) byTaskID.set(incoming.taskID, next);
|
|
1511
|
-
bySessionAlias.set(key(incoming.sessionID, incoming.alias), next);
|
|
1512
|
-
continue;
|
|
1513
|
-
}
|
|
1514
|
-
if (shouldKeepRunningOverBoardTerminal(out[index], incoming)) continue;
|
|
1515
|
-
out[index] = {
|
|
1516
|
-
...incoming,
|
|
1517
|
-
sessionID: incoming.sessionID !== "" ? incoming.sessionID : out[index]?.sessionID ?? ""
|
|
1518
|
-
};
|
|
1519
|
-
}
|
|
1520
|
-
out.sort(compareDelegationEntries);
|
|
1521
|
-
return out;
|
|
1522
|
-
}
|
|
1523
1382
|
/** Scope a fully-merged display list to the ACTIVE session only.
|
|
1524
1383
|
*
|
|
1525
|
-
* The panel is session-scoped: rows from other sessions (
|
|
1526
|
-
*
|
|
1384
|
+
* The panel is session-scoped: rows from other sessions (md history under
|
|
1385
|
+
* `.pantheon/delegations/<other-session>/`) and rows with no
|
|
1527
1386
|
* attributable session (empty sessionID) are DROPPED. The previous
|
|
1528
1387
|
* cross-session behavior rendered those rows and clicking them led to
|
|
1529
1388
|
* "Session not found" — there is no cross-session channel anymore.
|
|
1530
1389
|
*
|
|
1531
|
-
* The
|
|
1532
|
-
*
|
|
1533
|
-
*
|
|
1534
|
-
* ONCE, after every merge (children + live + md
|
|
1390
|
+
* The md channel still feeds the merge UNFILTERED so it can ENRICH an
|
|
1391
|
+
* active-session row with state/alias/agent (dedup by taskID), but it can
|
|
1392
|
+
* never introduce a row for another session: this filter is applied
|
|
1393
|
+
* ONCE, after every merge (children + live + md).
|
|
1535
1394
|
*
|
|
1536
1395
|
* Returns [] when no active session resolves (null/placeholder) — there is no
|
|
1537
1396
|
* scope to show. Native children always carry the active session id (stamped
|
|
@@ -1569,20 +1428,25 @@ function resolveCurrentSessionID(sources) {
|
|
|
1569
1428
|
return null;
|
|
1570
1429
|
}
|
|
1571
1430
|
/** THE single choke point for every `session.children` / session-API path.
|
|
1572
|
-
* Returns
|
|
1573
|
-
* null for anything else (placeholder,
|
|
1574
|
-
* skips the call entirely instead of
|
|
1575
|
-
* (the "%7BsessionID%7D" regression).
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1431
|
+
* Returns the v2 SDK parameter shape `{ sessionID }` ONLY for a
|
|
1432
|
+
* server-valid session id; returns null for anything else (placeholder,
|
|
1433
|
+
* empty, foreign id) so the caller skips the call entirely instead of
|
|
1434
|
+
* sending an unsubstituted placeholder (the "%7BsessionID%7D" regression).
|
|
1435
|
+
* The TUI client is `@opencode-ai/sdk/v2`, whose session methods take a
|
|
1436
|
+
* FLAT parameter object (`{ sessionID }`), NOT the v1 `{ path: { id } }`
|
|
1437
|
+
* envelope — passing the v1 shape left the v2 `{sessionID}` URL template
|
|
1438
|
+
* unsubstituted (`/session/%7BsessionID%7D/children`). Every session-API
|
|
1439
|
+
* call site MUST go through this function (enforced by the source-scan
|
|
1440
|
+
* test in tests/pantheon/tui-delegations.test.ts). */
|
|
1578
1441
|
function safeSessionPath(id) {
|
|
1579
1442
|
if (!isValidSessionId(id)) return null;
|
|
1580
|
-
return {
|
|
1443
|
+
return { sessionID: id };
|
|
1581
1444
|
}
|
|
1582
|
-
/** Build the `session.children`
|
|
1445
|
+
/** Build the `session.children` parameters ONLY from a validated session id.
|
|
1583
1446
|
* Delegates to {@link safeSessionPath} — the single choke point. Returns
|
|
1584
|
-
* null for null/invalid ids so the caller
|
|
1585
|
-
* sending an unsubstituted placeholder (the
|
|
1447
|
+
* the v2 SDK shape `{ sessionID }`; null for null/invalid ids so the caller
|
|
1448
|
+
* skips the fetch instead of sending an unsubstituted placeholder (the
|
|
1449
|
+
* "%7BsessionID%7D" regression). */
|
|
1586
1450
|
function buildChildrenPath(id) {
|
|
1587
1451
|
return safeSessionPath(id);
|
|
1588
1452
|
}
|
|
@@ -1600,10 +1464,10 @@ function childStatusToState(status) {
|
|
|
1600
1464
|
*
|
|
1601
1465
|
* A row is
|
|
1602
1466
|
* `{glyph} {alias:7} {elapsed:>5}` plus a muted description line. The short
|
|
1603
|
-
* alias (`apo-1`) is the single identity; rows without a
|
|
1467
|
+
* alias (`apo-1`) is the single identity; rows without a report alias show
|
|
1604
1468
|
* the agent instead (never both). */
|
|
1605
1469
|
/** Map every FSM/display state to one of the 4 row kinds. reconciled never
|
|
1606
|
-
* reaches the panel (the
|
|
1470
|
+
* reaches the panel (the md parser drops it) but reads as done;
|
|
1607
1471
|
* cancelled reads as done; startup_failed reads as failed while
|
|
1608
1472
|
* startup_unknown (no error known) reads as retry. Pure. */
|
|
1609
1473
|
function delegationRowStatus(state) {
|
|
@@ -1636,8 +1500,8 @@ function delegationRowMarker(state, now = Date.now()) {
|
|
|
1636
1500
|
const status = delegationRowStatus(state);
|
|
1637
1501
|
return `${status === "active" ? delegationSpinnerFrame(now) : delegationRowGlyph(status)} `;
|
|
1638
1502
|
}
|
|
1639
|
-
/** ONE identity per row: the short
|
|
1640
|
-
*
|
|
1503
|
+
/** ONE identity per row: the short report alias (`apo-1`); a row without a
|
|
1504
|
+
* report alias (native task() child `native-task` / `native-<id>`, or an
|
|
1641
1505
|
* alias-less native live row `live-<callID>` kept as children-only) shows
|
|
1642
1506
|
* the agent instead — never alias + agent stacked. Pure. */
|
|
1643
1507
|
function delegationRowIdentity(entry) {
|
|
@@ -1736,7 +1600,7 @@ function ceilingDelegationList(all, maxVisible = 8, now = Date.now()) {
|
|
|
1736
1600
|
};
|
|
1737
1601
|
}
|
|
1738
1602
|
/** Split a display list into native task() rows vs pantheon_delegate rows.
|
|
1739
|
-
* Native = source 'children-only' (no
|
|
1603
|
+
* Native = source 'children-only' (no delegate report); everything else counts
|
|
1740
1604
|
* as pantheon. Pure — powers the hooks.log line. */
|
|
1741
1605
|
function countDelegationSources(entries) {
|
|
1742
1606
|
let native = 0;
|
|
@@ -1748,7 +1612,7 @@ function countDelegationSources(entries) {
|
|
|
1748
1612
|
};
|
|
1749
1613
|
}
|
|
1750
1614
|
/** Diagnostic hooks.log line for a panel re-fetch, with the children
|
|
1751
|
-
* breakdown (pantheon = children WITH a
|
|
1615
|
+
* breakdown (pantheon = children WITH a delegate report, native = children
|
|
1752
1616
|
* WITHOUT one). Pure — the View logs the returned string verbatim. */
|
|
1753
1617
|
function formatPanelLogLine(children, pantheon, native, md, events) {
|
|
1754
1618
|
return `panel: children=${children}(pantheon=${pantheon} native=${native}) md=${md} events=${events}`;
|
|
@@ -1763,7 +1627,7 @@ function formatPanelLogLine(children, pantheon, native, md, events) {
|
|
|
1763
1627
|
* child of the current session — pantheon_delegate OR the native `task()`
|
|
1764
1628
|
* tool — carries parentID = caller), so it gets source 'children-only', a
|
|
1765
1629
|
* per-child alias 'native-<last4 of the child id>' (one identity per native
|
|
1766
|
-
* row) instead of a
|
|
1630
|
+
* row) instead of a report alias. The 'task nativa' description fallback
|
|
1767
1631
|
* keeps the row non-empty when the child carries no title.
|
|
1768
1632
|
* A FRESH terminal md state wins over the derived state (a stale md report
|
|
1769
1633
|
* from a previous incarnation never flips a live child — see
|
|
@@ -1776,7 +1640,7 @@ function formatPanelLogLine(children, pantheon, native, md, events) {
|
|
|
1776
1640
|
* Sorted running-first (compareDelegationEntries).
|
|
1777
1641
|
* Pure — no I/O. */
|
|
1778
1642
|
/** Temporal guard for the children channel (same recycled-alias class as the
|
|
1779
|
-
*
|
|
1643
|
+
* md merge): an md terminal report matched by taskID may still belong to
|
|
1780
1644
|
* a PREVIOUS job incarnation. Accept it only when its Finalized timestamp
|
|
1781
1645
|
* covers the child's latest activity, when either side has no timestamp to
|
|
1782
1646
|
* compare, or when both startedAt agree on a single job incarnation.
|
|
@@ -2028,24 +1892,19 @@ function View(props) {
|
|
|
2028
1892
|
} catch {
|
|
2029
1893
|
md = [];
|
|
2030
1894
|
}
|
|
2031
|
-
let board = [];
|
|
2032
|
-
try {
|
|
2033
|
-
board = boardRecordsToDelegationEntries(await readBoardState(projectRoot()));
|
|
2034
|
-
} catch {
|
|
2035
|
-
board = [];
|
|
2036
|
-
}
|
|
2037
1895
|
const sessionID = resolveCurrentSessionID({
|
|
2038
1896
|
sessionID: props.sessionID,
|
|
2039
1897
|
api: props.api
|
|
2040
1898
|
});
|
|
2041
1899
|
if (sessionID === null) {
|
|
2042
|
-
setChildDelegations(
|
|
1900
|
+
setChildDelegations([]);
|
|
2043
1901
|
panelLog.info(`${formatPanelLogLine(0, 0, 0, md.length, eventRefreshCount)} (no sessionID — inactive panel)`);
|
|
2044
1902
|
return;
|
|
2045
1903
|
}
|
|
2046
1904
|
let children = [];
|
|
2047
1905
|
try {
|
|
2048
|
-
const
|
|
1906
|
+
const childrenPath = buildChildrenPath(sessionID);
|
|
1907
|
+
const result = childrenPath ? await props.api.client.session.children(childrenPath) : void 0;
|
|
2049
1908
|
const data = result?.data ?? result;
|
|
2050
1909
|
children = Array.isArray(data) ? data : [];
|
|
2051
1910
|
} catch (err) {
|
|
@@ -2055,7 +1914,7 @@ function View(props) {
|
|
|
2055
1914
|
const resolveStatus = (childID) => {
|
|
2056
1915
|
if (!isValidSessionId(childID)) return void 0;
|
|
2057
1916
|
try {
|
|
2058
|
-
return state
|
|
1917
|
+
return state.session.status(childID)?.type;
|
|
2059
1918
|
} catch {
|
|
2060
1919
|
return;
|
|
2061
1920
|
}
|
|
@@ -2066,7 +1925,7 @@ function View(props) {
|
|
|
2066
1925
|
})), md, now(), sessionID);
|
|
2067
1926
|
for (const [k, v] of props.liveStore.map) if (v.alias === null && v.taskID === null && Date.now() - v.startedAt > 3e4) props.liveStore.map.delete(k);
|
|
2068
1927
|
const liveEntries = [...props.liveStore.map.values()].filter((entry) => entry.sessionID === sessionID);
|
|
2069
|
-
setChildDelegations(filterDelegationsToSession(
|
|
1928
|
+
setChildDelegations(filterDelegationsToSession(mergeChildDelegationSources(childEntries, liveEntries), sessionID));
|
|
2070
1929
|
const counts = countDelegationSources(childEntries);
|
|
2071
1930
|
panelLog.info(formatPanelLogLine(children.length, counts.pantheon, counts.native, md.length, eventRefreshCount));
|
|
2072
1931
|
} finally {
|
|
@@ -2378,6 +2237,6 @@ const plugin = {
|
|
|
2378
2237
|
setup: async () => {}
|
|
2379
2238
|
};
|
|
2380
2239
|
//#endregion
|
|
2381
|
-
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,
|
|
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 };
|
|
2382
2241
|
|
|
2383
2242
|
//# 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.18",
|
|
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.18",
|
|
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.18",
|
|
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": {
|