@foxden-app/foxclaw 0.3.12 → 0.3.13
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/codex_app/local_usage.d.ts +8 -0
- package/dist/codex_app/local_usage.js +73 -2
- package/dist/controller/controller.d.ts +2 -0
- package/dist/controller/controller.js +36 -1
- package/dist/controller/presentation.d.ts +2 -1
- package/dist/controller/presentation.js +60 -0
- package/dist/i18n.d.ts +8 -0
- package/dist/i18n.js +8 -0
- package/package.json +1 -1
|
@@ -11,6 +11,14 @@ export interface CodexLocalUsageStats {
|
|
|
11
11
|
turns: number;
|
|
12
12
|
usageEvents: number;
|
|
13
13
|
totals: CodexLocalUsageTotals;
|
|
14
|
+
outputSpeed: CodexLocalOutputSpeedStats;
|
|
14
15
|
latestSessionMtimeMs: number | null;
|
|
15
16
|
}
|
|
17
|
+
export interface CodexLocalOutputSpeedStats {
|
|
18
|
+
samples: number;
|
|
19
|
+
outputTokens: number;
|
|
20
|
+
seconds: number;
|
|
21
|
+
latestTokensPerSecond: number | null;
|
|
22
|
+
latestSampleAtMs: number | null;
|
|
23
|
+
}
|
|
16
24
|
export declare function readCodexLocalUsageStats(codexHome?: string): Promise<CodexLocalUsageStats>;
|
|
@@ -13,6 +13,7 @@ export async function readCodexLocalUsageStats(codexHome = resolveCodexHome()) {
|
|
|
13
13
|
let sessionsWithUsage = 0;
|
|
14
14
|
let usageEvents = 0;
|
|
15
15
|
let latestSessionMtimeMs = null;
|
|
16
|
+
const outputSpeed = emptyOutputSpeedStats();
|
|
16
17
|
for (const filePath of sessionFiles) {
|
|
17
18
|
const stat = await fs.stat(filePath).catch(() => null);
|
|
18
19
|
if (stat) {
|
|
@@ -20,6 +21,7 @@ export async function readCodexLocalUsageStats(codexHome = resolveCodexHome()) {
|
|
|
20
21
|
}
|
|
21
22
|
const fileUsage = await readSessionUsage(filePath, turnIds);
|
|
22
23
|
usageEvents += fileUsage.usageEvents;
|
|
24
|
+
addOutputSpeed(outputSpeed, fileUsage.outputSpeed);
|
|
23
25
|
if (fileUsage.totalUsage) {
|
|
24
26
|
sessionsWithUsage += 1;
|
|
25
27
|
addUsage(totals, fileUsage.totalUsage);
|
|
@@ -31,6 +33,7 @@ export async function readCodexLocalUsageStats(codexHome = resolveCodexHome()) {
|
|
|
31
33
|
turns: turnIds.size,
|
|
32
34
|
usageEvents,
|
|
33
35
|
totals,
|
|
36
|
+
outputSpeed,
|
|
34
37
|
latestSessionMtimeMs,
|
|
35
38
|
};
|
|
36
39
|
}
|
|
@@ -66,6 +69,8 @@ async function readSessionUsage(filePath, turnIds) {
|
|
|
66
69
|
const reader = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
67
70
|
let totalUsage = null;
|
|
68
71
|
let usageEvents = 0;
|
|
72
|
+
let generationStartMs = null;
|
|
73
|
+
const outputSpeed = emptyOutputSpeedStats();
|
|
69
74
|
try {
|
|
70
75
|
for await (const line of reader) {
|
|
71
76
|
if (!line.trim())
|
|
@@ -77,13 +82,22 @@ async function readSessionUsage(filePath, turnIds) {
|
|
|
77
82
|
catch {
|
|
78
83
|
continue;
|
|
79
84
|
}
|
|
85
|
+
const timestampMs = parseTimestampMs(event?.timestamp);
|
|
80
86
|
const turnId = typeof event?.payload?.turn_id === 'string' ? event.payload.turn_id : null;
|
|
81
87
|
if (turnId) {
|
|
82
88
|
turnIds.add(turnId);
|
|
83
89
|
}
|
|
90
|
+
if (timestampMs !== null && isGenerationBoundary(event)) {
|
|
91
|
+
generationStartMs = timestampMs;
|
|
92
|
+
}
|
|
84
93
|
const info = event?.payload?.info;
|
|
85
|
-
|
|
94
|
+
const lastTokenUsage = info?.last_token_usage ?? info?.lastTokenUsage;
|
|
95
|
+
if (lastTokenUsage) {
|
|
86
96
|
usageEvents += 1;
|
|
97
|
+
addOutputSpeedSample(outputSpeed, lastTokenUsage, generationStartMs, timestampMs);
|
|
98
|
+
if (timestampMs !== null) {
|
|
99
|
+
generationStartMs = timestampMs;
|
|
100
|
+
}
|
|
87
101
|
}
|
|
88
102
|
const totalTokenUsage = info?.total_token_usage ?? info?.totalTokenUsage;
|
|
89
103
|
if (totalTokenUsage) {
|
|
@@ -94,7 +108,7 @@ async function readSessionUsage(filePath, turnIds) {
|
|
|
94
108
|
finally {
|
|
95
109
|
reader.close();
|
|
96
110
|
}
|
|
97
|
-
return { usageEvents, totalUsage };
|
|
111
|
+
return { usageEvents, totalUsage, outputSpeed };
|
|
98
112
|
}
|
|
99
113
|
function emptyTotals() {
|
|
100
114
|
return {
|
|
@@ -105,6 +119,15 @@ function emptyTotals() {
|
|
|
105
119
|
totalTokens: 0,
|
|
106
120
|
};
|
|
107
121
|
}
|
|
122
|
+
function emptyOutputSpeedStats() {
|
|
123
|
+
return {
|
|
124
|
+
samples: 0,
|
|
125
|
+
outputTokens: 0,
|
|
126
|
+
seconds: 0,
|
|
127
|
+
latestTokensPerSecond: null,
|
|
128
|
+
latestSampleAtMs: null,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
108
131
|
function addUsage(totals, usage) {
|
|
109
132
|
const inputTokens = numberField(usage, 'input_tokens', 'inputTokens');
|
|
110
133
|
const outputTokens = numberField(usage, 'output_tokens', 'outputTokens');
|
|
@@ -115,9 +138,57 @@ function addUsage(totals, usage) {
|
|
|
115
138
|
totals.reasoningOutputTokens += numberField(usage, 'reasoning_output_tokens', 'reasoningOutputTokens');
|
|
116
139
|
totals.totalTokens += totalTokens || inputTokens + outputTokens;
|
|
117
140
|
}
|
|
141
|
+
function addOutputSpeed(target, source) {
|
|
142
|
+
target.samples += source.samples;
|
|
143
|
+
target.outputTokens += source.outputTokens;
|
|
144
|
+
target.seconds += source.seconds;
|
|
145
|
+
if (source.latestTokensPerSecond !== null
|
|
146
|
+
&& source.latestSampleAtMs !== null
|
|
147
|
+
&& (target.latestSampleAtMs === null || source.latestSampleAtMs > target.latestSampleAtMs)) {
|
|
148
|
+
target.latestTokensPerSecond = source.latestTokensPerSecond;
|
|
149
|
+
target.latestSampleAtMs = source.latestSampleAtMs;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function addOutputSpeedSample(stats, usage, generationStartMs, timestampMs) {
|
|
153
|
+
if (generationStartMs === null || timestampMs === null || timestampMs <= generationStartMs) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const outputTokens = numberField(usage, 'output_tokens', 'outputTokens');
|
|
157
|
+
if (outputTokens <= 0) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const seconds = (timestampMs - generationStartMs) / 1000;
|
|
161
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
stats.samples += 1;
|
|
165
|
+
stats.outputTokens += outputTokens;
|
|
166
|
+
stats.seconds += seconds;
|
|
167
|
+
stats.latestTokensPerSecond = outputTokens / seconds;
|
|
168
|
+
stats.latestSampleAtMs = timestampMs;
|
|
169
|
+
}
|
|
118
170
|
function numberField(source, snakeKey, camelKey) {
|
|
119
171
|
const snakeValue = source[snakeKey];
|
|
120
172
|
const value = snakeValue === undefined || snakeValue === null ? source[camelKey] : snakeValue;
|
|
121
173
|
const numeric = Number(value);
|
|
122
174
|
return Number.isFinite(numeric) ? numeric : 0;
|
|
123
175
|
}
|
|
176
|
+
function parseTimestampMs(value) {
|
|
177
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
const timestamp = Date.parse(value);
|
|
181
|
+
return Number.isFinite(timestamp) ? timestamp : null;
|
|
182
|
+
}
|
|
183
|
+
function isGenerationBoundary(event) {
|
|
184
|
+
const payload = event?.payload;
|
|
185
|
+
if (event?.type === 'response_item' && payload?.type === 'function_call_output') {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
if (event?.type !== 'event_msg') {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
return payload?.type === 'task_started'
|
|
192
|
+
|| payload?.type === 'exec_command_end'
|
|
193
|
+
|| payload?.type === 'user_message';
|
|
194
|
+
}
|
|
@@ -255,8 +255,10 @@ export declare class BridgeSessionCore {
|
|
|
255
255
|
private buildNativeCollaborationMode;
|
|
256
256
|
private buildCodexUsageStatusLines;
|
|
257
257
|
private buildCodexLocalUsageStatusLines;
|
|
258
|
+
private formatCodexLocalOutputSpeedStatusLines;
|
|
258
259
|
private resolveFastStatusLabel;
|
|
259
260
|
private readCachedCodexLocalUsageStats;
|
|
261
|
+
private sendThreadContextSummary;
|
|
260
262
|
private handleModelCommand;
|
|
261
263
|
private handleEffortCommand;
|
|
262
264
|
private handleThreadOpenCallback;
|
|
@@ -4,7 +4,7 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { normalizeLocale, t } from '../i18n.js';
|
|
6
6
|
import { parseCommand } from './commands.js';
|
|
7
|
-
import { buildAccessSettingsKeyboard, buildModelSettingsKeyboard, buildSetupPanelKeyboard, buildThreadListKeyboard, buildThreadsKeyboard, clampEffortToModel, formatAccessPresetLabel, formatActiveTurnMessageModeLabel, formatAccessSettingsMessage, formatApprovalPolicyLabel, formatCollaborationModeLabel, formatModelSettingsMessage, formatSandboxModeLabel, formatServiceTierStatusLabel, formatSetupPanelMessage, formatThreadsMessage, formatWeixinAccessCopyPaste, formatWeixinModelCopyPaste, formatWeixinThreadsCopyPaste, formatWeixinWhereNavCopyPaste, formatWhereMessage, normalizeRequestedEffort, resolveCurrentModel, resolveActiveTurnMessageMode, resolveRequestedModel, } from './presentation.js';
|
|
7
|
+
import { buildAccessSettingsKeyboard, buildModelSettingsKeyboard, buildSetupPanelKeyboard, buildThreadListKeyboard, buildThreadsKeyboard, clampEffortToModel, formatAccessPresetLabel, formatActiveTurnMessageModeLabel, formatAccessSettingsMessage, formatApprovalPolicyLabel, formatCollaborationModeLabel, formatModelSettingsMessage, formatSandboxModeLabel, formatServiceTierStatusLabel, formatSetupPanelMessage, formatThreadContextSummary, formatThreadsMessage, formatWeixinAccessCopyPaste, formatWeixinModelCopyPaste, formatWeixinThreadsCopyPaste, formatWeixinWhereNavCopyPaste, formatWhereMessage, normalizeRequestedEffort, resolveCurrentModel, resolveActiveTurnMessageMode, resolveRequestedModel, } from './presentation.js';
|
|
8
8
|
import { clampServiceTierToModel, resolveFastTierForModel } from './service_tier.js';
|
|
9
9
|
import { TELEGRAM_BOT_API_DOWNLOAD_LIMIT_BYTES, buildAttachmentPrompt, isNativeImageAttachment, planAttachmentStoragePath, summarizeTelegramInput, } from '../telegram/media.js';
|
|
10
10
|
import { TELEGRAM_MESSAGE_LIMIT, chunkTelegramMessage, chunkTelegramStreamMessage, clipTelegramDraftMessage, } from '../telegram/text.js';
|
|
@@ -505,6 +505,7 @@ export class BridgeSessionCore {
|
|
|
505
505
|
lines.push(revealError ? t(locale, 'codex_sync_failed', { error: revealError }) : t(locale, 'opened_in_codex'));
|
|
506
506
|
}
|
|
507
507
|
await this.sendMessage(scopeId, lines.join('\n'));
|
|
508
|
+
await this.sendThreadContextSummary(scopeId, locale, binding.threadId);
|
|
508
509
|
return;
|
|
509
510
|
}
|
|
510
511
|
case 'watch': {
|
|
@@ -831,13 +832,16 @@ export class BridgeSessionCore {
|
|
|
831
832
|
const mode = watch.mode;
|
|
832
833
|
if (mode === 'already') {
|
|
833
834
|
await this.sendMessage(scopeId, t(locale, 'watch_already_enabled', { threadId: watchedThreadId }));
|
|
835
|
+
await this.sendThreadContextSummary(scopeId, locale, watchedThreadId);
|
|
834
836
|
return;
|
|
835
837
|
}
|
|
836
838
|
if (mode === 'active') {
|
|
837
839
|
await this.sendMessage(scopeId, t(locale, 'watch_started_active', { threadId: watchedThreadId }));
|
|
840
|
+
await this.sendThreadContextSummary(scopeId, locale, watchedThreadId);
|
|
838
841
|
return;
|
|
839
842
|
}
|
|
840
843
|
await this.sendMessage(scopeId, t(locale, 'watch_started_idle', { threadId: watchedThreadId }));
|
|
844
|
+
await this.sendThreadContextSummary(scopeId, locale, watchedThreadId);
|
|
841
845
|
}
|
|
842
846
|
async handleThreadArchiveIndexCommand(scopeId, locale, args) {
|
|
843
847
|
const index = Number.parseInt(args[0] || '', 10);
|
|
@@ -4439,6 +4443,7 @@ export class BridgeSessionCore {
|
|
|
4439
4443
|
cached: formatTokenCount(stats.totals.cachedInputTokens),
|
|
4440
4444
|
reasoning: formatTokenCount(stats.totals.reasoningOutputTokens),
|
|
4441
4445
|
}),
|
|
4446
|
+
...this.formatCodexLocalOutputSpeedStatusLines(locale, stats),
|
|
4442
4447
|
];
|
|
4443
4448
|
}
|
|
4444
4449
|
catch (error) {
|
|
@@ -4446,6 +4451,18 @@ export class BridgeSessionCore {
|
|
|
4446
4451
|
return [t(locale, 'status_codex_local_usage_unavailable', { error: formatShortStatusError(error) })];
|
|
4447
4452
|
}
|
|
4448
4453
|
}
|
|
4454
|
+
formatCodexLocalOutputSpeedStatusLines(locale, stats) {
|
|
4455
|
+
const speed = stats.outputSpeed;
|
|
4456
|
+
if (speed.samples === 0 || speed.outputTokens <= 0 || speed.seconds <= 0) {
|
|
4457
|
+
return [];
|
|
4458
|
+
}
|
|
4459
|
+
const avg = speed.outputTokens / speed.seconds;
|
|
4460
|
+
return [t(locale, 'status_codex_local_speed', {
|
|
4461
|
+
avg: formatCompactNumber(avg),
|
|
4462
|
+
latest: speed.latestTokensPerSecond === null ? t(locale, 'unknown') : formatCompactNumber(speed.latestTokensPerSecond),
|
|
4463
|
+
samples: formatTokenCount(speed.samples),
|
|
4464
|
+
})];
|
|
4465
|
+
}
|
|
4449
4466
|
async resolveFastStatusLabel(locale, settings) {
|
|
4450
4467
|
try {
|
|
4451
4468
|
const models = await this.app.listModels();
|
|
@@ -4466,6 +4483,22 @@ export class BridgeSessionCore {
|
|
|
4466
4483
|
this.localUsageCache = { stats, expiresAt: now + CODEX_LOCAL_USAGE_CACHE_MS };
|
|
4467
4484
|
return stats;
|
|
4468
4485
|
}
|
|
4486
|
+
async sendThreadContextSummary(scopeId, locale, threadId) {
|
|
4487
|
+
try {
|
|
4488
|
+
const turns = await this.app.listThreadTurns(threadId, 5);
|
|
4489
|
+
const text = formatThreadContextSummary(locale, turns);
|
|
4490
|
+
if (text) {
|
|
4491
|
+
await this.sendMessage(scopeId, text);
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4494
|
+
catch (error) {
|
|
4495
|
+
this.logger.warn('codex.thread_context_summary_failed', {
|
|
4496
|
+
scopeId,
|
|
4497
|
+
threadId,
|
|
4498
|
+
error: formatUserError(error),
|
|
4499
|
+
});
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4469
4502
|
async handleModelCommand(event, locale, args) {
|
|
4470
4503
|
const scopeId = event.scopeId;
|
|
4471
4504
|
if (args.length === 0) {
|
|
@@ -4627,6 +4660,7 @@ export class BridgeSessionCore {
|
|
|
4627
4660
|
callbackText = revealError ? t(locale, 'opened_sync_failed_short') : t(locale, 'opened_in_codex_short');
|
|
4628
4661
|
}
|
|
4629
4662
|
await this.messaging.answerCallback(event.callbackQueryId, callbackText);
|
|
4663
|
+
await this.sendThreadContextSummary(scopeId, locale, binding.threadId);
|
|
4630
4664
|
}
|
|
4631
4665
|
async handleThreadActionCallback(event, action, threadId, locale) {
|
|
4632
4666
|
const scopeId = event.scopeId;
|
|
@@ -4673,6 +4707,7 @@ export class BridgeSessionCore {
|
|
|
4673
4707
|
const watch = await this.watchThread(scopeId, target.chatId, target.chatType, target.topicId, binding);
|
|
4674
4708
|
await this.showThreadsPanelFromStoredState(scopeId, event.messageId, locale, false);
|
|
4675
4709
|
await this.messaging.answerCallback(event.callbackQueryId, formatWatchCallbackText(locale, watch.mode, watch.threadId));
|
|
4710
|
+
await this.sendThreadContextSummary(scopeId, locale, watch.threadId);
|
|
4676
4711
|
return;
|
|
4677
4712
|
}
|
|
4678
4713
|
if (action === 'archive') {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccessPresetValue, ActiveTurnMessageMode, AppLocale, AppThread, ApprovalPolicyValue, ChatSessionSettings, CollaborationModeValue, ModelInfo, ReasoningEffortValue, SandboxModeValue } from '../types.js';
|
|
1
|
+
import type { AccessPresetValue, ActiveTurnMessageMode, AppLocale, AppThread, AppTurnSnapshot, ApprovalPolicyValue, ChatSessionSettings, CollaborationModeValue, ModelInfo, ReasoningEffortValue, SandboxModeValue } from '../types.js';
|
|
2
2
|
import type { ResolvedAccessMode } from './access.js';
|
|
3
3
|
type InlineButton = {
|
|
4
4
|
text: string;
|
|
@@ -60,6 +60,7 @@ pageOffset?: number): string;
|
|
|
60
60
|
export declare function formatWeixinModelCopyPaste(locale: AppLocale, models: ModelInfo[], settings: ChatSessionSettings | null): string;
|
|
61
61
|
export declare function formatWeixinAccessCopyPaste(locale: AppLocale): string;
|
|
62
62
|
export declare function formatWeixinWhereNavCopyPaste(locale: AppLocale, hasBinding: boolean, defaultCwd?: string): string;
|
|
63
|
+
export declare function formatThreadContextSummary(locale: AppLocale, turns: AppTurnSnapshot[]): string | null;
|
|
63
64
|
export declare function formatSetupPanelMessage(locale: AppLocale, ctx: SetupPanelContext): string;
|
|
64
65
|
export declare function buildSetupPanelKeyboard(locale: AppLocale, ctx: SetupPanelContext): InlineButton[][];
|
|
65
66
|
export declare function resolveSetupSummaryLine(ctx: SetupPanelContext, locale?: AppLocale): string;
|
|
@@ -225,6 +225,20 @@ export function formatWeixinWhereNavCopyPaste(locale, hasBinding, defaultCwd) {
|
|
|
225
225
|
}
|
|
226
226
|
return lines.join('\n');
|
|
227
227
|
}
|
|
228
|
+
export function formatThreadContextSummary(locale, turns) {
|
|
229
|
+
const context = selectRecentThreadContext(turns);
|
|
230
|
+
if (!context) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
return [
|
|
234
|
+
t(locale, 'thread_context_title'),
|
|
235
|
+
t(locale, 'thread_context_user'),
|
|
236
|
+
context.userText || t(locale, 'empty'),
|
|
237
|
+
'',
|
|
238
|
+
t(locale, 'thread_context_codex'),
|
|
239
|
+
context.codexText || t(locale, 'empty'),
|
|
240
|
+
].join('\n');
|
|
241
|
+
}
|
|
228
242
|
export function formatSetupPanelMessage(locale, ctx) {
|
|
229
243
|
const currentModel = resolveCurrentModel(ctx.models, ctx.settings?.model ?? null);
|
|
230
244
|
const fastTier = resolveFastTierForModel(currentModel);
|
|
@@ -393,6 +407,52 @@ function formatFastSetupLabel(locale, supported, enabled, tierName) {
|
|
|
393
407
|
function resolveCollaborationMode(mode) {
|
|
394
408
|
return mode === 'plan' ? 'plan' : 'default';
|
|
395
409
|
}
|
|
410
|
+
function selectRecentThreadContext(turns) {
|
|
411
|
+
for (const turn of turns) {
|
|
412
|
+
const userText = lastItemText(turn.items, isUserTurnItem);
|
|
413
|
+
const codexText = lastItemText(turn.items, isCodexTurnItem);
|
|
414
|
+
if (userText || codexText) {
|
|
415
|
+
return { userText, codexText };
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
function lastItemText(items, predicate) {
|
|
421
|
+
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
422
|
+
const item = items[index];
|
|
423
|
+
if (!predicate(item)) {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
const text = normalizeThreadContextText(item.text ?? item.aggregatedOutput ?? item.command ?? '');
|
|
427
|
+
if (text) {
|
|
428
|
+
return text;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
function isUserTurnItem(item) {
|
|
434
|
+
const type = normalizeTurnItemType(item.type);
|
|
435
|
+
return type === 'user'
|
|
436
|
+
|| type === 'usermessage'
|
|
437
|
+
|| type === 'humanmessage'
|
|
438
|
+
|| type === 'userinput'
|
|
439
|
+
|| type === 'userrequest';
|
|
440
|
+
}
|
|
441
|
+
function isCodexTurnItem(item) {
|
|
442
|
+
const type = normalizeTurnItemType(item.type);
|
|
443
|
+
return type === 'agent'
|
|
444
|
+
|| type === 'assistant'
|
|
445
|
+
|| type === 'agentmessage'
|
|
446
|
+
|| type === 'assistantmessage'
|
|
447
|
+
|| type === 'plan';
|
|
448
|
+
}
|
|
449
|
+
function normalizeTurnItemType(value) {
|
|
450
|
+
return value.replace(/[^a-z]/gi, '').toLowerCase();
|
|
451
|
+
}
|
|
452
|
+
function normalizeThreadContextText(value) {
|
|
453
|
+
const trimmed = value.replace(/\r\n/g, '\n').trim().replace(/\n{3,}/g, '\n\n');
|
|
454
|
+
return truncate(trimmed, 1200);
|
|
455
|
+
}
|
|
396
456
|
function selectedButtonText(selected, label) {
|
|
397
457
|
return `${selected ? '• ' : ''}${label}`;
|
|
398
458
|
}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -111,6 +111,7 @@ declare const MESSAGES: {
|
|
|
111
111
|
readonly status_codex_usage_unavailable: "Codex usage: unavailable ({error})";
|
|
112
112
|
readonly status_codex_local_history: "Codex local history: {sessions} sessions, {turns} turns, {events} usage records";
|
|
113
113
|
readonly status_codex_local_tokens: "Codex local tokens: total {total}; input {input}, output {output}, cached input {cached}, reasoning output {reasoning}";
|
|
114
|
+
readonly status_codex_local_speed: "Codex local output speed: avg {avg} token/s, latest {latest} token/s ({samples} samples)";
|
|
114
115
|
readonly status_codex_local_usage_unavailable: "Codex local history: unavailable ({error})";
|
|
115
116
|
readonly status_codex_credits: "Codex credits: {value}";
|
|
116
117
|
readonly status_codex_limit_reached: "Codex limit: {value}";
|
|
@@ -315,6 +316,9 @@ declare const MESSAGES: {
|
|
|
315
316
|
readonly threads_filter: "Filter: <code>{searchTerm}</code>";
|
|
316
317
|
readonly threads_range: "Showing {start}-{end}";
|
|
317
318
|
readonly threads_filter_cleared_short: "Filter cleared";
|
|
319
|
+
readonly thread_context_title: "Recent context:";
|
|
320
|
+
readonly thread_context_user: "User:";
|
|
321
|
+
readonly thread_context_codex: "Codex:";
|
|
318
322
|
readonly button_prev_page: "⬅️ Prev";
|
|
319
323
|
readonly button_next_page: "➡️ Next";
|
|
320
324
|
readonly button_clear_filter: "🧹 Clear";
|
|
@@ -661,6 +665,7 @@ declare const MESSAGES: {
|
|
|
661
665
|
readonly status_codex_usage_unavailable: "Codex 用量:无法获取({error})";
|
|
662
666
|
readonly status_codex_local_history: "Codex 本地历史:{sessions} 个会话,{turns} 轮,{events} 条用量记录";
|
|
663
667
|
readonly status_codex_local_tokens: "Codex 本地 Token:总计 {total};输入 {input},输出 {output},缓存输入 {cached},推理输出 {reasoning}";
|
|
668
|
+
readonly status_codex_local_speed: "Codex 本地输出速度:平均 {avg} token/s,最近 {latest} token/s({samples} 个样本)";
|
|
664
669
|
readonly status_codex_local_usage_unavailable: "Codex 本地历史:无法获取({error})";
|
|
665
670
|
readonly status_codex_credits: "Codex 额度:{value}";
|
|
666
671
|
readonly status_codex_limit_reached: "Codex 限制:{value}";
|
|
@@ -865,6 +870,9 @@ declare const MESSAGES: {
|
|
|
865
870
|
readonly threads_filter: "筛选:<code>{searchTerm}</code>";
|
|
866
871
|
readonly threads_range: "显示第 {start}-{end} 条";
|
|
867
872
|
readonly threads_filter_cleared_short: "已清除筛选";
|
|
873
|
+
readonly thread_context_title: "最近上下文:";
|
|
874
|
+
readonly thread_context_user: "用户指令:";
|
|
875
|
+
readonly thread_context_codex: "Codex 最后输出:";
|
|
868
876
|
readonly button_prev_page: "⬅️ 上一页";
|
|
869
877
|
readonly button_next_page: "➡️ 下一页";
|
|
870
878
|
readonly button_clear_filter: "🧹 清除";
|
package/dist/i18n.js
CHANGED
|
@@ -109,6 +109,7 @@ const MESSAGES = {
|
|
|
109
109
|
status_codex_usage_unavailable: 'Codex usage: unavailable ({error})',
|
|
110
110
|
status_codex_local_history: 'Codex local history: {sessions} sessions, {turns} turns, {events} usage records',
|
|
111
111
|
status_codex_local_tokens: 'Codex local tokens: total {total}; input {input}, output {output}, cached input {cached}, reasoning output {reasoning}',
|
|
112
|
+
status_codex_local_speed: 'Codex local output speed: avg {avg} token/s, latest {latest} token/s ({samples} samples)',
|
|
112
113
|
status_codex_local_usage_unavailable: 'Codex local history: unavailable ({error})',
|
|
113
114
|
status_codex_credits: 'Codex credits: {value}',
|
|
114
115
|
status_codex_limit_reached: 'Codex limit: {value}',
|
|
@@ -313,6 +314,9 @@ const MESSAGES = {
|
|
|
313
314
|
threads_filter: 'Filter: <code>{searchTerm}</code>',
|
|
314
315
|
threads_range: 'Showing {start}-{end}',
|
|
315
316
|
threads_filter_cleared_short: 'Filter cleared',
|
|
317
|
+
thread_context_title: 'Recent context:',
|
|
318
|
+
thread_context_user: 'User:',
|
|
319
|
+
thread_context_codex: 'Codex:',
|
|
316
320
|
button_prev_page: '⬅️ Prev',
|
|
317
321
|
button_next_page: '➡️ Next',
|
|
318
322
|
button_clear_filter: '🧹 Clear',
|
|
@@ -659,6 +663,7 @@ const MESSAGES = {
|
|
|
659
663
|
status_codex_usage_unavailable: 'Codex 用量:无法获取({error})',
|
|
660
664
|
status_codex_local_history: 'Codex 本地历史:{sessions} 个会话,{turns} 轮,{events} 条用量记录',
|
|
661
665
|
status_codex_local_tokens: 'Codex 本地 Token:总计 {total};输入 {input},输出 {output},缓存输入 {cached},推理输出 {reasoning}',
|
|
666
|
+
status_codex_local_speed: 'Codex 本地输出速度:平均 {avg} token/s,最近 {latest} token/s({samples} 个样本)',
|
|
662
667
|
status_codex_local_usage_unavailable: 'Codex 本地历史:无法获取({error})',
|
|
663
668
|
status_codex_credits: 'Codex 额度:{value}',
|
|
664
669
|
status_codex_limit_reached: 'Codex 限制:{value}',
|
|
@@ -863,6 +868,9 @@ const MESSAGES = {
|
|
|
863
868
|
threads_filter: '筛选:<code>{searchTerm}</code>',
|
|
864
869
|
threads_range: '显示第 {start}-{end} 条',
|
|
865
870
|
threads_filter_cleared_short: '已清除筛选',
|
|
871
|
+
thread_context_title: '最近上下文:',
|
|
872
|
+
thread_context_user: '用户指令:',
|
|
873
|
+
thread_context_codex: 'Codex 最后输出:',
|
|
866
874
|
button_prev_page: '⬅️ 上一页',
|
|
867
875
|
button_next_page: '➡️ 下一页',
|
|
868
876
|
button_clear_filter: '🧹 清除',
|