@theia/ai-codex 1.67.0-next.56

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.
@@ -0,0 +1,443 @@
1
+ /* *****************************************************************************
2
+ * Copyright (C) 2025 EclipseSource GmbH.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ * *****************************************************************************/
16
+
17
+ /* Command Execution Tool */
18
+ .codex-command-execution {
19
+ border: var(--theia-border-width) solid var(--theia-sideBarSectionHeader-border);
20
+ border-radius: var(--theia-ui-padding);
21
+ margin: var(--theia-ui-padding) 0;
22
+ background-color: var(--theia-sideBar-background);
23
+ overflow: hidden;
24
+ }
25
+
26
+ .codex-command-execution:hover {
27
+ background-color: var(--theia-list-hoverBackground);
28
+ }
29
+
30
+ .codex-tool-header {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: var(--theia-ui-padding);
34
+ padding: 6px;
35
+ background-color: var(--theia-editorGroupHeader-tabsBackground);
36
+ white-space: nowrap;
37
+ overflow: hidden;
38
+ }
39
+
40
+ .codex-tool-header.expandable {
41
+ cursor: pointer;
42
+ }
43
+
44
+ .codex-expand-icon {
45
+ flex-shrink: 0;
46
+ }
47
+
48
+ .codex-tool-icon {
49
+ color: var(--theia-charts-blue);
50
+ flex-shrink: 0;
51
+ }
52
+
53
+ .codex-tool-name {
54
+ font-weight: 600;
55
+ color: var(--theia-foreground);
56
+ font-size: var(--theia-ui-font-size1);
57
+ flex-shrink: 0;
58
+ }
59
+
60
+ .codex-command-line {
61
+ color: var(--theia-foreground);
62
+ font-family: var(--monaco-monospace-font);
63
+ font-size: var(--theia-ui-font-size1);
64
+ flex: 1;
65
+ min-width: 0;
66
+ overflow: hidden;
67
+ text-overflow: ellipsis;
68
+ }
69
+
70
+ .codex-exit-code {
71
+ font-size: var(--theia-ui-font-size0);
72
+ font-weight: 500;
73
+ padding: 2px 6px;
74
+ border-radius: calc(var(--theia-ui-padding) / 3);
75
+ flex-shrink: 0;
76
+ }
77
+
78
+ .codex-exit-code.success {
79
+ background-color: var(--theia-charts-green);
80
+ color: var(--theia-button-foreground);
81
+ }
82
+
83
+ .codex-exit-code.error {
84
+ background-color: var(--theia-charts-red);
85
+ color: var(--theia-button-foreground);
86
+ }
87
+
88
+ .codex-exit-code.in-progress {
89
+ background-color: var(--theia-charts-orange);
90
+ color: var(--theia-button-foreground);
91
+ }
92
+
93
+ .codex-tool-icon.codex-loading {
94
+ animation: codex-spin 1s linear infinite;
95
+ }
96
+
97
+ @keyframes codex-spin {
98
+ from {
99
+ transform: rotate(0deg);
100
+ }
101
+
102
+ to {
103
+ transform: rotate(360deg);
104
+ }
105
+ }
106
+
107
+ .codex-command-output {
108
+ margin: 0;
109
+ padding: var(--theia-ui-padding);
110
+ background-color: var(--theia-terminal-background);
111
+ color: var(--theia-terminal-foreground);
112
+ border-top: var(--theia-border-width) solid var(--theia-sideBarSectionHeader-border);
113
+ overflow-x: auto;
114
+ font-family: var(--monaco-monospace-font);
115
+ font-size: var(--theia-ui-font-size0);
116
+ max-height: 400px;
117
+ overflow-y: auto;
118
+ white-space: pre-wrap;
119
+ word-break: break-all;
120
+ }
121
+
122
+ /* Base container and structure for collapsible tools */
123
+ .codex-tool.container {
124
+ border: var(--theia-border-width) solid var(--theia-sideBarSectionHeader-border);
125
+ border-radius: var(--theia-ui-padding);
126
+ margin: var(--theia-ui-padding) 0;
127
+ background-color: var(--theia-sideBar-background);
128
+ overflow: hidden;
129
+ }
130
+
131
+ .codex-tool.container:hover {
132
+ background-color: var(--theia-list-hoverBackground);
133
+ }
134
+
135
+ .codex-tool.header {
136
+ display: flex;
137
+ align-items: center;
138
+ justify-content: space-between;
139
+ padding: 6px;
140
+ background-color: var(--theia-editorGroupHeader-tabsBackground);
141
+ white-space: nowrap;
142
+ overflow: hidden;
143
+ }
144
+
145
+ .codex-tool.header-left {
146
+ display: flex;
147
+ align-items: center;
148
+ gap: var(--theia-ui-padding);
149
+ min-width: 0;
150
+ flex: 1;
151
+ overflow: hidden;
152
+ }
153
+
154
+ .codex-tool.header-right {
155
+ flex-shrink: 0;
156
+ }
157
+
158
+ .codex-tool.icon {
159
+ color: var(--theia-charts-blue);
160
+ }
161
+
162
+ .codex-tool.title {
163
+ font-weight: 600;
164
+ color: var(--theia-foreground);
165
+ font-size: var(--theia-ui-font-size1);
166
+ }
167
+
168
+ .codex-tool.command {
169
+ color: var(--theia-foreground);
170
+ font-size: var(--theia-ui-font-size1);
171
+ font-family: var(--theia-ui-font-family-mono);
172
+ flex-shrink: 0;
173
+ }
174
+
175
+ .codex-tool.description {
176
+ color: var(--theia-descriptionForeground);
177
+ font-size: var(--theia-ui-font-size1);
178
+ overflow: hidden;
179
+ text-overflow: ellipsis;
180
+ white-space: nowrap;
181
+ min-width: 0;
182
+ }
183
+
184
+ .codex-tool.badge {
185
+ font-size: var(--theia-ui-font-size0);
186
+ font-weight: 500;
187
+ padding: 2px 6px;
188
+ background-color: var(--theia-badge-background);
189
+ color: var(--theia-badge-foreground);
190
+ border-radius: calc(var(--theia-ui-padding) / 3);
191
+ font-family: var(--theia-ui-font-family-mono);
192
+ }
193
+
194
+ .codex-tool.error {
195
+ padding: var(--theia-ui-padding);
196
+ color: var(--theia-errorForeground);
197
+ background-color: var(--theia-errorBackground);
198
+ border-radius: var(--theia-ui-padding);
199
+ margin: var(--theia-ui-padding) 0;
200
+ border: var(--theia-border-width) solid var(--theia-errorBorder);
201
+ }
202
+
203
+ /* Collapsible section styles */
204
+ .codex-tool.expand-icon {
205
+ margin-right: calc(var(--theia-ui-padding) / 2);
206
+ flex-shrink: 0;
207
+ }
208
+
209
+ .codex-tool.expanded-content {
210
+ padding: var(--theia-ui-padding);
211
+ }
212
+
213
+ /* Detail section styles */
214
+ .codex-tool.details {
215
+ display: flex;
216
+ flex-direction: column;
217
+ gap: calc(var(--theia-ui-padding) / 2);
218
+ }
219
+
220
+ .codex-tool.detail-row {
221
+ display: flex;
222
+ align-items: flex-start;
223
+ gap: calc(var(--theia-ui-padding) / 2);
224
+ margin-bottom: calc(var(--theia-ui-padding) / 3);
225
+ }
226
+
227
+ .codex-tool.detail-label {
228
+ color: var(--theia-descriptionForeground);
229
+ font-size: var(--theia-ui-font-size1);
230
+ font-weight: 500;
231
+ min-width: 80px;
232
+ flex-shrink: 0;
233
+ }
234
+
235
+ .codex-tool.detail-label::after {
236
+ content: ':';
237
+ margin-left: 2px;
238
+ }
239
+
240
+ .codex-tool.detail-value {
241
+ color: var(--theia-foreground);
242
+ font-size: var(--theia-ui-font-size1);
243
+ flex: 1;
244
+ min-width: 0;
245
+ word-break: break-word;
246
+ }
247
+
248
+ /* TodoWrite Renderer Styles */
249
+ .codex-tool.todo-list-container {
250
+ border: var(--theia-border-width) solid var(--theia-sideBarSectionHeader-border);
251
+ border-radius: var(--theia-ui-padding);
252
+ margin: var(--theia-ui-padding) 0;
253
+ background-color: var(--theia-sideBar-background);
254
+ overflow: hidden;
255
+ }
256
+
257
+ .codex-tool.todo-list-header {
258
+ display: flex;
259
+ align-items: center;
260
+ gap: var(--theia-ui-padding);
261
+ padding: 6px;
262
+ background-color: var(--theia-editorGroupHeader-tabsBackground);
263
+ border-bottom: var(--theia-border-width) solid var(--theia-sideBarSectionHeader-border);
264
+ }
265
+
266
+ .codex-tool.todo-list-icon {
267
+ color: var(--theia-button-background);
268
+ font-size: var(--theia-icon-size);
269
+ }
270
+
271
+ .codex-tool.todo-list-title {
272
+ font-weight: 600;
273
+ color: var(--theia-foreground);
274
+ font-size: var(--theia-ui-font-size1);
275
+ }
276
+
277
+ .codex-tool.todo-list-empty {
278
+ padding: var(--theia-ui-padding);
279
+ color: var(--theia-descriptionForeground);
280
+ font-style: italic;
281
+ text-align: center;
282
+ }
283
+
284
+ .codex-tool.todo-list-items {
285
+ padding: 0;
286
+ }
287
+
288
+ .codex-tool.todo-item {
289
+ border-bottom: var(--theia-border-width) solid var(--theia-sideBarSectionHeader-border);
290
+ transition: background-color 0.2s ease;
291
+ }
292
+
293
+ .codex-tool.todo-item:last-child {
294
+ border-bottom: none;
295
+ }
296
+
297
+ .codex-tool.todo-item:hover {
298
+ background-color: var(--theia-toolbar-hoverBackground);
299
+ }
300
+
301
+ .codex-tool.todo-item-main {
302
+ display: flex;
303
+ align-items: flex-start;
304
+ gap: calc(var(--theia-ui-padding) * 2 / 3);
305
+ padding: 6px;
306
+ }
307
+
308
+ .codex-tool.todo-item-status {
309
+ display: flex;
310
+ align-items: center;
311
+ justify-content: center;
312
+ min-width: var(--theia-icon-size);
313
+ height: var(--theia-icon-size);
314
+ margin-top: 2px;
315
+ }
316
+
317
+ .codex-tool.todo-status-icon {
318
+ font-size: var(--theia-icon-size);
319
+ }
320
+
321
+ .codex-tool.todo-status-icon.completed {
322
+ color: var(--theia-charts-green);
323
+ }
324
+
325
+ .codex-tool.todo-status-icon.in-progress {
326
+ color: var(--theia-progressBar-background);
327
+ }
328
+
329
+ .codex-tool.todo-status-icon.pending {
330
+ color: var(--theia-descriptionForeground);
331
+ }
332
+
333
+ .codex-tool.todo-item-content {
334
+ flex: 1;
335
+ min-width: 0;
336
+ display: flex;
337
+ align-items: center;
338
+ gap: calc(var(--theia-ui-padding) * 2 / 3);
339
+ }
340
+
341
+ .codex-tool.todo-item-text {
342
+ flex: 1;
343
+ color: var(--theia-foreground);
344
+ line-height: 1.4;
345
+ white-space: nowrap;
346
+ overflow: hidden;
347
+ text-overflow: ellipsis;
348
+ min-width: 0;
349
+ }
350
+
351
+ .codex-tool.todo-item.status-completed .codex-tool.todo-item-text {
352
+ text-decoration: line-through;
353
+ color: var(--theia-descriptionForeground);
354
+ }
355
+
356
+ .codex-tool.todo-priority {
357
+ padding: 2px 6px;
358
+ border-radius: calc(var(--theia-ui-padding) / 3);
359
+ font-size: var(--theia-ui-font-size0);
360
+ font-weight: 500;
361
+ text-transform: uppercase;
362
+ letter-spacing: 0.3px;
363
+ flex-shrink: 0;
364
+ }
365
+
366
+ .codex-tool.todo-priority.priority-high {
367
+ background-color: rgba(var(--theia-charts-red-rgb, 204, 0, 0), 0.8);
368
+ color: var(--theia-button-foreground);
369
+ }
370
+
371
+ .codex-tool.todo-priority.priority-medium {
372
+ background-color: rgba(var(--theia-charts-orange-rgb, 255, 165, 0), 0.8);
373
+ color: var(--theia-button-foreground);
374
+ }
375
+
376
+ .codex-tool.todo-priority.priority-low {
377
+ background-color: rgba(var(--theia-charts-blue-rgb, 0, 122, 204), 0.8);
378
+ color: var(--theia-button-foreground);
379
+ }
380
+
381
+ .codex-tool.todo-list-error {
382
+ padding: var(--theia-ui-padding);
383
+ color: var(--theia-errorForeground);
384
+ background-color: var(--theia-errorBackground);
385
+ border-radius: var(--theia-ui-padding);
386
+ margin: var(--theia-ui-padding) 0;
387
+ }
388
+
389
+ .codex-tool.todo-item.status-completed {
390
+ opacity: 0.8;
391
+ }
392
+
393
+ .codex-tool.todo-item.status-in-progress {
394
+ background-color: rgba(var(--theia-progressBar-background-rgb, 0, 122, 204), 0.05);
395
+ }
396
+
397
+ .codex-tool.todo-item.status-in-progress .codex-tool.todo-item-text {
398
+ font-weight: 500;
399
+ }
400
+
401
+ .codex-tool.progress-text {
402
+ color: var(--theia-descriptionForeground);
403
+ font-size: var(--theia-ui-font-size0);
404
+ }
405
+
406
+ /* WebSearch Renderer Styles */
407
+ .codex-tool.search-result {
408
+ padding: calc(var(--theia-ui-padding) / 2) 0;
409
+ border-bottom: var(--theia-border-width) solid var(--theia-sideBarSectionHeader-border);
410
+ }
411
+
412
+ .codex-tool.search-result:last-child {
413
+ border-bottom: none;
414
+ }
415
+
416
+ .codex-tool.search-result-title {
417
+ font-weight: 600;
418
+ color: var(--theia-foreground);
419
+ margin-bottom: calc(var(--theia-ui-padding) / 3);
420
+ }
421
+
422
+ .codex-tool.search-result-link {
423
+ color: var(--theia-textLink-foreground);
424
+ text-decoration: none;
425
+ }
426
+
427
+ .codex-tool.search-result-link:hover {
428
+ text-decoration: underline;
429
+ }
430
+
431
+ .codex-tool.search-result-url {
432
+ color: var(--theia-descriptionForeground);
433
+ font-size: var(--theia-ui-font-size0);
434
+ font-family: var(--theia-ui-font-family-mono);
435
+ margin-bottom: calc(var(--theia-ui-padding) / 3);
436
+ word-break: break-all;
437
+ }
438
+
439
+ .codex-tool.search-result-snippet {
440
+ color: var(--theia-foreground);
441
+ font-size: var(--theia-ui-font-size0);
442
+ line-height: 1.4;
443
+ }
File without changes
@@ -0,0 +1,32 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/common/ai-core-preferences';
18
+ import { nls, PreferenceSchema } from '@theia/core';
19
+
20
+ export const CODEX_API_KEY_PREF = 'ai-features.codex.apiKey';
21
+
22
+ export const CodexPreferencesSchema: PreferenceSchema = {
23
+ properties: {
24
+ [CODEX_API_KEY_PREF]: {
25
+ type: 'string',
26
+ markdownDescription: nls.localize('theia/ai/codex/apiKey/description',
27
+ 'OpenAI API key for Codex. If not set, falls back to the shared OpenAI API key (`ai-features.openAiOfficial.openAiApiKey`). ' +
28
+ 'Can also be set via `OPENAI_API_KEY` environment variable.'),
29
+ title: AI_CORE_PREFERENCES_TITLE,
30
+ },
31
+ }
32
+ };
@@ -0,0 +1,46 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import type {
18
+ ThreadEvent,
19
+ ThreadOptions
20
+ } from '@openai/codex-sdk';
21
+
22
+ export const CODEX_SERVICE_PATH = '/services/codex';
23
+
24
+ export interface CodexRequest {
25
+ prompt: string;
26
+ options?: Partial<ThreadOptions>;
27
+ sessionId?: string;
28
+ sandboxMode?: 'read-only' | 'workspace-write' | 'danger-full-access';
29
+ }
30
+
31
+ export interface CodexBackendRequest extends CodexRequest {
32
+ apiKey?: string;
33
+ sessionId?: string;
34
+ }
35
+
36
+ export const CodexClient = Symbol('CodexClient');
37
+ export interface CodexClient {
38
+ sendToken(streamId: string, token?: ThreadEvent): void;
39
+ sendError(streamId: string, error: Error): void;
40
+ }
41
+
42
+ export const CodexService = Symbol('CodexService');
43
+ export interface CodexService {
44
+ send(request: CodexBackendRequest, streamId: string): Promise<void>;
45
+ cancel(streamId: string): void;
46
+ }
@@ -0,0 +1,18 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ export * from './codex-service';
18
+ export * from './codex-preferences';
@@ -0,0 +1,42 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { ConnectionHandler, RpcConnectionHandler } from '@theia/core';
18
+ import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module';
19
+ import { ContainerModule } from '@theia/core/shared/inversify';
20
+ import {
21
+ CODEX_SERVICE_PATH,
22
+ CodexClient,
23
+ CodexService
24
+ } from '../common/codex-service';
25
+ import { CodexServiceImpl } from './codex-service-impl';
26
+
27
+ const codexConnectionModule = ConnectionContainerModule.create(({ bind }) => {
28
+ bind(CodexServiceImpl).toSelf().inSingletonScope();
29
+ bind(CodexService).toService(CodexServiceImpl);
30
+
31
+ bind(ConnectionHandler).toDynamicValue(ctx =>
32
+ new RpcConnectionHandler<CodexClient>(CODEX_SERVICE_PATH, client => {
33
+ const server = ctx.container.get<CodexServiceImpl>(CodexService);
34
+ server.setClient(client);
35
+ return server;
36
+ })
37
+ ).inSingletonScope();
38
+ });
39
+
40
+ export default new ContainerModule(bind => {
41
+ bind(ConnectionContainerModule).toConstantValue(codexConnectionModule);
42
+ });
@@ -0,0 +1,104 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { ILogger } from '@theia/core';
18
+ import { inject, injectable, named } from '@theia/core/shared/inversify';
19
+ import type { Thread } from '@openai/codex-sdk';
20
+ import {
21
+ CodexBackendRequest,
22
+ CodexClient,
23
+ CodexService
24
+ } from '../common/codex-service';
25
+
26
+ @injectable()
27
+ export class CodexServiceImpl implements CodexService {
28
+
29
+ @inject(ILogger) @named('Codex')
30
+ private logger: ILogger;
31
+
32
+ private client: CodexClient;
33
+ private sessionThreads = new Map<string, Thread>();
34
+ private abortControllers = new Map<string, AbortController>();
35
+
36
+ setClient(client: CodexClient): void {
37
+ this.client = client;
38
+ }
39
+
40
+ async send(request: CodexBackendRequest, streamId: string): Promise<void> {
41
+ if (!this.client) {
42
+ throw new Error('Codex client not initialized');
43
+ }
44
+ this.sendMessages(streamId, request);
45
+ }
46
+
47
+ protected async sendMessages(streamId: string, request: CodexBackendRequest): Promise<void> {
48
+ const abortController = new AbortController();
49
+ this.abortControllers.set(streamId, abortController);
50
+
51
+ try {
52
+ const dynamicImport = new Function('specifier', 'return import(specifier)') as (specifier: string) => Promise<typeof import('@openai/codex-sdk')>;
53
+ const { Codex } = await dynamicImport('@openai/codex-sdk');
54
+ const codex = new Codex();
55
+
56
+ const sessionId = request.sessionId || streamId;
57
+ let thread = this.sessionThreads.get(sessionId);
58
+ if (!thread) {
59
+ thread = codex.startThread(request.options);
60
+ this.sessionThreads.set(sessionId, thread);
61
+ this.logger.info(`Created new Codex thread for session: ${sessionId}`);
62
+ } else {
63
+ this.logger.info(`Reusing existing Codex thread for session: ${sessionId}`);
64
+ }
65
+
66
+ const { events } = await thread.runStreamed(request.prompt);
67
+
68
+ for await (const event of events) {
69
+ if (abortController.signal.aborted) {
70
+ this.logger.info('Codex request cancelled:', streamId);
71
+ break;
72
+ }
73
+
74
+ this.client.sendToken(streamId, event as Parameters<CodexClient['sendToken']>[1]);
75
+
76
+ if (typeof event === 'object' && event !== undefined && 'type' in event) {
77
+ const eventType = (event as { type: string }).type;
78
+ if (eventType === 'turn.completed' || eventType === 'turn.failed') {
79
+ break;
80
+ }
81
+ }
82
+ }
83
+
84
+ this.client.sendToken(streamId, undefined);
85
+ } catch (e) {
86
+ this.logger.error('Codex error:', e);
87
+ this.client.sendError(streamId, e instanceof Error ? e : new Error(String(e)));
88
+ } finally {
89
+ this.cleanup(streamId);
90
+ }
91
+ }
92
+
93
+ cancel(streamId: string): void {
94
+ const abortController = this.abortControllers.get(streamId);
95
+ if (abortController) {
96
+ abortController.abort('user canceled');
97
+ }
98
+ this.cleanup(streamId);
99
+ }
100
+
101
+ protected cleanup(streamId: string): void {
102
+ this.abortControllers.delete(streamId);
103
+ }
104
+ }