pantheon-opencode 1.3.7 → 1.4.1
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/README.md +36 -46
- package/opencode.json +2 -3
- package/package.json +1 -1
- package/scripts/install/opencode.mjs +2 -0
- package/src/mcp/mcp_codemap_module.py +185 -0
- package/src/mcp/memory_mcp_server.py +91 -3
- package/src/pantheon/chat-reminders.ts +4 -74
- package/src/pantheon/command-normalizer.ts +101 -0
- package/src/pantheon/compaction-assert.ts +28 -28
- package/src/pantheon/context-sandbox.ts +273 -0
- package/src/pantheon/delegation-finalize.ts +44 -11
- package/src/pantheon/delegation-toast.ts +73 -0
- package/src/pantheon/delegation.ts +20 -106
- package/src/pantheon/presets.d.mts +4 -3
- package/src/pantheon/presets.mjs +17 -21
- package/src/plugin.ts +62 -33
- package/src/plugins/pantheon-hooks.ts +15 -158
- package/src/plugins/tui/dist/tui.d.ts +18 -3
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +42 -10
- package/src/plugins/tui/dist/tui.tsx +69 -17
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +1 -1
- package/src/plugins/tui/src/index.tsx +14 -10
- package/src/skills/codemap/SKILL.md +7 -0
- package/src/skills/context-compression/SKILL.md +39 -0
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
* PANTHEON_TOASTS=council → {errors, council}
|
|
80
80
|
* PANTHEON_TOASTS=all → {errors, delegations, council}
|
|
81
81
|
* Unknown values fall back to the default. The gate controls the TUI
|
|
82
|
-
* display
|
|
82
|
+
* display only — every fired toast is also
|
|
83
83
|
* recorded to the structured log + hooks.log (script 'toast', level
|
|
84
84
|
* 'info') so the toast trail stays auditable at any setting.
|
|
85
85
|
* - Severity priority (2026-08-06): errors/blockers — hook failures
|
|
@@ -96,15 +96,9 @@
|
|
|
96
96
|
* One group buffer at a time (groups rarely overlap); a task call more
|
|
97
97
|
* than OLYMPIANS_DETECT_WINDOW_MS after the previous one starts a fresh
|
|
98
98
|
* group.
|
|
99
|
-
* - session.idle
|
|
100
|
-
* `session.idle
|
|
101
|
-
*
|
|
102
|
-
* "✅ <agent> concluiu" lines collapse into "✅ N agentes concluídos (...)")
|
|
103
|
-
* and re-armed with a fresh timestamp — so the completion summary
|
|
104
|
-
* survives until the next user message (delivered via chat.message) instead
|
|
105
|
-
* of expiring after CHAT_REMINDER_TTL_MS while the user reads the reply.
|
|
106
|
-
* Partial groups (some members completed, some never reported) and
|
|
107
|
-
* interrupted council counters are also flushed/reset here.
|
|
99
|
+
* - session.idle handling (2026-08-06): the `event` hook additionally handles
|
|
100
|
+
* `session.idle` to reset partial aggregation state and interrupted council
|
|
101
|
+
* counters. Completion visibility remains in the board and TUI only.
|
|
108
102
|
* - Council notifications (new category 2026-08-06): a `task` whose prompt/
|
|
109
103
|
* description matches "council"/"conselho"/"synthesi" is treated as council
|
|
110
104
|
* activity — "🏛️ Council: especialistas consultados" on start and
|
|
@@ -122,11 +116,8 @@
|
|
|
122
116
|
* with wrk"). The upstream fix (server-side workspace tagging) only exists
|
|
123
117
|
* in NEWER opencode versions. THEREFORE on 1.18.13 the toast path below is
|
|
124
118
|
* a documented TUI no-op and agent-lifecycle feedback is surfaced via the
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* <system-reminder> text part into the next user message. Toasts stay in
|
|
128
|
-
* place for future opencode versions that render them — upgrade opencode
|
|
129
|
-
* for real TUI toasts.
|
|
119
|
+
* no transcript fallback is used. Toasts stay in place for future opencode
|
|
120
|
+
* versions that render them — upgrade opencode for real TUI toasts.
|
|
130
121
|
* - tool.execute.after payload fix (P1-1 follow-up, 2026-08-06): the after
|
|
131
122
|
* hook receives `(input, output)` where `output` = {title, output,
|
|
132
123
|
* metadata} — the REAL tool result. It is now merged into the payload
|
|
@@ -185,13 +176,6 @@
|
|
|
185
176
|
import { appendFile, mkdir } from 'node:fs/promises'
|
|
186
177
|
import { join } from 'node:path'
|
|
187
178
|
import type { Plugin, PluginInput } from '@opencode-ai/plugin'
|
|
188
|
-
import type { Part } from '@opencode-ai/sdk'
|
|
189
|
-
import {
|
|
190
|
-
CHAT_REMINDER_TTL_MS,
|
|
191
|
-
drainFreshChatReminders,
|
|
192
|
-
enqueueChatReminder,
|
|
193
|
-
pendingChatReminders,
|
|
194
|
-
} from '../pantheon/chat-reminders.ts'
|
|
195
179
|
import { pantheonPluginOnce } from '../pantheon/plugin-once.ts'
|
|
196
180
|
import { type HookPayload, type HookResult, runHook } from './hook-runner.ts'
|
|
197
181
|
|
|
@@ -231,7 +215,7 @@ type ToastCategory = 'errors' | 'delegations' | 'council'
|
|
|
231
215
|
* of enabled categories; every notification declares its category and is
|
|
232
216
|
* skipped unless enabled. "off" = empty set, "all" = every category. Unknown
|
|
233
217
|
* values fall back to the default. The gate controls the TUI display +
|
|
234
|
-
*
|
|
218
|
+
* display only — the structured log + hooks.log channels
|
|
235
219
|
* always write (every fired toast is recorded via reportFailure with script
|
|
236
220
|
* 'toast').
|
|
237
221
|
*/
|
|
@@ -427,26 +411,8 @@ const BACKGROUND_DISPATCH_TEXT_RE =
|
|
|
427
411
|
const TASK_ID_TEXT_RE = /\btask\s+id="(ses_[^"]+)"/i
|
|
428
412
|
|
|
429
413
|
/**
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* that would fire a toast is ALSO queued here and injected into the next user
|
|
433
|
-
* message by the 'chat.message' hook as a single <system-reminder> text part.
|
|
434
|
-
*
|
|
435
|
-
* The buffer itself lives in the SHARED chat-reminders.ts module: this plugin
|
|
436
|
-
* file must export EXACTLY ONE function-valued export (legacy loader —
|
|
437
|
-
* see the IMPORTANT note at the top), so the enqueue API cannot be exported
|
|
438
|
-
* from here. Importing the shared module instead gives src/plugin.ts (a
|
|
439
|
-
* separate plugin file, loaded via plain dynamic import) the SAME buffer
|
|
440
|
-
* instance — release-134 Phase 4 (compaction-assert.ts) enqueues
|
|
441
|
-
* post-compaction state re-assertions here and this hook delivers them.
|
|
442
|
-
*
|
|
443
|
-
* Bounded: at most CHAT_REMINDER_MAX entries, each expiring
|
|
444
|
-
* CHAT_REMINDER_TTL_MS after its tool event. Consumed (and cleared) by the
|
|
445
|
-
* chat.message hook. Full-buffer enqueues are SKIPPED, never unbounded — the
|
|
446
|
-
* olympians/3-in-6s aggregates still cover delegation groups, matching the
|
|
447
|
-
* "throttled toasts are skipped, never backlogged" anti-spam philosophy.
|
|
448
|
-
* session.idle flushes the buffer into ONE fresh aggregated entry (see
|
|
449
|
-
* flushIdleReminders) so completions survive until the next user message.
|
|
414
|
+
* Delegation lifecycle state is intentionally not mirrored into the transcript.
|
|
415
|
+
* The board/list/read tools and TUI toasts are the supported visibility channels.
|
|
450
416
|
*/
|
|
451
417
|
|
|
452
418
|
/** Variants accepted by the opencode TUI showToast (TuiShowToastData.body.variant). */
|
|
@@ -462,11 +428,6 @@ type ToastOptions = {
|
|
|
462
428
|
dedupeKey?: string
|
|
463
429
|
/** Optional TUI toast headline (opencode showToast body.title). */
|
|
464
430
|
title?: string
|
|
465
|
-
/**
|
|
466
|
-
* Optional text for the 1.18.13 chat.message <system-reminder> fallback.
|
|
467
|
-
* Defaults to `message` when omitted — the reminder mirrors the toast.
|
|
468
|
-
*/
|
|
469
|
-
reminder?: string
|
|
470
431
|
}
|
|
471
432
|
|
|
472
433
|
/** Logging context threaded through safeRun — provided by the plugin input. */
|
|
@@ -757,10 +718,6 @@ async function notifyToast(ctx: HookContext, opts: ToastOptions): Promise<void>
|
|
|
757
718
|
toastShown.add(opts.dedupeKey)
|
|
758
719
|
}
|
|
759
720
|
if (!toastCategoryEnabled(opts.category)) return
|
|
760
|
-
// 1.18.13 fallback: queue the same signal for the chat.message hook (the
|
|
761
|
-
// TUI drops tui.toast.show on 1.18.13 — see header). Enqueued even if
|
|
762
|
-
// showToast fails, so headless/no-TUI servers still surface feedback.
|
|
763
|
-
enqueueChatReminder(opts.reminder ?? opts.message)
|
|
764
721
|
await ctx.client.tui.showToast({
|
|
765
722
|
body: {
|
|
766
723
|
...(opts.title !== undefined ? { title: opts.title } : {}),
|
|
@@ -907,7 +864,7 @@ async function agentFailedToast(
|
|
|
907
864
|
): Promise<void> {
|
|
908
865
|
const raw = result?.output ?? result?.title ?? ''
|
|
909
866
|
// Fix 1: the failure reason echoes tool RESULT content — mask any token so
|
|
910
|
-
// the toast/
|
|
867
|
+
// the toast/hooks.log trail never exposes a raw secret.
|
|
911
868
|
const reason = maskSecret(
|
|
912
869
|
trim(typeof raw === 'string' && raw.trim() !== '' ? raw : 'tool failed', 120),
|
|
913
870
|
)
|
|
@@ -1057,40 +1014,7 @@ function onCouncilDone(ctx: HookContext): void {
|
|
|
1057
1014
|
}
|
|
1058
1015
|
}
|
|
1059
1016
|
|
|
1060
|
-
/**
|
|
1061
|
-
* session.idle flush (see header): aggregate any pending chat reminders into
|
|
1062
|
-
* ONE fresh <system-reminder> entry (individual ✅ completions collapse into
|
|
1063
|
-
* "✅ N agentes concluídos (...)"), report partial groups whose members never
|
|
1064
|
-
* all completed, and reset interrupted council counters. The chat.message
|
|
1065
|
-
* hook remains the delivery path — the flushed entry has a fresh timestamp so
|
|
1066
|
-
* it survives until the next user message instead of expiring while the user
|
|
1067
|
-
* reads the reply.
|
|
1068
|
-
*/
|
|
1069
1017
|
function flushIdleReminders(ctx: HookContext): void {
|
|
1070
|
-
if (ENABLED_TOAST_CATEGORIES.size === 0) {
|
|
1071
|
-
pendingChatReminders.length = 0
|
|
1072
|
-
activeOlympians = null
|
|
1073
|
-
return
|
|
1074
|
-
}
|
|
1075
|
-
const now = Date.now()
|
|
1076
|
-
const fresh = pendingChatReminders.filter((r) => now - r.at <= CHAT_REMINDER_TTL_MS)
|
|
1077
|
-
pendingChatReminders.length = 0
|
|
1078
|
-
|
|
1079
|
-
// Partial group: some members completed but the group never finished (their
|
|
1080
|
-
// completion was suppressed waiting for the aggregate). Report the subset.
|
|
1081
|
-
const partialGroup = activeOlympians
|
|
1082
|
-
if (partialGroup !== null) {
|
|
1083
|
-
const done = [...partialGroup.agents].filter((a) => !partialGroup.pendingCompletions.has(a))
|
|
1084
|
-
if (partialGroup.detected && partialGroup.doneCount > 0 && done.length > 0) {
|
|
1085
|
-
fresh.push({
|
|
1086
|
-
text:
|
|
1087
|
-
done.length === 1
|
|
1088
|
-
? `✅ ${done[0]} concluiu`
|
|
1089
|
-
: `✅ ${done.length} agentes concluídos (${done.sort().join(', ')})`,
|
|
1090
|
-
at: now,
|
|
1091
|
-
})
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
1018
|
activeOlympians = null
|
|
1095
1019
|
|
|
1096
1020
|
// Interrupted council counters must not block a later council's verdict —
|
|
@@ -1105,34 +1029,12 @@ function flushIdleReminders(ctx: HookContext): void {
|
|
|
1105
1029
|
councilStartNotified = false
|
|
1106
1030
|
}
|
|
1107
1031
|
|
|
1108
|
-
// Collapse individual ✅ completions into one aggregate line.
|
|
1109
|
-
const doneAgents: string[] = []
|
|
1110
|
-
const others: string[] = []
|
|
1111
|
-
for (const r of fresh) {
|
|
1112
|
-
const m = /^✅ (.+?) concluiu$/.exec(r.text)
|
|
1113
|
-
if (m?.[1] !== undefined) doneAgents.push(m[1])
|
|
1114
|
-
else others.push(r.text)
|
|
1115
|
-
}
|
|
1116
|
-
const lines: string[] = []
|
|
1117
|
-
if (doneAgents.length > 0) {
|
|
1118
|
-
lines.push(
|
|
1119
|
-
doneAgents.length === 1
|
|
1120
|
-
? `✅ ${doneAgents[0]} concluiu`
|
|
1121
|
-
: `✅ ${doneAgents.length} agentes concluídos (${doneAgents.sort().join(', ')})`,
|
|
1122
|
-
)
|
|
1123
|
-
}
|
|
1124
|
-
lines.push(...others)
|
|
1125
|
-
if (lines.length === 0) return
|
|
1126
|
-
|
|
1127
|
-
// ONE aggregated entry, fresh timestamp — chat.message delivers it next.
|
|
1128
|
-
enqueueChatReminder(lines.join('\n'))
|
|
1129
|
-
// 1.3.4 dedup: log a SUMMARY here (count only), never the joined body — the
|
|
1130
|
-
// SAME lines used to be echoed twice (idle-flush with " | ", chat-reminder
|
|
1131
|
-
// delivery with "\n"). The content is logged exactly once, at delivery.
|
|
1132
1032
|
void reportFailure(
|
|
1133
1033
|
ctx,
|
|
1134
|
-
|
|
1135
|
-
{
|
|
1034
|
+
'[pantheon-hooks:idle-flush] delegation state reset',
|
|
1035
|
+
{
|
|
1036
|
+
script: 'idle-flush',
|
|
1037
|
+
},
|
|
1136
1038
|
'info',
|
|
1137
1039
|
)
|
|
1138
1040
|
}
|
|
@@ -1213,7 +1115,7 @@ const plugin: Plugin = async ({ client, directory }) => {
|
|
|
1213
1115
|
// merges the global config (npm-package plugin paths) with the project
|
|
1214
1116
|
// config (repo source paths), so this module loads from TWO filesystem
|
|
1215
1117
|
// paths in the same process and the factory would otherwise run twice —
|
|
1216
|
-
// every hook (tool.execute.before/after
|
|
1118
|
+
// every lifecycle hook (tool.execute.before/after and event) firing
|
|
1217
1119
|
// TWICE. The second invocation becomes a no-op (empty hooks object) so
|
|
1218
1120
|
// hooks register exactly once.
|
|
1219
1121
|
if (pantheonPluginOnce('pantheon:hooks')) {
|
|
@@ -1439,51 +1341,6 @@ const plugin: Plugin = async ({ client, directory }) => {
|
|
|
1439
1341
|
// Never let a hook take down the opencode session.
|
|
1440
1342
|
}
|
|
1441
1343
|
},
|
|
1442
|
-
// 1.18.13 fallback (Path C, oh-my-openagent pattern): the TUI drops
|
|
1443
|
-
// tui.toast.show events on opencode 1.18.13 (untagged — see header), so
|
|
1444
|
-
// queued agent-lifecycle signals (delegation start/done, hook failures,
|
|
1445
|
-
// olympians/council summaries) are injected here as ONE <system-reminder> text
|
|
1446
|
-
// part into the next user message, then cleared.
|
|
1447
|
-
// P0 (2026-08-11, live E2E — release blocker): this hook ALSO fires for the
|
|
1448
|
-
// child session's client.session.promptAsync (the old "subagent prompts use
|
|
1449
|
-
// a different path" claim is FALSE on 1.18.13), where input.messageID is
|
|
1450
|
-
// EMPTY/undefined. Injecting there with `?? ''` produced a part opencode's
|
|
1451
|
-
// schema rejects (SchemaError: Expected a string starting with "msg", got
|
|
1452
|
-
// "") → prompt_async failed → every delegation died in ~20ms. The guard
|
|
1453
|
-
// below skips injection ENTIRELY on that path WITHOUT draining the buffer,
|
|
1454
|
-
// so the reminder still lands on the parent's next real message.
|
|
1455
|
-
'chat.message': async (input, output) => {
|
|
1456
|
-
try {
|
|
1457
|
-
if (ENABLED_TOAST_CATEGORIES.size === 0) return
|
|
1458
|
-
if (pendingChatReminders.length === 0) return
|
|
1459
|
-
// Subagent promptAsync fires have no messageID — injecting any part
|
|
1460
|
-
// with an empty messageID crashes the child session (schema reject).
|
|
1461
|
-
// Early return BEFORE the buffer drain keeps the reminder queued for
|
|
1462
|
-
// the parent's next real (msg_-ID'd) message.
|
|
1463
|
-
if (!input.messageID) return
|
|
1464
|
-
const body = drainFreshChatReminders()
|
|
1465
|
-
if (body === undefined) return
|
|
1466
|
-
// Minimal TextPart — the core backfills messageID/sessionID for
|
|
1467
|
-
// hook-injected parts (see opencode chat.message trigger pipeline).
|
|
1468
|
-
// The guard above guarantees input.messageID is a non-empty string,
|
|
1469
|
-
// so no `?? ''` fallback is needed (an explicit "" is schema-rejected).
|
|
1470
|
-
output.parts.push({
|
|
1471
|
-
id: `prt_${crypto.randomUUID()}`,
|
|
1472
|
-
sessionID: input.sessionID,
|
|
1473
|
-
messageID: input.messageID,
|
|
1474
|
-
type: 'text',
|
|
1475
|
-
text: `<system-reminder>\n${body}\n</system-reminder>`,
|
|
1476
|
-
} satisfies Part)
|
|
1477
|
-
await reportFailure(
|
|
1478
|
-
ctx,
|
|
1479
|
-
`[pantheon-hooks:chat-reminder] ${body}`,
|
|
1480
|
-
{ script: 'chat-reminder' },
|
|
1481
|
-
'info',
|
|
1482
|
-
)
|
|
1483
|
-
} catch {
|
|
1484
|
-
// Never let a hook take down the opencode session.
|
|
1485
|
-
}
|
|
1486
|
-
},
|
|
1487
1344
|
event: async ({ event: ev }) => {
|
|
1488
1345
|
try {
|
|
1489
1346
|
if (ev.type === 'session.created') {
|
|
@@ -11,7 +11,7 @@ type DelegationEntry = {
|
|
|
11
11
|
taskID?: string;
|
|
12
12
|
/** Agent name, e.g. "apollo". */
|
|
13
13
|
agent: string;
|
|
14
|
-
state: 'running' | 'completed' | 'error' | 'startup_failed' | 'startup_unknown' | 'cancelled';
|
|
14
|
+
state: 'running' | 'completed' | 'error' | 'startup_failed' | 'startup_unknown' | 'cancelled' | 'stale-running';
|
|
15
15
|
/** Epoch ms of the `Started` header. */
|
|
16
16
|
startedAt: number;
|
|
17
17
|
/** Epoch ms of the `Finalized` header — null while still running. */
|
|
@@ -77,7 +77,22 @@ declare function compareDelegationEntries(a: DelegationEntry, b: DelegationEntry
|
|
|
77
77
|
* recent terminal reports (capped). Pure — so the history-only panel (no
|
|
78
78
|
* sessionID) is testable without the TUI runtime. The header count uses the
|
|
79
79
|
* same "running + recentes" list. */
|
|
80
|
-
declare function visibleDelegationList(all: readonly DelegationEntry[], maxTerminal?: number): DelegationEntry[];
|
|
80
|
+
declare function visibleDelegationList(all: readonly DelegationEntry[], maxTerminal?: number, now?: number, staleThresholdMs?: number): DelegationEntry[];
|
|
81
|
+
/** Default stale-running threshold: 30 minutes. */
|
|
82
|
+
declare const STALE_RUNNING_THRESHOLD_MS: number;
|
|
83
|
+
/** Idle silence window: if no updatedAt change in this window, the entry is
|
|
84
|
+
* considered stale. Combined with the stale-running threshold to produce the
|
|
85
|
+
* display-only `stale-running` state. */
|
|
86
|
+
declare const IDLE_SILENCE_MS: number;
|
|
87
|
+
/**
|
|
88
|
+
* Mark a running entry as `stale-running` if it has been running longer than
|
|
89
|
+
* the threshold AND has no recent activity (no `updatedAt` change in the last
|
|
90
|
+
* `IDLE_SILENCE_MS`). This is DISPLAY-ONLY — the board state is unchanged.
|
|
91
|
+
*
|
|
92
|
+
* A `stale-running` entry renders with a warning indicator but the underlying
|
|
93
|
+
* delegation is still treated as running by the backend.
|
|
94
|
+
*/
|
|
95
|
+
declare function markStaleIfRunning(entry: DelegationEntry, now: number, thresholdMs?: number): DelegationEntry;
|
|
81
96
|
/** Compact elapsed-time label: "5m 12s", "1h 30m", "2d 4h" — ticks every
|
|
82
97
|
* second for running jobs. */
|
|
83
98
|
declare function fmtElapsed(ms: number): string;
|
|
@@ -306,5 +321,5 @@ declare const plugin: TuiPluginModule & {
|
|
|
306
321
|
id: string;
|
|
307
322
|
};
|
|
308
323
|
//#endregion
|
|
309
|
-
export { ChildDelegationLike, DelegationActivity, DelegationEntry, DelegationToolPart, LiveDelegationEntry, LiveDelegationStore, ParsedDelegationToolPart, TuiSessionSources, buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationSpinnerFrame, delegationTag, fmtElapsed, isValidSessionId, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, safeSessionPath, seedLiveDelegationMap, toDelegationEntry, tuiLogPath, visibleDelegationList };
|
|
324
|
+
export { ChildDelegationLike, DelegationActivity, DelegationEntry, DelegationToolPart, IDLE_SILENCE_MS, LiveDelegationEntry, LiveDelegationStore, ParsedDelegationToolPart, STALE_RUNNING_THRESHOLD_MS, TuiSessionSources, buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationSpinnerFrame, delegationTag, fmtElapsed, isValidSessionId, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, safeSessionPath, seedLiveDelegationMap, toDelegationEntry, tuiLogPath, visibleDelegationList };
|
|
310
325
|
//# sourceMappingURL=tui.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;KAy6BY;;EAEV;;EAEA;;;;EAIA;;EAEA;EACA;;EASA;;EAEA;EACA;EACA;;EAEA;;;;EAIA;;;;;;;;;;;iBAsBc,wBACd,aACA,oBACA,qBACC;;;;;;iBAkHmB,sBAAsB,cAAc,QAAQ;;;;;;;;iBAwC5C,yBAAyB,eAAe,QAAQ;;;;;;;;iBAWtD,sBACd;EAAS;EAAoB;eAC7B;;;;;;iBAYc,YAAY;;;iBAMZ,WAAW;;;;iBAOX,yBAAyB,GAAG,iBAAiB,GAAG;;;;;iBAWhD,sBACd,cAAc,mBACd,sBACA,cACA,4BACC;;cASU;;;;cAKA;;;;;;;;;iBAUG,mBACd,OAAO,iBACP,aACA,uBACC;;;iBAWa,WAAW;;;iBAcX,kBAAkB,OAAO,iBAAiB;;;KAQ9C;iBAQI,mBAAmB,OAAO,kBAAkB;iBAc5C,wBAAwB,OAAO;;iBAoB/B,uBAAuB;;;;;;;iBAWvB,4BACd,mBAAmB,mBACnB,eAAe,wBACd;;KAmFS;EACV;EACA;EACA;EACA;EACA;EACA;IACE;IACA,QAAQ;IACR;IACA;IACA;MAAS;MAAgB;;;;;KAKjB;;EAEV;;EAEA;;EAEA;EACA;;EAEA;EACA;;EAEA;;EAEA;EACA;EACA;EACA;;EAEA;;;KAIU;EACV;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;iBAac,wBACd,MAAM,oBACN,eACC;;;iBA+Ea,yBACd,KAAK,YAAY,sBACjB,MAAM,oBACN;;;iBA+Fc,sBACd,KAAK,YAAY,sBACjB;;;;;;iBAiBc,2BACd;EAAqB;EAAa;iBAClC,YAAY,uDACX;;;;;iBA2Ba,sBACd,KAAK,YAAY,sBACjB,gBAAgB,sBAChB;;;iBAWc,kBAAkB,MAAM,sBAAsB;;;;;;iBAqB9C,uBACd,eAAe,uBACf,aAAa,oBACZ;;;;;;;;;iBA8Da,iBAAiB,cAAc;;;;KAOnC;;EAEV;EACA;;IAEE;MAAU;;;IAEV;MAAU;QAAY;QAAe,SAAS;;;;;;;;;;iBASlC,wBAAwB,SAAS;;;;;;;;iBAmBjC,gBAAgB;EAAgB;IAAQ;;;;;;;iBASxC,kBAAkB;EAAkC;IAAQ;;;;KAKhE;;EAEV;;EAEA;;;EAGA;;;EAGA;EACA;IACE;IACA;;;;;;;iBAQY,mBAAmB;;;;;iBASnB,cAAc,OAAO;;;;;;;;;;;;;;iBAiBrB,4BACd,mBAAmB,mCACnB,aAAa,mBACb,eACC;;;;;;iBA0Ca,4BACd;EAAS,YAAY,cAAc,SAAS;eAC5C;;;;;KAiLU;EACV,KAAK,YAAY;;EAEjB;;EAEA;;cAucI,QAAQ;EAAoB"}
|
|
@@ -120,8 +120,14 @@ async function resolvePresetForTui(env, cwd) {
|
|
|
120
120
|
}
|
|
121
121
|
async function detectVersion(api) {
|
|
122
122
|
try {
|
|
123
|
-
const
|
|
124
|
-
|
|
123
|
+
const pkgContent = await readFile(fileURLToPath(new URL("../../package.json", import.meta.url)), "utf8");
|
|
124
|
+
const pkg = JSON.parse(pkgContent);
|
|
125
|
+
if (pkg.version) return pkg.version;
|
|
126
|
+
} catch {}
|
|
127
|
+
try {
|
|
128
|
+
const pkgContent = await readFile(fileURLToPath(new URL("../../../../package.json", import.meta.url)), "utf8");
|
|
129
|
+
const pkg = JSON.parse(pkgContent);
|
|
130
|
+
if (pkg.version) return pkg.version;
|
|
125
131
|
} catch {}
|
|
126
132
|
try {
|
|
127
133
|
const wt = api.state.path?.worktree ?? "";
|
|
@@ -765,7 +771,8 @@ function parseDelegationMarkdown(raw, fileAlias, sessionID = "") {
|
|
|
765
771
|
const startedAt = started !== void 0 ? Date.parse(started) : NaN;
|
|
766
772
|
if (agent === void 0 || state === void 0 || Number.isNaN(startedAt)) return null;
|
|
767
773
|
const normalized = state.toLowerCase();
|
|
768
|
-
if (normalized
|
|
774
|
+
if (normalized === "running") return null;
|
|
775
|
+
if (normalized !== "completed" && normalized !== "error" && normalized !== "startup_failed" && normalized !== "startup_unknown" && normalized !== "cancelled") return null;
|
|
769
776
|
const finalizedAt = finalized !== void 0 ? Date.parse(finalized) : NaN;
|
|
770
777
|
return {
|
|
771
778
|
alias: title ?? (fileAlias !== void 0 ? stripMdSuffix(fileAlias) : "unknown"),
|
|
@@ -861,8 +868,8 @@ function tuiLogPath(projectRoot) {
|
|
|
861
868
|
* falling back to startedAt, descending). Shared by the md reader and
|
|
862
869
|
* mergeDelegationSources. */
|
|
863
870
|
function compareDelegationEntries(a, b) {
|
|
864
|
-
const aRun = a.state === "running" ? 1 : 0;
|
|
865
|
-
const bRun = b.state === "running" ? 1 : 0;
|
|
871
|
+
const aRun = a.state === "running" || a.state === "stale-running" ? 1 : 0;
|
|
872
|
+
const bRun = b.state === "running" || b.state === "stale-running" ? 1 : 0;
|
|
866
873
|
if (aRun !== bRun) return bRun - aRun;
|
|
867
874
|
return (b.updatedAt ?? b.startedAt) - (a.updatedAt ?? a.startedAt);
|
|
868
875
|
}
|
|
@@ -870,11 +877,34 @@ function compareDelegationEntries(a, b) {
|
|
|
870
877
|
* recent terminal reports (capped). Pure — so the history-only panel (no
|
|
871
878
|
* sessionID) is testable without the TUI runtime. The header count uses the
|
|
872
879
|
* same "running + recentes" list. */
|
|
873
|
-
function visibleDelegationList(all, maxTerminal = 8) {
|
|
874
|
-
const running = all.filter((d) => d.state === "running");
|
|
880
|
+
function visibleDelegationList(all, maxTerminal = 8, now = Date.now(), staleThresholdMs = 1800 * 1e3) {
|
|
881
|
+
const running = all.filter((d) => d.state === "running").map((d) => markStaleIfRunning(d, now, staleThresholdMs));
|
|
875
882
|
const terminal = all.filter((d) => d.state !== "running").slice(0, maxTerminal);
|
|
876
883
|
return [...running, ...terminal];
|
|
877
884
|
}
|
|
885
|
+
/** Default stale-running threshold: 30 minutes. */
|
|
886
|
+
const STALE_RUNNING_THRESHOLD_MS = 1800 * 1e3;
|
|
887
|
+
/** Idle silence window: if no updatedAt change in this window, the entry is
|
|
888
|
+
* considered stale. Combined with the stale-running threshold to produce the
|
|
889
|
+
* display-only `stale-running` state. */
|
|
890
|
+
const IDLE_SILENCE_MS = 60 * 1e3;
|
|
891
|
+
/**
|
|
892
|
+
* Mark a running entry as `stale-running` if it has been running longer than
|
|
893
|
+
* the threshold AND has no recent activity (no `updatedAt` change in the last
|
|
894
|
+
* `IDLE_SILENCE_MS`). This is DISPLAY-ONLY — the board state is unchanged.
|
|
895
|
+
*
|
|
896
|
+
* A `stale-running` entry renders with a warning indicator but the underlying
|
|
897
|
+
* delegation is still treated as running by the backend.
|
|
898
|
+
*/
|
|
899
|
+
function markStaleIfRunning(entry, now, thresholdMs = STALE_RUNNING_THRESHOLD_MS) {
|
|
900
|
+
if (entry.state !== "running") return entry;
|
|
901
|
+
if (now - entry.startedAt < thresholdMs) return entry;
|
|
902
|
+
if (entry.updatedAt !== null && now - entry.updatedAt < 6e4) return entry;
|
|
903
|
+
return {
|
|
904
|
+
...entry,
|
|
905
|
+
state: "stale-running"
|
|
906
|
+
};
|
|
907
|
+
}
|
|
878
908
|
/** Compact elapsed-time label: "5m 12s", "1h 30m", "2d 4h" — ticks every
|
|
879
909
|
* second for running jobs. */
|
|
880
910
|
function fmtElapsed(ms) {
|
|
@@ -891,7 +921,7 @@ function fmtElapsed(ms) {
|
|
|
891
921
|
/** Elapsed label for one entry: running → ticks `now - startedAt`, terminal
|
|
892
922
|
* → fixed `updatedAt - startedAt` (em dash when no finalized timestamp). */
|
|
893
923
|
function delegationElapsed(entry, now) {
|
|
894
|
-
if (entry.state === "running") return fmtElapsed(now - entry.startedAt);
|
|
924
|
+
if (entry.state === "running" || entry.state === "stale-running") return fmtElapsed(now - entry.startedAt);
|
|
895
925
|
return entry.updatedAt !== null ? fmtElapsed(entry.updatedAt - entry.startedAt) : "—";
|
|
896
926
|
}
|
|
897
927
|
/** The activity labels shown by the animated row. Keeping this pure makes the
|
|
@@ -1318,7 +1348,7 @@ function childrenToDelegationEntries(children, md, now = Date.now()) {
|
|
|
1318
1348
|
agent: mdEntry?.agent ?? child.agent ?? "agent",
|
|
1319
1349
|
state,
|
|
1320
1350
|
startedAt: mdEntry?.startedAt ?? child.time?.created ?? now,
|
|
1321
|
-
updatedAt: mdEntry?.updatedAt ?? (state === "running" ? null : child.time?.updated ?? null),
|
|
1351
|
+
updatedAt: mdEntry?.updatedAt ?? (state === "running" || state === "stale-running" ? null : child.time?.updated ?? null),
|
|
1322
1352
|
timedOut: mdEntry?.timedOut ?? false,
|
|
1323
1353
|
description: mdEntry !== void 0 && mdEntry.description !== "" ? mdEntry.description : child.title ?? "",
|
|
1324
1354
|
source: mdEntry !== void 0 ? "md" : "children-only"
|
|
@@ -1399,6 +1429,7 @@ function DelegationRow(props) {
|
|
|
1399
1429
|
const t = theme();
|
|
1400
1430
|
switch (props.job.state) {
|
|
1401
1431
|
case "running": return t.warning;
|
|
1432
|
+
case "stale-running": return t.error;
|
|
1402
1433
|
case "completed": return t.success;
|
|
1403
1434
|
case "error": return t.error;
|
|
1404
1435
|
default: return t.textMuted;
|
|
@@ -1406,6 +1437,7 @@ function DelegationRow(props) {
|
|
|
1406
1437
|
});
|
|
1407
1438
|
const marker = createMemo(() => {
|
|
1408
1439
|
if (props.job.state === "running") return `${delegationSpinnerFrame(props.animationNow)} `;
|
|
1440
|
+
if (props.job.state === "stale-running") return "⚠ ";
|
|
1409
1441
|
switch (props.job.state) {
|
|
1410
1442
|
case "completed": return "✓ ";
|
|
1411
1443
|
case "error": return "✕ ";
|
|
@@ -1842,6 +1874,6 @@ const plugin = {
|
|
|
1842
1874
|
tui
|
|
1843
1875
|
};
|
|
1844
1876
|
//#endregion
|
|
1845
|
-
export { buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationSpinnerFrame, delegationTag, fmtElapsed, isValidSessionId, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, safeSessionPath, seedLiveDelegationMap, toDelegationEntry, tuiLogPath, visibleDelegationList };
|
|
1877
|
+
export { IDLE_SILENCE_MS, STALE_RUNNING_THRESHOLD_MS, buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationSpinnerFrame, delegationTag, fmtElapsed, isValidSessionId, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, safeSessionPath, seedLiveDelegationMap, toDelegationEntry, tuiLogPath, visibleDelegationList };
|
|
1846
1878
|
|
|
1847
1879
|
//# sourceMappingURL=tui.js.map
|
|
@@ -154,25 +154,29 @@ async function resolvePresetForTui(
|
|
|
154
154
|
* Se falhar, retorna null e a View omite a versão.
|
|
155
155
|
*
|
|
156
156
|
* Try order:
|
|
157
|
+
* 0a. pantheon-tui/package.json (installed: dist/tui.js → 2 níveis)
|
|
158
|
+
* 0b. pantheon/package.json (dev: src/plugins/tui/dist/tui.tsx → 4 níveis)
|
|
157
159
|
* 1. api.client.file.read package.json
|
|
158
160
|
* 2. git describe --tags
|
|
159
161
|
* 3. opencode --version
|
|
160
162
|
* 4. null */
|
|
161
163
|
|
|
162
164
|
async function detectVersion(api: TuiPluginApi): Promise<string | null> {
|
|
163
|
-
// Try
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
// Try 0a: installed location (2 levels: dist/tui.js → pantheon-tui/package.json)
|
|
166
|
+
try {
|
|
167
|
+
const pkgUrl = new URL('../../package.json', import.meta.url)
|
|
168
|
+
const pkgContent = await readFile(fileURLToPath(pkgUrl), 'utf8')
|
|
169
|
+
const pkg = JSON.parse(pkgContent)
|
|
170
|
+
if (pkg.version) return pkg.version
|
|
171
|
+
} catch {}
|
|
172
|
+
|
|
173
|
+
// Try 0b: dev location (4 levels: src/plugins/tui/dist/tui.tsx → package.json)
|
|
168
174
|
try {
|
|
169
175
|
const pkgUrl = new URL('../../../../package.json', import.meta.url)
|
|
170
176
|
const pkgContent = await readFile(fileURLToPath(pkgUrl), 'utf8')
|
|
171
|
-
const
|
|
172
|
-
if (
|
|
173
|
-
} catch {
|
|
174
|
-
/* fall through */
|
|
175
|
-
}
|
|
177
|
+
const pkg = JSON.parse(pkgContent)
|
|
178
|
+
if (pkg.version) return pkg.version
|
|
179
|
+
} catch {}
|
|
176
180
|
|
|
177
181
|
// Try 1: package.json do projeto (via worktree path)
|
|
178
182
|
try {
|
|
@@ -942,7 +946,14 @@ export type DelegationEntry = {
|
|
|
942
946
|
taskID?: string
|
|
943
947
|
/** Agent name, e.g. "apollo". */
|
|
944
948
|
agent: string
|
|
945
|
-
state:
|
|
949
|
+
state:
|
|
950
|
+
| 'running'
|
|
951
|
+
| 'completed'
|
|
952
|
+
| 'error'
|
|
953
|
+
| 'startup_failed'
|
|
954
|
+
| 'startup_unknown'
|
|
955
|
+
| 'cancelled'
|
|
956
|
+
| 'stale-running'
|
|
946
957
|
/** Epoch ms of the `Started` header. */
|
|
947
958
|
startedAt: number
|
|
948
959
|
/** Epoch ms of the `Finalized` header — null while still running. */
|
|
@@ -1048,8 +1059,10 @@ export function parseDelegationMarkdown(
|
|
|
1048
1059
|
const startedAt = started !== undefined ? Date.parse(started) : NaN
|
|
1049
1060
|
if (agent === undefined || state === undefined || Number.isNaN(startedAt)) return null
|
|
1050
1061
|
const normalized = state.toLowerCase()
|
|
1062
|
+
// Running state should only come from live channels, never from persisted
|
|
1063
|
+
// reports. A stale "running" MD artifact contaminates the panel — skip it.
|
|
1064
|
+
if (normalized === 'running') return null
|
|
1051
1065
|
if (
|
|
1052
|
-
normalized !== 'running' &&
|
|
1053
1066
|
normalized !== 'completed' &&
|
|
1054
1067
|
normalized !== 'error' &&
|
|
1055
1068
|
normalized !== 'startup_failed' &&
|
|
@@ -1171,8 +1184,8 @@ export function tuiLogPath(projectRoot: string): string {
|
|
|
1171
1184
|
* falling back to startedAt, descending). Shared by the md reader and
|
|
1172
1185
|
* mergeDelegationSources. */
|
|
1173
1186
|
export function compareDelegationEntries(a: DelegationEntry, b: DelegationEntry): number {
|
|
1174
|
-
const aRun = a.state === 'running' ? 1 : 0
|
|
1175
|
-
const bRun = b.state === 'running' ? 1 : 0
|
|
1187
|
+
const aRun = a.state === 'running' || a.state === 'stale-running' ? 1 : 0
|
|
1188
|
+
const bRun = b.state === 'running' || b.state === 'stale-running' ? 1 : 0
|
|
1176
1189
|
if (aRun !== bRun) return bRun - aRun
|
|
1177
1190
|
return (b.updatedAt ?? b.startedAt) - (a.updatedAt ?? a.startedAt)
|
|
1178
1191
|
}
|
|
@@ -1184,12 +1197,45 @@ export function compareDelegationEntries(a: DelegationEntry, b: DelegationEntry)
|
|
|
1184
1197
|
export function visibleDelegationList(
|
|
1185
1198
|
all: readonly DelegationEntry[],
|
|
1186
1199
|
maxTerminal = 8,
|
|
1200
|
+
now = Date.now(),
|
|
1201
|
+
staleThresholdMs = 30 * 60 * 1000, // 30 minutes
|
|
1187
1202
|
): DelegationEntry[] {
|
|
1188
|
-
const running = all
|
|
1203
|
+
const running = all
|
|
1204
|
+
.filter((d) => d.state === 'running')
|
|
1205
|
+
.map((d) => markStaleIfRunning(d, now, staleThresholdMs))
|
|
1189
1206
|
const terminal = all.filter((d) => d.state !== 'running').slice(0, maxTerminal)
|
|
1190
1207
|
return [...running, ...terminal]
|
|
1191
1208
|
}
|
|
1192
1209
|
|
|
1210
|
+
/** Default stale-running threshold: 30 minutes. */
|
|
1211
|
+
export const STALE_RUNNING_THRESHOLD_MS = 30 * 60 * 1000
|
|
1212
|
+
|
|
1213
|
+
/** Idle silence window: if no updatedAt change in this window, the entry is
|
|
1214
|
+
* considered stale. Combined with the stale-running threshold to produce the
|
|
1215
|
+
* display-only `stale-running` state. */
|
|
1216
|
+
export const IDLE_SILENCE_MS = 60 * 1000
|
|
1217
|
+
|
|
1218
|
+
/**
|
|
1219
|
+
* Mark a running entry as `stale-running` if it has been running longer than
|
|
1220
|
+
* the threshold AND has no recent activity (no `updatedAt` change in the last
|
|
1221
|
+
* `IDLE_SILENCE_MS`). This is DISPLAY-ONLY — the board state is unchanged.
|
|
1222
|
+
*
|
|
1223
|
+
* A `stale-running` entry renders with a warning indicator but the underlying
|
|
1224
|
+
* delegation is still treated as running by the backend.
|
|
1225
|
+
*/
|
|
1226
|
+
export function markStaleIfRunning(
|
|
1227
|
+
entry: DelegationEntry,
|
|
1228
|
+
now: number,
|
|
1229
|
+
thresholdMs = STALE_RUNNING_THRESHOLD_MS,
|
|
1230
|
+
): DelegationEntry {
|
|
1231
|
+
if (entry.state !== 'running') return entry
|
|
1232
|
+
const elapsed = now - entry.startedAt
|
|
1233
|
+
if (elapsed < thresholdMs) return entry
|
|
1234
|
+
// If updatedAt exists and is recent, entry is still active — not stale.
|
|
1235
|
+
if (entry.updatedAt !== null && now - entry.updatedAt < IDLE_SILENCE_MS) return entry
|
|
1236
|
+
return { ...entry, state: 'stale-running' as const }
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1193
1239
|
/** Compact elapsed-time label: "5m 12s", "1h 30m", "2d 4h" — ticks every
|
|
1194
1240
|
* second for running jobs. */
|
|
1195
1241
|
export function fmtElapsed(ms: number): string {
|
|
@@ -1207,7 +1253,8 @@ export function fmtElapsed(ms: number): string {
|
|
|
1207
1253
|
/** Elapsed label for one entry: running → ticks `now - startedAt`, terminal
|
|
1208
1254
|
* → fixed `updatedAt - startedAt` (em dash when no finalized timestamp). */
|
|
1209
1255
|
export function delegationElapsed(entry: DelegationEntry, now: number): string {
|
|
1210
|
-
if (entry.state === 'running'
|
|
1256
|
+
if (entry.state === 'running' || entry.state === 'stale-running')
|
|
1257
|
+
return fmtElapsed(now - entry.startedAt)
|
|
1211
1258
|
return entry.updatedAt !== null ? fmtElapsed(entry.updatedAt - entry.startedAt) : '\u2014'
|
|
1212
1259
|
}
|
|
1213
1260
|
|
|
@@ -1874,7 +1921,9 @@ export function childrenToDelegationEntries(
|
|
|
1874
1921
|
agent: mdEntry?.agent ?? child.agent ?? 'agent',
|
|
1875
1922
|
state,
|
|
1876
1923
|
startedAt: mdEntry?.startedAt ?? child.time?.created ?? now,
|
|
1877
|
-
updatedAt:
|
|
1924
|
+
updatedAt:
|
|
1925
|
+
mdEntry?.updatedAt ??
|
|
1926
|
+
(state === 'running' || state === 'stale-running' ? null : (child.time?.updated ?? null)),
|
|
1878
1927
|
timedOut: mdEntry?.timedOut ?? false,
|
|
1879
1928
|
description:
|
|
1880
1929
|
mdEntry !== undefined && mdEntry.description !== ''
|
|
@@ -2008,6 +2057,8 @@ function DelegationRow(props: {
|
|
|
2008
2057
|
switch (props.job.state) {
|
|
2009
2058
|
case 'running':
|
|
2010
2059
|
return t.warning
|
|
2060
|
+
case 'stale-running':
|
|
2061
|
+
return t.error // warning color for stale delegations
|
|
2011
2062
|
case 'completed':
|
|
2012
2063
|
return t.success
|
|
2013
2064
|
case 'error':
|
|
@@ -2019,6 +2070,7 @@ function DelegationRow(props: {
|
|
|
2019
2070
|
|
|
2020
2071
|
const marker = createMemo(() => {
|
|
2021
2072
|
if (props.job.state === 'running') return `${delegationSpinnerFrame(props.animationNow)} `
|
|
2073
|
+
if (props.job.state === 'stale-running') return '\u26a0 ' // warning triangle
|
|
2022
2074
|
switch (props.job.state) {
|
|
2023
2075
|
case 'completed':
|
|
2024
2076
|
return '\u2713 '
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pantheon-tui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pantheon-tui",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.4.1",
|
|
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.
|
|
4
|
+
"version": "1.4.1",
|
|
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": {
|