@foxden-app/foxclaw 0.7.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.env.example +14 -0
  2. package/CHANGELOG.md +129 -0
  3. package/README.md +22 -2
  4. package/README_EN.md +22 -2
  5. package/dist/antigravity/adapter.d.ts +17 -0
  6. package/dist/antigravity/adapter.js +256 -0
  7. package/dist/antigravity/auth.d.ts +121 -0
  8. package/dist/antigravity/auth.js +896 -0
  9. package/dist/antigravity/client.d.ts +32 -0
  10. package/dist/antigravity/client.js +238 -0
  11. package/dist/antigravity/controller.d.ts +96 -0
  12. package/dist/antigravity/controller.js +1922 -0
  13. package/dist/antigravity/conversations.d.ts +29 -0
  14. package/dist/antigravity/conversations.js +289 -0
  15. package/dist/antigravity/events.d.ts +101 -0
  16. package/dist/antigravity/events.js +114 -0
  17. package/dist/antigravity/runtime.d.ts +47 -0
  18. package/dist/antigravity/runtime.js +82 -0
  19. package/dist/auth/cross_node_sync.js +2 -2
  20. package/dist/channels/telegram/telegram_channel_adapter.d.ts +10 -3
  21. package/dist/channels/telegram/telegram_channel_adapter.js +2 -1
  22. package/dist/channels/weixin/weixin_channel_adapter.d.ts +5 -1
  23. package/dist/codex_app/adapter.d.ts +20 -0
  24. package/dist/codex_app/adapter.js +213 -0
  25. package/dist/codex_app/client.d.ts +2 -1
  26. package/dist/codex_app/client.js +47 -4
  27. package/dist/codex_app/force_takeover.d.ts +12 -0
  28. package/dist/codex_app/force_takeover.js +27 -0
  29. package/dist/config.d.ts +12 -0
  30. package/dist/config.js +42 -0
  31. package/dist/controller/controller.d.ts +15 -5
  32. package/dist/controller/controller.js +252 -20
  33. package/dist/core/attachments.d.ts +11 -0
  34. package/dist/core/attachments.js +56 -0
  35. package/dist/core/engine_spi.d.ts +76 -0
  36. package/dist/core/engine_spi.js +1 -0
  37. package/dist/core/orchestrator.d.ts +101 -0
  38. package/dist/core/orchestrator.js +1404 -0
  39. package/dist/core/stream_preview.d.ts +29 -0
  40. package/dist/core/stream_preview.js +146 -0
  41. package/dist/core/turn_queue.d.ts +19 -0
  42. package/dist/core/turn_queue.js +41 -0
  43. package/dist/i18n.d.ts +36 -6
  44. package/dist/i18n.js +81 -6
  45. package/dist/main.js +190 -20
  46. package/dist/opencode/adapter.d.ts +11 -0
  47. package/dist/opencode/adapter.js +168 -0
  48. package/dist/store/database.d.ts +41 -0
  49. package/dist/store/database.js +242 -43
  50. package/dist/store/token_usage.d.ts +23 -0
  51. package/dist/store/token_usage.js +61 -0
  52. package/dist/telegram/api.js +34 -1
  53. package/dist/telegram/bot_home.d.ts +3 -0
  54. package/dist/telegram/bot_home.js +107 -0
  55. package/dist/telegram/gateway.d.ts +5 -0
  56. package/dist/telegram/gateway.js +21 -0
  57. package/dist/types.d.ts +1 -0
  58. package/dist/update.d.ts +6 -0
  59. package/dist/update.js +78 -6
  60. package/dist/voice/target.js +2 -1
  61. package/dist/voice/tts.js +4 -2
  62. package/docs/devlogs//346/212/200/346/234/257/351/200/232/350/256/257/02./347/273/237/344/270/200/351/200/232/351/201/223/350/260/203/345/272/246/345/231/250/344/270/216/345/217/257/346/217/222/346/213/224/345/274/225/346/223/216SPI/357/274/232FoxClaw/344/273/216/345/215/225/344/275/223/350/265/260/345/220/221/345/244/232Agent/345/272/225/345/272/247.md +192 -0
  63. package/docs/devlogs//347/254/254/344/270/200/344/272/272/347/247/260/347/211/210/02./347/257/207/344/272/214_/345/275/223/344/270/200/344/270/252/346/241/245/346/216/245/345/231/250/345/274/200/345/247/213/345/255/246/344/274/232/345/220/254/346/207/202/346/211/200/346/234/211Agent.md +45 -0
  64. package/docs/user-manual.md +11 -1
  65. package/docs/zh/2026-09-05-bot-home-migration.md +26 -0
  66. package/docs/zh/2026-09-05-reliability-review.md +53 -0
  67. package/docs/zh/troubleshooting.md +12 -0
  68. package/docs/zh/user-manual.md +13 -1
  69. package/package.json +2 -2
  70. package/scripts/force-takeover.py +118 -0
  71. package/scripts/force-takeover.test.py +122 -0
