pantheon-opencode 1.5.0-beta.14 → 1.5.0-beta.15

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/package.json +1 -1
  3. package/plugin.json +1 -1
  4. package/pyproject.toml +2 -5
  5. package/src/agents/zeus.md +4 -26
  6. package/src/pantheon/background-job-board.ts +4 -4
  7. package/src/pantheon/delegate-manager.ts +95 -10
  8. package/src/pantheon/delegation-classifier.ts +3 -4
  9. package/src/pantheon/delegation-finalize.ts +69 -7
  10. package/src/pantheon/delegation.ts +16 -4
  11. package/src/pantheon/presets.d.mts +0 -20
  12. package/src/pantheon/presets.mjs +0 -64
  13. package/src/pantheon/toon-codec.ts +1 -1
  14. package/src/plugin-v2.ts +4 -6
  15. package/src/plugin.ts +6 -6
  16. package/src/plugins/pantheon-hooks.ts +1 -1
  17. package/src/plugins/tui/dist/tui.d.ts +43 -36
  18. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  19. package/src/plugins/tui/dist/tui.js +102 -38
  20. package/src/plugins/tui/package-lock.json +2 -2
  21. package/src/plugins/tui/package.json +1 -1
  22. package/src/plugins/tui/src/index.tsx +168 -67
  23. package/src/routing.yml +0 -16
  24. package/scripts/hash_verify.py +0 -192
  25. package/scripts/test-context-compression.sh +0 -169
  26. package/src/pantheon/auto-wake.ts +0 -85
  27. package/src/pantheon/chat-reminders.ts +0 -9
  28. package/src/pantheon/comment-checker.ts +0 -48
  29. package/src/pantheon/compaction-assert.ts +0 -160
  30. package/src/pantheon/compaction-handoff.ts +0 -304
  31. package/src/pantheon/cost-tracker.ts +0 -108
  32. package/src/pantheon/dispatch-guard.ts +0 -109
  33. package/src/pantheon/permission-eval.ts +0 -284
  34. package/src/pantheon/preemptive-compact.ts +0 -76
  35. package/src/pantheon/provider-capabilities.ts +0 -221
  36. package/src/pantheon/provider-stream-tracker.ts +0 -182
  37. package/src/pantheon/retry-policy.ts +0 -212
  38. package/src/pantheon/token-opt.ts +0 -689
  39. package/src/pantheon/v2-events.ts +0 -148
  40. package/src/pantheon/v2-hooks.ts +0 -287
  41. package/src/pantheon/v2-tool-definitions.ts +0 -331
  42. package/src/pantheon/zeus-delegate-with-retry.ts +0 -406
@@ -213,70 +213,6 @@ function loadRoutingYaml(routingPath) {
213
213
  return routing && typeof routing === 'object' ? routing : null
214
214
  }
215
215
 
216
- const RETRY_ERROR_TYPES = new Set(['auth', 'rate_limit', 'timeout', 'other'])
217
-
218
- /**
219
- * Load the R1 per-error-type retry policy from routing.yml (`retry_policy:`).
220
- * Fail-open: missing/unparseable config yields null (the caller falls back
221
- * to DEFAULT_RETRY_POLICY) and warns via the optional logger.
222
- *
223
- * @param {object} [opts]
224
- * @param {string} [opts.routingPath]
225
- * @param {{warn?: Function}} [opts.logger]
226
- * @returns {Record<string, number>|null} error-type → max retries
227
- */
228
- export function loadRoutingRetryPolicy({ routingPath, logger = console } = {}) {
229
- try {
230
- const routing = loadRoutingYaml(routingPath)
231
- const policy = routing?.retry_policy
232
- if (!policy || typeof policy !== 'object' || Array.isArray(policy)) return null
233
- const out = {}
234
- for (const [type, max] of Object.entries(policy)) {
235
- if (RETRY_ERROR_TYPES.has(type) && Number.isSafeInteger(max) && max >= 0) {
236
- out[type] = max
237
- }
238
- }
239
- return Object.keys(out).length > 0 ? out : null
240
- } catch (err) {
241
- const reason = err instanceof Error ? err.message : String(err)
242
- logger.warn?.(`presets: routing.yml retry_policy unavailable (${reason})`)
243
- return null
244
- }
245
- }
246
-
247
- /**
248
- * Load the R1 provider cooldown config from routing.yml (`cooldown:`).
249
- * Fail-open: missing/unparseable config yields null (caller falls back to
250
- * DEFAULT_COOLDOWN).
251
- *
252
- * @param {object} [opts]
253
- * @param {string} [opts.routingPath]
254
- * @param {{warn?: Function}} [opts.logger]
255
- * @returns {{allowed_fails: number, cooldown_time_seconds: number}|null}
256
- */
257
- export function loadRoutingCooldown({ routingPath, logger = console } = {}) {
258
- try {
259
- const routing = loadRoutingYaml(routingPath)
260
- const cooldown = routing?.cooldown
261
- if (!cooldown || typeof cooldown !== 'object' || Array.isArray(cooldown)) return null
262
- const allowedFails = cooldown.allowed_fails
263
- const cooldownTimeSeconds = cooldown.cooldown_time_seconds
264
- if (
265
- !Number.isSafeInteger(allowedFails) ||
266
- allowedFails < 1 ||
267
- !Number.isSafeInteger(cooldownTimeSeconds) ||
268
- cooldownTimeSeconds < 1
269
- ) {
270
- return null
271
- }
272
- return { allowed_fails: allowedFails, cooldown_time_seconds: cooldownTimeSeconds }
273
- } catch (err) {
274
- const reason = err instanceof Error ? err.message : String(err)
275
- logger.warn?.(`presets: routing.yml cooldown unavailable (${reason})`)
276
- return null
277
- }
278
- }
279
-
280
216
  /**
281
217
  * Load the R4 per-agent step caps from routing.yml
282
218
  * (`agents.<name>.max_steps`). Fail-open: missing config yields {}.
@@ -53,7 +53,7 @@ function looksNumeric(text: string): boolean {
53
53
  return INT_RE.test(text) || FLOAT_RE.test(text)
54
54
  }
55
55
 
56
- /** Nominal chars-per-token ratio (same fixed basis as token-opt metering). */
56
+ /** Nominal chars-per-token ratio (same fixed basis as toon-codec metering). */
57
57
  export const CHARS_PER_TOKEN = 4
