@foxden-app/foxclaw 0.5.60 → 0.5.62

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/.env.example CHANGED
@@ -38,9 +38,9 @@ TELEGRAM_PREVIEW_THROTTLE_MS=800
38
38
  # Set to false if you prefer to keep "read files / edited files / ran command"
39
39
  # detail cards in the chat history.
40
40
  # TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL=true
41
- # Delete time-sensitive interactive panels such as /auth and /threads after 30 minutes.
41
+ # Delete time-sensitive interactive panels such as /auth, /setup, and /threads after 5 minutes.
42
42
  # The timer restarts whenever the panel is refreshed. Set to 0 to disable.
43
- # TELEGRAM_PANEL_TTL_MS=1800000
43
+ # TELEGRAM_PANEL_TTL_MS=300000
44
44
  THREAD_LIST_LIMIT=10
45
45
  CODEX_CLI_BIN=/absolute/path/to/codex
46
46
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable FoxClaw changes are listed here. Each release note is bilingual so GitHub Releases and the npm package are useful to both Chinese and English readers.
4
4
 
5
+ ## 0.5.62 - 2026-06-22
6
+
7
+ ### 中文
8
+ - 修复超长工具操作明细无法折叠或最终删除的问题:当 Telegram 拒绝编辑超长归档消息时,FoxClaw 会降级为短摘要,并仍登记原消息用于最终回复后的清理。
9
+ - 工具归档 details 现在会截短单条命令行,避免长 shell 命令触发 `MESSAGE_TOO_LONG` 后反复重试。
10
+ - 归档操作明细的 message id 会在登记时同步持久化,降低网络切换或重启窗口中丢失最终清理目标的概率。
11
+
12
+ ### English
13
+ - Fixed oversized tool activity details failing to collapse or delete after the final reply. If Telegram rejects an oversized archive edit, FoxClaw now falls back to a short summary while still tracking the message for final cleanup.
14
+ - Tool archive details now truncate individual command lines to avoid repeated `MESSAGE_TOO_LONG` retries for long shell commands.
15
+ - Archived tool-detail message ids are persisted as soon as they are registered, reducing cleanup loss during network changes or restarts.
16
+
17
+ ## 0.5.61 - 2026-06-21
18
+
19
+ ### 中文
20
+ - 将 `/auth`、`/setup`、`/threads` 等交互面板的默认自动清理时间从 30 分钟改为 5 分钟,并把 `/where`、旧 model/access 设置面板和 `/config` 面板纳入同一类时效清理。
21
+ - `/config` 现在明确定位为 FoxClaw 自身设置面板,新增“最终回复后删除操作明细”开关;可通过按钮或 `/config delete_tool_details <on|off>` 修改,并写回 `TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL`。
22
+ - `/config` 展示当前 `TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL` 和 `TELEGRAM_PANEL_TTL_MS`,方便确认聊天清理策略是否生效。
23
+
24
+ ### English
25
+ - Changed the default cleanup timeout for interactive panels such as `/auth`, `/setup`, and `/threads` from 30 minutes to 5 minutes, and applied the same stale-panel cleanup to `/where`, legacy model/access settings panels, and `/config`.
26
+ - `/config` is now the FoxClaw runtime settings panel and includes a "delete operation details after final reply" toggle. It can be changed via the button or `/config delete_tool_details <on|off>`, writing `TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL`.
27
+ - `/config` shows the current `TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL` and `TELEGRAM_PANEL_TTL_MS` values so cleanup behavior is visible.
28
+
5
29
  ## 0.5.60 - 2026-06-21
6
30
 
7
31
  ### 中文