@@ -0,0 +1,29 @@
1
+ import type { AppLocale } from '../types.js';
2
+ export interface StreamPreviewState {
3
+ toolLines: string[];
4
+ accumulatedText: string;
5
+ isBoost?: boolean | undefined;
6
+ engineName?: string | undefined;
7
+ stepIndex?: number | undefined;
8
+ toolCount?: number | undefined;
9
+ currentTool?: string | null | undefined;
10
+ elapsedSeconds?: number | undefined;
11
+ }
12
+ export interface FoldedToolsSummaryOptions {
13
+ stepIndex?: number | undefined;
14
+ toolLines?: string[] | undefined;
15
+ toolCount?: number | undefined;
16
+ startTime?: number | undefined;
17
+ durationSeconds?: number | undefined;
18
+ usage?: {
19
+ inputTokens?: number | undefined;
20
+ outputTokens?: number | undefined;
21
+ cachedTokens?: number | undefined;
22
+ totalTokens?: number | undefined;
23
+ } | undefined;
24
+ locale?: AppLocale | undefined;
25
+ }
26
+ export declare function formatElapsedDuration(seconds: number, locale?: AppLocale): string;
27
+ export declare function buildFoldedToolsSummary(options: FoldedToolsSummaryOptions): string;
28
+ export declare function combineSummaryAndResponse(foldedSummary: string, responseText: string, limit?: number): string[];
29
+ export declare function renderStreamPreviewContent(state: StreamPreviewState): string;
@@ -0,0 +1,146 @@
1
+ import { formatMetricTokenCount } from '../store/token_usage.js';
2
+ import { chunkTelegramMessage } from '../telegram/text.js';
3
+ export function formatElapsedDuration(seconds, locale = 'zh') {
4
+ if (!Number.isFinite(seconds) || seconds <= 0)
5
+ return '0s';
6
+ if (seconds < 60)
7
+ return locale === 'zh' ? `${seconds}秒` : `${seconds}s`;
8
+ const mins = Math.floor(seconds / 60);
9
+ const secs = seconds % 60;
10
+ if (mins < 60) {
11
+ return locale === 'zh'
12
+ ? `${mins}分${secs > 0 ? `${secs}秒` : ''}`
13
+ : `${mins}m${secs > 0 ? ` ${secs}s` : ''}`;
14
+ }
15
+ const hours = Math.floor(mins / 60);
16
+ const remMins = mins % 60;
17
+ return locale === 'zh'
18
+ ? `${hours}小时${remMins > 0 ? `${remMins}分` : ''}`
19
+ : `${hours}h${remMins > 0 ? ` ${remMins}m` : ''}`;
20
+ }
21
+ export function buildFoldedToolsSummary(options) {
22
+ const { stepIndex, toolLines = [], toolCount = toolLines.length, startTime, durationSeconds, usage, locale = 'zh', } = options;
23
+ if (toolCount === 0 && (!stepIndex || stepIndex <= 1) && !usage) {
24
+ return '';
25
+ }
26
+ const roundText = stepIndex && stepIndex > 1
27
+ ? (locale === 'zh' ? ` · 共 ${stepIndex} 轮` : ` · ${stepIndex} turns`)
28
+ : '';
29
+ const toolText = toolCount > 0
30
+ ? (locale === 'zh' ? ` · 累计执行 ${toolCount} 次工具` : ` · ${toolCount} tools`)
31
+ : '';
32
+ let durationText = '';
33
+ const elapsedSec = typeof durationSeconds === 'number' && durationSeconds > 0
34
+ ? durationSeconds
35
+ : startTime
36
+ ? Math.max(1, Math.round((Date.now() - startTime) / 1000))
37
+ : 0;
38
+ if (elapsedSec > 0) {
39
+ durationText = locale === 'zh'
40
+ ? ` · 耗时 ${formatElapsedDuration(elapsedSec, 'zh')}`
41
+ : ` · in ${formatElapsedDuration(elapsedSec, 'en')}`;
42
+ }
43
+ // Header line
44
+ const header = `🛠️ <b>${locale === 'zh' ? '执行小结' : 'Execution Summary'}${roundText}${toolText}${durationText}</b>`;
45
+ // Token line
46
+ let usageLine = '';
47
+ if (usage && (usage.totalTokens || usage.inputTokens || usage.outputTokens || usage.cachedTokens)) {
48
+ const totalVal = usage.totalTokens || ((usage.inputTokens || 0) + (usage.outputTokens || 0));
49
+ const total = formatMetricTokenCount(totalVal);
50
+ const input = formatMetricTokenCount(usage.inputTokens || 0);
51
+ const output = formatMetricTokenCount(usage.outputTokens || 0);
52
+ const cached = formatMetricTokenCount(usage.cachedTokens || 0);
53
+ if (locale === 'zh') {
54
+ usageLine = `\n🪙 <b>Token 消耗</b>: 总计 ${total} (输入 ${input} · 输出 ${output} · 缓存 ${cached})`;
55
+ }
56
+ else {
57
+ usageLine = `\n🪙 <b>Token Usage</b>: Total ${total} (Input ${input} · Output ${output} · Cached ${cached})`;
58
+ }
59
+ }
60
+ // Tool detail section:
61
+ // If toolLines <= 25 (e.g. 10-20 step summaries in Codex/AGY): preserve ALL of them!
62
+ // If toolLines > 25 (e.g. 100+ raw tool calls): keep first 3 + omitted notice + last 15.
63
+ let toolDetailSection = '';
64
+ if (toolLines.length > 0) {
65
+ let previewLines = [];
66
+ if (toolLines.length <= 25) {
67
+ previewLines = toolLines;
68
+ }
69
+ else {
70
+ const head = toolLines.slice(0, 3);
71
+ const tail = toolLines.slice(-15);
72
+ const omitted = toolCount - (head.length + tail.length);
73
+ const omittedNote = omitted > 0
74
+ ? `<i>${locale === 'zh' ? `(中间 ${omitted} 项历史工具调用已折叠收起)` : `(${omitted} intermediate tools collapsed)`}</i>`
75
+ : '';
76
+ previewLines = [...head, omittedNote, ...tail].filter(Boolean);
77
+ }
78
+ toolDetailSection = `\n\n<i>${locale === 'zh' ? '工具调用记录:' : 'Tool calls:'}</i>\n${previewLines.join('\n')}`;
79
+ }
80
+ return `<blockquote expandable>${header}${usageLine}${toolDetailSection}</blockquote>\n\n`;
81
+ }
82
+ export function combineSummaryAndResponse(foldedSummary, responseText, limit = 4000) {
83
+ const content = (responseText || '(无输出 / No output)').trim();
84
+ if (!foldedSummary) {
85
+ return chunkTelegramMessage(content, limit);
86
+ }
87
+ const combined = `${foldedSummary}${content}`;
88
+ if (combined.length <= limit) {
89
+ return [combined];
90
+ }
91
+ // Combined exceeds limit: chunk responseText cleanly without tearing foldedSummary apart!
92
+ const remainingInFirstChunk = limit - foldedSummary.length;
93
+ if (remainingInFirstChunk >= 300) {
94
+ const window = content.slice(0, remainingInFirstChunk);
95
+ const splitAt = Math.max(window.lastIndexOf('\n\n'), window.lastIndexOf('\n'));
96
+ if (splitAt >= Math.floor(remainingInFirstChunk / 3)) {
97
+ const firstChunkText = content.slice(0, splitAt + 1).trim();
98
+ const rest = content.slice(splitAt + 1).trim();
99
+ const restChunks = chunkTelegramMessage(rest, limit);
100
+ return [`${foldedSummary}${firstChunkText}`, ...restChunks];
101
+ }
102
+ }
103
+ // If foldedSummary takes up most of the message or no clean paragraph break was found,
104
+ // foldedSummary gets its own clean message, and responseText is chunked independently.
105
+ const responseChunks = chunkTelegramMessage(content, limit);
106
+ return [foldedSummary.trim(), ...responseChunks];
107
+ }
108
+ export function renderStreamPreviewContent(state) {
109
+ const parts = [];
110
+ const name = state.engineName || 'AI';
111
+ const roundText = state.stepIndex && state.stepIndex > 0 ? `第 ${state.stepIndex} 轮` : '';
112
+ const toolText = typeof state.toolCount === 'number' && state.toolCount > 0 ? `累计执行 ${state.toolCount} 次工具` : '';
113
+ const timeText = typeof state.elapsedSeconds === 'number' && state.elapsedSeconds > 0 ? `已耗时 ${state.elapsedSeconds}s` : '';
114
+ const meta = [roundText, toolText, timeText].filter(Boolean).join(' · ');
115
+ if (meta || state.currentTool || state.toolLines.length > 0) {
116
+ if (state.isBoost) {
117
+ parts.push(`🚀 <b>${name} (Boost 模式) 正在执行中</b>${meta ? ` (${meta})` : ''}`);
118
+ }
119
+ else {
120
+ parts.push(`🔄 <b>${name} 正在执行中</b>${meta ? ` (${meta})` : ''}`);
121
+ }
122
+ }
123
+ if (state.currentTool) {
124
+ parts.push(`⚙️ <b>当前正在运行</b>: <code>${escapeTelegramHtml(state.currentTool)}</code>`);
125
+ parts.push('');
126
+ }
127
+ if (state.toolLines.length > 0) {
128
+ const recent = state.toolLines.slice(-6);
129
+ parts.push(`<blockquote expandable>🛠️ <b>正在调用工具 (${state.toolLines.length} 项)</b>\n${recent.join('\n')}</blockquote>`);
130
+ parts.push('');
131
+ }
132
+ if (state.accumulatedText) {
133
+ const preview = state.accumulatedText.slice(-3000);
134
+ parts.push(preview);
135
+ }
136
+ else {
137
+ parts.push(state.isBoost ? `🚀 ${name} (Boost 模式) 正在深度思考中…` : `⏳ ${name} 正在思考中…`);
138
+ }
139
+ return parts.join('\n');
140
+ }
141
+ function escapeTelegramHtml(value) {
142
+ return value
143
+ .replace(/&/g, '&amp;')
144
+ .replace(/</g, '&lt;')
145
+ .replace(/>/g, '&gt;');
146
+ }
@@ -0,0 +1,19 @@
1
+ export type TurnMessageMode = 'steer' | 'queue';
2
+ export interface QueuedTurnItem<T = unknown> {
3
+ id: string;
4
+ scopeId: string;
5
+ prompt: string;
6
+ payload: T;
7
+ enqueuedAt: number;
8
+ }
9
+ export declare class TurnQueueManager<T = unknown> {
10
+ private readonly queues;
11
+ private readonly activeModes;
12
+ getMode(scopeId: string, defaultMode?: TurnMessageMode): TurnMessageMode;
13
+ setMode(scopeId: string, mode: TurnMessageMode): void;
14
+ enqueue(scopeId: string, item: Omit<QueuedTurnItem<T>, 'id' | 'enqueuedAt'>): QueuedTurnItem<T>;
15
+ dequeue(scopeId: string): QueuedTurnItem<T> | undefined;
16
+ peek(scopeId: string): QueuedTurnItem<T> | undefined;
17
+ getQueueLength(scopeId: string): number;
18
+ clearQueue(scopeId: string): number;
19
+ }
@@ -0,0 +1,41 @@
1
+ export class TurnQueueManager {
2
+ queues = new Map();
3
+ activeModes = new Map();
4
+ getMode(scopeId, defaultMode = 'queue') {
5
+ return this.activeModes.get(scopeId) ?? defaultMode;
6
+ }
7
+ setMode(scopeId, mode) {
8
+ this.activeModes.set(scopeId, mode);
9
+ }
10
+ enqueue(scopeId, item) {
11
+ const fullItem = {
12
+ ...item,
13
+ id: `q_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
14
+ enqueuedAt: Date.now(),
15
+ };
16
+ const list = this.queues.get(scopeId) || [];
17
+ list.push(fullItem);
18
+ this.queues.set(scopeId, list);
19
+ return fullItem;
20
+ }
21
+ dequeue(scopeId) {
22
+ const list = this.queues.get(scopeId);
23
+ if (!list || list.length === 0)
24
+ return undefined;
25
+ const item = list.shift();
26
+ if (list.length === 0)
27
+ this.queues.delete(scopeId);
28
+ return item;
29
+ }
30
+ peek(scopeId) {
31
+ return this.queues.get(scopeId)?.[0];
32
+ }
33
+ getQueueLength(scopeId) {
34
+ return this.queues.get(scopeId)?.length ?? 0;
35
+ }
36
+ clearQueue(scopeId) {
37
+ const count = this.getQueueLength(scopeId);
38
+ this.queues.delete(scopeId);
39
+ return count;
40
+ }
41
+ }
package/dist/i18n.d.ts CHANGED
@@ -43,6 +43,7 @@ declare const MESSAGES: {
43
43
  readonly usage_files: "Usage: /files <query>";
44
44
  readonly cmd_desc_help: "Show commands";
45
45
  readonly cmd_desc_setup: "Unified preference panel";
46
+ readonly cmd_desc_backend: "Switch backend engine and account";
46
47
  readonly cmd_desc_fast: "Toggle Fast mode";
47
48
  readonly cmd_desc_active: "Active-turn message behavior";
48
49
  readonly cmd_desc_rich: "Telegram RichMessage demo";
@@ -52,6 +53,8 @@ declare const MESSAGES: {
52
53
  readonly cmd_desc_account: "Codex account";
53
54
  readonly cmd_desc_quota: "Codex quota";
54
55
  readonly cmd_desc_login_device: "ChatGPT device login";
56
+ readonly cmd_desc_login_cancel: "Cancel the pending login";
57
+ readonly cmd_desc_cli: "Continue this session in the local CLI";
55
58
  readonly cmd_desc_threads: "Recent threads";
56
59
  readonly cmd_desc_open: "Open a cached thread";
57
60
  readonly cmd_desc_goal: "Thread goal";
@@ -246,13 +249,13 @@ declare const MESSAGES: {
246
249
  readonly auth_sync_test_sent: "Auth sync test complete: sent {sent}, replies {replied}.";
247
250
  readonly auth_sync_test_missing: "Missing replies: {value}";
248
251
  readonly auth_sync_push_blocked_active: "Cannot push auth sync while any runtime, approval, input, login, or auth mirror write is active.";
249
- readonly auth_sync_safe_starting: "Auditing every node, selecting the newest valid auth, and safely synchronizing the cluster...";
252
+ readonly auth_sync_safe_starting: "Auditing every node and distributing valid auth. An unreachable peer can add up to 5 minutes per audit phase. Use /auth sync status to check progress.";
250
253
  readonly auth_sync_safe_done: "Safe auth sync complete: local synced {localSynced}, local skipped {localSkipped}; cross-node sent {sent}, skipped {skipped}.";
251
254
  readonly auth_cluster_audit_starting: "Asking all auth-sync peers to validate accounts, reconcile the newest usable credentials, and refresh credentials at least 8 days old...";
252
255
  readonly auth_cluster_audit_blocked_active: "Cluster auth check requires every local runtime, approval, input, login, and auth mirror write to be idle.";
253
256
  readonly auth_cluster_audit_failed: "Cluster auth check failed: {error}";
254
257
  readonly auth_cluster_audit_summary: "Cluster auth check: nodes {responded}/{expected}; accounts checked {checked}, usable {valid}, unusable {invalid}.";
255
- readonly auth_cluster_audit_synced: "Newest usable auth distributed: {value}";
258
+ readonly auth_cluster_audit_synced: "Valid auth selected and sent (remote import not confirmed): {value}";
256
259
  readonly auth_cluster_audit_repair: "Marked for manual repair after multi-node consensus: {value}";
257
260
  readonly auth_cluster_audit_missing: "No response: {value}";
258
261
  readonly auth_cluster_audit_busy: "Busy nodes: {value}";
@@ -301,7 +304,17 @@ declare const MESSAGES: {
301
304
  readonly working: "Working...";
302
305
  readonly usage_open: "Usage: /open <n>";
303
306
  readonly usage_watch: "Usage: /watch [n]";
304
- readonly usage_takeover: "Usage: /takeover <message>";
307
+ readonly usage_takeover: "Usage: /takeover <message> or /takeover --force <message> (confirm stopping a local CLI)";
308
+ readonly force_takeover_trusted_telegram_only: "Force takeover is only available to the configured trusted Telegram user.";
309
+ readonly force_takeover_confirm: "Force takeover of thread {threadId}?\nLocal Codex CLI PID: {pid}\nWorking directory: {cwd}\nNext prompt: {prompt}\nThis will terminate that CLI (SIGTERM, then SIGKILL after 5 seconds if needed). Unfinished work may be interrupted; child commands may continue running. No edits are rolled back. Confirmation expires in 60 seconds.";
310
+ readonly button_force_takeover: "Confirm force takeover";
311
+ readonly force_takeover_busy: "Force takeover is in progress. Please wait before switching threads.";
312
+ readonly force_takeover_hint: "Use /watch then /queue <message> to queue for that CLI. To stop a local CLI and hand the thread to this bridge, use /takeover --force <message> and confirm.";
313
+ readonly force_takeover_expired: "Confirmation expired or thread changed. Send /takeover --force <message> again.";
314
+ readonly force_takeover_cancelled: "Force takeover cancelled. Nothing was stopped or submitted.";
315
+ readonly force_takeover_stopping: "Stopping the confirmed CLI PID {pid} and checking its writer lock...";
316
+ readonly force_takeover_failed: "Force takeover not completed; no new prompt was sent. If stopping already began, the CLI may have exited. Check /status before retrying. Reason: {error}";
317
+ readonly force_takeover_acquired: "Bridge acquired thread {threadId}. Preparing the requested prompt; this is not task completion.";
305
318
  readonly usage_queue: "Usage: /queue <message>";
306
319
  readonly unknown_cached_thread: "Unknown cached thread. Run /threads first.";
307
320
  readonly cached_thread_unavailable: "That cached thread is no longer available. Run /threads again.";
@@ -771,6 +784,7 @@ declare const MESSAGES: {
771
784
  readonly usage_files: "用法:/files <关键词>";
772
785
  readonly cmd_desc_help: "查看命令";
773
786
  readonly cmd_desc_setup: "统一偏好面板";
787
+ readonly cmd_desc_backend: "切换后端运行环境与账号";
774
788
  readonly cmd_desc_fast: "切换 Fast 模式";
775
789
  readonly cmd_desc_active: "运行中新消息处理方式";
776
790
  readonly cmd_desc_rich: "Telegram RichMessage 演示";
@@ -780,6 +794,8 @@ declare const MESSAGES: {
780
794
  readonly cmd_desc_account: "Codex 账号";
781
795
  readonly cmd_desc_quota: "Codex 用量";
782
796
  readonly cmd_desc_login_device: "ChatGPT 设备登录";
797
+ readonly cmd_desc_login_cancel: "取消当前登录流程";
798
+ readonly cmd_desc_cli: "从本机 CLI 继续当前会话";
783
799
  readonly cmd_desc_threads: "最近线程";
784
800
  readonly cmd_desc_open: "打开缓存线程";
785
801
  readonly cmd_desc_goal: "线程目标";
@@ -974,13 +990,13 @@ declare const MESSAGES: {
974
990
  readonly auth_sync_test_sent: "auth sync 测试完成:已发送 {sent},收到回应 {replied}。";
975
991
  readonly auth_sync_test_missing: "未回应:{value}";
976
992
  readonly auth_sync_push_blocked_active: "当前有任一 runtime、审批、待输入、登录或 auth 镜像写入在进行中,不能推送 auth 同步。";
977
- readonly auth_sync_safe_starting: "正在自检全部节点、选择最新有效 auth,并安全同步整个集群...";
993
+ readonly auth_sync_safe_starting: "正在自检各节点并分发有效授权。节点未回应时,每轮自检最多等待 5 分钟;可用 /auth sync status 查看进展。";
978
994
  readonly auth_sync_safe_done: "安全 auth 同步完成:本机同步 {localSynced},本机跳过 {localSkipped};跨节点发送 {sent},跳过 {skipped}。";
979
995
  readonly auth_cluster_audit_starting: "正在通知所有 auth sync 节点逐账号自检,协商最新有效凭据,并由本节点刷新已满 8 天的 auth...";
980
996
  readonly auth_cluster_audit_blocked_active: "集群 auth 自检要求本机所有 runtime、审批、待输入、登录和 auth 镜像写入均为空闲。";
981
997
  readonly auth_cluster_audit_failed: "集群 auth 自检失败:{error}";
982
998
  readonly auth_cluster_audit_summary: "集群 auth 自检:节点 {responded}/{expected};检查账号 {checked},有效 {valid},无有效副本 {invalid}。";
983
- readonly auth_cluster_audit_synced: "已采用并分发最新有效 auth:{value}";
999
+ readonly auth_cluster_audit_synced: "已选出并发送有效授权(远端导入尚未确认):{value}";
984
1000
  readonly auth_cluster_audit_repair: "多节点确认无效,已标记问号等待人工处理:{value}";
985
1001
  readonly auth_cluster_audit_missing: "未回应节点:{value}";
986
1002
  readonly auth_cluster_audit_busy: "忙碌节点:{value}";
@@ -1029,7 +1045,17 @@ declare const MESSAGES: {
1029
1045
  readonly working: "处理中...";
1030
1046
  readonly usage_open: "用法:/open <编号>";
1031
1047
  readonly usage_watch: "用法:/watch [编号]";
1032
- readonly usage_takeover: "用法:/takeover <消息>";
1048
+ readonly usage_takeover: "用法:/takeover <消息>;或 /takeover --force <消息>(确认后停止本机 CLI)";
1049
+ readonly force_takeover_trusted_telegram_only: "强制接管仅对配置中的可信 Telegram 用户开放。";
1050
+ readonly force_takeover_confirm: "强制接管 thread {threadId}?\n本机 Codex CLI PID:{pid}\n工作目录:{cwd}\n接管后消息:{prompt}\n将终止这个 CLI(先 SIGTERM,5 秒后仍未退出则 SIGKILL)。未完成任务可能被中断,已启动的子命令可能继续运行;不会回滚文件修改。确认 60 秒内有效。";
1051
+ readonly button_force_takeover: "确认强制接管";
1052
+ readonly force_takeover_busy: "正在强制接管,请等待结果后再切换会话。";
1053
+ readonly force_takeover_hint: "可先 /watch 再 /queue <消息> 排队给原 CLI;如需停止本机 CLI、让桥接管,请发送 /takeover --force <消息> 并确认。";
1054
+ readonly force_takeover_expired: "确认已过期或 thread 已切换,请重新发送 /takeover --force <消息>。";
1055
+ readonly force_takeover_cancelled: "已取消强制接管,没有停止进程或发送新任务。";
1056
+ readonly force_takeover_stopping: "正在停止已确认的 CLI PID {pid},并检查写入锁是否释放…";
1057
+ readonly force_takeover_failed: "强制接管未完成,没有发送新任务。如果已开始停止,CLI 可能已经退出。请先 /status 检查再重试。原因:{error}";
1058
+ readonly force_takeover_acquired: "桥已取得 thread {threadId} 的写入权,准备发送指定消息;这不代表任务已完成。";
1033
1059
  readonly usage_queue: "用法:/queue <消息>";
1034
1060
  readonly unknown_cached_thread: "找不到这个缓存线程,请先运行 /threads。";
1035
1061
  readonly cached_thread_unavailable: "这个缓存线程已经不可用,请重新运行 /threads。";
@@ -1469,4 +1495,8 @@ export declare function getOpencodeTelegramCommands(locale: AppLocale): Array<{
1469
1495
  command: string;
1470
1496
  description: string;
1471
1497
  }>;
1498
+ export declare function getAntigravityTelegramCommands(locale: AppLocale): Array<{
1499
+ command: string;
1500
+ description: string;
1501
+ }>;
1472
1502
  export {};
package/dist/i18n.js CHANGED
@@ -41,6 +41,7 @@ const MESSAGES = {
41
41
  usage_files: 'Usage: /files <query>',
42
42
  cmd_desc_help: 'Show commands',
43
43
  cmd_desc_setup: 'Unified preference panel',
44
+ cmd_desc_backend: 'Switch backend engine and account',
44
45
  cmd_desc_fast: 'Toggle Fast mode',
45
46
  cmd_desc_active: 'Active-turn message behavior',
46
47
  cmd_desc_rich: 'Telegram RichMessage demo',
@@ -50,6 +51,8 @@ const MESSAGES = {
50
51
  cmd_desc_account: 'Codex account',
51
52
  cmd_desc_quota: 'Codex quota',
52
53
  cmd_desc_login_device: 'ChatGPT device login',
54
+ cmd_desc_login_cancel: 'Cancel the pending login',
55
+ cmd_desc_cli: 'Continue this session in the local CLI',
53
56
  cmd_desc_threads: 'Recent threads',
54
57
  cmd_desc_open: 'Open a cached thread',
55
58
  cmd_desc_goal: 'Thread goal',
@@ -244,13 +247,13 @@ const MESSAGES = {
244
247
  auth_sync_test_sent: 'Auth sync test complete: sent {sent}, replies {replied}.',
245
248
  auth_sync_test_missing: 'Missing replies: {value}',
246
249
  auth_sync_push_blocked_active: 'Cannot push auth sync while any runtime, approval, input, login, or auth mirror write is active.',
247
- auth_sync_safe_starting: 'Auditing every node, selecting the newest valid auth, and safely synchronizing the cluster...',
250
+ auth_sync_safe_starting: 'Auditing every node and distributing valid auth. An unreachable peer can add up to 5 minutes per audit phase. Use /auth sync status to check progress.',
248
251
  auth_sync_safe_done: 'Safe auth sync complete: local synced {localSynced}, local skipped {localSkipped}; cross-node sent {sent}, skipped {skipped}.',
249
252
  auth_cluster_audit_starting: 'Asking all auth-sync peers to validate accounts, reconcile the newest usable credentials, and refresh credentials at least 8 days old...',
250
253
  auth_cluster_audit_blocked_active: 'Cluster auth check requires every local runtime, approval, input, login, and auth mirror write to be idle.',
251
254
  auth_cluster_audit_failed: 'Cluster auth check failed: {error}',
252
255
  auth_cluster_audit_summary: 'Cluster auth check: nodes {responded}/{expected}; accounts checked {checked}, usable {valid}, unusable {invalid}.',
253
- auth_cluster_audit_synced: 'Newest usable auth distributed: {value}',
256
+ auth_cluster_audit_synced: 'Valid auth selected and sent (remote import not confirmed): {value}',
254
257
  auth_cluster_audit_repair: 'Marked for manual repair after multi-node consensus: {value}',
255
258
  auth_cluster_audit_missing: 'No response: {value}',
256
259
  auth_cluster_audit_busy: 'Busy nodes: {value}',
@@ -299,7 +302,17 @@ const MESSAGES = {
299
302
  working: 'Working...',
300
303
  usage_open: 'Usage: /open <n>',
301
304
  usage_watch: 'Usage: /watch [n]',
302
- usage_takeover: 'Usage: /takeover <message>',
305
+ usage_takeover: 'Usage: /takeover <message> or /takeover --force <message> (confirm stopping a local CLI)',
306
+ force_takeover_trusted_telegram_only: 'Force takeover is only available to the configured trusted Telegram user.',
307
+ force_takeover_confirm: 'Force takeover of thread {threadId}?\nLocal Codex CLI PID: {pid}\nWorking directory: {cwd}\nNext prompt: {prompt}\nThis will terminate that CLI (SIGTERM, then SIGKILL after 5 seconds if needed). Unfinished work may be interrupted; child commands may continue running. No edits are rolled back. Confirmation expires in 60 seconds.',
308
+ button_force_takeover: 'Confirm force takeover',
309
+ force_takeover_busy: 'Force takeover is in progress. Please wait before switching threads.',
310
+ force_takeover_hint: 'Use /watch then /queue <message> to queue for that CLI. To stop a local CLI and hand the thread to this bridge, use /takeover --force <message> and confirm.',
311
+ force_takeover_expired: 'Confirmation expired or thread changed. Send /takeover --force <message> again.',
312
+ force_takeover_cancelled: 'Force takeover cancelled. Nothing was stopped or submitted.',
313
+ force_takeover_stopping: 'Stopping the confirmed CLI PID {pid} and checking its writer lock...',
314
+ force_takeover_failed: 'Force takeover not completed; no new prompt was sent. If stopping already began, the CLI may have exited. Check /status before retrying. Reason: {error}',
315
+ force_takeover_acquired: 'Bridge acquired thread {threadId}. Preparing the requested prompt; this is not task completion.',
303
316
  usage_queue: 'Usage: /queue <message>',
304
317
  unknown_cached_thread: 'Unknown cached thread. Run /threads first.',
305
318
  cached_thread_unavailable: 'That cached thread is no longer available. Run /threads again.',
@@ -769,6 +782,7 @@ const MESSAGES = {
769
782
  usage_files: '用法:/files <关键词>',
770
783
  cmd_desc_help: '查看命令',
771
784
  cmd_desc_setup: '统一偏好面板',
785
+ cmd_desc_backend: '切换后端运行环境与账号',
772
786
  cmd_desc_fast: '切换 Fast 模式',
773
787
  cmd_desc_active: '运行中新消息处理方式',
774
788
  cmd_desc_rich: 'Telegram RichMessage 演示',
@@ -778,6 +792,8 @@ const MESSAGES = {
778
792
  cmd_desc_account: 'Codex 账号',
779
793
  cmd_desc_quota: 'Codex 用量',
780
794
  cmd_desc_login_device: 'ChatGPT 设备登录',
795
+ cmd_desc_login_cancel: '取消当前登录流程',
796
+ cmd_desc_cli: '从本机 CLI 继续当前会话',
781
797
  cmd_desc_threads: '最近线程',
782
798
  cmd_desc_open: '打开缓存线程',
783
799
  cmd_desc_goal: '线程目标',
@@ -972,13 +988,13 @@ const MESSAGES = {
972
988
  auth_sync_test_sent: 'auth sync 测试完成:已发送 {sent},收到回应 {replied}。',
973
989
  auth_sync_test_missing: '未回应:{value}',
974
990
  auth_sync_push_blocked_active: '当前有任一 runtime、审批、待输入、登录或 auth 镜像写入在进行中,不能推送 auth 同步。',
975
- auth_sync_safe_starting: '正在自检全部节点、选择最新有效 auth,并安全同步整个集群...',
991
+ auth_sync_safe_starting: '正在自检各节点并分发有效授权。节点未回应时,每轮自检最多等待 5 分钟;可用 /auth sync status 查看进展。',
976
992
  auth_sync_safe_done: '安全 auth 同步完成:本机同步 {localSynced},本机跳过 {localSkipped};跨节点发送 {sent},跳过 {skipped}。',
977
993
  auth_cluster_audit_starting: '正在通知所有 auth sync 节点逐账号自检,协商最新有效凭据,并由本节点刷新已满 8 天的 auth...',
978
994
  auth_cluster_audit_blocked_active: '集群 auth 自检要求本机所有 runtime、审批、待输入、登录和 auth 镜像写入均为空闲。',
979
995
  auth_cluster_audit_failed: '集群 auth 自检失败:{error}',
980
996
  auth_cluster_audit_summary: '集群 auth 自检:节点 {responded}/{expected};检查账号 {checked},有效 {valid},无有效副本 {invalid}。',
981
- auth_cluster_audit_synced: '已采用并分发最新有效 auth:{value}',
997
+ auth_cluster_audit_synced: '已选出并发送有效授权(远端导入尚未确认):{value}',
982
998
  auth_cluster_audit_repair: '多节点确认无效,已标记问号等待人工处理:{value}',
983
999
  auth_cluster_audit_missing: '未回应节点:{value}',
984
1000
  auth_cluster_audit_busy: '忙碌节点:{value}',
@@ -1027,7 +1043,17 @@ const MESSAGES = {
1027
1043
  working: '处理中...',
1028
1044
  usage_open: '用法:/open <编号>',
1029
1045
  usage_watch: '用法:/watch [编号]',
1030
- usage_takeover: '用法:/takeover <消息>',
1046
+ usage_takeover: '用法:/takeover <消息>;或 /takeover --force <消息>(确认后停止本机 CLI)',
1047
+ force_takeover_trusted_telegram_only: '强制接管仅对配置中的可信 Telegram 用户开放。',
1048
+ force_takeover_confirm: '强制接管 thread {threadId}?\n本机 Codex CLI PID:{pid}\n工作目录:{cwd}\n接管后消息:{prompt}\n将终止这个 CLI(先 SIGTERM,5 秒后仍未退出则 SIGKILL)。未完成任务可能被中断,已启动的子命令可能继续运行;不会回滚文件修改。确认 60 秒内有效。',
1049
+ button_force_takeover: '确认强制接管',
1050
+ force_takeover_busy: '正在强制接管,请等待结果后再切换会话。',
1051
+ force_takeover_hint: '可先 /watch 再 /queue <消息> 排队给原 CLI;如需停止本机 CLI、让桥接管,请发送 /takeover --force <消息> 并确认。',
1052
+ force_takeover_expired: '确认已过期或 thread 已切换,请重新发送 /takeover --force <消息>。',
1053
+ force_takeover_cancelled: '已取消强制接管,没有停止进程或发送新任务。',
1054
+ force_takeover_stopping: '正在停止已确认的 CLI PID {pid},并检查写入锁是否释放…',
1055
+ force_takeover_failed: '强制接管未完成,没有发送新任务。如果已开始停止,CLI 可能已经退出。请先 /status 检查再重试。原因:{error}',
1056
+ force_takeover_acquired: '桥已取得 thread {threadId} 的写入权,准备发送指定消息;这不代表任务已完成。',
1031
1057
  usage_queue: '用法:/queue <消息>',
1032
1058
  unknown_cached_thread: '找不到这个缓存线程,请先运行 /threads。',
1033
1059
  cached_thread_unavailable: '这个缓存线程已经不可用,请重新运行 /threads。',
@@ -1468,10 +1494,12 @@ export function getTelegramCommands(locale) {
1468
1494
  return [
1469
1495
  { command: 'help', description: t(locale, 'cmd_desc_help') },
1470
1496
  { command: 'setup', description: t(locale, 'cmd_desc_setup') },
1497
+ { command: 'backend', description: t(locale, 'cmd_desc_backend') },
1471
1498
  { command: 'status', description: t(locale, 'cmd_desc_status') },
1472
1499
  { command: 'update', description: t(locale, 'cmd_desc_update') },
1473
1500
  { command: 'threads', description: t(locale, 'cmd_desc_threads') },
1474
1501
  { command: 'auth', description: t(locale, 'cmd_desc_auth') },
1502
+ { command: 'boost', description: locale === 'zh' ? '切换 Boost 深度推理模式' : 'Toggle Boost reasoning mode' },
1475
1503
  { command: 'fast', description: t(locale, 'cmd_desc_fast') },
1476
1504
  { command: 'active', description: t(locale, 'cmd_desc_active') },
1477
1505
  { command: 'rich', description: t(locale, 'cmd_desc_rich') },
@@ -1479,6 +1507,8 @@ export function getTelegramCommands(locale) {
1479
1507
  { command: 'account', description: t(locale, 'cmd_desc_account') },
1480
1508
  { command: 'quota', description: t(locale, 'cmd_desc_quota') },
1481
1509
  { command: 'login_device', description: t(locale, 'cmd_desc_login_device') },
1510
+ { command: 'login_cancel', description: t(locale, 'cmd_desc_login_cancel') },
1511
+ { command: 'cli', description: t(locale, 'cmd_desc_cli') },
1482
1512
  { command: 'open', description: t(locale, 'cmd_desc_open') },
1483
1513
  { command: 'goal', description: t(locale, 'cmd_desc_goal') },
1484
1514
  { command: 'history', description: t(locale, 'cmd_desc_history') },
@@ -1552,3 +1582,48 @@ export function getOpencodeTelegramCommands(locale) {
1552
1582
  .map(([command, description]) => ({ command, description })),
1553
1583
  ];
1554
1584
  }
1585
+ export function getAntigravityTelegramCommands(locale) {
1586
+ return locale === 'zh'
1587
+ ? [
1588
+ { command: 'help', description: '查看帮助' },
1589
+ { command: 'setup', description: '控制面板与设置' },
1590
+ { command: 'backend', description: '切换后端运行环境与账号' },
1591
+ { command: 'threads', description: '查看会话列表' },
1592
+ { command: 'open', description: '打开指定会话' },
1593
+ { command: 'watch', description: '只读观察会话' },
1594
+ { command: 'unwatch', description: '停止观察' },
1595
+ { command: 'new', description: '新建会话' },
1596
+ { command: 'boost', description: '切换 Boost 深度推理模式' },
1597
+ { command: 'models', description: '切换模型' },
1598
+ { command: 'auth', description: '账号管理与切换' },
1599
+ { command: 'login', description: '网页授权登录新账号' },
1600
+ { command: 'active', description: '设置运行中消息模式 (插话/排队)' },
1601
+ { command: 'queue', description: '排队下一轮回复' },
1602
+ { command: 'steer', description: '插话并中断当前回复' },
1603
+ { command: 'effort', description: '设置思考深度' },
1604
+ { command: 'status', description: '运行状态' },
1605
+ { command: 'update', description: '自升级服务及后端 (Codex/AGY)' },
1606
+ { command: 'interrupt', description: '打断当前任务' },
1607
+ ]
1608
+ : [
1609
+ { command: 'help', description: 'Show commands' },
1610
+ { command: 'setup', description: 'Control panel & settings' },
1611
+ { command: 'backend', description: 'Switch backend and account' },
1612
+ { command: 'threads', description: 'List conversations' },
1613
+ { command: 'open', description: 'Open conversation' },
1614
+ { command: 'watch', description: 'Watch conversation' },
1615
+ { command: 'unwatch', description: 'Stop watching' },
1616
+ { command: 'new', description: 'New session' },
1617
+ { command: 'boost', description: 'Toggle Boost reasoning mode' },
1618
+ { command: 'models', description: 'Select model' },
1619
+ { command: 'auth', description: 'Manage accounts' },
1620
+ { command: 'login', description: 'Sign in new Google account' },
1621
+ { command: 'active', description: 'Active-turn message mode' },
1622
+ { command: 'queue', description: 'Queue next prompt' },
1623
+ { command: 'steer', description: 'Interrupt and steer turn' },
1624
+ { command: 'effort', description: 'Reasoning effort' },
1625
+ { command: 'status', description: 'Bridge status' },
1626
+ { command: 'update', description: 'Self-update service & backends' },
1627
+ { command: 'interrupt', description: 'Stop active turn' },
1628
+ ];
1629
+ }