aiexecode 1.0.94 → 1.0.127
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.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/README.md +198 -88
- package/index.js +310 -86
- package/mcp-agent-lib/src/mcp_message_logger.js +17 -16
- package/package.json +4 -4
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/_next/static/chunks/{37d0cd2587a38f79.js → b6c0459f3789d25c.js} +1 -1
- package/payload_viewer/out/_next/static/chunks/b75131b58f8ca46a.css +3 -0
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +3 -3
- package/payload_viewer/web_server.js +361 -0
- package/prompts/completion_judge.txt +4 -0
- package/prompts/orchestrator.txt +116 -3
- package/src/LLMClient/client.js +401 -18
- package/src/LLMClient/converters/responses-to-claude.js +67 -18
- package/src/LLMClient/converters/responses-to-zai.js +667 -0
- package/src/LLMClient/errors.js +30 -4
- package/src/LLMClient/index.js +5 -0
- package/src/ai_based/completion_judge.js +263 -186
- package/src/ai_based/orchestrator.js +171 -35
- package/src/commands/agents.js +70 -0
- package/src/commands/apikey.js +1 -1
- package/src/commands/bg.js +129 -0
- package/src/commands/commands.js +51 -0
- package/src/commands/debug.js +52 -0
- package/src/commands/help.js +11 -1
- package/src/commands/model.js +42 -7
- package/src/commands/reasoning_effort.js +2 -2
- package/src/commands/skills.js +46 -0
- package/src/config/ai_models.js +106 -6
- package/src/config/constants.js +71 -0
- package/src/config/feature_flags.js +6 -7
- package/src/frontend/App.js +108 -1
- package/src/frontend/components/AutocompleteMenu.js +7 -1
- package/src/frontend/components/BackgroundProcessList.js +175 -0
- package/src/frontend/components/ConversationItem.js +26 -10
- package/src/frontend/components/CurrentModelView.js +2 -2
- package/src/frontend/components/HelpView.js +106 -2
- package/src/frontend/components/Input.js +33 -11
- package/src/frontend/components/ModelListView.js +1 -1
- package/src/frontend/components/SetupWizard.js +51 -8
- package/src/frontend/hooks/useFileCompletion.js +467 -0
- package/src/frontend/utils/toolUIFormatter.js +261 -0
- package/src/system/agents_loader.js +289 -0
- package/src/system/ai_request.js +156 -12
- package/src/system/background_process.js +317 -0
- package/src/system/code_executer.js +496 -56
- package/src/system/command_parser.js +33 -3
- package/src/system/conversation_state.js +265 -0
- package/src/system/conversation_trimmer.js +132 -0
- package/src/system/custom_command_loader.js +386 -0
- package/src/system/file_integrity.js +73 -10
- package/src/system/log.js +10 -2
- package/src/system/output_helper.js +52 -9
- package/src/system/session.js +213 -58
- package/src/system/session_memory.js +30 -2
- package/src/system/skill_loader.js +318 -0
- package/src/system/system_info.js +254 -40
- package/src/system/tool_approval.js +10 -0
- package/src/system/tool_registry.js +15 -1
- package/src/system/ui_events.js +11 -0
- package/src/tools/code_editor.js +16 -10
- package/src/tools/file_reader.js +66 -9
- package/src/tools/glob.js +0 -3
- package/src/tools/ripgrep.js +5 -7
- package/src/tools/skill_tool.js +122 -0
- package/src/tools/web_downloader.js +0 -3
- package/src/util/clone.js +174 -0
- package/src/util/config.js +55 -2
- package/src/util/config_migration.js +174 -0
- package/src/util/debug_log.js +8 -2
- package/src/util/exit_handler.js +8 -0
- package/src/util/file_reference_parser.js +132 -0
- package/src/util/path_validator.js +178 -0
- package/src/util/prompt_loader.js +91 -1
- package/src/util/safe_fs.js +66 -3
- package/payload_viewer/out/_next/static/chunks/ecd2072ebf41611f.css +0 -3
- /package/payload_viewer/out/_next/static/{wkEKh6i9XPSyP6rjDRvHn → 42iEoi-1o5MxNIZ1SWSvV}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{wkEKh6i9XPSyP6rjDRvHn → 42iEoi-1o5MxNIZ1SWSvV}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{wkEKh6i9XPSyP6rjDRvHn → 42iEoi-1o5MxNIZ1SWSvV}/_ssgManifest.js +0 -0
package/src/commands/model.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { uiEvents } from '../system/ui_events.js';
|
|
3
|
-
import { loadSettings, saveSettings, SETTINGS_FILE } from '../util/config.js';
|
|
3
|
+
import { loadSettings, saveSettings, SETTINGS_FILE, getDefaultBaseUrlForProvider } from '../util/config.js';
|
|
4
4
|
import { resetAIClients } from '../system/ai_request.js';
|
|
5
|
-
import { AI_MODELS, getModelsByProvider, DEFAULT_MODEL } from '../config/ai_models.js';
|
|
5
|
+
import { AI_MODELS, getModelsByProvider, DEFAULT_MODEL, ENABLED_PROVIDERS, isModelEnabled } from '../config/ai_models.js';
|
|
6
6
|
import { renderInkComponent } from '../frontend/utils/renderInkComponent.js';
|
|
7
7
|
import { ModelListView } from '../frontend/components/ModelListView.js';
|
|
8
8
|
import { CurrentModelView } from '../frontend/components/CurrentModelView.js';
|
|
@@ -14,10 +14,10 @@ function getProviderForModel(modelId) {
|
|
|
14
14
|
return modelInfo ? modelInfo.provider : null;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// 모든 모델 목록 표시
|
|
17
|
+
// 모든 모델 목록 표시 (공식 지원 provider만)
|
|
18
18
|
async function listAllModels() {
|
|
19
|
-
//
|
|
20
|
-
const providers =
|
|
19
|
+
// 공식 지원 provider만 사용
|
|
20
|
+
const providers = ENABLED_PROVIDERS;
|
|
21
21
|
|
|
22
22
|
// provider별로 모델 그룹화
|
|
23
23
|
const modelsByProvider = {};
|
|
@@ -63,7 +63,7 @@ async function showCurrentModel() {
|
|
|
63
63
|
*/
|
|
64
64
|
export default {
|
|
65
65
|
name: 'model',
|
|
66
|
-
description: 'Select AI model
|
|
66
|
+
description: 'Select AI model',
|
|
67
67
|
usage: '/model [model-id] or /model list',
|
|
68
68
|
handler: async (args, context) => {
|
|
69
69
|
// 인자가 없으면 현재 모델 표시
|
|
@@ -94,6 +94,16 @@ export default {
|
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// 공식 지원 provider 확인
|
|
98
|
+
if (!isModelEnabled(modelId)) {
|
|
99
|
+
uiEvents.addSystemMessage(
|
|
100
|
+
`Model not available: ${modelId}\n\n` +
|
|
101
|
+
`This model is not officially supported.\n` +
|
|
102
|
+
`Use \`/model list\` to see available models.`
|
|
103
|
+
);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
97
107
|
try {
|
|
98
108
|
// 현재 설정 로드
|
|
99
109
|
const settings = await loadSettings();
|
|
@@ -102,6 +112,20 @@ export default {
|
|
|
102
112
|
settings.MODEL = modelId;
|
|
103
113
|
process.env.MODEL = modelId;
|
|
104
114
|
|
|
115
|
+
// Provider별 BASE_URL 자동 설정
|
|
116
|
+
const defaultBaseUrl = getDefaultBaseUrlForProvider(provider);
|
|
117
|
+
if (defaultBaseUrl) {
|
|
118
|
+
// Z.AI 등 특정 provider는 BASE_URL 자동 설정
|
|
119
|
+
settings.BASE_URL = defaultBaseUrl;
|
|
120
|
+
process.env.BASE_URL = defaultBaseUrl;
|
|
121
|
+
} else if (provider !== 'zai') {
|
|
122
|
+
// 다른 provider로 변경 시 BASE_URL 초기화 (Z.AI에서 다른 모델로 변경)
|
|
123
|
+
if (settings.BASE_URL === 'https://api.z.ai/api/anthropic') {
|
|
124
|
+
settings.BASE_URL = '';
|
|
125
|
+
delete process.env.BASE_URL;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
105
129
|
// 설정 저장
|
|
106
130
|
await saveSettings(settings);
|
|
107
131
|
|
|
@@ -114,11 +138,22 @@ export default {
|
|
|
114
138
|
// 성공 메시지
|
|
115
139
|
const modelInfo = AI_MODELS[modelId];
|
|
116
140
|
|
|
141
|
+
// Provider별 경고 메시지 구성
|
|
117
142
|
let warning = null;
|
|
118
143
|
if (!settings.API_KEY) {
|
|
144
|
+
// Provider별 API 키 안내 (공식 지원 provider만)
|
|
145
|
+
const apiKeyHints = {
|
|
146
|
+
'zai': 'Set your Z.AI API key with: `/apikey <your-key>`'
|
|
147
|
+
};
|
|
119
148
|
warning = {
|
|
120
149
|
message: 'API key is not configured.',
|
|
121
|
-
hint: 'Set your API key with: `/apikey
|
|
150
|
+
hint: apiKeyHints[provider] || 'Set your API key with: `/apikey <your-key>`'
|
|
151
|
+
};
|
|
152
|
+
} else if (provider === 'zai') {
|
|
153
|
+
// Z.AI 모델 선택 시 추가 안내
|
|
154
|
+
warning = {
|
|
155
|
+
message: `BASE_URL automatically set to: ${defaultBaseUrl}`,
|
|
156
|
+
hint: 'Ensure your API key is valid for Z.AI service.'
|
|
122
157
|
};
|
|
123
158
|
}
|
|
124
159
|
|
|
@@ -105,11 +105,11 @@ function listEffortLevels(currentModel) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
* /reasoning_effort 커맨드 -
|
|
108
|
+
* /reasoning_effort 커맨드 - reasoning_effort 설정
|
|
109
109
|
*/
|
|
110
110
|
export default {
|
|
111
111
|
name: 'reasoning_effort',
|
|
112
|
-
description: 'Set reasoning effort for
|
|
112
|
+
description: 'Set reasoning effort for supported models (minimal, low, medium, high)',
|
|
113
113
|
usage: '/reasoning_effort [level] or /reasoning_effort list',
|
|
114
114
|
handler: async (args, context) => {
|
|
115
115
|
// 모델 지원 확인
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { uiEvents } from '../system/ui_events.js';
|
|
2
|
+
import { formatSkillList, findSkillByName, loadSkillAsPrompt } from '../system/skill_loader.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* /skills 커맨드 - 스킬 목록 조회 및 스킬 정보 확인
|
|
6
|
+
*
|
|
7
|
+
* 사용법:
|
|
8
|
+
* /skills - 모든 스킬 목록 표시
|
|
9
|
+
* /skills <name> - 특정 스킬 상세 정보 표시
|
|
10
|
+
*/
|
|
11
|
+
export default {
|
|
12
|
+
name: 'skills',
|
|
13
|
+
description: 'List available skills or show skill details',
|
|
14
|
+
usage: '/skills [skill-name]',
|
|
15
|
+
handler: async (args, context) => {
|
|
16
|
+
if (!args || args.length === 0) {
|
|
17
|
+
// 스킬 목록 표시
|
|
18
|
+
const skillList = await formatSkillList();
|
|
19
|
+
uiEvents.addSystemMessage(skillList);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 특정 스킬 정보 표시
|
|
24
|
+
const skillName = args[0];
|
|
25
|
+
const skill = await findSkillByName(skillName);
|
|
26
|
+
|
|
27
|
+
if (!skill) {
|
|
28
|
+
uiEvents.addSystemMessage(`Skill not found: ${skillName}\n\nUse /skills to see available skills.`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const info = [
|
|
33
|
+
`Skill: ${skill.name}`,
|
|
34
|
+
`Source: ${skill.source}`,
|
|
35
|
+
`Path: ${skill.path}`,
|
|
36
|
+
'',
|
|
37
|
+
'Frontmatter:',
|
|
38
|
+
JSON.stringify(skill.frontmatter, null, 2),
|
|
39
|
+
'',
|
|
40
|
+
'Content Preview:',
|
|
41
|
+
skill.content.substring(0, 500) + (skill.content.length > 500 ? '...' : '')
|
|
42
|
+
].join('\n');
|
|
43
|
+
|
|
44
|
+
uiEvents.addSystemMessage(info);
|
|
45
|
+
}
|
|
46
|
+
};
|
package/src/config/ai_models.js
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* 새 모델 추가 시 이 파일만 수정하면 됩니다.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* 공식 지원 Provider 목록
|
|
10
|
+
* 런타임에서 이 목록에 포함된 provider의 모델만 노출됩니다.
|
|
11
|
+
* 다른 provider의 구현 코드는 유지되지만 사용자에게 노출되지 않습니다.
|
|
12
|
+
*/
|
|
13
|
+
export const ENABLED_PROVIDERS = ['zai'];
|
|
14
|
+
|
|
8
15
|
export const AI_MODELS = {
|
|
9
16
|
// ========================================
|
|
10
17
|
// Claude 시리즈
|
|
@@ -62,6 +69,49 @@ export const AI_MODELS = {
|
|
|
62
69
|
maxTokens: 4096,
|
|
63
70
|
},
|
|
64
71
|
|
|
72
|
+
// ========================================
|
|
73
|
+
// Z.AI GLM 시리즈 (Anthropic API 호환)
|
|
74
|
+
// - interleaved-thinking 지원
|
|
75
|
+
// - 프롬프트 캐싱 지원 (cache_read_input_tokens)
|
|
76
|
+
// - 서버 측 웹 검색 지원
|
|
77
|
+
// ========================================
|
|
78
|
+
'glm-4.7': {
|
|
79
|
+
provider: 'zai',
|
|
80
|
+
name: 'GLM-4.7',
|
|
81
|
+
contextWindow: 200000, // 200K
|
|
82
|
+
maxTokens: 128000, // 128K
|
|
83
|
+
supportsThinking: true,
|
|
84
|
+
supportsCaching: true,
|
|
85
|
+
supportsServerTools: true,
|
|
86
|
+
},
|
|
87
|
+
'glm-4.6': {
|
|
88
|
+
provider: 'zai',
|
|
89
|
+
name: 'GLM-4.6',
|
|
90
|
+
contextWindow: 200000, // 200K
|
|
91
|
+
maxTokens: 128000, // 128K
|
|
92
|
+
supportsThinking: true,
|
|
93
|
+
supportsCaching: true,
|
|
94
|
+
supportsServerTools: true,
|
|
95
|
+
},
|
|
96
|
+
'glm-4.5-air': {
|
|
97
|
+
provider: 'zai',
|
|
98
|
+
name: 'GLM-4.5 Air',
|
|
99
|
+
contextWindow: 128000, // 128K
|
|
100
|
+
maxTokens: 96000, // 96K
|
|
101
|
+
supportsThinking: true,
|
|
102
|
+
supportsCaching: true,
|
|
103
|
+
supportsServerTools: true,
|
|
104
|
+
},
|
|
105
|
+
'glm-4.5': {
|
|
106
|
+
provider: 'zai',
|
|
107
|
+
name: 'GLM-4.5',
|
|
108
|
+
contextWindow: 128000, // 128K
|
|
109
|
+
maxTokens: 96000, // 96K
|
|
110
|
+
supportsThinking: true,
|
|
111
|
+
supportsCaching: true,
|
|
112
|
+
supportsServerTools: true,
|
|
113
|
+
},
|
|
114
|
+
|
|
65
115
|
// ========================================
|
|
66
116
|
// Google Gemini 시리즈
|
|
67
117
|
// ========================================
|
|
@@ -137,6 +187,14 @@ export function getModelInfo(modelId) {
|
|
|
137
187
|
return AI_MODELS[modelId] || null;
|
|
138
188
|
}
|
|
139
189
|
|
|
190
|
+
/**
|
|
191
|
+
* 모델이 공식 지원 provider인지 확인
|
|
192
|
+
*/
|
|
193
|
+
export function isModelEnabled(modelId) {
|
|
194
|
+
const model = AI_MODELS[modelId];
|
|
195
|
+
return model ? ENABLED_PROVIDERS.includes(model.provider) : false;
|
|
196
|
+
}
|
|
197
|
+
|
|
140
198
|
/**
|
|
141
199
|
* 모델 ID로 max_tokens 가져오기
|
|
142
200
|
*/
|
|
@@ -154,16 +212,21 @@ export function getContextWindow(modelId) {
|
|
|
154
212
|
}
|
|
155
213
|
|
|
156
214
|
/**
|
|
157
|
-
* 모든 모델 ID 목록 가져오기
|
|
215
|
+
* 모든 모델 ID 목록 가져오기 (공식 지원 provider만)
|
|
158
216
|
*/
|
|
159
217
|
export function getAllModelIds() {
|
|
160
|
-
return Object.keys(AI_MODELS)
|
|
218
|
+
return Object.keys(AI_MODELS).filter(
|
|
219
|
+
modelId => ENABLED_PROVIDERS.includes(AI_MODELS[modelId].provider)
|
|
220
|
+
);
|
|
161
221
|
}
|
|
162
222
|
|
|
163
223
|
/**
|
|
164
|
-
* 특정 제조사의 모델 ID 목록 가져오기
|
|
224
|
+
* 특정 제조사의 모델 ID 목록 가져오기 (공식 지원 provider만)
|
|
165
225
|
*/
|
|
166
226
|
export function getModelsByProvider(provider) {
|
|
227
|
+
if (!ENABLED_PROVIDERS.includes(provider)) {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
167
230
|
return Object.keys(AI_MODELS).filter(
|
|
168
231
|
modelId => AI_MODELS[modelId].provider === provider
|
|
169
232
|
);
|
|
@@ -177,14 +240,51 @@ export function getGPT5Models() {
|
|
|
177
240
|
}
|
|
178
241
|
|
|
179
242
|
/**
|
|
180
|
-
* Reasoning 지원 모델 ID 목록
|
|
243
|
+
* Reasoning 지원 모델 ID 목록 (공식 지원 provider만)
|
|
181
244
|
*/
|
|
182
245
|
export function getReasoningModels() {
|
|
183
246
|
return Object.keys(AI_MODELS).filter(
|
|
184
|
-
modelId => AI_MODELS[modelId].supportsReasoning
|
|
247
|
+
modelId => AI_MODELS[modelId].supportsReasoning &&
|
|
248
|
+
ENABLED_PROVIDERS.includes(AI_MODELS[modelId].provider)
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Thinking 지원 모델 ID 목록 (Z.AI GLM 등, 공식 지원 provider만)
|
|
254
|
+
*/
|
|
255
|
+
export function getThinkingModels() {
|
|
256
|
+
return Object.keys(AI_MODELS).filter(
|
|
257
|
+
modelId => AI_MODELS[modelId].supportsThinking &&
|
|
258
|
+
ENABLED_PROVIDERS.includes(AI_MODELS[modelId].provider)
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 캐싱 지원 모델 ID 목록 (공식 지원 provider만)
|
|
264
|
+
*/
|
|
265
|
+
export function getCachingModels() {
|
|
266
|
+
return Object.keys(AI_MODELS).filter(
|
|
267
|
+
modelId => AI_MODELS[modelId].supportsCaching &&
|
|
268
|
+
ENABLED_PROVIDERS.includes(AI_MODELS[modelId].provider)
|
|
185
269
|
);
|
|
186
270
|
}
|
|
187
271
|
|
|
272
|
+
/**
|
|
273
|
+
* 특정 모델이 thinking을 지원하는지 확인
|
|
274
|
+
*/
|
|
275
|
+
export function supportsThinking(modelId) {
|
|
276
|
+
const model = AI_MODELS[modelId];
|
|
277
|
+
return model?.supportsThinking || false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* 특정 모델이 캐싱을 지원하는지 확인
|
|
282
|
+
*/
|
|
283
|
+
export function supportsCaching(modelId) {
|
|
284
|
+
const model = AI_MODELS[modelId];
|
|
285
|
+
return model?.supportsCaching || false;
|
|
286
|
+
}
|
|
287
|
+
|
|
188
288
|
/**
|
|
189
289
|
* 특정 모델이 특정 reasoning effort를 지원하는지 확인
|
|
190
290
|
*/
|
|
@@ -199,7 +299,7 @@ export function supportsReasoningEffort(modelId, effort) {
|
|
|
199
299
|
/**
|
|
200
300
|
* 기본 권장 모델 ID
|
|
201
301
|
*/
|
|
202
|
-
export const DEFAULT_MODEL = '
|
|
302
|
+
export const DEFAULT_MODEL = 'glm-4.5';
|
|
203
303
|
|
|
204
304
|
// ========================================
|
|
205
305
|
// 하위 호환성을 위한 별칭 (Deprecated)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 전역 상수 정의
|
|
3
|
+
* 하드코딩된 값들을 중앙에서 관리합니다.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================
|
|
7
|
+
// Session 관련 상수
|
|
8
|
+
// ============================================
|
|
9
|
+
|
|
10
|
+
/** 서브미션 이름의 최대 길이 */
|
|
11
|
+
export const SUB_MISSION_MAX_LENGTH = 120;
|
|
12
|
+
|
|
13
|
+
/** 기본 최대 반복 횟수 */
|
|
14
|
+
export const DEFAULT_MAX_ITERATIONS = 50;
|
|
15
|
+
|
|
16
|
+
/** reasoning만 있는 응답의 최대 허용 횟수 */
|
|
17
|
+
export const MAX_REASONING_ONLY_RESPONSES = 5;
|
|
18
|
+
|
|
19
|
+
// ============================================
|
|
20
|
+
// Ripgrep 관련 상수
|
|
21
|
+
// ============================================
|
|
22
|
+
|
|
23
|
+
/** 기본 타임아웃 (ms) */
|
|
24
|
+
export const RIPGREP_DEFAULT_TIMEOUT_MS = 120000;
|
|
25
|
+
|
|
26
|
+
/** 기본 최대 매치 수 */
|
|
27
|
+
export const RIPGREP_DEFAULT_MAX_COUNT = 500;
|
|
28
|
+
|
|
29
|
+
/** 최대 출력 크기 (bytes) */
|
|
30
|
+
export const RIPGREP_MAX_OUTPUT_SIZE = 30000;
|
|
31
|
+
|
|
32
|
+
// ============================================
|
|
33
|
+
// File Reader 관련 상수
|
|
34
|
+
// ============================================
|
|
35
|
+
|
|
36
|
+
/** 파일 읽기 최대 줄 수 */
|
|
37
|
+
export const FILE_READER_MAX_LINES = 2000;
|
|
38
|
+
|
|
39
|
+
/** 파일 크기 제한 (bytes) - 10MB */
|
|
40
|
+
export const FILE_READER_MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
41
|
+
|
|
42
|
+
// ============================================
|
|
43
|
+
// AI 요청 관련 상수
|
|
44
|
+
// ============================================
|
|
45
|
+
|
|
46
|
+
/** 컨텍스트 trim 후 재시도를 위한 메시지 최소 수 */
|
|
47
|
+
export const MIN_MESSAGES_FOR_RETRY = 3;
|
|
48
|
+
|
|
49
|
+
// ============================================
|
|
50
|
+
// UI/Output 관련 상수
|
|
51
|
+
// ============================================
|
|
52
|
+
|
|
53
|
+
/** 출력 클램핑 최대 길이 */
|
|
54
|
+
export const OUTPUT_CLAMP_MAX_LENGTH = 30000;
|
|
55
|
+
|
|
56
|
+
// ============================================
|
|
57
|
+
// MCP 관련 상수
|
|
58
|
+
// ============================================
|
|
59
|
+
|
|
60
|
+
/** MCP 서버 연결 타임아웃 (ms) */
|
|
61
|
+
export const MCP_CONNECTION_TIMEOUT_MS = 30000;
|
|
62
|
+
|
|
63
|
+
// ============================================
|
|
64
|
+
// 에러 재시도 관련 상수
|
|
65
|
+
// ============================================
|
|
66
|
+
|
|
67
|
+
/** 세션 저장 최대 재시도 횟수 */
|
|
68
|
+
export const SESSION_SAVE_MAX_RETRIES = 3;
|
|
69
|
+
|
|
70
|
+
/** 세션 저장 재시도 지연 (ms) */
|
|
71
|
+
export const SESSION_SAVE_RETRY_DELAY_MS = 1000;
|
|
@@ -4,13 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* 에러 메시지 상세도 설정
|
|
7
|
+
* 에러 메시지 상세도 설정 (deprecated)
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* 이 설정은 더 이상 사용되지 않습니다.
|
|
10
|
+
* 에러 상세도는 /debug on|off 명령으로 제어됩니다.
|
|
11
|
+
* - /debug off (기본): 사용자 친화적 에러 메시지
|
|
12
|
+
* - /debug on: 상세 에러 정보 (코드, 스택 트레이스 등)
|
|
11
13
|
*/
|
|
12
|
-
export const ERROR_VERBOSITY = 'verbose';
|
|
13
14
|
|
|
14
|
-
export default {
|
|
15
|
-
ERROR_VERBOSITY
|
|
16
|
-
};
|
|
15
|
+
export default {};
|
package/src/frontend/App.js
CHANGED
|
@@ -13,6 +13,7 @@ import { ConversationItem } from './components/ConversationItem.js';
|
|
|
13
13
|
import { BlankLine } from './components/BlankLine.js';
|
|
14
14
|
import { TodoList } from './components/TodoList.js';
|
|
15
15
|
import { LoadingIndicator } from './components/LoadingIndicator.js';
|
|
16
|
+
import { BackgroundProcessList } from './components/BackgroundProcessList.js';
|
|
16
17
|
import { useTextBuffer } from './utils/inputBuffer.js';
|
|
17
18
|
import { uiEvents } from '../system/ui_events.js';
|
|
18
19
|
import { getToolDisplayConfig, extractMessageFromArgs, formatToolCall, formatToolResult, getToolDisplayName } from '../system/tool_registry.js';
|
|
@@ -361,6 +362,9 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
361
362
|
const [approvalRequest, setApprovalRequest] = useState(null);
|
|
362
363
|
const [showSetupWizard, setShowSetupWizard] = useState(false);
|
|
363
364
|
const [todos, setTodos] = useState([]);
|
|
365
|
+
const [backgroundProcesses, setBackgroundProcesses] = useState([]);
|
|
366
|
+
const [showBackgroundPanel, setShowBackgroundPanel] = useState(false);
|
|
367
|
+
const [backgroundPanelFocused, setBackgroundPanelFocused] = useState(false);
|
|
364
368
|
|
|
365
369
|
// Static items: 메모이제이션된 React 엘리먼트들 (Static 컴포넌트 제거로 스크롤 문제 해결)
|
|
366
370
|
const [staticItems, setStaticItems] = useState(() => []);
|
|
@@ -888,6 +892,14 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
888
892
|
if (event.todos) {
|
|
889
893
|
debugLog(`[handleTodosUpdate] Updating todos: ${event.todos.length} items`);
|
|
890
894
|
setTodos(event.todos);
|
|
895
|
+
|
|
896
|
+
// 모든 todo가 completed 상태면 잠시 후 자동으로 제거
|
|
897
|
+
const allCompleted = event.todos.length > 0 && event.todos.every(todo => todo.status === 'completed');
|
|
898
|
+
if (allCompleted) {
|
|
899
|
+
setTimeout(() => {
|
|
900
|
+
setTodos([]);
|
|
901
|
+
}, 1500);
|
|
902
|
+
}
|
|
891
903
|
}
|
|
892
904
|
};
|
|
893
905
|
|
|
@@ -959,6 +971,94 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
959
971
|
};
|
|
960
972
|
}, [addToHistory, handleSessionTransition, version]);
|
|
961
973
|
|
|
974
|
+
// 백그라운드 프로세스 이벤트 핸들링
|
|
975
|
+
const showBackgroundPanelRef = useRef(showBackgroundPanel);
|
|
976
|
+
useEffect(() => {
|
|
977
|
+
showBackgroundPanelRef.current = showBackgroundPanel;
|
|
978
|
+
}, [showBackgroundPanel]);
|
|
979
|
+
|
|
980
|
+
useEffect(() => {
|
|
981
|
+
let manager = null;
|
|
982
|
+
let updateInterval = null;
|
|
983
|
+
let cleanupFn = null;
|
|
984
|
+
let isMounted = true;
|
|
985
|
+
|
|
986
|
+
const setupBackgroundProcessListener = async () => {
|
|
987
|
+
const { getBackgroundProcessManager } = await import('../system/background_process.js');
|
|
988
|
+
if (!isMounted) return;
|
|
989
|
+
|
|
990
|
+
manager = getBackgroundProcessManager();
|
|
991
|
+
|
|
992
|
+
const updateProcessList = () => {
|
|
993
|
+
if (!isMounted) return;
|
|
994
|
+
const processes = manager.list();
|
|
995
|
+
// 실행 중인 프로세스만 표시
|
|
996
|
+
const runningProcesses = processes.filter(p => p.status === 'running');
|
|
997
|
+
setBackgroundProcesses(runningProcesses);
|
|
998
|
+
|
|
999
|
+
// 실행 중인 프로세스가 있으면 패널 자동 표시
|
|
1000
|
+
if (runningProcesses.length > 0 && !showBackgroundPanelRef.current) {
|
|
1001
|
+
setShowBackgroundPanel(true);
|
|
1002
|
+
}
|
|
1003
|
+
// 실행 중인 프로세스가 없으면 패널 숨기기
|
|
1004
|
+
if (runningProcesses.length === 0) {
|
|
1005
|
+
setShowBackgroundPanel(false);
|
|
1006
|
+
setBackgroundPanelFocused(false);
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
// 이벤트 핸들러
|
|
1011
|
+
const handleStarted = () => {
|
|
1012
|
+
updateProcessList();
|
|
1013
|
+
};
|
|
1014
|
+
const handleClose = () => updateProcessList();
|
|
1015
|
+
const handleKilled = () => updateProcessList();
|
|
1016
|
+
|
|
1017
|
+
manager.on('started', handleStarted);
|
|
1018
|
+
manager.on('close', handleClose);
|
|
1019
|
+
manager.on('killed', handleKilled);
|
|
1020
|
+
|
|
1021
|
+
// 초기 로드
|
|
1022
|
+
updateProcessList();
|
|
1023
|
+
|
|
1024
|
+
// 주기적 업데이트 (실행 시간 표시용)
|
|
1025
|
+
updateInterval = setInterval(updateProcessList, 1000);
|
|
1026
|
+
|
|
1027
|
+
cleanupFn = () => {
|
|
1028
|
+
manager.off('started', handleStarted);
|
|
1029
|
+
manager.off('close', handleClose);
|
|
1030
|
+
manager.off('killed', handleKilled);
|
|
1031
|
+
if (updateInterval) clearInterval(updateInterval);
|
|
1032
|
+
};
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1035
|
+
setupBackgroundProcessListener();
|
|
1036
|
+
|
|
1037
|
+
return () => {
|
|
1038
|
+
isMounted = false;
|
|
1039
|
+
if (cleanupFn) cleanupFn();
|
|
1040
|
+
if (updateInterval) clearInterval(updateInterval);
|
|
1041
|
+
};
|
|
1042
|
+
}, []);
|
|
1043
|
+
|
|
1044
|
+
// 백그라운드 프로세스 kill 핸들러
|
|
1045
|
+
const handleKillBackgroundProcess = useCallback(async (id) => {
|
|
1046
|
+
const { killBackgroundProcess } = await import('../system/background_process.js');
|
|
1047
|
+
killBackgroundProcess(id);
|
|
1048
|
+
}, []);
|
|
1049
|
+
|
|
1050
|
+
// 백그라운드 패널 닫기 핸들러
|
|
1051
|
+
const handleCloseBackgroundPanel = useCallback(() => {
|
|
1052
|
+
setBackgroundPanelFocused(false);
|
|
1053
|
+
}, []);
|
|
1054
|
+
|
|
1055
|
+
// 백그라운드 패널 포커스 토글 핸들러
|
|
1056
|
+
const handleToggleBackgroundPanelFocus = useCallback(() => {
|
|
1057
|
+
if (backgroundProcesses.length > 0 && showBackgroundPanel) {
|
|
1058
|
+
setBackgroundPanelFocused(prev => !prev);
|
|
1059
|
+
}
|
|
1060
|
+
}, [backgroundProcesses.length, showBackgroundPanel]);
|
|
1061
|
+
|
|
962
1062
|
// 승인 요청 처리
|
|
963
1063
|
const handleApprovalDecision = useCallback((decision) => {
|
|
964
1064
|
if (approvalRequest && approvalRequest.resolve) {
|
|
@@ -1103,13 +1203,20 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
1103
1203
|
!approvalRequest && todos.length > 0 && React.createElement(TodoList, {
|
|
1104
1204
|
todos: todos
|
|
1105
1205
|
}),
|
|
1206
|
+
!approvalRequest && showBackgroundPanel && backgroundProcesses.length > 0 && React.createElement(BackgroundProcessList, {
|
|
1207
|
+
processes: backgroundProcesses,
|
|
1208
|
+
isActive: backgroundPanelFocused && !isSessionRunning && !approvalRequest,
|
|
1209
|
+
onKill: handleKillBackgroundProcess,
|
|
1210
|
+
onClose: handleCloseBackgroundPanel,
|
|
1211
|
+
onToggleFocus: handleToggleBackgroundPanelFocus
|
|
1212
|
+
}),
|
|
1106
1213
|
!approvalRequest && React.createElement(Input, {
|
|
1107
1214
|
buffer,
|
|
1108
1215
|
onSubmit: handleSubmit,
|
|
1109
1216
|
onClearScreen: handleClearScreen,
|
|
1110
1217
|
onExit: onExit,
|
|
1111
1218
|
commands,
|
|
1112
|
-
focus:
|
|
1219
|
+
focus: !backgroundPanelFocused,
|
|
1113
1220
|
isSessionRunning
|
|
1114
1221
|
}),
|
|
1115
1222
|
React.createElement(MemoizedFooter, { model: currentModel, reasoningEffort })
|
|
@@ -21,13 +21,19 @@ export function AutocompleteMenu({ suggestions = [], activeIndex = 0 }) {
|
|
|
21
21
|
React.createElement(Text, { color: theme.text.secondary, bold: true }, 'Suggestions'),
|
|
22
22
|
suggestions.map((suggestion, index) => {
|
|
23
23
|
const isActive = index === activeIndex;
|
|
24
|
+
// 아이콘이 있으면 표시, 없으면 값만 표시
|
|
25
|
+
const icon = suggestion.icon || '';
|
|
26
|
+
const displayText = suggestion.displayValue || suggestion.value;
|
|
27
|
+
const prefix = icon ? `${icon} ` : '';
|
|
28
|
+
|
|
24
29
|
return React.createElement(Box, { key: index, marginLeft: 1 },
|
|
25
30
|
React.createElement(Text, {
|
|
26
31
|
color: isActive ? theme.text.accent : theme.text.primary,
|
|
27
32
|
bold: isActive
|
|
28
33
|
},
|
|
29
34
|
isActive ? '▶ ' : ' ',
|
|
30
|
-
|
|
35
|
+
prefix,
|
|
36
|
+
displayText,
|
|
31
37
|
suggestion.description && React.createElement(Text, { color: theme.text.secondary },
|
|
32
38
|
' - ' + suggestion.description)
|
|
33
39
|
)
|