58
58
 
59
59
  function charsToTokens(chars: number): number {
package/src/plugin-v2.ts CHANGED
@@ -681,12 +681,10 @@ function createV2ToolDefinitionsFromContext(_context: PluginContext): V2ToolDef[
681
681
  name,
682
682
  description: toolDescriptions[name] ?? `Pantheon tool: ${name}`,
683
683
  input: { type: 'object' as const, properties: {} },
684
- // FOLLOW-UP: connect the real wrappers from
685
- // src/pantheon/v2-tool-definitions.ts (createV2ToolDefinitions with
686
- // costCommand/goalTools) once V1 infrastructure is resolvable in the V2
687
- // standalone context. The before/after factories in v2-hooks.ts:216-282
688
- // are likewise unwired. Until then, placeholders resolve to the object
689
- // shape so the beta host never sees a bare string.
684
+ // FOLLOW-UP: wire the real tool wrappers (costCommand/goalTools) once V1
685
+ // infrastructure is resolvable in the V2 standalone context. Until then,
686
+ // placeholders resolve to the object shape so the beta host never sees a
687
+ // bare string.
690
688
  execute: async (_input: Record<string, unknown>, _context: unknown): Promise<V2ToolResult> => {
691
689
  return { output: `${name}: ${v2UnavailableMessage}` }
692
690
  },
package/src/plugin.ts CHANGED
@@ -66,10 +66,10 @@ import { activePresetCandidates, createVisionHandler } from './pantheon/vision.t
66
66
  const log = createPantheonLogger({ module: 'pantheon-plugin' })
67
67
 
68
68
  // R4: per-agent step caps from routing.yml `agents.<name>.max_steps`
69
- // (fail-open → {} = no agent capped). R1 retry_policy/cooldown are NOT
70
- // wired here — the plugin has no retry path (opencode cannot intercept task
71
- // completion); zeus waves load them via presets.mjs loaders and pass them
72
- // to zeusDelegateWithRetry explicitly.
69
+ // (fail-open → {} = no agent capped). The R1 retry_policy/cooldown block and
70
+ // its presets.mjs loaders were removed together with their only consumer
71
+ // (zeus-delegate-with-retry.ts); the plugin has no retry path (opencode cannot
72
+ // intercept task completion).
73
73
  const stepCapTracker = new StepCapTracker(loadRoutingMaxSteps({ logger: log }))
74
74
 
75
75
  // O5: permission.task glob rules from routing.yml `permission.task`
@@ -517,8 +517,8 @@ const plugin: Plugin = async (input: PluginInput) => {
517
517
 
518
518
  // Wave 4 (PR #46): /cost — delegation cost + token visibility. Reads
519
519
  // opencode.db read-only via the single node:sqlite backend.
520
- // Fully wired (unlike dispatch-guard, which is manual-orchestration-only
521
- // because opencode 1.18.x cannot intercept task completion via hooks).
520
+ // Fully wired. (opencode 1.18.x cannot intercept task completion via hooks,
521
+ // so empty-result handling lives in delegation-classifier.ts.)
522
522
  const costCommand = createCostCommand()
523
523
  const modelCommand = createModelCommand()
524
524
 
@@ -783,7 +783,7 @@ function delegationAgent(tool: string, args?: unknown): string {
783
783
  * panel tracks the child end-to-end. Fire-and-forget and silent: a mirror
784
784
  * failure must never surface into the tool call or the transcript.
785
785
  * registerLaunchIfAbsent keeps it idempotent against any other dispatcher
786
- * (v2-events, plugin double-load, repeated after-hook firings).
786
+ * (plugin double-load, repeated after-hook firings).
787
787
  */
788
788
  function mirrorNativeBackgroundDispatch(
789
789
  input: { tool: string; sessionID: string },
@@ -44,11 +44,12 @@ declare function parseDelegationMarkdown(raw: string, fileAlias?: string, sessio
44
44
  declare function readDelegationEntries(dir: string): Promise<DelegationEntry[]>;
45
45
  /** Read delegation reports from EVERY session under
46
46
  * `<root>/.pantheon/delegations/<sessionID>/<alias>.md` — the panel's
47
- * HISTORY channel. Unlike the children/live channels it does NOT depend on a
48
- * resolved sessionID: with no focused session (null/placeholder), the panel
49
- * still shows the reports from all past sessions (running first, Finalized
50
- * desc — the sort applied by readDelegationEntries). Fail-open: a
51
- * missing/unreadable directory yields []. */
47
+ * ENRICHMENT source. The read is deliberately unfiltered (each entry carries
48
+ * its directory's sessionID, so a taskID match can enrich any channel);
49
+ * callers MUST scope the merged result with {@link filterDelegationsToSession}
50
+ * before rendering — only the active session's reports are shown. Entries are
51
+ * running first, Finalized desc (the sort applied by readDelegationEntries).
52
+ * Fail-open: a missing/unreadable directory yields []. */
52
53
  declare function readAllDelegationEntries(root: string): Promise<DelegationEntry[]>;
53
54
  /** The persisted subset of BackgroundJobRecord the panel consumes. Duck-typed
54
55
  * so a corrupt/older record never breaks parsing. */
@@ -122,6 +123,11 @@ declare const IDLE_SILENCE_MS: number;
122
123
  * board; these constants only control which rows enter the TUI window. */
123
124
  declare const DELEGATION_DONE_RETENTION_MS: number;
124
125
  declare const DELEGATION_FAILED_RETENTION_MS: number;
126
+ /** Alias-less NATIVE task() live entries never receive a board alias (the
127
+ * task tool output carries none), so the 30s alias-less prune in
128
+ * mergeChildDelegationSources must not apply to them — 5 minutes covers a
129
+ * slow child listing while still bounding the live map. */
130
+ declare const NATIVE_LIVE_ALIASLESS_TTL_MS: number;
125
131
  /**
126
132
  * Mark a running entry as `stale-running` if it has been running longer than
127
133
  * the threshold AND has no recent activity (no `updatedAt` change in the last
@@ -154,11 +160,12 @@ declare function delegationSpinnerFrame(now: number): string;
154
160
  type DelegationDisplayState = DelegationEntry['state'];
155
161
  /** Semantic tone mapped to the TUI theme at the row ({@link DelegationRow}).
156
162
  * Kept separate + pure so the color channel is testable without booting the
157
- * renderer. */
158
- type DelegationStateTone = 'warning' | 'error' | 'success' | 'muted';
159
- /** Color is the ONLY channel separating `stale-running` from a live `running`
160
- * row — the 4-glyph row design has no dedicated stale shape. Display-only;
161
- * no behavior change. */
163
+ * renderer. Every display state resolves to one of the three status colors;
164
+ * the row paints its whole content with it (see {@link DelegationRow}). */
165
+ type DelegationStateTone = 'warning' | 'error' | 'success';
166
+ /** Status → color mapping for the whole-row tone. Red = failure, green =
167
+ * terminal, yellow = in flight. The glyph remains a redundant channel so the
168
+ * state stays legible in monochrome. Display-only; no behavior change. */
162
169
  declare function delegationStateTone(state: DelegationDisplayState): DelegationStateTone;
163
170
  type ToolActivity = {
164
171
  tool: string;
@@ -291,10 +298,29 @@ declare function boardRecordToDelegationEntry(record: BoardJobRecord): Delegatio
291
298
  * other channel (running first, then most recent). Pure. */
292
299
  declare function boardRecordsToDelegationEntries(records: readonly BoardJobRecord[]): DelegationEntry[];
293
300
  /** Merge the board over any other channel. The board is AUTHORITATIVE for
294
- * state/alias/agent (it is the persisted FSM) and it is the only channel
295
- * that carries jobs from OTHER sessions. Dedup by taskID first, then by
296
- * (sessionID, alias) — aliases are per parent session. Pure. */
301
+ * state/alias/agent (it is the persisted FSM). This merge is intentionally
302
+ * UNSCOPED so the board can enrich an active-session row (dedup by taskID);
303
+ * the caller scopes the merged list with {@link filterDelegationsToSession},
304
+ * which is what keeps other-session board jobs out of the panel. Dedup by
305
+ * taskID first, then by (sessionID, alias) — aliases are per parent session. Pure. */
297
306
  declare function mergeBoardDelegationSources(base: readonly DelegationEntry[], board: readonly DelegationEntry[]): DelegationEntry[];
307
+ /** Scope a fully-merged display list to the ACTIVE session only.
308
+ *
309
+ * The panel is session-scoped: rows from other sessions (board snapshot, md
310
+ * history under `.pantheon/delegations/<other-session>/`) and rows with no
311
+ * attributable session (empty sessionID) are DROPPED. The previous
312
+ * cross-session behavior rendered those rows and clicking them led to
313
+ * "Session not found" — there is no cross-session channel anymore.
314
+ *
315
+ * The board/md channels still feed the merge UNFILTERED so the board can
316
+ * ENRICH an active-session row with state/alias/agent (dedup by taskID), but
317
+ * they can never introduce a row for another session: this filter is applied
318
+ * ONCE, after every merge (children + live + md + board).
319
+ *
320
+ * Returns [] when no active session resolves (null/placeholder) — there is no
321
+ * scope to show. Native children always carry the active session id (stamped
322
+ * by `childrenToDelegationEntries`), so they survive the filter. Pure. */
323
+ declare function filterDelegationsToSession(entries: readonly DelegationEntry[], activeSessionID: string | null | undefined): DelegationEntry[];
298
324
  /** Server-aligned session id validity: opencode rejects anything not starting
299
325
  * with "ses" (SchemaError). This deliberately mirrors that exact contract —
300
326
  * nothing stricter, nothing looser — so a template placeholder ("{sessionID}"),
@@ -394,8 +420,9 @@ declare function delegationRowGlyph(status: DelegationRowStatus): string;
394
420
  * Pure. */
395
421
  declare function delegationRowMarker(state: DelegationDisplayState, now?: number): string;
396
422
  /** ONE identity per row: the short board alias (`apo-1`); a row without a
397
- * board alias (native task() child, alias `native-task`) shows the agent
398
- * instead — never alias + agent stacked. Pure. */
423
+ * board alias (native task() child `native-task` / `native-<id>`, or an
424
+ * alias-less native live row `live-<callID>` kept as children-only) shows
425
+ * the agent instead — never alias + agent stacked. Pure. */
399
426
  declare function delegationRowIdentity(entry: DelegationEntry): string;
400
427
  /** Fixed alias cell, pad-right + hard-truncate to `width` (default 7) so
401
428
  * identities line up across rows. Pure. */
@@ -447,26 +474,6 @@ declare function countDelegationSources(entries: readonly DelegationEntry[]): {
447
474
  * breakdown (pantheon = children WITH a board report, native = children
448
475
  * WITHOUT one). Pure — the View logs the returned string verbatim. */
449
476
  declare function formatPanelLogLine(children: number, pantheon: number, native: number, md: number, events: number): string;
450
- /** Turn child sessions (PRIMARY) enriched with md reports into the display
451
- * list. One entry per child id (duplicates across re-fetches collapse).
452
- * The md report is matched by `Task ID` (== child.id) and supplies alias,
453
- * agent, description, terminal state and duration. A child without a
454
- * report still renders: description from its title, agent from the child
455
- * itself (fallback 'agent'), state derived from its status, startedAt from
456
- * time.created. A report-less child is a NATIVE task() child (every
457
- * child of the current session — pantheon_delegate OR the native `task()`
458
- * tool — carries parentID = caller), so it gets source 'children-only',
459
- * the internal alias 'native-task' instead of a
460
- * board alias. The 'task nativa' description fallback keeps the row
461
- * non-empty when the child carries no title.
462
- * Terminal md state wins over the derived state; a running md defers to
463
- * the child's live status. A running child is NEVER archived — it always
464
- * lands in the active split (splitDelegationList active = running/retry).
465
- * `parentSessionID` (the focused session id) is stamped on report-less
466
- * children so the row carries its origin session; omit it and they stay
467
- * unscoped ('').
468
- * Sorted running-first (compareDelegationEntries).
469
- * Pure — no I/O. */
470
477
  declare function childrenToDelegationEntries(children: readonly ChildDelegationLike[] | undefined, md: readonly DelegationEntry[], now?: number, parentSessionID?: string): DelegationEntry[];
471
478
  /** Navigate the TUI to a child session (click/Enter on a delegation row).
472
479
  * Returns false when the route API is unavailable or the target id is
@@ -496,5 +503,5 @@ declare const plugin: TuiPluginModule & {
496
503
  setup: () => Promise<void>;
497
504
  };
498
505
  //#endregion
499
- export { BoardJobRecord, 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, ParsedDelegationToolPart, STALE_RUNNING_THRESHOLD_MS, ToolActivity, TuiSessionSources, boardRecordToDelegationEntry, boardRecordsToDelegationEntries, boardStatePath, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, createDelegationRowOpenHandler, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationRowGlyph, delegationRowIdentity, delegationRowMarker, delegationRowStatus, delegationSpinnerFrame, delegationStateTone, extractToolActivity, fmtElapsed, formatDelegationAlias, formatDelegationElapsed, formatDelegationHeader, formatDelegationRowLead, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeBoardDelegationSources, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readBoardState, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
506
+ export { BoardJobRecord, 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, boardRecordToDelegationEntry, boardRecordsToDelegationEntries, boardStatePath, 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, mergeBoardDelegationSources, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readBoardState, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
500
507
  //# sourceMappingURL=tui.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":"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"}
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.14";
168
+ return "1.5.0-beta.15";
169
169
  }
170
170
  /**
171
171
  * usage-bar — AI subscription usage gauge for the opencode TUI.
@@ -832,11 +832,12 @@ async function readDelegationEntries(dir) {
832
832
  }
833
833
  /** Read delegation reports from EVERY session under
834
834
  * `<root>/.pantheon/delegations/<sessionID>/<alias>.md` — the panel's
835
- * HISTORY channel. Unlike the children/live channels it does NOT depend on a
836
- * resolved sessionID: with no focused session (null/placeholder), the panel
837
- * still shows the reports from all past sessions (running first, Finalized
838
- * desc — the sort applied by readDelegationEntries). Fail-open: a
839
- * missing/unreadable directory yields []. */
835
+ * ENRICHMENT source. The read is deliberately unfiltered (each entry carries
836
+ * its directory's sessionID, so a taskID match can enrich any channel);
837
+ * callers MUST scope the merged result with {@link filterDelegationsToSession}
838
+ * before rendering — only the active session's reports are shown. Entries are
839
+ * running first, Finalized desc (the sort applied by readDelegationEntries).
840
+ * Fail-open: a missing/unreadable directory yields []. */
840
841
  async function readAllDelegationEntries(root) {
841
842
  return readDelegationEntries(join(root, ".pantheon", "delegations"));
842
843
  }
@@ -965,6 +966,11 @@ const IDLE_SILENCE_MS = 60 * 1e3;
965
966
  * board; these constants only control which rows enter the TUI window. */
966
967
  const DELEGATION_DONE_RETENTION_MS = 120 * 1e3;
967
968
  const DELEGATION_FAILED_RETENTION_MS = 600 * 1e3;
969
+ /** Alias-less NATIVE task() live entries never receive a board alias (the
970
+ * task tool output carries none), so the 30s alias-less prune in
971
+ * mergeChildDelegationSources must not apply to them — 5 minutes covers a
972
+ * slow child listing while still bounding the live map. */
973
+ const NATIVE_LIVE_ALIASLESS_TTL_MS = 300 * 1e3;
968
974
  /**
969
975
  * Mark a running entry as `stale-running` if it has been running longer than
970
976
  * the threshold AND has no recent activity (no `updatedAt` change in the last
@@ -1048,20 +1054,21 @@ function delegationSpinnerFrame(now) {
1048
1054
  * pre-dispatch tool part maps to `running` in {@link reduceDelegationToolPart}. */
1049
1055
  /** Semantic tone mapped to the TUI theme at the row ({@link DelegationRow}).
1050
1056
  * Kept separate + pure so the color channel is testable without booting the
1051
- * renderer. */
1052
- /** Color is the ONLY channel separating `stale-running` from a live `running`
1053
- * row — the 4-glyph row design has no dedicated stale shape. Display-only;
1054
- * no behavior change. */
1057
+ * renderer. Every display state resolves to one of the three status colors;
1058
+ * the row paints its whole content with it (see {@link DelegationRow}). */
1059
+ /** Status → color mapping for the whole-row tone. Red = failure, green =
1060
+ * terminal, yellow = in flight. The glyph remains a redundant channel so the
1061
+ * state stays legible in monochrome. Display-only; no behavior change. */
1055
1062
  function delegationStateTone(state) {
1056
1063
  switch (state) {
1057
1064
  case "running":
1058
1065
  case "retry":
1059
- case "startup_unknown": return "warning";
1066
+ case "startup_unknown":
1060
1067
  case "stale-running": return "warning";
1061
1068
  case "error":
1062
1069
  case "startup_failed": return "error";
1063
- case "completed": return "success";
1064
- case "cancelled": return "muted";
1070
+ case "completed":
1071
+ case "cancelled": return "success";
1065
1072
  }
1066
1073
  }
1067
1074
  /** Bounded tracker: child session id → latest running tool call. */
@@ -1136,13 +1143,11 @@ function mergeChildDelegationSources(children, live) {
1136
1143
  bySessionAlias.set(key(entry.sessionID, entry.alias), index);
1137
1144
  });
1138
1145
  for (const liveEntry of live) {
1139
- if (liveEntry.alias === null && liveEntry.taskID === null && Date.now() - liveEntry.startedAt > 3e4) continue;
1146
+ const aliaslessTTL = liveEntry.tool === "task" ? NATIVE_LIVE_ALIASLESS_TTL_MS : 3e4;
1147
+ if (liveEntry.alias === null && liveEntry.taskID === null && Date.now() - liveEntry.startedAt > aliaslessTTL) continue;
1140
1148
  const incoming = toDelegationEntry(liveEntry);
1141
1149
  const isNativeLive = liveEntry.tool === "task" && liveEntry.alias === null;
1142
- if (isNativeLive) {
1143
- incoming.source = "children-only";
1144
- incoming.alias = "native-task";
1145
- }
1150
+ if (isNativeLive) incoming.source = "children-only";
1146
1151
  const index = (incoming.taskID !== void 0 ? byTask.get(incoming.taskID) : void 0) ?? (incoming.alias !== "" ? bySessionAlias.get(key(incoming.sessionID, incoming.alias)) : void 0);
1147
1152
  if (index === void 0) {
1148
1153
  result.push(incoming);
@@ -1465,10 +1470,29 @@ function boardRecordsToDelegationEntries(records) {
1465
1470
  out.sort(compareDelegationEntries);
1466
1471
  return out;
1467
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
+ }
1468
1490
  /** Merge the board over any other channel. The board is AUTHORITATIVE for
1469
- * state/alias/agent (it is the persisted FSM) and it is the only channel
1470
- * that carries jobs from OTHER sessions. Dedup by taskID first, then by
1471
- * (sessionID, alias) — aliases are per parent session. Pure. */
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. */
1472
1496
  function mergeBoardDelegationSources(base, board) {
1473
1497
  const key = (sessionID, alias) => `${sessionID}\u0000${alias}`;
1474
1498
  const byTaskID = /* @__PURE__ */ new Map();
@@ -1487,6 +1511,7 @@ function mergeBoardDelegationSources(base, board) {
1487
1511
  bySessionAlias.set(key(incoming.sessionID, incoming.alias), next);
1488
1512
  continue;
1489
1513
  }
1514
+ if (shouldKeepRunningOverBoardTerminal(out[index], incoming)) continue;
1490
1515
  out[index] = {
1491
1516
  ...incoming,
1492
1517
  sessionID: incoming.sessionID !== "" ? incoming.sessionID : out[index]?.sessionID ?? ""
@@ -1495,6 +1520,26 @@ function mergeBoardDelegationSources(base, board) {
1495
1520
  out.sort(compareDelegationEntries);
1496
1521
  return out;
1497
1522
  }
1523
+ /** Scope a fully-merged display list to the ACTIVE session only.
1524
+ *
1525
+ * The panel is session-scoped: rows from other sessions (board snapshot, md
1526
+ * history under `.pantheon/delegations/<other-session>/`) and rows with no
1527
+ * attributable session (empty sessionID) are DROPPED. The previous
1528
+ * cross-session behavior rendered those rows and clicking them led to
1529
+ * "Session not found" — there is no cross-session channel anymore.
1530
+ *
1531
+ * The board/md channels still feed the merge UNFILTERED so the board can
1532
+ * ENRICH an active-session row with state/alias/agent (dedup by taskID), but
1533
+ * they can never introduce a row for another session: this filter is applied
1534
+ * ONCE, after every merge (children + live + md + board).
1535
+ *
1536
+ * Returns [] when no active session resolves (null/placeholder) — there is no
1537
+ * scope to show. Native children always carry the active session id (stamped
1538
+ * by `childrenToDelegationEntries`), so they survive the filter. Pure. */
1539
+ function filterDelegationsToSession(entries, activeSessionID) {
1540
+ if (!isValidSessionId(activeSessionID)) return [];
1541
+ return entries.filter((entry) => entry.sessionID === activeSessionID);
1542
+ }
1498
1543
  /** Server-aligned session id validity: opencode rejects anything not starting
1499
1544
  * with "ses" (SchemaError). This deliberately mirrors that exact contract —
1500
1545
  * nothing stricter, nothing looser — so a template placeholder ("{sessionID}"),
@@ -1592,10 +1637,13 @@ function delegationRowMarker(state, now = Date.now()) {
1592
1637
  return `${status === "active" ? delegationSpinnerFrame(now) : delegationRowGlyph(status)} `;
1593
1638
  }
1594
1639
  /** ONE identity per row: the short board alias (`apo-1`); a row without a
1595
- * board alias (native task() child, alias `native-task`) shows the agent
1596
- * instead — never alias + agent stacked. Pure. */
1640
+ * board alias (native task() child `native-task` / `native-<id>`, or an
1641
+ * alias-less native live row `live-<callID>` kept as children-only) shows
1642
+ * the agent instead — never alias + agent stacked. Pure. */
1597
1643
  function delegationRowIdentity(entry) {
1598
- if (entry.alias === "native-task" && entry.agent !== "") return entry.agent;
1644
+ if (entry.agent === "") return entry.alias;
1645
+ if (entry.alias === "native-task" || entry.alias.startsWith("native-")) return entry.agent;
1646
+ if (entry.alias.startsWith("live-") && entry.source === "children-only") return entry.agent;
1599
1647
  return entry.alias;
1600
1648
  }
1601
1649
  /** Fixed alias cell, pad-right + hard-truncate to `width` (default 7) so
@@ -1713,11 +1761,13 @@ function formatPanelLogLine(children, pantheon, native, md, events) {
1713
1761
  * itself (fallback 'agent'), state derived from its status, startedAt from
1714
1762
  * time.created. A report-less child is a NATIVE task() child (every
1715
1763
  * child of the current session — pantheon_delegate OR the native `task()`
1716
- * tool — carries parentID = caller), so it gets source 'children-only',
1717
- * the internal alias 'native-task' instead of a
1718
- * board alias. The 'task nativa' description fallback keeps the row
1719
- * non-empty when the child carries no title.
1720
- * Terminal md state wins over the derived state; a running md defers to
1764
+ * tool — carries parentID = caller), so it gets source 'children-only', a
1765
+ * per-child alias 'native-<last4 of the child id>' (one identity per native
1766
+ * row) instead of a board alias. The 'task nativa' description fallback
1767
+ * keeps the row non-empty when the child carries no title.
1768
+ * A FRESH terminal md state wins over the derived state (a stale md report
1769
+ * from a previous incarnation never flips a live child — see
1770
+ * mdTerminalCoversChild); a running md defers to
1721
1771
  * the child's live status. A running child is NEVER archived — it always
1722
1772
  * lands in the active split (splitDelegationList active = running/retry).
1723
1773
  * `parentSessionID` (the focused session id) is stamped on report-less
@@ -1725,6 +1775,20 @@ function formatPanelLogLine(children, pantheon, native, md, events) {
1725
1775
  * unscoped ('').
1726
1776
  * Sorted running-first (compareDelegationEntries).
1727
1777
  * Pure — no I/O. */
1778
+ /** Temporal guard for the children channel (same recycled-alias class as the
1779
+ * board merge): an md terminal report matched by taskID may still belong to
1780
+ * a PREVIOUS job incarnation. Accept it only when its Finalized timestamp
1781
+ * covers the child's latest activity, when either side has no timestamp to
1782
+ * compare, or when both startedAt agree on a single job incarnation.
1783
+ * Pure — no I/O. */
1784
+ function mdTerminalCoversChild(md, child) {
1785
+ const childUpdated = child.time?.updated;
1786
+ if (md.updatedAt !== null && childUpdated !== void 0) {
1787
+ if (md.updatedAt >= childUpdated) return true;
1788
+ return md.startedAt === child.time?.created;
1789
+ }
1790
+ return true;
1791
+ }
1728
1792
  function childrenToDelegationEntries(children, md, now = Date.now(), parentSessionID = "") {
1729
1793
  const byTaskID = /* @__PURE__ */ new Map();
1730
1794
  for (const m of md) if (m.taskID !== void 0 && !byTaskID.has(m.taskID)) byTaskID.set(m.taskID, m);
@@ -1734,9 +1798,9 @@ function childrenToDelegationEntries(children, md, now = Date.now(), parentSessi
1734
1798
  if (child.id === "" || seen.has(child.id)) continue;
1735
1799
  seen.add(child.id);
1736
1800
  const mdEntry = byTaskID.get(child.id);
1737
- const state = mdEntry !== void 0 && mdEntry.state !== "running" ? mdEntry.state : childStatusToState(child.status);
1801
+ const state = mdEntry !== void 0 && mdEntry.state !== "running" && mdTerminalCoversChild(mdEntry, child) ? mdEntry.state : childStatusToState(child.status);
1738
1802
  out.push({
1739
- alias: mdEntry?.alias ?? "native-task",
1803
+ alias: mdEntry?.alias ?? `native-${child.id.slice(-4)}`,
1740
1804
  sessionID: mdEntry?.sessionID ?? parentSessionID,
1741
1805
  taskID: child.id,
1742
1806
  agent: mdEntry?.agent ?? child.agent ?? "agent",
@@ -1866,7 +1930,7 @@ function DelegationRow(props) {
1866
1930
  get children() {
1867
1931
  var _el$18 = createElement("text");
1868
1932
  insert(_el$18, () => ` ${description()}`);
1869
- effect((_$p) => setProp(_el$18, "fg", theme().textMuted, _$p));
1933
+ effect((_$p) => setProp(_el$18, "fg", tone(), _$p));
1870
1934
  return _el$18;
1871
1935
  }
1872
1936
  }), null);
@@ -1877,12 +1941,12 @@ function DelegationRow(props) {
1877
1941
  children: (a) => (() => {
1878
1942
  var _el$19 = createElement("text");
1879
1943
  insert(_el$19, () => ` ↳ ${truncateDelegationDescription(`${a().tool}${a().summary !== "" ? ` ${a().summary}` : ""}`)}`);
1880
- effect((_$p) => setProp(_el$19, "fg", theme().textMuted, _$p));
1944
+ effect((_$p) => setProp(_el$19, "fg", tone(), _$p));
1881
1945
  return _el$19;
1882
1946
  })()
1883
1947
  }), null);
1884
1948
  effect((_p$) => {
1885
- var _v$5 = tone(), _v$6 = theme().textMuted;
1949
+ var _v$5 = tone(), _v$6 = tone();
1886
1950
  _v$5 !== _p$.e && (_p$.e = setProp(_el$16, "fg", _v$5, _p$.e));
1887
1951
  _v$6 !== _p$.t && (_p$.t = setProp(_el$17, "fg", _v$6, _p$.t));
1888
1952
  return _p$;
@@ -1975,8 +2039,8 @@ function View(props) {
1975
2039
  api: props.api
1976
2040
  });
1977
2041
  if (sessionID === null) {
1978
- setChildDelegations(mergeBoardDelegationSources(md, board));
1979
- panelLog.info(`${formatPanelLogLine(0, 0, 0, md.length, eventRefreshCount)} (no sessionID — history+board shown)`);
2042
+ setChildDelegations(filterDelegationsToSession(mergeBoardDelegationSources(md, board), sessionID));
2043
+ panelLog.info(`${formatPanelLogLine(0, 0, 0, md.length, eventRefreshCount)} (no sessionID — inactive panel)`);
1980
2044
  return;
1981
2045
  }
1982
2046
  let children = [];
@@ -2002,7 +2066,7 @@ function View(props) {
2002
2066
  })), md, now(), sessionID);
2003
2067
  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);
2004
2068
  const liveEntries = [...props.liveStore.map.values()].filter((entry) => entry.sessionID === sessionID);
2005
- setChildDelegations(mergeBoardDelegationSources(mergeChildDelegationSources(childEntries, liveEntries), board));
2069
+ setChildDelegations(filterDelegationsToSession(mergeBoardDelegationSources(mergeChildDelegationSources(childEntries, liveEntries), board), sessionID));
2006
2070
  const counts = countDelegationSources(childEntries);
2007
2071
  panelLog.info(formatPanelLogLine(children.length, counts.pantheon, counts.native, md.length, eventRefreshCount));
2008
2072
  } finally {
@@ -2314,6 +2378,6 @@ const plugin = {
2314
2378
  setup: async () => {}
2315
2379
  };
2316
2380
  //#endregion
2317
- 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, STALE_RUNNING_THRESHOLD_MS, boardRecordToDelegationEntry, boardRecordsToDelegationEntries, boardStatePath, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, createDelegationRowOpenHandler, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationRowGlyph, delegationRowIdentity, delegationRowMarker, delegationRowStatus, delegationSpinnerFrame, delegationStateTone, extractToolActivity, fmtElapsed, formatDelegationAlias, formatDelegationElapsed, formatDelegationHeader, formatDelegationRowLead, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeBoardDelegationSources, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readBoardState, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
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, boardRecordToDelegationEntry, boardRecordsToDelegationEntries, boardStatePath, 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, mergeBoardDelegationSources, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readBoardState, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
2318
2382
 
2319
2383
  //# sourceMappingURL=tui.js.map
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pantheon-tui",
3
- "version": "1.5.0-beta.14",
3
+ "version": "1.5.0-beta.15",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pantheon-tui",
9
- "version": "1.5.0-beta.14",
9
+ "version": "1.5.0-beta.15",
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.14",
4
+ "version": "1.5.0-beta.15",
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": {