package/dist/config.js CHANGED
@@ -78,7 +78,7 @@ export function loadConfig() {
78
78
  telegramPollIntervalMs: intEnv('TELEGRAM_POLL_INTERVAL_MS', 1200),
79
79
  telegramPreviewThrottleMs: intEnv('TELEGRAM_PREVIEW_THROTTLE_MS', 800),
80
80
  telegramDeleteToolDetailsAfterFinal: boolEnv('TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL', true),
81
- telegramPanelTtlMs: intEnv('TELEGRAM_PANEL_TTL_MS', 30 * 60_000),
81
+ telegramPanelTtlMs: intEnv('TELEGRAM_PANEL_TTL_MS', 5 * 60_000),
82
82
  threadListLimit: intEnv('THREAD_LIST_LIMIT', 10),
83
83
  statusPath: DEFAULT_STATUS_PATH,
84
84
  logPath: DEFAULT_LOG_PATH,
@@ -352,7 +352,7 @@ export declare class BridgeSessionCore {
352
352
  private handleFeaturesCommand;
353
353
  private handleConfigCommand;
354
354
  private handleConfigToggleCallback;
355
- private setAuthAutoDeleteNeedsRepair;
355
+ private setFoxClawBooleanConfig;
356
356
  private formatConfigToggleUpdate;
357
357
  private handleRequirementsCommand;
358
358
  private handleProviderCommand;
@@ -464,6 +464,7 @@ export declare class BridgeSessionCore {
464
464
  private ensureStatusMessage;
465
465
  private rebaseStatusMessage;
466
466
  private archiveStatusMessage;
467
+ private recordArchivedMessageId;
467
468
  private noteToolCommandStart;
468
469
  private noteToolCommandEnd;
469
470
  private scheduleToolBatchArchive;
@@ -28,6 +28,8 @@ import { renderActiveTurnStatus } from './status.js';
28
28
  import { writeRuntimeStatus } from '../runtime.js';
29
29
  const AUTH_DELETE_REASON_NEEDS_REPAIR = 'needs_repair';
30
30
  const RESTART_PREVIEW_RECOVERY_RETRY_DELAYS_MS = [3000, 7000, 15_000, 30_000, 45_000, 60_000, 60_000, 60_000];
31
+ const TOOL_ARCHIVE_MAX_LINES = 12;
32
+ const TOOL_ARCHIVE_LINE_LIMIT = 240;
31
33
  class UserFacingError extends Error {
32
34
  }
33
35
  const OBSERVED_THREAD_POLL_MS = 1500;
@@ -1088,9 +1090,9 @@ export class BridgeSessionCore {
1088
1090
  await this.handleSetupCallback(event, setupMatch[1], setupMatch[2], locale);
1089
1091
  return;
1090
1092
  }
1091
- const configMatch = /^config:auth_auto_delete:(on|off)$/.exec(event.data);
1093
+ const configMatch = /^config:(auth_auto_delete|delete_tool_details):(on|off)$/.exec(event.data);
1092
1094
  if (configMatch) {
1093
- await this.handleConfigToggleCallback(event, configMatch[1] === 'on', locale);
1095
+ await this.handleConfigToggleCallback(event, configMatch[1], configMatch[2] === 'on', locale);
1094
1096
  return;
1095
1097
  }
1096
1098
  const voiceMatch = /^voice:([a-f0-9]+)$/.exec(event.data);
@@ -5583,46 +5585,73 @@ export class BridgeSessionCore {
5583
5585
  await this.sendMessage(scopeId, t(locale, 'config_auth_auto_delete_usage'));
5584
5586
  return;
5585
5587
  }
5586
- const update = await this.setAuthAutoDeleteNeedsRepair(enabled);
5588
+ const update = await this.setFoxClawBooleanConfig('auth_auto_delete', enabled);
5587
5589
  const binding = this.store.getBinding(scopeId);
5588
5590
  const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
5589
- await this.sendMessage(scopeId, `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`, configKeyboard(locale, this.config));
5591
+ const sentMessageId = await this.sendMessage(scopeId, `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`, configKeyboard(locale, this.config));
5592
+ this.scheduleStalePanelDeletion(scopeId, sentMessageId);
5593
+ return;
5594
+ }
5595
+ if (['delete_tool_details', 'delete-tool-details', 'tool_details', 'tool-details'].includes(action)) {
5596
+ const enabled = parseConfigBooleanArg(args[1]);
5597
+ if (enabled === null) {
5598
+ await this.sendMessage(scopeId, t(locale, 'config_delete_tool_details_usage'));
5599
+ return;
5600
+ }
5601
+ const update = await this.setFoxClawBooleanConfig('delete_tool_details', enabled);
5602
+ const binding = this.store.getBinding(scopeId);
5603
+ const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
5604
+ const sentMessageId = await this.sendMessage(scopeId, `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`, configKeyboard(locale, this.config));
5605
+ this.scheduleStalePanelDeletion(scopeId, sentMessageId);
5590
5606
  return;
5591
5607
  }
5592
5608
  const binding = this.store.getBinding(scopeId);
5593
5609
  const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
5594
- await this.sendMessage(scopeId, formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats()), configKeyboard(locale, this.config));
5610
+ const sentMessageId = await this.sendMessage(scopeId, formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats()), configKeyboard(locale, this.config));
5611
+ this.scheduleStalePanelDeletion(scopeId, sentMessageId);
5595
5612
  }
