@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.
- package/.env.example +18 -1
- package/CHANGELOG.md +36 -0
- package/README.md +4 -3
- package/README_EN.md +4 -3
- 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 -106
- package/dist/antigravity/controller.js +2 -2083
- 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/config.d.ts +4 -0
- package/dist/config.js +41 -5
- 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 +20 -5
- 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 +57 -22
- package/dist/core/orchestrator.js +877 -477
- 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.d.ts +48 -0
- package/dist/dsh/adapter.js +205 -0
- package/dist/dsh/backend.d.ts +7 -0
- package/dist/dsh/backend.js +21 -0
- package/dist/dsh/client.d.ts +46 -0
- package/dist/dsh/client.js +184 -0
- package/dist/dsh/permissions_plugin.d.ts +30 -0
- package/dist/dsh/permissions_plugin.js +21 -0
- package/dist/dsh/runtime.d.ts +20 -0
- package/dist/dsh/runtime.js +36 -0
- package/dist/dsh/ui.d.ts +38 -0
- package/dist/dsh/ui.js +342 -0
- package/dist/i18n.d.ts +4 -0
- package/dist/i18n.js +12 -0
- package/dist/main.js +144 -72
- 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 +10 -4
- package/dist/store/database.js +13 -3
- 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 +2 -2
- package/docs/dsh.md +83 -0
- package/docs/release.md +13 -11
- package/docs/zh/architecture-review.md +106 -0
- package/docs/zh/dsh.md +83 -0
- package/docs/zh/release.md +13 -11
- package/docs/zh/telegram-experience.md +40 -0
- package/package.json +7 -5
|
@@ -0,0 +1,418 @@
|
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import type { IEngineAdapter, EngineModel, EngineTurnRequest, EngineTurnExecution } from '../core/engine_spi.js';
|
|
2
2
|
import type { CodexAppClient } from './client.js';
|
|
3
|
-
import type { SandboxModeValue } from '../types.js';
|
|
3
|
+
import type { SandboxModeValue, ApprovalPolicyValue } from '../types.js';
|
|
4
4
|
export declare class CodexEngineAdapter implements IEngineAdapter {
|
|
5
5
|
private readonly client;
|
|
6
6
|
readonly id: string;
|
|
@@ -12,7 +12,7 @@ export declare class CodexEngineAdapter implements IEngineAdapter {
|
|
|
12
12
|
id?: string;
|
|
13
13
|
name?: string;
|
|
14
14
|
defaultModel?: string | undefined;
|
|
15
|
-
defaultApprovalPolicy?:
|
|
15
|
+
defaultApprovalPolicy?: ApprovalPolicyValue;
|
|
16
16
|
defaultSandboxMode?: SandboxModeValue;
|
|
17
17
|
});
|
|
18
18
|
listModels(): Promise<EngineModel[]>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import { normalizeTurnActivityEvent } from '
|
|
3
|
-
import { buildAttachmentPrompt } from '../
|
|
2
|
+
import { normalizeTurnActivityEvent } from './activity.js';
|
|
3
|
+
import { buildAttachmentPrompt } from '../core/attachment_files.js';
|
|
4
|
+
import { resolveAccessMode } from '../core/access.js';
|
|
4
5
|
export class CodexEngineAdapter {
|
|
5
6
|
client;
|
|
6
7
|
id;
|
|
@@ -24,15 +25,8 @@ export class CodexEngineAdapter {
|
|
|
24
25
|
name: m.displayName || m.id,
|
|
25
26
|
description: m.description,
|
|
26
27
|
isDefault: m.isDefault || m.id === this.defaultModel,
|
|
28
|
+
supportedReasoningEfforts: m.supportedReasoningEfforts,
|
|
27
29
|
}));
|
|
28
|
-
if (!list.some((m) => m.id === 'gpt-6-sol')) {
|
|
29
|
-
list.push({
|
|
30
|
-
id: 'gpt-6-sol',
|
|
31
|
-
name: 'GPT-6-Sol',
|
|
32
|
-
description: 'Next-generation flagship model with deep reasoning',
|
|
33
|
-
isDefault: false,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
30
|
return list;
|
|
37
31
|
}
|
|
38
32
|
catch {
|
|
@@ -41,9 +35,11 @@ export class CodexEngineAdapter {
|
|
|
41
35
|
}
|
|
42
36
|
executeTurn(request) {
|
|
43
37
|
const emitter = new EventEmitter();
|
|
38
|
+
const access = resolveAccessMode({ defaultApprovalPolicy: this.defaultApprovalPolicy, defaultSandboxMode: this.defaultSandboxMode }, { accessPreset: request.accessPreset ?? 'default' });
|
|
44
39
|
let threadId = request.threadId;
|
|
45
40
|
let turnId = null;
|
|
46
41
|
let cancelled = false;
|
|
42
|
+
const earlyNotifications = [];
|
|
47
43
|
let promptText = request.prompt;
|
|
48
44
|
if (request.stagedAttachments && request.stagedAttachments.length > 0 && !request.prompt.includes('Telegram attachments:')) {
|
|
49
45
|
promptText = buildAttachmentPrompt(request.prompt, request.stagedAttachments);
|
|
@@ -58,6 +54,16 @@ export class CodexEngineAdapter {
|
|
|
58
54
|
}
|
|
59
55
|
};
|
|
60
56
|
const onNotification = (msg) => {
|
|
57
|
+
if (cancelled || finalResult || turnError)
|
|
58
|
+
return;
|
|
59
|
+
if (threadId && msg.params?.threadId && msg.params.threadId !== threadId)
|
|
60
|
+
return;
|
|
61
|
+
if (!turnId) {
|
|
62
|
+
earlyNotifications.push(msg);
|
|
63
|
+
if (earlyNotifications.length > 1000)
|
|
64
|
+
earlyNotifications.shift();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
61
67
|
try {
|
|
62
68
|
const ev = normalizeTurnActivityEvent(msg);
|
|
63
69
|
if (!ev)
|
|
@@ -100,6 +106,7 @@ export class CodexEngineAdapter {
|
|
|
100
106
|
};
|
|
101
107
|
const cleanup = () => {
|
|
102
108
|
this.client.off('notification', onNotification);
|
|
109
|
+
earlyNotifications.length = 0;
|
|
103
110
|
};
|
|
104
111
|
this.client.on('notification', onNotification);
|
|
105
112
|
const effectiveModel = request.model && request.model !== 'default'
|
|
@@ -111,8 +118,8 @@ export class CodexEngineAdapter {
|
|
|
111
118
|
const session = await this.client.startThread({
|
|
112
119
|
cwd: request.cwd,
|
|
113
120
|
model: effectiveModel,
|
|
114
|
-
approvalPolicy:
|
|
115
|
-
sandboxMode:
|
|
121
|
+
approvalPolicy: access.approvalPolicy,
|
|
122
|
+
sandboxMode: access.sandboxMode,
|
|
116
123
|
});
|
|
117
124
|
threadId = session.thread.threadId;
|
|
118
125
|
}
|
|
@@ -121,7 +128,8 @@ export class CodexEngineAdapter {
|
|
|
121
128
|
await this.client.resumeThread({
|
|
122
129
|
threadId,
|
|
123
130
|
cwd: request.cwd,
|
|
124
|
-
approvalPolicy:
|
|
131
|
+
approvalPolicy: access.approvalPolicy,
|
|
132
|
+
sandboxMode: access.sandboxMode,
|
|
125
133
|
});
|
|
126
134
|
}
|
|
127
135
|
catch {
|
|
@@ -135,6 +143,7 @@ export class CodexEngineAdapter {
|
|
|
135
143
|
if (!threadId) {
|
|
136
144
|
throw new Error('Failed to resolve or create Codex thread for turn');
|
|
137
145
|
}
|
|
146
|
+
emitter.emit('conversation', threadId);
|
|
138
147
|
let turn;
|
|
139
148
|
try {
|
|
140
149
|
turn = await this.client.startTurn({
|
|
@@ -145,8 +154,8 @@ export class CodexEngineAdapter {
|
|
|
145
154
|
effort: request.effort ?? null,
|
|
146
155
|
serviceTier: request.serviceTier ?? undefined,
|
|
147
156
|
collaborationMode: null,
|
|
148
|
-
approvalPolicy:
|
|
149
|
-
sandboxMode:
|
|
157
|
+
approvalPolicy: access.approvalPolicy,
|
|
158
|
+
sandboxMode: access.sandboxMode,
|
|
150
159
|
});
|
|
151
160
|
}
|
|
152
161
|
catch (turnErr) {
|
|
@@ -155,13 +164,20 @@ export class CodexEngineAdapter {
|
|
|
155
164
|
errMsg.includes('No thread') ||
|
|
156
165
|
errMsg.includes('stale') ||
|
|
157
166
|
errMsg.includes('invalid thread')) {
|
|
167
|
+
if (cancelled)
|
|
168
|
+
throw turnErr;
|
|
158
169
|
const session = await this.client.startThread({
|
|
159
170
|
cwd: request.cwd,
|
|
160
171
|
model: effectiveModel,
|
|
161
|
-
approvalPolicy:
|
|
162
|
-
sandboxMode:
|
|
172
|
+
approvalPolicy: access.approvalPolicy,
|
|
173
|
+
sandboxMode: access.sandboxMode,
|
|
163
174
|
});
|
|
164
175
|
threadId = session.thread.threadId;
|
|
176
|
+
if (cancelled) {
|
|
177
|
+
cleanup();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
emitter.emit('conversation', threadId);
|
|
165
181
|
turn = await this.client.startTurn({
|
|
166
182
|
threadId,
|
|
167
183
|
input: [{ type: 'text', text: promptText, text_elements: [] }],
|
|
@@ -170,8 +186,8 @@ export class CodexEngineAdapter {
|
|
|
170
186
|
effort: request.effort ?? null,
|
|
171
187
|
serviceTier: request.serviceTier ?? undefined,
|
|
172
188
|
collaborationMode: null,
|
|
173
|
-
approvalPolicy:
|
|
174
|
-
sandboxMode:
|
|
189
|
+
approvalPolicy: access.approvalPolicy,
|
|
190
|
+
sandboxMode: access.sandboxMode,
|
|
175
191
|
});
|
|
176
192
|
}
|
|
177
193
|
else {
|
|
@@ -179,6 +195,8 @@ export class CodexEngineAdapter {
|
|
|
179
195
|
}
|
|
180
196
|
}
|
|
181
197
|
turnId = turn.id;
|
|
198
|
+
for (const notification of earlyNotifications.splice(0))
|
|
199
|
+
onNotification(notification);
|
|
182
200
|
}
|
|
183
201
|
catch (err) {
|
|
184
202
|
cleanup();
|
|
@@ -187,14 +205,26 @@ export class CodexEngineAdapter {
|
|
|
187
205
|
resolveWaiters(null);
|
|
188
206
|
}
|
|
189
207
|
};
|
|
190
|
-
|
|
208
|
+
const running = run();
|
|
209
|
+
let cancelling;
|
|
191
210
|
return {
|
|
192
|
-
turnId
|
|
211
|
+
get turnId() { return turnId ?? undefined; },
|
|
193
212
|
cancel: () => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
213
|
+
if (finalResult)
|
|
214
|
+
return Promise.resolve();
|
|
215
|
+
cancelling ??= (async () => {
|
|
216
|
+
cancelled = true;
|
|
217
|
+
await running;
|
|
218
|
+
if (finalResult)
|
|
219
|
+
return;
|
|
220
|
+
if (threadId && turnId)
|
|
221
|
+
await this.client.interruptTurn(threadId, turnId);
|
|
222
|
+
cleanup();
|
|
223
|
+
finalResult = { kind: 'result', status: 'INTERRUPTED', response: accumulatedResponse, conversationId: threadId };
|
|
224
|
+
emitter.emit('result', finalResult);
|
|
225
|
+
resolveWaiters(finalResult);
|
|
226
|
+
})();
|
|
227
|
+
return cancelling.catch(error => { cancelling = undefined; throw error; });
|
|
198
228
|
},
|
|
199
229
|
waitForResult: async () => {
|
|
200
230
|
if (finalResult)
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BackendUi, BackendUiHost } from '../core/backend_ui.js';
|
|
2
|
+
import type { BridgeSessionCore } from '../controller/controller.js';
|
|
3
|
+
/** Native Codex controls own their RPC subscriptions; execution remains owned by the orchestrator. */
|
|
4
|
+
export declare function createCodexBackendUi(core: BridgeSessionCore, host: BackendUiHost, fallback: BackendUi): BackendUi;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const nativeCallbacks = /^(thread:|auth:|settings:|setup:|nav:|approval:|ui:|mcpel:|login:|config:|voice:)/;
|
|
2
|
+
const pendingCallbacks = /^(approval:|ui:|mcpel:|login:)/;
|
|
3
|
+
const commands = new Set(['threads', 'open', 'auth', 'login_device', 'login_cancel', 'logout', 'permissions', 'access',
|
|
4
|
+
'skills', 'skill', 'skill_enable', 'skill_disable', 'hooks', 'plugins', 'plugin', 'plugin_skill', 'apps', 'features',
|
|
5
|
+
'config', 'requirements', 'provider', 'mcp', 'mcp_reload', 'mcp_login', 'mcp_resource', 'approve', 'deny', 'answer', 'mcpel']);
|
|
6
|
+
/** Native Codex controls own their RPC subscriptions; execution remains owned by the orchestrator. */
|
|
7
|
+
export function createCodexBackendUi(core, host, fallback) {
|
|
8
|
+
return {
|
|
9
|
+
...fallback,
|
|
10
|
+
ownsCallback: data => nativeCallbacks.test(data) || data.startsWith('codex:'),
|
|
11
|
+
getPendingApprovals: () => core.getPendingApprovalCount(),
|
|
12
|
+
stop: () => core.stopControlPlane(),
|
|
13
|
+
handleCustomInbound: async (event, locale) => {
|
|
14
|
+
if (core.hasPendingInteraction(event.scopeId)) {
|
|
15
|
+
await core.dispatchBackendCommand(event);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
return await fallback.handleCustomInbound?.(event, locale) ?? false;
|
|
19
|
+
},
|
|
20
|
+
handleCustomCommand: async (scopeId, command, args, locale, event) => {
|
|
21
|
+
if (!commands.has(command))
|
|
22
|
+
return await fallback.handleCustomCommand?.(scopeId, command, args, locale, event) ?? false;
|
|
23
|
+
if (['open', 'logout'].includes(command) && host.hasActiveTurn(scopeId)) {
|
|
24
|
+
await host.sendMessage(scopeId, locale === 'zh' ? '请先结束当前任务。' : 'Finish the active task first.');
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const inbound = event ?? { scopeId, chatId: scopeId, topicId: null, chatType: 'private', userId: 'system',
|
|
28
|
+
messageId: 0, text: `/${command} ${args}`, attachments: [], entities: [], replyToBot: false };
|
|
29
|
+
await core.dispatchBackendCommand(inbound);
|
|
30
|
+
host.syncCurrentBackendSettings(scopeId);
|
|
31
|
+
return true;
|
|
32
|
+
},
|
|
33
|
+
handleCustomCallback: async (scopeId, data, locale, messageId, event) => {
|
|
34
|
+
if (!nativeCallbacks.test(data) || !event)
|
|
35
|
+
return await fallback.handleCustomCallback?.(scopeId, data, locale, messageId, event) ?? false;
|
|
36
|
+
if (!pendingCallbacks.test(data) && host.getBackendDescriptorForScope(scopeId).engineType !== 'codex')
|
|
37
|
+
return true;
|
|
38
|
+
if (data.startsWith('thread:') && host.hasActiveTurn(scopeId)) {
|
|
39
|
+
await host.sendMessage(scopeId, locale === 'zh' ? '请先结束当前任务。' : 'Finish the active task first.');
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
await core.handleCallback(event);
|
|
43
|
+
if (!pendingCallbacks.test(data))
|
|
44
|
+
host.syncCurrentBackendSettings(scopeId);
|
|
45
|
+
return true;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
package/dist/codex_app/client.js
CHANGED
|
@@ -284,18 +284,6 @@ export class CodexAppClient extends EventEmitter {
|
|
|
284
284
|
models.push(...rows.map(mapModel));
|
|
285
285
|
cursor = typeof result.nextCursor === 'string' ? result.nextCursor : null;
|
|
286
286
|
} while (cursor);
|
|
287
|
-
if (!models.some((m) => m.id === 'gpt-6-sol' || m.model === 'gpt-6-sol')) {
|
|
288
|
-
models.push({
|
|
289
|
-
id: 'gpt-6-sol',
|
|
290
|
-
model: 'gpt-6-sol',
|
|
291
|
-
displayName: 'GPT-6-Sol',
|
|
292
|
-
description: 'Next-generation flagship model with deep reasoning',
|
|
293
|
-
isDefault: false,
|
|
294
|
-
supportedReasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
295
|
-
defaultReasoningEffort: 'medium',
|
|
296
|
-
serviceTiers: [],
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
287
|
return models;
|
|
300
288
|
}
|
|
301
289
|
async readAccount(refreshToken = false) {
|