@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.
- package/README.md +70 -0
- package/package.json +59 -0
- package/src/browser/codex-chat-agent.spec.ts +672 -0
- package/src/browser/codex-chat-agent.ts +654 -0
- package/src/browser/codex-frontend-module.ts +61 -0
- package/src/browser/codex-frontend-service.spec.ts +245 -0
- package/src/browser/codex-frontend-service.ts +221 -0
- package/src/browser/codex-tool-call-content.ts +42 -0
- package/src/browser/renderers/collapsible-tool-renderer.tsx +75 -0
- package/src/browser/renderers/command-execution-renderer.tsx +104 -0
- package/src/browser/renderers/todo-list-renderer.tsx +151 -0
- package/src/browser/renderers/web-search-renderer.tsx +88 -0
- package/src/browser/style/.gitkeep +0 -0
- package/src/browser/style/codex-tool-renderers.css +443 -0
- package/src/common/.gitkeep +0 -0
- package/src/common/codex-preferences.ts +32 -0
- package/src/common/codex-service.ts +46 -0
- package/src/common/index.ts +18 -0
- package/src/node/codex-backend-module.ts +42 -0
- package/src/node/codex-service-impl.ts +104 -0
|
@@ -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 { ChatResponseContent } from '@theia/ai-chat';
|
|
18
|
+
import { ChatResponsePartRenderer } from '@theia/ai-chat-ui/lib/browser/chat-response-part-renderer';
|
|
19
|
+
import { codicon } from '@theia/core/lib/browser';
|
|
20
|
+
import { injectable } from '@theia/core/shared/inversify';
|
|
21
|
+
import * as React from '@theia/core/shared/react';
|
|
22
|
+
import { ReactNode } from '@theia/core/shared/react';
|
|
23
|
+
import type { CommandExecutionItem } from '@openai/codex-sdk';
|
|
24
|
+
import { CodexToolCallChatResponseContent } from '../codex-tool-call-content';
|
|
25
|
+
import { nls } from '@theia/core';
|
|
26
|
+
|
|
27
|
+
@injectable()
|
|
28
|
+
export class CommandExecutionRenderer implements ChatResponsePartRenderer<CodexToolCallChatResponseContent> {
|
|
29
|
+
canHandle(response: ChatResponseContent): number {
|
|
30
|
+
return response.kind === 'toolCall' &&
|
|
31
|
+
(response as CodexToolCallChatResponseContent).name === 'command_execution'
|
|
32
|
+
? 100
|
|
33
|
+
: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
render(content: CodexToolCallChatResponseContent): ReactNode {
|
|
37
|
+
let item: CommandExecutionItem | undefined;
|
|
38
|
+
|
|
39
|
+
if (content.result) {
|
|
40
|
+
try {
|
|
41
|
+
item = typeof content.result === 'string'
|
|
42
|
+
? JSON.parse(content.result)
|
|
43
|
+
: content.result as CommandExecutionItem;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Failed to parse command execution result:', error);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!item) {
|
|
51
|
+
const args = content.arguments ? JSON.parse(content.arguments) : {};
|
|
52
|
+
return <CommandExecutionInProgressComponent command={args.command || 'executing...'} />;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return <CommandExecutionComponent item={item} />;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const CommandExecutionInProgressComponent: React.FC<{
|
|
60
|
+
command: string;
|
|
61
|
+
}> = ({ command }) => (
|
|
62
|
+
<div className="codex-command-execution">
|
|
63
|
+
<div className="codex-tool-header">
|
|
64
|
+
<span className={`${codicon('loading')} codex-tool-icon codex-loading`}></span>
|
|
65
|
+
<span className="codex-tool-name">{nls.localizeByDefault('Terminal')}</span>
|
|
66
|
+
<code className="codex-command-line">{command}</code>
|
|
67
|
+
<span className="codex-exit-code in-progress">
|
|
68
|
+
{nls.localize('theia/ai/codex/running', 'Running...')}
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const CommandExecutionComponent: React.FC<{
|
|
75
|
+
item: CommandExecutionItem;
|
|
76
|
+
}> = ({ item }) => {
|
|
77
|
+
const [isExpanded, setIsExpanded] = React.useState(false);
|
|
78
|
+
const success = item.exit_code === 0;
|
|
79
|
+
|
|
80
|
+
const hasOutput = item.aggregated_output && item.aggregated_output.trim().length > 0;
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className="codex-command-execution">
|
|
84
|
+
<div
|
|
85
|
+
className={`codex-tool-header${hasOutput ? ' expandable' : ''}`}
|
|
86
|
+
onClick={() => hasOutput && setIsExpanded(!isExpanded)}
|
|
87
|
+
style={{ cursor: hasOutput ? 'pointer' : 'default' }}
|
|
88
|
+
>
|
|
89
|
+
{hasOutput && (
|
|
90
|
+
<span className={`${codicon(isExpanded ? 'chevron-down' : 'chevron-right')} codex-expand-icon`} />
|
|
91
|
+
)}
|
|
92
|
+
<span className={`${codicon('terminal')} codex-tool-icon`}></span>
|
|
93
|
+
<span className="codex-tool-name">{nls.localizeByDefault('Terminal')}</span>
|
|
94
|
+
<code className="codex-command-line">{item.command}</code>
|
|
95
|
+
<span className={`codex-exit-code ${success ? 'success' : 'error'}`}>
|
|
96
|
+
{nls.localize('theia/ai/codex/exitCode', 'Exit code: {0}', item.exit_code)}
|
|
97
|
+
</span>
|
|
98
|
+
</div>
|
|
99
|
+
{hasOutput && isExpanded && (
|
|
100
|
+
<pre className="codex-command-output">{item.aggregated_output}</pre>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
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 { ChatResponsePartRenderer } from '@theia/ai-chat-ui/lib/browser/chat-response-part-renderer';
|
|
18
|
+
import { ResponseNode } from '@theia/ai-chat-ui/lib/browser/chat-tree-view';
|
|
19
|
+
import { ChatResponseContent } from '@theia/ai-chat/lib/common';
|
|
20
|
+
import { codicon } from '@theia/core/lib/browser';
|
|
21
|
+
import { nls } from '@theia/core';
|
|
22
|
+
import { injectable } from '@theia/core/shared/inversify';
|
|
23
|
+
import * as React from '@theia/core/shared/react';
|
|
24
|
+
import { ReactNode } from '@theia/core/shared/react';
|
|
25
|
+
import type { TodoListItem } from '@openai/codex-sdk';
|
|
26
|
+
import { CodexToolCallChatResponseContent } from '../codex-tool-call-content';
|
|
27
|
+
import { CollapsibleToolRenderer } from './collapsible-tool-renderer';
|
|
28
|
+
|
|
29
|
+
@injectable()
|
|
30
|
+
export class TodoListRenderer implements ChatResponsePartRenderer<CodexToolCallChatResponseContent> {
|
|
31
|
+
|
|
32
|
+
canHandle(response: ChatResponseContent): number {
|
|
33
|
+
return response.kind === 'toolCall' &&
|
|
34
|
+
(response as CodexToolCallChatResponseContent).name === 'todo_list'
|
|
35
|
+
? 15
|
|
36
|
+
: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
render(content: CodexToolCallChatResponseContent, parentNode: ResponseNode): ReactNode {
|
|
40
|
+
let item: TodoListItem | undefined;
|
|
41
|
+
|
|
42
|
+
if (content.result) {
|
|
43
|
+
try {
|
|
44
|
+
item = typeof content.result === 'string'
|
|
45
|
+
? JSON.parse(content.result)
|
|
46
|
+
: content.result as TodoListItem;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('[TodoListRenderer] Failed to parse todo_list result:', error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!item && content.arguments) {
|
|
53
|
+
try {
|
|
54
|
+
const args = JSON.parse(content.arguments);
|
|
55
|
+
if (args.items) {
|
|
56
|
+
item = {
|
|
57
|
+
id: args.id || 'unknown',
|
|
58
|
+
type: 'todo_list',
|
|
59
|
+
items: args.items
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('[TodoListRenderer] Failed to parse todo_list arguments:', error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!item) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return <TodoListComponent item={item} />;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const TodoListComponent: React.FC<{ item: TodoListItem }> = ({ item }) => {
|
|
76
|
+
const items = item.items || [];
|
|
77
|
+
const completedCount = items.filter(todo => todo.completed).length;
|
|
78
|
+
const totalCount = items.length;
|
|
79
|
+
|
|
80
|
+
if (totalCount === 0) {
|
|
81
|
+
// Show empty state
|
|
82
|
+
return (
|
|
83
|
+
<div className="codex-tool container">
|
|
84
|
+
<div className="codex-tool header">
|
|
85
|
+
<div className="codex-tool header-left">
|
|
86
|
+
<span className="codex-tool title">{nls.localize('theia/ai/codex/todoList', 'Todo List')}</span>
|
|
87
|
+
<span className={`${codicon('checklist')} codex-tool icon`} />
|
|
88
|
+
<span className="codex-tool progress-text">
|
|
89
|
+
{nls.localize('theia/ai/codex/loading', 'Loading...')}
|
|
90
|
+
</span>
|
|
91
|
+
</div>
|
|
92
|
+
<div className="codex-tool header-right">
|
|
93
|
+
<span className="codex-tool badge">
|
|
94
|
+
{nls.localize('theia/ai/codex/noItems', 'No items')}
|
|
95
|
+
</span>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const compactHeader = (
|
|
103
|
+
<>
|
|
104
|
+
<div className="codex-tool header-left">
|
|
105
|
+
<span className="codex-tool title">{nls.localize('theia/ai/codex/todoList', 'Todo List')}</span>
|
|
106
|
+
<span className={`${codicon('checklist')} codex-tool icon`} />
|
|
107
|
+
<span className="codex-tool progress-text">
|
|
108
|
+
{nls.localize('theia/ai/codex/completedCount', '{0}/{1} completed', completedCount, totalCount)}
|
|
109
|
+
</span>
|
|
110
|
+
</div>
|
|
111
|
+
<div className="codex-tool header-right">
|
|
112
|
+
<span className="codex-tool badge">
|
|
113
|
+
{totalCount === 1
|
|
114
|
+
? nls.localize('theia/ai/codex/oneItem', '1 item')
|
|
115
|
+
: nls.localize('theia/ai/codex/itemCount', '{0} items', totalCount)}
|
|
116
|
+
</span>
|
|
117
|
+
</div>
|
|
118
|
+
</>
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const expandedContent = (
|
|
122
|
+
<div className="codex-tool details">
|
|
123
|
+
<div className="codex-tool todo-list-items">
|
|
124
|
+
{items.map((todo, index) => {
|
|
125
|
+
const statusIcon = todo.completed
|
|
126
|
+
? <span className={`${codicon('check')} codex-tool todo-status-icon completed`} />
|
|
127
|
+
: <span className={`${codicon('circle-outline')} codex-tool todo-status-icon pending`} />;
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<div key={index} className={`codex-tool todo-item ${todo.completed ? 'status-completed' : 'status-pending'}`}>
|
|
131
|
+
<div className="codex-tool todo-item-main">
|
|
132
|
+
<div className="codex-tool todo-item-status">{statusIcon}</div>
|
|
133
|
+
<div className="codex-tool todo-item-content">
|
|
134
|
+
<span className="codex-tool todo-item-text">{todo.text}</span>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
})}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<CollapsibleToolRenderer
|
|
146
|
+
compactHeader={compactHeader}
|
|
147
|
+
expandedContent={expandedContent}
|
|
148
|
+
defaultExpanded={true}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
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 { ChatResponsePartRenderer } from '@theia/ai-chat-ui/lib/browser/chat-response-part-renderer';
|
|
18
|
+
import { ChatResponseContent } from '@theia/ai-chat/lib/common';
|
|
19
|
+
import { codicon } from '@theia/core/lib/browser';
|
|
20
|
+
import { nls } from '@theia/core';
|
|
21
|
+
import { injectable } from '@theia/core/shared/inversify';
|
|
22
|
+
import * as React from '@theia/core/shared/react';
|
|
23
|
+
import { ReactNode } from '@theia/core/shared/react';
|
|
24
|
+
import type { WebSearchItem } from '@openai/codex-sdk';
|
|
25
|
+
import { CodexToolCallChatResponseContent } from '../codex-tool-call-content';
|
|
26
|
+
|
|
27
|
+
@injectable()
|
|
28
|
+
export class WebSearchRenderer implements ChatResponsePartRenderer<CodexToolCallChatResponseContent> {
|
|
29
|
+
|
|
30
|
+
canHandle(response: ChatResponseContent): number {
|
|
31
|
+
return response.kind === 'toolCall' &&
|
|
32
|
+
(response as CodexToolCallChatResponseContent).name === 'web_search'
|
|
33
|
+
? 15
|
|
34
|
+
: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
render(content: CodexToolCallChatResponseContent): ReactNode {
|
|
38
|
+
let item: WebSearchItem | undefined;
|
|
39
|
+
|
|
40
|
+
if (content.result) {
|
|
41
|
+
try {
|
|
42
|
+
item = typeof content.result === 'string'
|
|
43
|
+
? JSON.parse(content.result)
|
|
44
|
+
: content.result as WebSearchItem;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('Failed to parse web_search result:', error);
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!item) {
|
|
52
|
+
const args = content.arguments ? JSON.parse(content.arguments) : {};
|
|
53
|
+
return <WebSearchInProgressComponent query={args.query || ''} />;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return <WebSearchCompletedComponent item={item} />;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const WebSearchInProgressComponent: React.FC<{ query: string }> = ({ query }) => (
|
|
61
|
+
<div className="codex-tool container">
|
|
62
|
+
<div className="codex-tool header">
|
|
63
|
+
<div className="codex-tool header-left">
|
|
64
|
+
<span className="codex-tool title">{nls.localize('theia/ai/codex/searching', 'Searching')}</span>
|
|
65
|
+
<span className={`${codicon('loading')} codex-tool icon theia-animation-spin`} />
|
|
66
|
+
<span className="codex-tool command">{query}</span>
|
|
67
|
+
</div>
|
|
68
|
+
<div className="codex-tool header-right">
|
|
69
|
+
<span className="codex-tool badge">{nls.localize('theia/ai/codex/webSearch', 'Web Search')}</span>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const WebSearchCompletedComponent: React.FC<{ item: WebSearchItem }> = ({ item }) => (
|
|
76
|
+
<div className="codex-tool container">
|
|
77
|
+
<div className="codex-tool header">
|
|
78
|
+
<div className="codex-tool header-left">
|
|
79
|
+
<span className="codex-tool title">{nls.localize('theia/ai/codex/searched', 'Searched')}</span>
|
|
80
|
+
<span className={`${codicon('globe')} codex-tool icon`} />
|
|
81
|
+
<span className="codex-tool command">{item.query}</span>
|
|
82
|
+
</div>
|
|
83
|
+
<div className="codex-tool header-right">
|
|
84
|
+
<span className="codex-tool badge">{nls.localize('theia/ai/codex/webSearch', 'Web Search')}</span>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
File without changes
|