5596
- async handleConfigToggleCallback(event, enabled, locale) {
5597
- const update = await this.setAuthAutoDeleteNeedsRepair(enabled);
5613
+ async handleConfigToggleCallback(event, key, enabled, locale) {
5614
+ const update = await this.setFoxClawBooleanConfig(key, enabled);
5598
5615
  await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'decision_recorded'));
5599
5616
  const binding = this.store.getBinding(event.scopeId);
5600
5617
  const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
5601
5618
  const message = `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`;
5602
5619
  if (event.messageId !== null) {
5603
5620
  await this.editMessage(event.scopeId, event.messageId, message, configKeyboard(locale, this.config));
5621
+ this.scheduleStalePanelDeletion(event.scopeId, event.messageId);
5604
5622
  }
5605
5623
  else {
5606
- await this.sendMessage(event.scopeId, message, configKeyboard(locale, this.config));
5624
+ const sentMessageId = await this.sendMessage(event.scopeId, message, configKeyboard(locale, this.config));
5625
+ this.scheduleStalePanelDeletion(event.scopeId, sentMessageId);
5607
5626
  }
5608
5627
  }
5609
- async setAuthAutoDeleteNeedsRepair(enabled) {
5610
- this.config.authAutoDeleteNeedsRepair = enabled;
5628
+ async setFoxClawBooleanConfig(key, enabled) {
5629
+ const envKey = key === 'auth_auto_delete'
5630
+ ? 'AUTH_AUTO_DELETE_NEEDS_REPAIR'
5631
+ : 'TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL';
5632
+ if (key === 'auth_auto_delete') {
5633
+ this.config.authAutoDeleteNeedsRepair = enabled;
5634
+ }
5635
+ else {
5636
+ this.config.telegramDeleteToolDetailsAfterFinal = enabled;
5637
+ }
5611
5638
  const envPath = this.config.envPath;
5612
5639
  if (!envPath) {
5613
- return { enabled, envPath: null, envUpdated: false, envError: null };
5640
+ return { key, enabled, envKey, envPath: null, envUpdated: false, envError: null };
5614
5641
  }
5615
5642
  try {
5616
- await writeEnvBoolean(envPath, 'AUTH_AUTO_DELETE_NEEDS_REPAIR', enabled);
5617
- return { enabled, envPath, envUpdated: true, envError: null };
5643
+ await writeEnvBoolean(envPath, envKey, enabled);
5644
+ return { key, enabled, envKey, envPath, envUpdated: true, envError: null };
5618
5645
  }
5619
5646
  catch (error) {
5620
- this.logger.warn('config.env_update_failed', { key: 'AUTH_AUTO_DELETE_NEEDS_REPAIR', envPath, error: toErrorMeta(error) });
5621
- return { enabled, envPath, envUpdated: false, envError: formatUserError(error) };
5647
+ this.logger.warn('config.env_update_failed', { key: envKey, envPath, error: toErrorMeta(error) });
5648
+ return { key, enabled, envKey, envPath, envUpdated: false, envError: formatUserError(error) };
5622
5649
  }
5623
5650
  }
5624
5651
  formatConfigToggleUpdate(locale, update) {
5625
- const lines = [t(locale, 'config_auth_auto_delete_updated', { value: t(locale, update.enabled ? 'yes' : 'no') })];
5652
+ const lines = [t(locale, update.key === 'auth_auto_delete' ? 'config_auth_auto_delete_updated' : 'config_delete_tool_details_updated', {
5653
+ value: t(locale, update.enabled ? 'yes' : 'no'),
5654
+ })];
5626
5655
  if (update.envError) {
5627
5656
  lines.push(t(locale, 'config_env_update_failed', { value: update.envPath ?? t(locale, 'unknown'), error: update.envError }));
5628
5657
  }
@@ -7207,9 +7236,11 @@ export class BridgeSessionCore {
7207
7236
  }
7208
7237
  if (messageId !== undefined) {
7209
7238
  await this.editMessage(scopeId, messageId, text, whereKeyboard(locale, false));
7239
+ this.scheduleStalePanelDeletion(scopeId, messageId);
7210
7240
  return;
7211
7241
  }
7212
- await this.sendMessage(scopeId, text, whereKeyboard(locale, false));
7242
+ const sentMessageId = await this.sendMessage(scopeId, text, whereKeyboard(locale, false));
7243
+ this.scheduleStalePanelDeletion(scopeId, sentMessageId);
7213
7244
  return;
7214
7245
  }
7215
7246
  const readyBinding = await this.ensureThreadReady(scopeId, binding);
@@ -7221,9 +7252,11 @@ export class BridgeSessionCore {
7221
7252
  }
7222
7253
  if (messageId !== undefined) {
7223
7254
  await this.editMessage(scopeId, messageId, text, whereKeyboard(locale, false));
7255
+ this.scheduleStalePanelDeletion(scopeId, messageId);
7224
7256
  return;
7225
7257
  }
7226
- await this.sendMessage(scopeId, text, whereKeyboard(locale, false));
7258
+ const sentMessageId = await this.sendMessage(scopeId, text, whereKeyboard(locale, false));
7259
+ this.scheduleStalePanelDeletion(scopeId, sentMessageId);
7227
7260
  return;
7228
7261
  }
