@foxden-app/foxclaw 0.11.1 → 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.
Files changed (138) hide show
  1. package/.env.example +18 -1
  2. package/CHANGELOG.md +36 -0
  3. package/README.md +4 -3
  4. package/README_EN.md +4 -3
  5. package/dist/antigravity/account_ui.d.ts +34 -0
  6. package/dist/antigravity/account_ui.js +354 -0
  7. package/dist/antigravity/adapter.js +1 -1
  8. package/dist/antigravity/auth.d.ts +4 -1
  9. package/dist/antigravity/auth.js +17 -5
  10. package/dist/antigravity/controller.d.ts +3 -106
  11. package/dist/antigravity/controller.js +2 -2083
  12. package/dist/antigravity/conversation_ui.d.ts +56 -0
  13. package/dist/antigravity/conversation_ui.js +509 -0
  14. package/dist/antigravity/ui_callbacks.d.ts +6 -0
  15. package/dist/antigravity/ui_callbacks.js +6 -0
  16. package/dist/bridge/unified_bridge.d.ts +114 -0
  17. package/dist/bridge/unified_bridge.js +1343 -0
  18. package/dist/channels/bridge_messaging_router.d.ts +8 -4
  19. package/dist/channels/bridge_messaging_router.js +15 -4
  20. package/dist/channels/telegram/telegram_messaging_port.d.ts +16 -0
  21. package/dist/channels/telegram/telegram_messaging_port.js +22 -2
  22. package/dist/channels/text_actions.d.ts +14 -0
  23. package/dist/channels/text_actions.js +48 -0
  24. package/dist/channels/weixin/weixin_channel_adapter.d.ts +3 -4
  25. package/dist/channels/weixin/weixin_channel_adapter.js +2 -2
  26. package/dist/channels/weixin/weixin_messaging_port.d.ts +16 -1
  27. package/dist/channels/weixin/weixin_messaging_port.js +34 -7
  28. package/dist/codex_app/activity.d.ts +62 -0
  29. package/dist/codex_app/activity.js +418 -0
  30. package/dist/codex_app/adapter.d.ts +2 -2
  31. package/dist/codex_app/adapter.js +55 -25
  32. package/dist/codex_app/backend_ui.d.ts +4 -0
  33. package/dist/codex_app/backend_ui.js +48 -0
  34. package/dist/codex_app/client.js +0 -12
  35. package/dist/config.d.ts +4 -0
  36. package/dist/config.js +41 -5
  37. package/dist/controller/access.d.ts +1 -11
  38. package/dist/controller/access.js +1 -33
  39. package/dist/controller/activity.d.ts +1 -62
  40. package/dist/controller/activity.js +2 -418
  41. package/dist/controller/auth_errors.d.ts +11 -0
  42. package/dist/controller/auth_errors.js +124 -0
  43. package/dist/controller/auth_files.d.ts +13 -0
  44. package/dist/controller/auth_files.js +156 -0
  45. package/dist/controller/auth_presentation.d.ts +77 -0
  46. package/dist/controller/auth_presentation.js +697 -0
  47. package/dist/controller/auth_quota_service.d.ts +27 -0
  48. package/dist/controller/auth_quota_service.js +151 -0
  49. package/dist/controller/controller.d.ts +30 -56
  50. package/dist/controller/controller.js +181 -3497
  51. package/dist/controller/interactions.d.ts +74 -0
  52. package/dist/controller/interactions.js +560 -0
  53. package/dist/controller/local_usage_service.d.ts +19 -0
  54. package/dist/controller/local_usage_service.js +62 -0
  55. package/dist/controller/native_panel_service.d.ts +55 -0
  56. package/dist/controller/native_panel_service.js +214 -0
  57. package/dist/controller/native_panels.d.ts +61 -0
  58. package/dist/controller/native_panels.js +784 -0
  59. package/dist/controller/shared_helpers.d.ts +53 -0
  60. package/dist/controller/shared_helpers.js +399 -0
  61. package/dist/controller/state_types.d.ts +390 -0
  62. package/dist/controller/state_types.js +82 -0
  63. package/dist/controller/turn_rendering.d.ts +22 -0
  64. package/dist/controller/turn_rendering.js +288 -0
  65. package/dist/core/access.d.ts +11 -0
  66. package/dist/core/access.js +33 -0
  67. package/dist/core/attachment_files.d.ts +13 -0
  68. package/dist/core/attachment_files.js +180 -0
  69. package/dist/core/attachment_types.d.ts +22 -0
  70. package/dist/core/attachment_types.js +1 -0
  71. package/dist/core/attachments.d.ts +5 -3
  72. package/dist/core/attachments.js +2 -2
  73. package/dist/core/backend_registry.d.ts +22 -0
  74. package/dist/core/backend_registry.js +50 -0
  75. package/dist/core/backend_ui.d.ts +28 -0
  76. package/dist/core/backend_ui.js +1 -0
  77. package/dist/core/channel_events.d.ts +48 -0
  78. package/dist/core/channel_events.js +1 -0
  79. package/dist/core/channel_port.d.ts +52 -8
  80. package/dist/core/engine_spi.d.ts +20 -5
  81. package/dist/core/listener_scope.d.ts +11 -0
  82. package/dist/core/listener_scope.js +10 -0
  83. package/dist/core/message_text.d.ts +2 -0
  84. package/dist/core/message_text.js +28 -0
  85. package/dist/core/network_errors.d.ts +1 -0
  86. package/dist/core/network_errors.js +23 -0
  87. package/dist/core/orchestrator.d.ts +57 -22
  88. package/dist/core/orchestrator.js +877 -477
  89. package/dist/core/queued_engine_input.d.ts +12 -0
  90. package/dist/core/queued_engine_input.js +16 -0
  91. package/dist/core/runtime_supervisor.d.ts +19 -0
  92. package/dist/core/runtime_supervisor.js +45 -0
  93. package/dist/core/scope_operations.d.ts +7 -0
  94. package/dist/core/scope_operations.js +19 -0
  95. package/dist/core/stream_preview.js +21 -9
  96. package/dist/dsh/adapter.d.ts +48 -0
  97. package/dist/dsh/adapter.js +205 -0
  98. package/dist/dsh/backend.d.ts +7 -0
  99. package/dist/dsh/backend.js +21 -0
  100. package/dist/dsh/client.d.ts +46 -0
  101. package/dist/dsh/client.js +184 -0
  102. package/dist/dsh/permissions_plugin.d.ts +30 -0
  103. package/dist/dsh/permissions_plugin.js +21 -0
  104. package/dist/dsh/runtime.d.ts +20 -0
  105. package/dist/dsh/runtime.js +36 -0
  106. package/dist/dsh/ui.d.ts +38 -0
  107. package/dist/dsh/ui.js +342 -0
  108. package/dist/i18n.d.ts +4 -0
  109. package/dist/i18n.js +12 -0
  110. package/dist/main.js +144 -72
  111. package/dist/opencode/adapter.js +28 -9
  112. package/dist/opencode/controller.js +1 -1
  113. package/dist/store/channel_inbox.d.ts +14 -0
  114. package/dist/store/channel_inbox.js +21 -0
  115. package/dist/store/database.d.ts +10 -4
  116. package/dist/store/database.js +13 -3
  117. package/dist/store/task_journal.d.ts +32 -0
  118. package/dist/store/task_journal.js +70 -0
  119. package/dist/telegram/api.d.ts +2 -2
  120. package/dist/telegram/api.js +10 -28
  121. package/dist/telegram/gateway.d.ts +13 -27
  122. package/dist/telegram/gateway.js +86 -43
  123. package/dist/telegram/media.d.ts +2 -34
  124. package/dist/telegram/media.js +1 -180
  125. package/dist/telegram/task_preview.d.ts +13 -0
  126. package/dist/telegram/task_preview.js +92 -0
  127. package/dist/telegram/task_result.d.ts +2 -0
  128. package/dist/telegram/task_result.js +12 -0
  129. package/dist/telegram/text.d.ts +1 -1
  130. package/dist/telegram/text.js +2 -27
  131. package/dist/types.d.ts +2 -2
  132. package/docs/dsh.md +83 -0
  133. package/docs/release.md +13 -11
  134. package/docs/zh/architecture-review.md +106 -0
  135. package/docs/zh/dsh.md +83 -0
  136. package/docs/zh/release.md +13 -11
  137. package/docs/zh/telegram-experience.md +40 -0
  138. package/package.json +7 -5
