@foxden-app/foxclaw 0.12.0 → 0.13.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.
- package/CHANGELOG.md +20 -0
- package/dist/antigravity/account_ui.d.ts +34 -0
- package/dist/antigravity/account_ui.js +354 -0
- package/dist/antigravity/adapter.js +1 -1
- package/dist/antigravity/auth.d.ts +4 -1
- package/dist/antigravity/auth.js +17 -5
- package/dist/antigravity/controller.d.ts +3 -108
- package/dist/antigravity/controller.js +2 -2129
- package/dist/antigravity/conversation_ui.d.ts +56 -0
- package/dist/antigravity/conversation_ui.js +509 -0
- package/dist/antigravity/ui_callbacks.d.ts +6 -0
- package/dist/antigravity/ui_callbacks.js +6 -0
- package/dist/bridge/unified_bridge.d.ts +114 -0
- package/dist/bridge/unified_bridge.js +1343 -0
- package/dist/channels/bridge_messaging_router.d.ts +8 -4
- package/dist/channels/bridge_messaging_router.js +15 -4
- package/dist/channels/telegram/telegram_messaging_port.d.ts +16 -0
- package/dist/channels/telegram/telegram_messaging_port.js +22 -2
- package/dist/channels/text_actions.d.ts +14 -0
- package/dist/channels/text_actions.js +48 -0
- package/dist/channels/weixin/weixin_channel_adapter.d.ts +3 -4
- package/dist/channels/weixin/weixin_channel_adapter.js +2 -2
- package/dist/channels/weixin/weixin_messaging_port.d.ts +16 -1
- package/dist/channels/weixin/weixin_messaging_port.js +34 -7
- package/dist/codex_app/activity.d.ts +62 -0
- package/dist/codex_app/activity.js +418 -0
- package/dist/codex_app/adapter.d.ts +2 -2
- package/dist/codex_app/adapter.js +55 -25
- package/dist/codex_app/backend_ui.d.ts +4 -0
- package/dist/codex_app/backend_ui.js +48 -0
- package/dist/codex_app/client.js +0 -12
- package/dist/controller/access.d.ts +1 -11
- package/dist/controller/access.js +1 -33
- package/dist/controller/activity.d.ts +1 -62
- package/dist/controller/activity.js +2 -418
- package/dist/controller/auth_errors.d.ts +11 -0
- package/dist/controller/auth_errors.js +124 -0
- package/dist/controller/auth_files.d.ts +13 -0
- package/dist/controller/auth_files.js +156 -0
- package/dist/controller/auth_presentation.d.ts +77 -0
- package/dist/controller/auth_presentation.js +697 -0
- package/dist/controller/auth_quota_service.d.ts +27 -0
- package/dist/controller/auth_quota_service.js +151 -0
- package/dist/controller/controller.d.ts +30 -56
- package/dist/controller/controller.js +181 -3497
- package/dist/controller/interactions.d.ts +74 -0
- package/dist/controller/interactions.js +560 -0
- package/dist/controller/local_usage_service.d.ts +19 -0
- package/dist/controller/local_usage_service.js +62 -0
- package/dist/controller/native_panel_service.d.ts +55 -0
- package/dist/controller/native_panel_service.js +214 -0
- package/dist/controller/native_panels.d.ts +61 -0
- package/dist/controller/native_panels.js +784 -0
- package/dist/controller/shared_helpers.d.ts +53 -0
- package/dist/controller/shared_helpers.js +399 -0
- package/dist/controller/state_types.d.ts +390 -0
- package/dist/controller/state_types.js +82 -0
- package/dist/controller/turn_rendering.d.ts +22 -0
- package/dist/controller/turn_rendering.js +288 -0
- package/dist/core/access.d.ts +11 -0
- package/dist/core/access.js +33 -0
- package/dist/core/attachment_files.d.ts +13 -0
- package/dist/core/attachment_files.js +180 -0
- package/dist/core/attachment_types.d.ts +22 -0
- package/dist/core/attachment_types.js +1 -0
- package/dist/core/attachments.d.ts +5 -3
- package/dist/core/attachments.js +2 -2
- package/dist/core/backend_registry.d.ts +22 -0
- package/dist/core/backend_registry.js +50 -0
- package/dist/core/backend_ui.d.ts +28 -0
- package/dist/core/backend_ui.js +1 -0
- package/dist/core/channel_events.d.ts +48 -0
- package/dist/core/channel_events.js +1 -0
- package/dist/core/channel_port.d.ts +52 -8
- package/dist/core/engine_spi.d.ts +19 -4
- package/dist/core/listener_scope.d.ts +11 -0
- package/dist/core/listener_scope.js +10 -0
- package/dist/core/message_text.d.ts +2 -0
- package/dist/core/message_text.js +28 -0
- package/dist/core/network_errors.d.ts +1 -0
- package/dist/core/network_errors.js +23 -0
- package/dist/core/orchestrator.d.ts +53 -24
- package/dist/core/orchestrator.js +855 -507
- package/dist/core/queued_engine_input.d.ts +12 -0
- package/dist/core/queued_engine_input.js +16 -0
- package/dist/core/runtime_supervisor.d.ts +19 -0
- package/dist/core/runtime_supervisor.js +45 -0
- package/dist/core/scope_operations.d.ts +7 -0
- package/dist/core/scope_operations.js +19 -0
- package/dist/core/stream_preview.js +21 -9
- package/dist/dsh/adapter.js +18 -5
- package/dist/dsh/backend.d.ts +7 -0
- package/dist/dsh/backend.js +21 -0
- package/dist/dsh/runtime.d.ts +0 -1
- package/dist/dsh/runtime.js +4 -13
- package/dist/dsh/ui.d.ts +8 -8
- package/dist/dsh/ui.js +1 -1
- package/dist/main.js +55 -43
- package/dist/opencode/adapter.js +28 -9
- package/dist/opencode/controller.js +1 -1
- package/dist/store/channel_inbox.d.ts +14 -0
- package/dist/store/channel_inbox.js +21 -0
- package/dist/store/database.d.ts +7 -3
- package/dist/store/database.js +7 -2
- package/dist/store/task_journal.d.ts +32 -0
- package/dist/store/task_journal.js +70 -0
- package/dist/telegram/api.d.ts +2 -2
- package/dist/telegram/api.js +10 -28
- package/dist/telegram/gateway.d.ts +13 -27
- package/dist/telegram/gateway.js +86 -43
- package/dist/telegram/media.d.ts +2 -34
- package/dist/telegram/media.js +1 -180
- package/dist/telegram/task_preview.d.ts +13 -0
- package/dist/telegram/task_preview.js +92 -0
- package/dist/telegram/task_result.d.ts +2 -0
- package/dist/telegram/task_result.js +12 -0
- package/dist/telegram/text.d.ts +1 -1
- package/dist/telegram/text.js +2 -27
- package/dist/types.d.ts +1 -1
- package/docs/release.md +13 -11
- package/docs/zh/architecture-review.md +106 -0
- package/docs/zh/release.md +13 -11
- package/docs/zh/telegram-experience.md +40 -0
- package/package.json +5 -4
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AppLocale, ActiveTurnMessageMode, CodexThreadGoal, CollaborationModeValue, AppTurnSnapshot, ReviewTarget } from '../types.js';
|
|
2
|
+
import { ApprovalAction, McpElicitationAction, AuthRetryContext } from './state_types.js';
|
|
3
|
+
export declare function firstLine(value: string): string;
|
|
4
|
+
export declare function parseReviewTarget(args: string[]): ReviewTarget | null;
|
|
5
|
+
export declare function summarizeTurnItems(items: AppTurnSnapshot['items']): string;
|
|
6
|
+
export declare function turnHasRelayableOutcome(turn: AppTurnSnapshot): boolean;
|
|
7
|
+
export declare function mapGoalNotification(raw: any): CodexThreadGoal | null;
|
|
8
|
+
export declare function formatWarningNotification(locale: AppLocale, method: string, params: any): string;
|
|
9
|
+
export declare function isCodexTransportFallbackWarning(method: string, params: any): boolean;
|
|
10
|
+
export declare function normalizeThreadStatusLabel(raw: any): string;
|
|
11
|
+
export declare function formatThreadTokenUsage(raw: any): {
|
|
12
|
+
percent: number;
|
|
13
|
+
total: number;
|
|
14
|
+
limit: number;
|
|
15
|
+
} | null;
|
|
16
|
+
export declare function numberOrNull(value: unknown): number | null;
|
|
17
|
+
export declare function parsePositiveInt(value: string | undefined, fallback: number, min: number, max: number): number | null;
|
|
18
|
+
export declare function formatConfigValue(value: unknown): string;
|
|
19
|
+
export declare function formatRawLabel(value: unknown): string;
|
|
20
|
+
export declare function toErrorMeta(error: unknown): Record<string, unknown>;
|
|
21
|
+
export declare function formatUserError(error: unknown): string;
|
|
22
|
+
export declare function normalizeRequestedCollaborationMode(value: string): CollaborationModeValue | null;
|
|
23
|
+
export declare function normalizeRequestedActiveTurnMessageMode(value: string): ActiveTurnMessageMode | null;
|
|
24
|
+
export declare function normalizeApprovalTextAction(value: string): ApprovalAction | null;
|
|
25
|
+
export declare function normalizePlanImplementationTextAction(value: string): 'run' | 'fresh' | 'stay' | null;
|
|
26
|
+
export declare function normalizeMcpElicitationTextAction(value: string): McpElicitationAction | null;
|
|
27
|
+
export declare function resolveCollaborationMode(mode: CollaborationModeValue | null | undefined): CollaborationModeValue;
|
|
28
|
+
export declare function attachedThreadKey(scopeId: string, threadId: string): string;
|
|
29
|
+
export declare function activeTurnKey(scopeId: string, turnId: string): string;
|
|
30
|
+
export declare function parseActiveTurnKey(key: string): {
|
|
31
|
+
scopeId: string;
|
|
32
|
+
turnId: string;
|
|
33
|
+
} | null;
|
|
34
|
+
export declare function normalizeHelpUsageKey(name: string): string | null;
|
|
35
|
+
export declare function formatWatchCallbackText(locale: AppLocale, mode: 'already' | 'active' | 'idle', threadId: string): string;
|
|
36
|
+
export declare function cloneAuthRetryContext(context: AuthRetryContext): AuthRetryContext;
|
|
37
|
+
export declare function clipButtonText(text: string): string;
|
|
38
|
+
export declare function stringOrNull(value: unknown): string | null;
|
|
39
|
+
export declare function formatCompactNumber(value: number): string;
|
|
40
|
+
export declare function formatTokenCount(value: number): string;
|
|
41
|
+
export declare function formatCodexTokenCountWithMetric(value: number): string;
|
|
42
|
+
export declare function formatLocalTimestamp(seconds: number): string;
|
|
43
|
+
export declare function formatShortStatusError(error: unknown): string;
|
|
44
|
+
export declare function isThreadNotFoundError(error: unknown): boolean;
|
|
45
|
+
export declare function isThreadActiveWriterError(error: unknown): boolean;
|
|
46
|
+
export declare function isNoActiveTurnToSteerError(error: unknown): boolean;
|
|
47
|
+
export declare function isThreadQueueUnsupportedError(error: unknown): boolean;
|
|
48
|
+
export declare function isThreadNewCwdCreateConfirmation(text: string): boolean;
|
|
49
|
+
export declare function isThreadNewCwdCancelConfirmation(text: string): boolean;
|
|
50
|
+
export declare function isTelegramMessageGone(error: unknown): boolean;
|
|
51
|
+
export declare function isTelegramMessageTooLong(error: unknown): boolean;
|
|
52
|
+
export declare function isFileMissingError(error: unknown): boolean;
|
|
53
|
+
export declare function isReadableSessionPath(sessionPath: string): Promise<boolean>;
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { t } from '../i18n.js';
|
|
2
|
+
import { formatMetricTokenCount } from '../store/token_usage.js';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import { truncateInline } from './turn_rendering.js';
|
|
5
|
+
import { DEFAULT_COLLABORATION_MODE, PINNED_HELP_COMMANDS, DYNAMIC_HELP_COMMANDS, } from './state_types.js';
|
|
6
|
+
import { formatKnownCodexAccessError, cleanUserFacingError } from './auth_errors.js';
|
|
7
|
+
export function firstLine(value) {
|
|
8
|
+
return value.split(/\r?\n/, 1)[0]?.trim() || value.trim();
|
|
9
|
+
}
|
|
10
|
+
export function parseReviewTarget(args) {
|
|
11
|
+
if (args.length === 0) {
|
|
12
|
+
return { type: 'uncommittedChanges' };
|
|
13
|
+
}
|
|
14
|
+
const [kind, ...rest] = args;
|
|
15
|
+
if (kind === 'base') {
|
|
16
|
+
const branch = rest.join(' ').trim();
|
|
17
|
+
return branch ? { type: 'baseBranch', branch } : null;
|
|
18
|
+
}
|
|
19
|
+
if (kind === 'commit') {
|
|
20
|
+
const sha = rest[0]?.trim();
|
|
21
|
+
return sha ? { type: 'commit', sha, title: rest.slice(1).join(' ').trim() || null } : null;
|
|
22
|
+
}
|
|
23
|
+
if (kind === 'custom') {
|
|
24
|
+
const instructions = rest.join(' ').trim();
|
|
25
|
+
return instructions ? { type: 'custom', instructions } : null;
|
|
26
|
+
}
|
|
27
|
+
return { type: 'custom', instructions: args.join(' ').trim() };
|
|
28
|
+
}
|
|
29
|
+
export function summarizeTurnItems(items) {
|
|
30
|
+
const counts = new Map();
|
|
31
|
+
for (const item of items) {
|
|
32
|
+
const type = item.type || 'item';
|
|
33
|
+
counts.set(type, (counts.get(type) ?? 0) + 1);
|
|
34
|
+
}
|
|
35
|
+
return [...counts.entries()]
|
|
36
|
+
.slice(0, 8)
|
|
37
|
+
.map(([type, count]) => `${type}=${count}`)
|
|
38
|
+
.join(', ');
|
|
39
|
+
}
|
|
40
|
+
export function turnHasRelayableOutcome(turn) {
|
|
41
|
+
if (turn.error?.trim()) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return turn.items.some((item) => {
|
|
45
|
+
const type = item.type.toLowerCase();
|
|
46
|
+
if (!item.text?.trim()) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (type === 'plan') {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (type !== 'agentmessage' && type !== 'assistantmessage') {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const phase = item.phase?.toLowerCase() ?? null;
|
|
56
|
+
return phase === null || phase.startsWith('final');
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
export function mapGoalNotification(raw) {
|
|
60
|
+
if (!raw || typeof raw !== 'object') {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const status = raw.status === 'paused' || raw.status === 'budgetLimited' || raw.status === 'complete'
|
|
64
|
+
? raw.status
|
|
65
|
+
: 'active';
|
|
66
|
+
return {
|
|
67
|
+
threadId: String(raw.threadId ?? ''),
|
|
68
|
+
objective: String(raw.objective ?? ''),
|
|
69
|
+
status,
|
|
70
|
+
tokenBudget: numberOrNull(raw.tokenBudget),
|
|
71
|
+
tokensUsed: numberOrNull(raw.tokensUsed) ?? 0,
|
|
72
|
+
timeUsedSeconds: numberOrNull(raw.timeUsedSeconds) ?? 0,
|
|
73
|
+
createdAt: numberOrNull(raw.createdAt) ?? 0,
|
|
74
|
+
updatedAt: numberOrNull(raw.updatedAt) ?? 0,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function formatWarningNotification(locale, method, params) {
|
|
78
|
+
if (method === 'configWarning') {
|
|
79
|
+
return [
|
|
80
|
+
t(locale, 'warning_config_title'),
|
|
81
|
+
String(params?.summary ?? t(locale, 'unknown')),
|
|
82
|
+
params?.path ? `path: ${String(params.path)}` : null,
|
|
83
|
+
params?.details ? truncateInline(String(params.details), 600) : null,
|
|
84
|
+
].filter(Boolean).join('\n');
|
|
85
|
+
}
|
|
86
|
+
if (method === 'deprecationNotice') {
|
|
87
|
+
return [
|
|
88
|
+
t(locale, 'warning_deprecation_title'),
|
|
89
|
+
String(params?.summary ?? t(locale, 'unknown')),
|
|
90
|
+
params?.details ? truncateInline(String(params.details), 600) : null,
|
|
91
|
+
].filter(Boolean).join('\n');
|
|
92
|
+
}
|
|
93
|
+
if (method === 'guardianWarning') {
|
|
94
|
+
return `${t(locale, 'warning_guardian_title')}\n${String(params?.message ?? t(locale, 'unknown'))}`;
|
|
95
|
+
}
|
|
96
|
+
return `${t(locale, 'warning_title')}\n${String(params?.message ?? t(locale, 'unknown'))}`;
|
|
97
|
+
}
|
|
98
|
+
export function isCodexTransportFallbackWarning(method, params) {
|
|
99
|
+
if (method !== 'warning') {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return /falling back from websockets? to https transport/i.test(String(params?.message ?? ''));
|
|
103
|
+
}
|
|
104
|
+
export function normalizeThreadStatusLabel(raw) {
|
|
105
|
+
if (typeof raw === 'string') {
|
|
106
|
+
return raw;
|
|
107
|
+
}
|
|
108
|
+
if (typeof raw?.type === 'string') {
|
|
109
|
+
return raw.type;
|
|
110
|
+
}
|
|
111
|
+
return 'unknown';
|
|
112
|
+
}
|
|
113
|
+
export function formatThreadTokenUsage(raw) {
|
|
114
|
+
const total = numberOrNull(raw?.last?.totalTokens ?? raw?.last?.total_tokens);
|
|
115
|
+
const limit = numberOrNull(raw?.modelContextWindow ?? raw?.model_context_window);
|
|
116
|
+
if (total === null || limit === null || total <= 0 || limit <= 0) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const rawPercent = Math.round((total / limit) * 100);
|
|
120
|
+
const percent = Math.min(100, rawPercent);
|
|
121
|
+
return rawPercent >= 85 ? { percent, total, limit } : null;
|
|
122
|
+
}
|
|
123
|
+
export function numberOrNull(value) {
|
|
124
|
+
if (typeof value === 'number') {
|
|
125
|
+
return Number.isFinite(value) ? value : null;
|
|
126
|
+
}
|
|
127
|
+
if (typeof value === 'string' && value.trim()) {
|
|
128
|
+
const parsed = Number(value);
|
|
129
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
export function parsePositiveInt(value, fallback, min, max) {
|
|
134
|
+
if (value === undefined || value.trim() === '') {
|
|
135
|
+
return fallback;
|
|
136
|
+
}
|
|
137
|
+
const parsed = Number.parseInt(value, 10);
|
|
138
|
+
if (!Number.isFinite(parsed) || parsed < min || parsed > max) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
return parsed;
|
|
142
|
+
}
|
|
143
|
+
export function formatConfigValue(value) {
|
|
144
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
145
|
+
return String(value);
|
|
146
|
+
}
|
|
147
|
+
if (Array.isArray(value)) {
|
|
148
|
+
return value.map(formatConfigValue).join(', ');
|
|
149
|
+
}
|
|
150
|
+
if (value && typeof value === 'object') {
|
|
151
|
+
const keys = Object.keys(value);
|
|
152
|
+
if (keys.length === 1) {
|
|
153
|
+
return keys[0];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return truncateInline(JSON.stringify(value), 160);
|
|
157
|
+
}
|
|
158
|
+
export function formatRawLabel(value) {
|
|
159
|
+
if (value === null || value === undefined) {
|
|
160
|
+
return 'unknown';
|
|
161
|
+
}
|
|
162
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
163
|
+
return String(value);
|
|
164
|
+
}
|
|
165
|
+
if (typeof value === 'object') {
|
|
166
|
+
const keys = Object.keys(value);
|
|
167
|
+
if (keys.length === 1) {
|
|
168
|
+
return keys[0];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return truncateInline(JSON.stringify(value), 160);
|
|
172
|
+
}
|
|
173
|
+
export function toErrorMeta(error) {
|
|
174
|
+
if (error instanceof Error) {
|
|
175
|
+
return { message: error.message, stack: error.stack };
|
|
176
|
+
}
|
|
177
|
+
return { error: String(error) };
|
|
178
|
+
}
|
|
179
|
+
export function formatUserError(error) {
|
|
180
|
+
if (error instanceof Error) {
|
|
181
|
+
return error.message;
|
|
182
|
+
}
|
|
183
|
+
return String(error);
|
|
184
|
+
}
|
|
185
|
+
export function normalizeRequestedCollaborationMode(value) {
|
|
186
|
+
const normalized = value.trim().toLowerCase();
|
|
187
|
+
if (normalized === 'plan' || normalized === '计划' || normalized === '规划') {
|
|
188
|
+
return 'plan';
|
|
189
|
+
}
|
|
190
|
+
if (normalized === 'default' || normalized === 'agent' || normalized === '默认') {
|
|
191
|
+
return 'default';
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
export function normalizeRequestedActiveTurnMessageMode(value) {
|
|
196
|
+
const normalized = value.trim().toLowerCase();
|
|
197
|
+
if (normalized === 'steer' || normalized === 'guide' || normalized === '引导') {
|
|
198
|
+
return 'steer';
|
|
199
|
+
}
|
|
200
|
+
if (normalized === 'queue' || normalized === '排队') {
|
|
201
|
+
return 'queue';
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
export function normalizeApprovalTextAction(value) {
|
|
206
|
+
const normalized = value.trim().toLowerCase();
|
|
207
|
+
if (normalized === 'allow' || normalized === 'accept' || normalized === 'yes' || normalized === '同意' || normalized === '允许') {
|
|
208
|
+
return 'accept';
|
|
209
|
+
}
|
|
210
|
+
if (normalized === 'session' || normalized === 'allow_session' || normalized === '本会话' || normalized === '本会话允许') {
|
|
211
|
+
return 'session';
|
|
212
|
+
}
|
|
213
|
+
if (normalized === 'deny' || normalized === 'decline' || normalized === 'no' || normalized === '拒绝') {
|
|
214
|
+
return 'deny';
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
export function normalizePlanImplementationTextAction(value) {
|
|
219
|
+
const normalized = value.trim().toLowerCase();
|
|
220
|
+
if (normalized === 'run' || normalized === 'execute' || normalized === '执行') {
|
|
221
|
+
return 'run';
|
|
222
|
+
}
|
|
223
|
+
if (normalized === 'fresh' || normalized === 'clear' || normalized === '新上下文') {
|
|
224
|
+
return 'fresh';
|
|
225
|
+
}
|
|
226
|
+
if (normalized === 'stay' || normalized === 'plan' || normalized === '继续规划') {
|
|
227
|
+
return 'stay';
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
export function normalizeMcpElicitationTextAction(value) {
|
|
232
|
+
const normalized = value.trim().toLowerCase();
|
|
233
|
+
if (normalized === 'accept' || normalized === 'allow' || normalized === '同意' || normalized === '接受') {
|
|
234
|
+
return 'accept';
|
|
235
|
+
}
|
|
236
|
+
if (normalized === 'decline' || normalized === 'deny' || normalized === '拒绝') {
|
|
237
|
+
return 'decline';
|
|
238
|
+
}
|
|
239
|
+
if (normalized === 'cancel' || normalized === '取消') {
|
|
240
|
+
return 'cancel';
|
|
241
|
+
}
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
export function resolveCollaborationMode(mode) {
|
|
245
|
+
return mode ?? DEFAULT_COLLABORATION_MODE;
|
|
246
|
+
}
|
|
247
|
+
export function attachedThreadKey(scopeId, threadId) {
|
|
248
|
+
return `${scopeId}:${threadId}`;
|
|
249
|
+
}
|
|
250
|
+
export function activeTurnKey(scopeId, turnId) {
|
|
251
|
+
return `${encodeURIComponent(scopeId)}:${encodeURIComponent(turnId)}`;
|
|
252
|
+
}
|
|
253
|
+
export function parseActiveTurnKey(key) {
|
|
254
|
+
const split = key.indexOf(':');
|
|
255
|
+
if (split === -1) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
scopeId: decodeURIComponent(key.slice(0, split)),
|
|
260
|
+
turnId: decodeURIComponent(key.slice(split + 1)),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
export function normalizeHelpUsageKey(name) {
|
|
264
|
+
const normalized = name.toLowerCase();
|
|
265
|
+
switch (normalized) {
|
|
266
|
+
case 'start':
|
|
267
|
+
return 'help';
|
|
268
|
+
case 'followup':
|
|
269
|
+
return 'active';
|
|
270
|
+
case 'login':
|
|
271
|
+
return 'login_device';
|
|
272
|
+
case 'codex_restart':
|
|
273
|
+
return 'auth_reload';
|
|
274
|
+
case 'file':
|
|
275
|
+
return 'files';
|
|
276
|
+
case 'rollback':
|
|
277
|
+
return 'undo';
|
|
278
|
+
case 'access':
|
|
279
|
+
return 'permissions';
|
|
280
|
+
case 'focus':
|
|
281
|
+
return 'reveal';
|
|
282
|
+
case 'model':
|
|
283
|
+
return 'models';
|
|
284
|
+
default:
|
|
285
|
+
if (PINNED_HELP_COMMANDS.some(entry => entry.key === normalized)) {
|
|
286
|
+
return normalized;
|
|
287
|
+
}
|
|
288
|
+
if (DYNAMIC_HELP_COMMANDS.some(entry => entry.key === normalized)) {
|
|
289
|
+
return normalized;
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
export function formatWatchCallbackText(locale, mode, threadId) {
|
|
295
|
+
if (mode === 'already') {
|
|
296
|
+
return t(locale, 'watch_already_enabled', { threadId });
|
|
297
|
+
}
|
|
298
|
+
if (mode === 'active') {
|
|
299
|
+
return t(locale, 'watch_started_active', { threadId });
|
|
300
|
+
}
|
|
301
|
+
return t(locale, 'watch_started_idle', { threadId });
|
|
302
|
+
}
|
|
303
|
+
export function cloneAuthRetryContext(context) {
|
|
304
|
+
return {
|
|
305
|
+
input: context.input,
|
|
306
|
+
threadId: context.threadId,
|
|
307
|
+
cwd: context.cwd,
|
|
308
|
+
chatId: context.chatId,
|
|
309
|
+
chatType: context.chatType,
|
|
310
|
+
topicId: context.topicId,
|
|
311
|
+
collaborationMode: context.collaborationMode,
|
|
312
|
+
failedAuthTargets: new Set(context.failedAuthTargets),
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
export function clipButtonText(text) {
|
|
316
|
+
const trimmed = text.replace(/\s+/g, ' ').trim();
|
|
317
|
+
return trimmed.length > 48 ? `${trimmed.slice(0, 47)}...` : trimmed;
|
|
318
|
+
}
|
|
319
|
+
export function stringOrNull(value) {
|
|
320
|
+
if (typeof value !== 'string') {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
const trimmed = value.trim();
|
|
324
|
+
return trimmed ? trimmed : null;
|
|
325
|
+
}
|
|
326
|
+
export function formatCompactNumber(value) {
|
|
327
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(1).replace(/\.0$/, '');
|
|
328
|
+
}
|
|
329
|
+
export function formatTokenCount(value) {
|
|
330
|
+
if (!Number.isFinite(value)) {
|
|
331
|
+
return '?';
|
|
332
|
+
}
|
|
333
|
+
return Math.round(value).toLocaleString('en-US');
|
|
334
|
+
}
|
|
335
|
+
export function formatCodexTokenCountWithMetric(value) {
|
|
336
|
+
if (value >= 1_000_000) {
|
|
337
|
+
return `${formatMetricTokenCount(value)} (${formatTokenCount(value)})`;
|
|
338
|
+
}
|
|
339
|
+
return formatTokenCount(value);
|
|
340
|
+
}
|
|
341
|
+
export function formatLocalTimestamp(seconds) {
|
|
342
|
+
const date = new Date(seconds * 1000);
|
|
343
|
+
const pad = (value) => String(value).padStart(2, '0');
|
|
344
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
345
|
+
}
|
|
346
|
+
export function formatShortStatusError(error) {
|
|
347
|
+
const raw = formatUserError(error);
|
|
348
|
+
const known = formatKnownCodexAccessError(raw);
|
|
349
|
+
if (known) {
|
|
350
|
+
return known.length > 120 ? `${known.slice(0, 117)}...` : known;
|
|
351
|
+
}
|
|
352
|
+
const message = cleanUserFacingError(raw);
|
|
353
|
+
return message.length > 120 ? `${message.slice(0, 117)}...` : message;
|
|
354
|
+
}
|
|
355
|
+
export function isThreadNotFoundError(error) {
|
|
356
|
+
return error instanceof Error && /(thread not found|no rollout found for thread id)/i.test(error.message);
|
|
357
|
+
}
|
|
358
|
+
export function isThreadActiveWriterError(error) {
|
|
359
|
+
return error instanceof Error && /thread\s+\S+\s+already has an active writer/i.test(error.message);
|
|
360
|
+
}
|
|
361
|
+
export function isNoActiveTurnToSteerError(error) {
|
|
362
|
+
return error instanceof Error && /no active turn to steer/i.test(error.message);
|
|
363
|
+
}
|
|
364
|
+
export function isThreadQueueUnsupportedError(error) {
|
|
365
|
+
const message = formatUserError(error).toLowerCase();
|
|
366
|
+
return message.includes('method not found')
|
|
367
|
+
|| message.includes('unknown method')
|
|
368
|
+
|| message.includes('thread/queue/add') && message.includes('not supported');
|
|
369
|
+
}
|
|
370
|
+
export function isThreadNewCwdCreateConfirmation(text) {
|
|
371
|
+
return /^(y|yes|ok|okay|confirm|create|mkdir|确定|确认|创建|新建|好)$/i.test(text.trim());
|
|
372
|
+
}
|
|
373
|
+
export function isThreadNewCwdCancelConfirmation(text) {
|
|
374
|
+
return /^(n|no|cancel|stop|取消|不要|不用|算了)$/i.test(text.trim());
|
|
375
|
+
}
|
|
376
|
+
export function isTelegramMessageGone(error) {
|
|
377
|
+
const message = formatUserError(error).toLowerCase();
|
|
378
|
+
return message.includes('message to delete not found')
|
|
379
|
+
|| message.includes('message to edit not found')
|
|
380
|
+
|| message.includes('message not found');
|
|
381
|
+
}
|
|
382
|
+
export function isTelegramMessageTooLong(error) {
|
|
383
|
+
const message = formatUserError(error).toLowerCase();
|
|
384
|
+
return message.includes('message_too_long')
|
|
385
|
+
|| message.includes('message is too long')
|
|
386
|
+
|| message.includes('message too long');
|
|
387
|
+
}
|
|
388
|
+
export function isFileMissingError(error) {
|
|
389
|
+
return error instanceof Error && /enoent|no such file or directory/i.test(error.message);
|
|
390
|
+
}
|
|
391
|
+
export async function isReadableSessionPath(sessionPath) {
|
|
392
|
+
try {
|
|
393
|
+
const stats = await fs.stat(sessionPath);
|
|
394
|
+
return stats.isFile();
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
}
|