@leo000001/opencode-quota-sidebar 3.0.5 → 3.0.6
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/dist/events.d.ts +1 -1
- package/dist/events.js +3 -5
- package/dist/index.js +40 -12
- package/dist/tools.js +1 -12
- package/package.json +1 -1
package/dist/events.d.ts
CHANGED
|
@@ -9,5 +9,5 @@ export declare function createEventDispatcher(handlers: {
|
|
|
9
9
|
sessionID: string;
|
|
10
10
|
messageID?: string;
|
|
11
11
|
}) => Promise<void>;
|
|
12
|
-
|
|
12
|
+
onAssistantMessageUpdated: (message: AssistantMessage) => Promise<void>;
|
|
13
13
|
}): (event: Event) => Promise<void>;
|
package/dist/events.js
CHANGED
|
@@ -16,7 +16,8 @@ export function createEventDispatcher(handlers) {
|
|
|
16
16
|
await handlers.onSessionDeleted(event.properties.info);
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
-
if (tui.type === 'tui.prompt.append' ||
|
|
19
|
+
if (tui.type === 'tui.prompt.append' ||
|
|
20
|
+
tui.type === 'tui.command.execute') {
|
|
20
21
|
await handlers.onTuiActivity();
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
@@ -39,9 +40,6 @@ export function createEventDispatcher(handlers) {
|
|
|
39
40
|
return;
|
|
40
41
|
if (!isAssistantMessage(event.properties.info))
|
|
41
42
|
return;
|
|
42
|
-
|
|
43
|
-
if (typeof completed !== 'number' || !Number.isFinite(completed))
|
|
44
|
-
return;
|
|
45
|
-
await handlers.onAssistantMessageCompleted(event.properties.info);
|
|
43
|
+
await handlers.onAssistantMessageUpdated(event.properties.info);
|
|
46
44
|
};
|
|
47
45
|
}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { createUsageService } from './usage_service.js';
|
|
|
13
13
|
import { createTitleApplicator } from './title_apply.js';
|
|
14
14
|
const SHUTDOWN_HOOK_KEY = Symbol.for('opencode-quota-sidebar.shutdown-hook');
|
|
15
15
|
const SHUTDOWN_CALLBACKS_KEY = Symbol.for('opencode-quota-sidebar.shutdown-callbacks');
|
|
16
|
+
const SESSION_ACTIVE_GRACE_MS = 15_000;
|
|
16
17
|
export async function QuotaSidebarPlugin(input) {
|
|
17
18
|
const quotaRuntime = createQuotaRuntime();
|
|
18
19
|
const config = await loadConfig(quotaConfigPaths(input.worktree, input.directory));
|
|
@@ -116,11 +117,33 @@ export async function QuotaSidebarPlugin(input) {
|
|
|
116
117
|
});
|
|
117
118
|
const summarizeSessionUsageForDisplay = usageService.summarizeSessionUsageForDisplay;
|
|
118
119
|
const summarizeForTool = usageService.summarizeForTool;
|
|
120
|
+
const activeSessionUntil = new Map();
|
|
121
|
+
const markSessionActive = (sessionID, now = Date.now()) => {
|
|
122
|
+
activeSessionUntil.set(sessionID, now + SESSION_ACTIVE_GRACE_MS);
|
|
123
|
+
};
|
|
124
|
+
const clearSessionActivity = (sessionID) => {
|
|
125
|
+
activeSessionUntil.delete(sessionID);
|
|
126
|
+
};
|
|
127
|
+
const isSessionActive = (sessionID, now = Date.now()) => {
|
|
128
|
+
const expiresAt = activeSessionUntil.get(sessionID);
|
|
129
|
+
if (expiresAt === undefined)
|
|
130
|
+
return false;
|
|
131
|
+
if (expiresAt > now)
|
|
132
|
+
return true;
|
|
133
|
+
activeSessionUntil.delete(sessionID);
|
|
134
|
+
return false;
|
|
135
|
+
};
|
|
119
136
|
// title apply / refresh lifecycle
|
|
120
137
|
let scheduleTitleRefresh = (sessionID, delay = 250) => {
|
|
121
138
|
void sessionID;
|
|
122
139
|
void delay;
|
|
123
140
|
};
|
|
141
|
+
const scheduleActiveTitleRefresh = (sessionID, delay = 250) => {
|
|
142
|
+
if (!isSessionActive(sessionID))
|
|
143
|
+
return false;
|
|
144
|
+
scheduleTitleRefresh(sessionID, delay);
|
|
145
|
+
return true;
|
|
146
|
+
};
|
|
124
147
|
const scheduleParentRefreshIfSafe = (sessionID, parentID) => {
|
|
125
148
|
if (!config.sidebar.includeChildren)
|
|
126
149
|
return;
|
|
@@ -140,7 +163,7 @@ export async function QuotaSidebarPlugin(input) {
|
|
|
140
163
|
seen.add(current);
|
|
141
164
|
current = state.sessions[current]?.parentID;
|
|
142
165
|
}
|
|
143
|
-
|
|
166
|
+
scheduleActiveTitleRefresh(parentID, 0);
|
|
144
167
|
};
|
|
145
168
|
const titleApplicator = createTitleApplicator({
|
|
146
169
|
state,
|
|
@@ -159,6 +182,8 @@ export async function QuotaSidebarPlugin(input) {
|
|
|
159
182
|
});
|
|
160
183
|
const titleRefresh = createTitleRefreshScheduler({
|
|
161
184
|
apply: async (sessionID) => {
|
|
185
|
+
if (!isSessionActive(sessionID))
|
|
186
|
+
return;
|
|
162
187
|
await titleApplicator.applyTitle(sessionID);
|
|
163
188
|
},
|
|
164
189
|
onError: swallow('titleRefresh'),
|
|
@@ -186,10 +211,7 @@ export async function QuotaSidebarPlugin(input) {
|
|
|
186
211
|
startupTitleWork = runStartupRestore().catch(swallow('startup:restoreAllVisibleTitles'));
|
|
187
212
|
}
|
|
188
213
|
else {
|
|
189
|
-
startupTitleWork = Promise.
|
|
190
|
-
refreshAllVisibleTitles().catch(swallow('startup:refreshAllVisibleTitles')),
|
|
191
|
-
refreshAllTouchedTitles().catch(swallow('startup:refreshAllTouchedTitles')),
|
|
192
|
-
]).then(() => undefined);
|
|
214
|
+
startupTitleWork = Promise.resolve();
|
|
193
215
|
}
|
|
194
216
|
const shutdown = async () => {
|
|
195
217
|
await Promise.race([
|
|
@@ -318,7 +340,7 @@ export async function QuotaSidebarPlugin(input) {
|
|
|
318
340
|
sessionID: session.id,
|
|
319
341
|
incomingTitle: session.title,
|
|
320
342
|
sessionState,
|
|
321
|
-
scheduleRefresh:
|
|
343
|
+
scheduleRefresh: scheduleActiveTitleRefresh,
|
|
322
344
|
});
|
|
323
345
|
},
|
|
324
346
|
onSessionDeleted: async (session) => {
|
|
@@ -328,6 +350,7 @@ export async function QuotaSidebarPlugin(input) {
|
|
|
328
350
|
usageService.forgetSession(session.id);
|
|
329
351
|
titleApplicator.forgetSession(session.id);
|
|
330
352
|
titleRefresh.cancel(session.id);
|
|
353
|
+
clearSessionActivity(session.id);
|
|
331
354
|
const dateKey = state.sessionDateMap[session.id] ||
|
|
332
355
|
dateKeyFromTimestamp(session.time.created);
|
|
333
356
|
state.deletedSessionDateMap[session.id] = dateKey;
|
|
@@ -341,23 +364,28 @@ export async function QuotaSidebarPlugin(input) {
|
|
|
341
364
|
scheduleSave();
|
|
342
365
|
}
|
|
343
366
|
if (config.sidebar.includeChildren && session.parentID) {
|
|
344
|
-
|
|
367
|
+
scheduleActiveTitleRefresh(session.parentID, 0);
|
|
345
368
|
}
|
|
346
369
|
},
|
|
347
370
|
onTuiActivity: async () => {
|
|
348
371
|
return;
|
|
349
372
|
},
|
|
350
373
|
onTuiSessionSelect: async (sessionID) => {
|
|
351
|
-
|
|
374
|
+
scheduleActiveTitleRefresh(sessionID, 0);
|
|
352
375
|
},
|
|
353
376
|
onMessageRemoved: async (info) => {
|
|
354
377
|
usageService.markForceRescan(info.sessionID);
|
|
355
|
-
|
|
378
|
+
scheduleActiveTitleRefresh(info.sessionID, 0);
|
|
356
379
|
scheduleParentRefreshIfSafe(info.sessionID, state.sessions[info.sessionID]?.parentID);
|
|
357
380
|
},
|
|
358
|
-
|
|
381
|
+
onAssistantMessageUpdated: async (message) => {
|
|
382
|
+
markSessionActive(message.sessionID);
|
|
383
|
+
const completed = message.time.completed;
|
|
384
|
+
if (typeof completed !== 'number' || !Number.isFinite(completed)) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
359
387
|
usageService.markSessionDirty(message.sessionID);
|
|
360
|
-
|
|
388
|
+
scheduleActiveTitleRefresh(message.sessionID);
|
|
361
389
|
void maybeShowExpiryToast(message.sessionID);
|
|
362
390
|
},
|
|
363
391
|
});
|
|
@@ -378,7 +406,7 @@ export async function QuotaSidebarPlugin(input) {
|
|
|
378
406
|
scheduleSave,
|
|
379
407
|
flushSave,
|
|
380
408
|
waitForStartupTitleWork: () => startupTitleWork,
|
|
381
|
-
refreshSessionTitle: (sessionID, delay) =>
|
|
409
|
+
refreshSessionTitle: (sessionID, delay) => scheduleActiveTitleRefresh(sessionID, delay ?? 250),
|
|
382
410
|
cancelAllTitleRefreshes: () => titleRefresh.cancelAll(),
|
|
383
411
|
flushScheduledTitleRefreshes: () => titleRefresh.flushScheduled(),
|
|
384
412
|
waitForTitleRefreshIdle: () => titleRefresh.waitForIdle(),
|
package/dist/tools.js
CHANGED
|
@@ -63,25 +63,16 @@ export function createQuotaSidebarTools(deps) {
|
|
|
63
63
|
deps.setTitleEnabled(true);
|
|
64
64
|
deps.scheduleSave();
|
|
65
65
|
await deps.flushSave();
|
|
66
|
-
const visible = await deps.refreshAllVisibleTitles();
|
|
67
|
-
const touched = await deps.refreshAllTouchedTitles();
|
|
68
66
|
deps.refreshSessionTitle(context.sessionID, 0);
|
|
69
67
|
if (startupTimedOut) {
|
|
70
68
|
void deps.waitForStartupTitleWork().then(() => {
|
|
71
69
|
if (!deps.getTitleEnabled())
|
|
72
70
|
return;
|
|
73
|
-
void deps.refreshAllVisibleTitles();
|
|
74
|
-
void deps.refreshAllTouchedTitles();
|
|
75
71
|
deps.refreshSessionTitle(context.sessionID, 0);
|
|
76
72
|
});
|
|
77
73
|
}
|
|
78
74
|
await deps.showToast('toggle', 'Sidebar usage display: ON');
|
|
79
|
-
|
|
80
|
-
visible.refreshed < visible.attempted ||
|
|
81
|
-
touched.refreshed < touched.attempted) {
|
|
82
|
-
return 'Sidebar usage display is now ON. Visible-session refresh failed, so only touched/current session titles are guaranteed to refresh immediately.';
|
|
83
|
-
}
|
|
84
|
-
return 'Sidebar usage display is now ON. Visible session titles are refreshing to show token usage and quota.';
|
|
75
|
+
return 'Sidebar usage display is now ON. Only assistant-active sessions will refresh shared titles.';
|
|
85
76
|
}
|
|
86
77
|
deps.setTitleEnabled(false);
|
|
87
78
|
deps.scheduleSave();
|
|
@@ -96,8 +87,6 @@ export function createQuotaSidebarTools(deps) {
|
|
|
96
87
|
deps.setTitleEnabled(true);
|
|
97
88
|
deps.scheduleSave();
|
|
98
89
|
await deps.flushSave();
|
|
99
|
-
await deps.refreshAllVisibleTitles();
|
|
100
|
-
await deps.refreshAllTouchedTitles();
|
|
101
90
|
deps.refreshSessionTitle(context.sessionID, 0);
|
|
102
91
|
await deps.showToast('toggle', 'Sidebar usage display: OFF failed');
|
|
103
92
|
return 'Sidebar usage display remains ON because some touched session titles could not be restored. Try again after the session service recovers.';
|
package/package.json
CHANGED