package/dist/config.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { LogLevel } from './logger.js';
2
2
  import type { ApprovalPolicyValue, SandboxModeValue } from './types.js';
3
+ import type { DshClientOptions } from './dsh/client.js';
3
4
  export declare const APP_HOME: string;
4
5
  export declare const DEFAULT_STORE_PATH: string;
5
6
  export declare const DEFAULT_STATUS_PATH: string;
@@ -19,6 +20,8 @@ export declare function resolveEnvPath(): string;
19
20
  export declare function getLoadedEnvPath(): string | null;
20
21
  export declare function loadEnv(): void;
21
22
  export interface AppConfig {
23
+ dsh?: DshClientOptions;
24
+ dshBotToken?: string | null;
22
25
  codexBotTokens: string[];
23
26
  antigravityBotTokens: string[];
24
27
  tgBotToken: string;
@@ -112,6 +115,7 @@ export interface CodexApiProviderConfig {
112
115
  export declare function loadConfig(): AppConfig;
113
116
  export declare function buildCodexApiProviderOverrides(providers: readonly CodexApiProviderConfig[], defaultProviderId?: string | null): string[];
114
117
  export declare function validateCodexBotTokens(tokens: readonly string[]): void;
118
+ export declare function parseDshPatches(value?: string): string[];
115
119
  export declare function validateOpencodeBotToken(token: string | null, codexTokens: readonly string[]): void;
116
120
  export declare function validateAntigravityBotTokens(tokens: readonly string[], codexTokens: readonly string[], opencodeToken: string | null): void;
117
121
  export declare function validateAntigravityBotToken(token: string | null, codexTokens: readonly string[], opencodeToken: string | null): void;
package/dist/config.js CHANGED
@@ -50,8 +50,12 @@ export function loadConfig() {
50
50
  const codexBotTokens = parseCommaSeparatedIds(process.env.CODEX_BOT_TOKENS || process.env.CODEX_BOT_TOKEN);
51
51
  const antigravityBotTokens = parseCommaSeparatedIds(process.env.ANTIGRAVITY_BOT_TOKENS || process.env.ANTIGRAVITY_BOT_TOKEN);
52
52
  const opencodeBotToken = optional('OPENCODE_BOT_TOKEN');
53
- if (codexBotTokens.length === 0 && antigravityBotTokens.length === 0) {
54
- throw new Error('At least one Telegram bot token must be configured via CODEX_BOT_TOKENS or ANTIGRAVITY_BOT_TOKENS');
53
+ const dshBotToken = optional('DSH_BOT_TOKEN');
54
+ if (dshBotToken && [...codexBotTokens, ...antigravityBotTokens, opencodeBotToken].includes(dshBotToken)) {
55
+ throw new Error('DSH_BOT_TOKEN must use a different bot from Codex, Antigravity, and OpenCode');
56
+ }
57
+ if (codexBotTokens.length === 0 && antigravityBotTokens.length === 0 && !dshBotToken) {
58
+ throw new Error('At least one Telegram bot token must be configured via CODEX_BOT_TOKENS, ANTIGRAVITY_BOT_TOKENS, or DSH_BOT_TOKEN');
55
59
  }
56
60
  validateCodexBotTokens(codexBotTokens);
57
61
  validateAntigravityBotTokens(antigravityBotTokens, codexBotTokens, opencodeBotToken);
@@ -60,11 +64,23 @@ export function loadConfig() {
60
64
  const antigravityDefaultRuntimeBotToken = selectDefaultRuntimeBotToken(antigravityBotTokens, optional('ANTIGRAVITY_DEFAULT_RUNTIME_BOT_TOKEN')) ||
61
65
  antigravityBotTokens[0] ||
62
66
  null;
63
- const totalBots = codexBotTokens.length + antigravityBotTokens.length;
64
- const tgBotTokens = codexBotTokens.length > 0 ? codexBotTokens : antigravityBotTokens;
65
- const tgBotToken = (codexBotTokens[0] || antigravityBotTokens[0]);
67
+ const totalBots = codexBotTokens.length + antigravityBotTokens.length + (dshBotToken ? 1 : 0);
68
+ const tgBotTokens = codexBotTokens.length > 0 ? codexBotTokens : antigravityBotTokens.length > 0 ? antigravityBotTokens : dshBotToken ? [dshBotToken] : [];
69
+ const tgBotToken = (codexBotTokens[0] || antigravityBotTokens[0] || dshBotToken);
66
70
  const antigravityBotToken = antigravityBotTokens[0] || null;
67
71
  const config = {
72
+ dshBotToken,
73
+ ...(boolEnv('DSH_ENABLED', Boolean(dshBotToken || optional('DSH_SOURCE_DIR') || optional('DSH_CLI_BIN'))) ? {
74
+ dsh: {
75
+ cliBin: optional('DSH_CLI_BIN') || resolveCommand('dsh') || 'dsh',
76
+ sourceDir: optional('DSH_SOURCE_DIR'),
77
+ home: optional('DSH_HOME'),
78
+ profile: optional('DSH_PROFILE') || 'acp',
79
+ patches: parseDshPatches(process.env.DSH_PATCHES),
80
+ runtimeDir: path.join(APP_HOME, 'runtime', 'dsh'),
81
+ startupTimeoutMs: Number(optional('DSH_STARTUP_TIMEOUT_MS') || 60000),
82
+ },
83
+ } : {}),
68
84
  codexBotTokens,
69
85
  antigravityBotTokens,
70
86
  tgBotToken,
@@ -142,6 +158,12 @@ export function loadConfig() {
142
158
  voiceTextLimit: intEnv('VOICE_TEXT_LIMIT', 2800),
143
159
  voiceTtsTimeoutMs: intEnv('VOICE_TTS_TIMEOUT_MS', 300_000),
144
160
  };
161
+ if (dshBotToken && !config.dsh)
162
+ throw new Error('DSH_BOT_TOKEN requires DSH_ENABLED=true');
163
+ if (config.dsh && (!Number.isSafeInteger(config.dsh.startupTimeoutMs) || config.dsh.startupTimeoutMs <= 0))
164
+ throw new Error('DSH_STARTUP_TIMEOUT_MS must be a positive integer');
165
+ if (config.wxEnabled && codexBotTokens.length === 0 && antigravityBotTokens.length === 0)
166
+ throw new Error('WX_ENABLED currently requires a Codex or Antigravity runtime alongside DSH');
145
167
  ensureAppDirs(config);
146
168
  return config;
147
169
  }
@@ -176,6 +198,20 @@ export function validateCodexBotTokens(tokens) {
176
198
  seen.add(token);
177
199
  }
178
200
  }
201
+ export function parseDshPatches(value) {
202
+ if (!value?.trim())
203
+ return [];
204
+ let parsed;
205
+ try {
206
+ parsed = JSON.parse(value);
207
+ }
208
+ catch {
209
+ throw new Error('DSH_PATCHES must be a JSON array of patch paths');
210
+ }
211
+ if (!Array.isArray(parsed) || !parsed.every(item => typeof item === 'string' && item.trim().length > 0))
212
+ throw new Error('DSH_PATCHES must be a JSON array of patch paths');
213
+ return parsed.map(item => path.resolve(item));
214
+ }
179
215
  export function validateOpencodeBotToken(token, codexTokens) {
180
216
  if (!token)
181
217
  return;
@@ -1,11 +1 @@
1
- import type { AppConfig } from '../config.js';
2
- import type { AccessPresetValue, ApprovalPolicyValue, ChatSessionSettings, SandboxModeValue } from '../types.js';
3
- export interface ResolvedAccessMode {
4
- preset: AccessPresetValue;
5
- approvalPolicy: ApprovalPolicyValue;
6
- sandboxMode: SandboxModeValue;
7
- }
8
- export declare const ACCESS_PRESETS: AccessPresetValue[];
9
- export declare function normalizeAccessPreset(value: string | null | undefined): AccessPresetValue | null;
10
- export declare function resolveConfiguredAccessPreset(settings: Pick<ChatSessionSettings, 'accessPreset'> | null | undefined): AccessPresetValue;
11
- export declare function resolveAccessMode(config: Pick<AppConfig, 'defaultApprovalPolicy' | 'defaultSandboxMode'>, settings: Pick<ChatSessionSettings, 'accessPreset'> | null | undefined): ResolvedAccessMode;
1
+ export * from '../core/access.js';
@@ -1,33 +1 @@
1
- export const ACCESS_PRESETS = ['read-only', 'default', 'full-access'];
2
- export function normalizeAccessPreset(value) {
3
- if (value === 'read-only' || value === 'default' || value === 'full-access') {
4
- return value;
5
- }
6
- return null;
7
- }
8
- export function resolveConfiguredAccessPreset(settings) {
9
- return settings?.accessPreset ?? 'default';
10
- }
11
- export function resolveAccessMode(config, settings) {
12
- const preset = resolveConfiguredAccessPreset(settings);
13
- switch (preset) {
14
- case 'read-only':
15
- return {
16
- preset,
17
- approvalPolicy: 'on-request',
18
- sandboxMode: 'read-only',
19
- };
20
- case 'full-access':
21
- return {
22
- preset,
23
- approvalPolicy: 'never',
24
- sandboxMode: 'danger-full-access',
25
- };
26
- default:
27
- return {
28
- preset: 'default',
29
- approvalPolicy: config.defaultApprovalPolicy,
30
- sandboxMode: config.defaultSandboxMode,
31
- };
32
- }
33
- }
1
+ export * from '../core/access.js';
@@ -1,62 +1 @@
1
- import type { JsonRpcNotification } from '../codex_app/client.js';
2
- export interface RawExecCommandEvent {
3
- callId: string;
4
- turnId: string;
5
- command: string[];
6
- cwd: string | null;
7
- parsedCmd: any[];
8
- }
9
- export type TurnActivityState = 'thinking' | 'reading' | 'searching' | 'editing' | 'running_command' | 'approval_waiting' | 'completed' | 'interrupted';
10
- export type TurnOutputKind = 'commentary' | 'final_answer' | 'tool_summary' | 'error';
11
- export type TurnActivityEvent = {
12
- kind: 'user_message';
13
- turnId: string;
14
- text: string;
15
- } | {
16
- kind: 'agent_message_started';
17
- turnId: string;
18
- itemId: string;
19
- phase: string | null;
20
- outputKind: TurnOutputKind;
21
- isPlan?: boolean;
22
- } | {
23
- kind: 'agent_message_delta';
24
- turnId: string;
25
- itemId: string;
26
- delta: string;
27
- outputKind: TurnOutputKind;
28
- isPlan?: boolean;
29
- } | {
30
- kind: 'agent_message_completed';
31
- turnId: string;
32
- itemId: string;
33
- phase: string | null;
34
- text: string | null;
35
- outputKind: TurnOutputKind;
36
- isPlan?: boolean;
37
- } | {
38
- kind: 'reasoning_started';
39
- turnId: string;
40
- state: 'thinking';
41
- } | {
42
- kind: 'reasoning_completed';
43
- turnId: string;
44
- state: 'thinking';
45
- } | {
46
- kind: 'tool_started';
47
- turnId: string;
48
- exec: RawExecCommandEvent;
49
- state: TurnActivityState;
50
- } | {
51
- kind: 'tool_completed';
52
- turnId: string;
53
- exec: RawExecCommandEvent;
54
- state: TurnActivityState;
55
- } | {
56
- kind: 'turn_completed';
57
- turnId: string;
58
- state: 'completed' | 'interrupted';
59
- };
60
- export declare function normalizeTurnActivityEvent(notification: JsonRpcNotification): TurnActivityEvent | null;
61
- export declare function classifyAgentOutput(phase: string | null, completed: boolean): TurnOutputKind;
62
- export declare function inferToolActivityState(event: RawExecCommandEvent): TurnActivityState;
1
+ export * from '../codex_app/activity.js';
@@ -1,418 +1,2 @@
1
- export function normalizeTurnActivityEvent(notification) {
2
- switch (notification.method) {
3
- case 'item/started':
4
- return normalizeStartedEvent(notification.params);
5
- case 'item/agentMessage/delta':
6
- return normalizeAgentDeltaEvent(notification.params);
7
- case 'item/plan/delta':
8
- return normalizePlanDeltaEvent(notification.params);
9
- case 'item/completed':
10
- return normalizeCompletedEvent(notification.params);
11
- case 'turn/plan/updated':
12
- return normalizePlanUpdatedEvent(notification.params);
13
- case 'codex/event/exec_command_begin':
14
- return normalizeToolEvent(notification.params, 'tool_started');
15
- case 'codex/event/exec_command_end':
16
- return normalizeToolEvent(notification.params, 'tool_completed');
17
- case 'turn/completed': {
18
- const turnId = extractTurnId(notification.params);
19
- if (!turnId)
20
- return null;
21
- return {
22
- kind: 'turn_completed',
23
- turnId,
24
- state: 'completed',
25
- };
26
- }
27
- case 'turn/interrupted':
28
- case 'turn/aborted': {
29
- const turnId = extractTurnId(notification.params);
30
- if (!turnId)
31
- return null;
32
- return {
33
- kind: 'turn_completed',
34
- turnId,
35
- state: 'interrupted',
36
- };
37
- }
38
- default:
39
- return null;
40
- }
41
- }
42
- function normalizeStartedEvent(params) {
43
- const turnId = extractTurnId(params);
44
- if (!turnId)
45
- return null;
46
- const item = params?.item;
47
- const itemType = normalizeEventItemType(item);
48
- if (itemType === 'reasoning') {
49
- return {
50
- kind: 'reasoning_started',
51
- turnId,
52
- state: 'thinking',
53
- };
54
- }
55
- const toolEvent = normalizeThreadItemToolEvent(params, 'tool_started');
56
- if (toolEvent) {
57
- return toolEvent;
58
- }
59
- if (!isRelayableTextItemType(itemType)) {
60
- return null;
61
- }
62
- const itemId = extractItemId(item);
63
- if (!itemId)
64
- return null;
65
- const phase = itemType === 'plan' ? 'commentary' : extractAgentPhase(item);
66
- return {
67
- kind: 'agent_message_started',
68
- turnId,
69
- itemId,
70
- phase,
71
- outputKind: outputKindForItemType(itemType, phase, false),
72
- isPlan: itemType === 'plan',
73
- };
74
- }
75
- function normalizeAgentDeltaEvent(params) {
76
- const turnId = extractTurnId(params);
77
- const delta = extractAgentDeltaText(params);
78
- const itemId = extractItemId(params);
79
- if (!turnId || !delta || !itemId) {
80
- return null;
81
- }
82
- const phase = extractAgentPhase(params);
83
- return {
84
- kind: 'agent_message_delta',
85
- turnId,
86
- itemId,
87
- delta,
88
- outputKind: classifyAgentOutput(phase, false),
89
- };
90
- }
91
- function normalizePlanDeltaEvent(params) {
92
- const turnId = extractTurnId(params);
93
- const delta = extractAgentDeltaText(params);
94
- const itemId = extractItemId(params);
95
- if (!turnId || !delta || !itemId) {
96
- return null;
97
- }
98
- return {
99
- kind: 'agent_message_delta',
100
- turnId,
101
- itemId,
102
- delta,
103
- outputKind: 'commentary',
104
- isPlan: true,
105
- };
106
- }
107
- function normalizeCompletedEvent(params) {
108
- const turnId = extractTurnId(params);
109
- if (!turnId)
110
- return null;
111
- const item = params?.item;
112
- const itemType = normalizeEventItemType(item);
113
- if (itemType === 'reasoning') {
114
- return {
115
- kind: 'reasoning_completed',
116
- turnId,
117
- state: 'thinking',
118
- };
119
- }
120
- const toolEvent = normalizeThreadItemToolEvent(params, 'tool_completed');
121
- if (toolEvent) {
122
- return toolEvent;
123
- }
124
- if (!isRelayableTextItemType(itemType)) {
125
- return null;
126
- }
127
- const itemId = extractItemId(item);
128
- if (!itemId)
129
- return null;
130
- const phase = itemType === 'plan' ? 'commentary' : extractAgentPhase(item);
131
- return {
132
- kind: 'agent_message_completed',
133
- turnId,
134
- itemId,
135
- phase,
136
- text: extractCompletedAgentText(params),
137
- outputKind: outputKindForItemType(itemType, phase, true),
138
- isPlan: itemType === 'plan',
139
- };
140
- }
141
- function normalizePlanUpdatedEvent(params) {
142
- const turnId = extractTurnId(params);
143
- if (!turnId) {
144
- return null;
145
- }
146
- const text = formatPlanUpdateText(params);
147
- if (!text) {
148
- return null;
149
- }
150
- return {
151
- kind: 'agent_message_completed',
152
- turnId,
153
- itemId: `${turnId}:plan`,
154
- phase: 'commentary',
155
- text,
156
- outputKind: 'commentary',
157
- isPlan: true,
158
- };
159
- }
160
- function normalizeToolEvent(params, kind) {
161
- const exec = extractRawExecCommandEvent(params);
162
- if (!exec) {
163
- return null;
164
- }
165
- return {
166
- kind,
167
- turnId: exec.turnId,
168
- exec,
169
- state: inferToolActivityState(exec),
170
- };
171
- }
172
- function normalizeThreadItemToolEvent(params, kind) {
173
- const turnId = extractTurnId(params);
174
- const item = params?.item;
175
- const itemId = extractItemId(item);
176
- const itemType = normalizeEventItemType(item);
177
- if (!turnId || !itemId || !itemType) {
178
- return null;
179
- }
180
- let command = [];
181
- let cwd = null;
182
- let parsedCmd = [];
183
- switch (itemType) {
184
- case 'commandexecution':
185
- command = typeof item.command === 'string' ? [item.command] : [];
186
- cwd = typeof item.cwd === 'string' ? item.cwd : null;
187
- parsedCmd = normalizeCommandActions(item.commandActions);
188
- break;
189
- case 'filechange':
190
- parsedCmd = normalizeFileChanges(item.changes);
191
- break;
192
- case 'websearch':
193
- parsedCmd = [{
194
- type: 'search',
195
- query: typeof item.query === 'string' ? item.query : '',
196
- path: null,
197
- }];
198
- break;
199
- case 'imageview':
200
- parsedCmd = [{ type: 'read', path: typeof item.path === 'string' ? item.path : null }];
201
- break;
202
- case 'mcptoolcall':
203
- command = [`MCP ${String(item.server ?? 'server')}/${String(item.tool ?? 'tool')}`];
204
- break;
205
- case 'dynamictoolcall':
206
- command = [`Tool ${item.namespace ? `${String(item.namespace)}/` : ''}${String(item.tool ?? 'tool')}`];
207
- break;
208
- case 'collabagenttoolcall':
209
- command = [`Agent ${String(item.tool ?? 'operation')}`];
210
- break;
211
- case 'imagegeneration':
212
- command = ['Image generation'];
213
- break;
214
- default:
215
- return null;
216
- }
217
- const exec = {
218
- callId: itemId,
219
- turnId,
220
- command,
221
- cwd,
222
- parsedCmd,
223
- };
224
- return {
225
- kind,
226
- turnId,
227
- exec,
228
- state: inferToolActivityState(exec),
229
- };
230
- }
231
- function normalizeCommandActions(value) {
232
- if (!Array.isArray(value)) {
233
- return [];
234
- }
235
- return value.map((entry) => {
236
- const type = typeof entry?.type === 'string' ? entry.type : '';
237
- if (type === 'listFiles') {
238
- return { ...entry, type: 'list_files' };
239
- }
240
- return entry;
241
- });
242
- }
243
- function normalizeFileChanges(value) {
244
- if (!Array.isArray(value)) {
245
- return [];
246
- }
247
- return value.map((entry) => ({
248
- type: 'apply_patch',
249
- path: typeof entry?.path === 'string' ? entry.path : null,
250
- }));
251
- }
252
- export function classifyAgentOutput(phase, completed) {
253
- if (!phase) {
254
- return completed ? 'final_answer' : 'commentary';
255
- }
256
- const normalized = phase.replace(/[^a-z]/gi, '').toLowerCase();
257
- if (normalized === 'final' || normalized === 'finalanswer' || normalized === 'answer' || normalized === 'response') {
258
- return 'final_answer';
259
- }
260
- return 'commentary';
261
- }
262
- export function inferToolActivityState(event) {
263
- const parsedTypes = (event.parsedCmd ?? [])
264
- .map((entry) => (typeof entry?.type === 'string' ? entry.type : ''))
265
- .filter(Boolean);
266
- if (parsedTypes.includes('search')) {
267
- return 'searching';
268
- }
269
- if (parsedTypes.includes('read') || parsedTypes.includes('list_files')) {
270
- return 'reading';
271
- }
272
- if (parsedTypes.some(type => ['write', 'edit', 'apply_patch', 'move', 'copy', 'delete', 'mkdir'].includes(type))) {
273
- return 'editing';
274
- }
275
- return 'running_command';
276
- }
277
- function extractRawExecCommandEvent(params) {
278
- const msg = params?.msg;
279
- if (!msg || typeof msg !== 'object') {
280
- return null;
281
- }
282
- const callId = typeof msg.call_id === 'string' ? msg.call_id : null;
283
- const turnId = typeof msg.turn_id === 'string' ? msg.turn_id : null;
284
- if (!callId || !turnId) {
285
- return null;
286
- }
287
- return {
288
- callId,
289
- turnId,
290
- command: Array.isArray(msg.command) ? msg.command.map((entry) => String(entry)) : [],
291
- cwd: msg.cwd ? String(msg.cwd) : null,
292
- parsedCmd: Array.isArray(msg.parsed_cmd) ? msg.parsed_cmd : [],
293
- };
294
- }
295
- function extractTurnId(params) {
296
- const candidates = [
297
- params?.turnId,
298
- params?.turn_id,
299
- params?.turn?.id,
300
- params?.turn?.turnId,
301
- params?.item?.turnId,
302
- params?.item?.turn_id,
303
- ];
304
- for (const candidate of candidates) {
305
- if (candidate !== null && candidate !== undefined && String(candidate).trim()) {
306
- return String(candidate);
307
- }
308
- }
309
- return null;
310
- }
311
- function extractItemId(value) {
312
- const candidates = [
313
- value?.itemId,
314
- value?.item_id,
315
- value?.id,
316
- value?.item?.id,
317
- ];
318
- for (const candidate of candidates) {
319
- if (candidate !== null && candidate !== undefined && String(candidate).trim()) {
320
- return String(candidate);
321
- }
322
- }
323
- return null;
324
- }
325
- function extractAgentPhase(value) {
326
- const phase = value?.phase ?? value?.item?.phase ?? null;
327
- return typeof phase === 'string' && phase.trim() ? phase : null;
328
- }
329
- function extractAgentDeltaText(params) {
330
- const candidates = [
331
- params?.delta,
332
- params?.textDelta,
333
- params?.contentDelta,
334
- params?.text,
335
- ];
336
- for (const candidate of candidates) {
337
- const text = extractTextCandidate(candidate);
338
- if (text) {
339
- return text;
340
- }
341
- }
342
- return null;
343
- }
344
- function extractCompletedAgentText(params) {
345
- const itemType = normalizeEventItemType(params?.item ?? params);
346
- if (!isRelayableTextItemType(itemType)) {
347
- return null;
348
- }
349
- const item = params?.item ?? params;
350
- const directText = extractTextCandidate(item?.text)
351
- ?? extractTextCandidate(item?.content)
352
- ?? extractTextCandidate(item?.value);
353
- if (directText !== null) {
354
- return directText;
355
- }
356
- return '';
357
- }
358
- function isRelayableTextItemType(itemType) {
359
- return itemType === 'agentmessage' || itemType === 'assistantmessage' || itemType === 'plan';
360
- }
361
- function outputKindForItemType(itemType, phase, completed) {
362
- if (itemType === 'plan') {
363
- return 'commentary';
364
- }
365
- return classifyAgentOutput(phase, completed);
366
- }
367
- function formatPlanUpdateText(params) {
368
- const lines = [];
369
- const explanation = extractTextCandidate(params?.explanation);
370
- if (explanation) {
371
- lines.push(explanation);
372
- }
373
- const plan = Array.isArray(params?.plan) ? params.plan : [];
374
- for (const entry of plan) {
375
- const step = extractTextCandidate(entry?.step);
376
- if (!step) {
377
- continue;
378
- }
379
- const status = typeof entry?.status === 'string' && entry.status ? ` [${entry.status}]` : '';
380
- lines.push(`- ${step}${status}`);
381
- }
382
- return lines.join('\n').trim() || null;
383
- }
384
- function normalizeEventItemType(value) {
385
- const raw = value?.type ?? value?.itemType ?? value?.item_type ?? value?.kind;
386
- if (typeof raw !== 'string') {
387
- return null;
388
- }
389
- return raw.replace(/[^a-z]/gi, '').toLowerCase();
390
- }
391
- function extractTextCandidate(value) {
392
- if (typeof value === 'string') {
393
- return value;
394
- }
395
- if (!value || typeof value !== 'object') {
396
- return null;
397
- }
398
- for (const key of ['text', 'delta', 'content', 'value']) {
399
- const candidate = value[key];
400
- if (typeof candidate === 'string') {
401
- return candidate;
402
- }
403
- }
404
- for (const key of ['parts', 'segments', 'content']) {
405
- const candidate = value[key];
406
- if (!Array.isArray(candidate)) {
407
- continue;
408
- }
409
- const text = candidate
410
- .map((entry) => extractTextCandidate(entry))
411
- .filter((entry) => entry !== null)
412
- .join('');
413
- if (text) {
414
- return text;
415
- }
416
- }
417
- return null;
418
- }
1
+ // Compatibility export for existing control-plane consumers.
2
+ export * from '../codex_app/activity.js';
@@ -0,0 +1,11 @@
1
+ import { CodexAuthRotationReason } from './state_types.js';
2
+ export declare function classifyCodexAuthRotationError(params: any): CodexAuthRotationReason | null;
3
+ export declare function isCodexAuthInvalidError(text: string): boolean;
4
+ export declare function isCodexQuotaLimitError(text: string): boolean;
5
+ export declare function formatCodexNotificationError(params: any): string;
6
+ export declare function isRetryableCodexTransportError(params: any): boolean;
7
+ export declare function collectCodexErrorText(params: any): string;
8
+ export declare function formatKnownCodexAccessError(raw: string): string | null;
9
+ export declare function isChatGptBackendAccessBlocked(raw: string): boolean;
10
+ export declare function cleanUserFacingError(raw: string): string;
11
+ export declare function clipUserFacingError(raw: string, limit?: number): string;