7229
7262
  let text = formatWhereMessage(locale, thread, settings, this.config.defaultCwd, access, fastStatus);
@@ -7232,9 +7265,11 @@ export class BridgeSessionCore {
7232
7265
  }
7233
7266
  if (messageId !== undefined) {
7234
7267
  await this.editMessage(scopeId, messageId, text, whereKeyboard(locale, true));
7268
+ this.scheduleStalePanelDeletion(scopeId, messageId);
7235
7269
  return;
7236
7270
  }
7237
- await this.sendMessage(scopeId, text, whereKeyboard(locale, true));
7271
+ const sentMessageId = await this.sendMessage(scopeId, text, whereKeyboard(locale, true));
7272
+ this.scheduleStalePanelDeletion(scopeId, sentMessageId);
7238
7273
  }
7239
7274
  async handleThreadListNavigationCallback(event, action, locale) {
7240
7275
  const state = this.threadListPresentationState.get(event.scopeId) ?? {
@@ -7342,9 +7377,11 @@ export class BridgeSessionCore {
7342
7377
  const keyboard = buildModelSettingsKeyboard(locale, models, settings);
7343
7378
  if (messageId !== undefined) {
7344
7379
  await this.editHtmlMessage(scopeId, messageId, text, keyboard);
7380
+ this.scheduleStalePanelDeletion(scopeId, messageId);
7345
7381
  return;
7346
7382
  }
7347
- await this.sendHtmlMessage(scopeId, text, keyboard);
7383
+ const sentMessageId = await this.sendHtmlMessage(scopeId, text, keyboard);
7384
+ this.scheduleStalePanelDeletion(scopeId, sentMessageId);
7348
7385
  }
7349
7386
  async showSetupPanel(scopeId, focus, messageId, locale = this.localeForChat(scopeId)) {
7350
7387
  const models = await this.app.listModels();
@@ -7375,9 +7412,11 @@ export class BridgeSessionCore {
7375
7412
  const keyboard = buildAccessSettingsKeyboard(locale, access);
7376
7413
  if (messageId !== undefined) {
7377
7414
  await this.editHtmlMessage(scopeId, messageId, text, keyboard);
7415
+ this.scheduleStalePanelDeletion(scopeId, messageId);
7378
7416
  return;
7379
7417
  }
7380
- await this.sendHtmlMessage(scopeId, text, keyboard);
7418
+ const sentMessageId = await this.sendHtmlMessage(scopeId, text, keyboard);
7419
+ this.scheduleStalePanelDeletion(scopeId, sentMessageId);
7381
7420
  }
7382
7421
  async handleSettingsCallback(event, kind, rawValue, locale) {
7383
7422
  await this.handleSetupCallback(event, kind, rawValue, locale);
@@ -8138,11 +8177,23 @@ export class BridgeSessionCore {
8138
8177
  messageId = await this.sendMessage(active.scopeId, content.text);
8139
8178
  }
8140
8179
  if (messageId !== null) {
8141
- active.archivedMessageIds.push(messageId);
8180
+ this.recordArchivedMessageId(active, messageId);
8142
8181
  }
8143
8182
  }
8144
8183
  catch (error) {
8145
- this.logger.warn('telegram.preview_archive_send_failed', { error: String(error), turnId: active.turnId });
8184
+ if (isTelegramMessageTooLong(error)) {
8185
+ try {
8186
+ const messageId = await this.sendMessage(active.scopeId, firstLine(content.text));
8187
+ this.recordArchivedMessageId(active, messageId);
8188
+ return true;
8189
+ }
8190
+ catch (fallbackError) {
8191
+ this.logger.warn('telegram.preview_archive_fallback_send_failed', { error: String(fallbackError), turnId: active.turnId });
8192
+ }
8193
+ }
8194
+ else {
8195
+ this.logger.warn('telegram.preview_archive_send_failed', { error: String(error), turnId: active.turnId });
8196
+ }
8146
8197
  this.scheduleRenderRetry(active);
8147
8198
  return false;
8148
8199
  }
@@ -8155,7 +8206,7 @@ export class BridgeSessionCore {
8155
8206
  else {
8156
8207
  await this.editMessage(active.scopeId, active.previewMessageId, content.text, []);
8157
8208
  }
8158
- active.archivedMessageIds.push(active.previewMessageId);
8209
+ this.recordArchivedMessageId(active, active.previewMessageId);
8159
8210
  }
8160
8211
  catch (error) {
8161
8212
  if (isTelegramMessageGone(error)) {
@@ -8165,6 +8216,37 @@ export class BridgeSessionCore {
8165
8216
  this.store.removeActiveTurnPreview(active.turnId);
8166
8217
  return this.archiveStatusMessage(active, content);
8167
8218
  }
8219
+ if (isTelegramMessageTooLong(error)) {
8220
+ try {
8221
+ await this.editMessage(active.scopeId, active.previewMessageId, firstLine(content.text), []);
8222
+ this.recordArchivedMessageId(active, active.previewMessageId);
8223
+ active.previewActive = false;
8224
+ active.statusMessageText = null;
8225
+ active.statusNeedsRebase = false;
8226
+ this.store.removeActiveTurnPreview(active.turnId);
8227
+ return true;
8228
+ }
8229
+ catch (fallbackError) {
8230
+ if (isTelegramMessageGone(fallbackError)) {
8231
+ active.previewActive = false;
8232
+ active.statusMessageText = null;
8233
+ active.statusNeedsRebase = false;
8234
+ this.store.removeActiveTurnPreview(active.turnId);
8235
+ return this.archiveStatusMessage(active, { text: firstLine(content.text), html: null });
8236
+ }
8237
+ this.logger.warn('telegram.preview_archive_fallback_failed', {
8238
+ error: String(fallbackError),
8239
+ turnId: active.turnId,
8240
+ messageId: active.previewMessageId,
8241
+ });
8242
+ this.recordArchivedMessageId(active, active.previewMessageId);
8243
+ active.previewActive = false;
8244
+ active.statusMessageText = null;
8245
+ active.statusNeedsRebase = false;
8246
+ this.store.removeActiveTurnPreview(active.turnId);
8247
+ return true;
8248
+ }
8249
+ }
8168
8250
  this.logger.warn('telegram.preview_archive_failed', {
8169
8251
  error: String(error),
8170
8252
  turnId: active.turnId,
@@ -8179,6 +8261,21 @@ export class BridgeSessionCore {
8179
8261
  this.store.removeActiveTurnPreview(active.turnId);
8180
8262
  return true;
8181
8263
  }
8264
+ recordArchivedMessageId(active, messageId) {
8265
+ if (!active.archivedMessageIds.includes(messageId)) {
8266
+ active.archivedMessageIds.push(messageId);
8267
+ }
8268
+ if (active.previewActive && active.previewMessageId > 0) {
8269
+ this.store.saveActiveTurnPreview({
8270
+ turnId: active.turnId,
8271
+ scopeId: active.scopeId,
8272
+ threadId: active.threadId,
8273
+ messageId: active.previewMessageId,
8274
+ isObserved: active.isObserved,
8275
+ archivedMessageIds: active.archivedMessageIds,
8276
+ });
8277
+ }
8278
+ }
8182
8279
  noteToolCommandStart(active, event) {
8183
8280
  if (!active.toolBatch) {
8184
8281
  active.toolBatch = createToolBatchState();
@@ -8615,9 +8712,12 @@ function renderArchivedToolBatchStatus(locale, counts, actionLines) {
8615
8712
  return { text, html: null };
8616
8713
  }
8617
8714
  const heading = formatToolBatchHeading(locale, counts, false);
8715
+ const detailLines = actionLines
8716
+ .slice(0, TOOL_ARCHIVE_MAX_LINES)
8717
+ .map(line => truncateInline(line, TOOL_ARCHIVE_LINE_LIMIT));
8618
8718
  const html = [
8619
8719
  telegramBold(heading),
8620
- telegramExpandableBlockquote(actionLines.slice(0, 12).join('\n')),
8720
+ telegramExpandableBlockquote(detailLines.join('\n')),
8621
8721
  ].join('\n');
8622
8722
  return { text, html };
8623
8723
  }
@@ -8766,6 +8866,9 @@ function truncateInline(value, limit) {
8766
8866
  }
8767
8867
  return `${value.slice(0, Math.max(0, limit - 1))}…`;
8768
8868
  }
8869
+ function firstLine(value) {
8870
+ return value.split(/\r?\n/, 1)[0]?.trim() || value.trim();
8871
+ }
8769
8872
  function parseReviewTarget(args) {
8770
8873
  if (args.length === 0) {
8771
8874
  return { type: 'uncommittedChanges' };
@@ -9359,6 +9462,12 @@ function formatConfigMessage(locale, result, appConfig, authPoolStats) {
9359
9462
  value: t(locale, appConfig.authAutoDeleteNeedsRepair ? 'yes' : 'no'),
9360
9463
  }));
9361
9464
  lines.push(`AUTH_AUTO_DELETE_NEEDS_REPAIR=${appConfig.authAutoDeleteNeedsRepair ? 'true' : 'false'}`);
9465
+ lines.push(t(locale, 'config_delete_tool_details_after_final', {
9466
+ value: t(locale, appConfig.telegramDeleteToolDetailsAfterFinal ? 'yes' : 'no'),
9467
+ }));
9468
+ lines.push(`TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL=${appConfig.telegramDeleteToolDetailsAfterFinal ? 'true' : 'false'}`);
9469
+ lines.push(t(locale, 'config_panel_ttl', { value: formatDurationMs(locale, appConfig.telegramPanelTtlMs) }));
9470
+ lines.push(`TELEGRAM_PANEL_TTL_MS=${appConfig.telegramPanelTtlMs}`);
9362
9471
  lines.push(formatCodexAuthPoolSummary(locale, authPoolStats));
9363
9472
  return lines.join('\n');
9364
9473
  }
@@ -9373,11 +9482,32 @@ function isInvalidCodexAuthDeleteReason(reason) {
9373
9482
  return reason === AUTH_DELETE_REASON_NEEDS_REPAIR;
9374
9483
  }
9375
9484
  function configKeyboard(locale, appConfig) {
9376
- const enabled = appConfig.authAutoDeleteNeedsRepair;
9377
- return [[{
9378
- text: t(locale, enabled ? 'button_config_auth_auto_delete_off' : 'button_config_auth_auto_delete_on'),
9379
- callback_data: `config:auth_auto_delete:${enabled ? 'off' : 'on'}`,
9380
- }]];
9485
+ const authAutoDeleteEnabled = appConfig.authAutoDeleteNeedsRepair;
9486
+ const deleteToolDetailsEnabled = appConfig.telegramDeleteToolDetailsAfterFinal;
9487
+ return [
9488
+ [{
9489
+ text: t(locale, authAutoDeleteEnabled ? 'button_config_auth_auto_delete_off' : 'button_config_auth_auto_delete_on'),
9490
+ callback_data: `config:auth_auto_delete:${authAutoDeleteEnabled ? 'off' : 'on'}`,
9491
+ }],
9492
+ [{
9493
+ text: t(locale, deleteToolDetailsEnabled ? 'button_config_delete_tool_details_off' : 'button_config_delete_tool_details_on'),
9494
+ callback_data: `config:delete_tool_details:${deleteToolDetailsEnabled ? 'off' : 'on'}`,
9495
+ }],
9496
+ ];
9497
+ }
9498
+ function formatDurationMs(locale, value) {
9499
+ if (value <= 0) {
9500
+ return locale === 'zh' ? '关闭' : 'disabled';
9501
+ }
9502
+ if (value % 60_000 === 0) {
9503
+ const minutes = value / 60_000;
9504
+ return locale === 'zh' ? `${minutes} 分钟` : `${minutes} min`;
9505
+ }
9506
+ if (value % 1000 === 0) {
9507
+ const seconds = value / 1000;
9508
+ return locale === 'zh' ? `${seconds} 秒` : `${seconds} sec`;
9509
+ }
9510
+ return `${value}ms`;
9381
9511
  }
9382
9512
  function parseConfigBooleanArg(value) {
9383
9513
  const normalized = value?.trim().toLowerCase();
@@ -11342,6 +11472,12 @@ function isTelegramMessageGone(error) {
11342
11472
  || message.includes('message to edit not found')
11343
11473
  || message.includes('message not found');
11344
11474
  }
11475
+ function isTelegramMessageTooLong(error) {
11476
+ const message = formatUserError(error).toLowerCase();
11477
+ return message.includes('message_too_long')
11478
+ || message.includes('message is too long')
11479
+ || message.includes('message too long');
11480
+ }
11345
11481
  function isFileMissingError(error) {
11346
11482
  return error instanceof Error && /enoent|no such file or directory/i.test(error.message);
11347
11483
  }
package/dist/i18n.d.ts CHANGED
@@ -282,6 +282,8 @@ declare const MESSAGES: {
282
282
  readonly button_auth_filter_attention: "Attention";
283
283
  readonly button_config_auth_auto_delete_on: "Auto-delete on";
284
284
  readonly button_config_auth_auto_delete_off: "Auto-delete off";
285
+ readonly button_config_delete_tool_details_on: "Delete tool details";
286
+ readonly button_config_delete_tool_details_off: "Keep tool details";
285
287
  readonly another_turn_running: "Another turn is already running. Use /interrupt, /takeover, /queue, or wait.";
286
288
  readonly working: "Working...";
287
289
  readonly usage_open: "Usage: /open <n>";
@@ -617,6 +619,10 @@ declare const MESSAGES: {
617
619
  readonly config_auth_auto_delete_needs_repair: "Auto-delete unrecoverable auth candidates: {value}";
618
620
  readonly config_auth_auto_delete_updated: "Auto-delete unrecoverable auth candidates set to: {value}";
619
621
  readonly config_auth_auto_delete_usage: "Usage: /config auth_auto_delete <on|off>";
622
+ readonly config_delete_tool_details_after_final: "Delete operation details after final reply: {value}";
623
+ readonly config_delete_tool_details_updated: "Delete operation details after final reply set to: {value}";
624
+ readonly config_delete_tool_details_usage: "Usage: /config delete_tool_details <on|off>";
625
+ readonly config_panel_ttl: "Interactive panel cleanup: {value}";
620
626
  readonly config_env_update_failed: "Runtime setting changed, but updating {value} failed: {error}";
621
627
  readonly requirements_title: "Config requirements";
622
628
  readonly requirements_empty: "No config requirements are configured.";
@@ -982,6 +988,8 @@ declare const MESSAGES: {
982
988
  readonly button_auth_filter_attention: "需关注";
983
989
  readonly button_config_auth_auto_delete_on: "开启自动剔除";
984
990
  readonly button_config_auth_auto_delete_off: "关闭自动剔除";
991
+ readonly button_config_delete_tool_details_on: "删除操作明细";
992
+ readonly button_config_delete_tool_details_off: "保留操作明细";
985
993
  readonly another_turn_running: "已经有一个回复在进行中。请先等待,或使用 /interrupt、/takeover、/queue。";
986
994
  readonly working: "处理中...";
987
995
  readonly usage_open: "用法:/open <编号>";
@@ -1317,6 +1325,10 @@ declare const MESSAGES: {
1317
1325
  readonly config_auth_auto_delete_needs_repair: "自动剔除无法恢复的 auth 候选:{value}";
1318
1326
  readonly config_auth_auto_delete_updated: "自动剔除无法恢复的 auth 候选已设置为:{value}";
1319
1327
  readonly config_auth_auto_delete_usage: "用法:/config auth_auto_delete <on|off>";
1328
+ readonly config_delete_tool_details_after_final: "最终回复后删除操作明细:{value}";
1329
+ readonly config_delete_tool_details_updated: "最终回复后删除操作明细已设置为:{value}";
1330
+ readonly config_delete_tool_details_usage: "用法:/config delete_tool_details <on|off>";
1331
+ readonly config_panel_ttl: "交互面板自动清理:{value}";
1320
1332
  readonly config_env_update_failed: "运行时设置已改变,但更新 {value} 失败:{error}";
1321
1333
  readonly requirements_title: "配置要求";
1322
1334
  readonly requirements_empty: "当前没有配置要求。";
package/dist/i18n.js CHANGED
@@ -280,6 +280,8 @@ const MESSAGES = {
280
280
  button_auth_filter_attention: 'Attention',
281
281
  button_config_auth_auto_delete_on: 'Auto-delete on',
282
282
  button_config_auth_auto_delete_off: 'Auto-delete off',
283
+ button_config_delete_tool_details_on: 'Delete tool details',
284
+ button_config_delete_tool_details_off: 'Keep tool details',
283
285
  another_turn_running: 'Another turn is already running. Use /interrupt, /takeover, /queue, or wait.',
284
286
  working: 'Working...',
285
287
  usage_open: 'Usage: /open <n>',
@@ -615,6 +617,10 @@ const MESSAGES = {
615
617
  config_auth_auto_delete_needs_repair: 'Auto-delete unrecoverable auth candidates: {value}',
616
618
  config_auth_auto_delete_updated: 'Auto-delete unrecoverable auth candidates set to: {value}',
617
619
  config_auth_auto_delete_usage: 'Usage: /config auth_auto_delete <on|off>',
620
+ config_delete_tool_details_after_final: 'Delete operation details after final reply: {value}',
621
+ config_delete_tool_details_updated: 'Delete operation details after final reply set to: {value}',
622
+ config_delete_tool_details_usage: 'Usage: /config delete_tool_details <on|off>',
623
+ config_panel_ttl: 'Interactive panel cleanup: {value}',
618
624
  config_env_update_failed: 'Runtime setting changed, but updating {value} failed: {error}',
619
625
  requirements_title: 'Config requirements',
620
626
  requirements_empty: 'No config requirements are configured.',
@@ -980,6 +986,8 @@ const MESSAGES = {
980
986
  button_auth_filter_attention: '需关注',
981
987
  button_config_auth_auto_delete_on: '开启自动剔除',
982
988
  button_config_auth_auto_delete_off: '关闭自动剔除',
989
+ button_config_delete_tool_details_on: '删除操作明细',
990
+ button_config_delete_tool_details_off: '保留操作明细',
983
991
  another_turn_running: '已经有一个回复在进行中。请先等待,或使用 /interrupt、/takeover、/queue。',
984
992
  working: '处理中...',
985
993
  usage_open: '用法:/open <编号>',
@@ -1315,6 +1323,10 @@ const MESSAGES = {
1315
1323
  config_auth_auto_delete_needs_repair: '自动剔除无法恢复的 auth 候选:{value}',
1316
1324
  config_auth_auto_delete_updated: '自动剔除无法恢复的 auth 候选已设置为:{value}',
1317
1325
  config_auth_auto_delete_usage: '用法:/config auth_auto_delete <on|off>',
1326
+ config_delete_tool_details_after_final: '最终回复后删除操作明细:{value}',
1327
+ config_delete_tool_details_updated: '最终回复后删除操作明细已设置为:{value}',
1328
+ config_delete_tool_details_usage: '用法:/config delete_tool_details <on|off>',
1329
+ config_panel_ttl: '交互面板自动清理:{value}',
1318
1330
  config_env_update_failed: '运行时设置已改变,但更新 {value} 失败:{error}',
1319
1331
  requirements_title: '配置要求',
1320
1332
  requirements_empty: '当前没有配置要求。',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.5.60",
3
+ "version": "0.5.62",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",