tagword-codeagent 1.1.6__py3-none-any.whl
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.
- codeagent/__init__.py +10 -0
- codeagent/__main__.py +13 -0
- codeagent/cli/__init__.py +13 -0
- codeagent/cli/entrypoint.py +161 -0
- codeagent/cli/main.py +979 -0
- codeagent/core/__init__.py +2 -0
- codeagent/core/attachments.py +342 -0
- codeagent/core/audio_models.py +50 -0
- codeagent/core/bootstrap.py +178 -0
- codeagent/core/default_presets.py +159 -0
- codeagent/core/env.py +106 -0
- codeagent/core/folder_picker.py +151 -0
- codeagent/core/image_understanding.py +90 -0
- codeagent/core/models.py +6 -0
- codeagent/core/paths.py +238 -0
- codeagent/core/pricing.py +107 -0
- codeagent/core/process_ports.py +258 -0
- codeagent/core/seed_bridge.py +21 -0
- codeagent/core/settings.py +98 -0
- codeagent/core/speech_synth.py +330 -0
- codeagent/core/team_config.py +247 -0
- codeagent/core/team_manager.py +245 -0
- codeagent/core/token_counter.py +41 -0
- codeagent/core/tts_voices.py +103 -0
- codeagent/core/usage_billing.py +72 -0
- codeagent/core/vision_models.py +93 -0
- codeagent/memory/diary.py +144 -0
- codeagent/persona_defaults/README.md +12 -0
- codeagent/persona_defaults/agent.md +352 -0
- codeagent/persona_defaults/identity.md +29 -0
- codeagent/persona_defaults/memory.md +4 -0
- codeagent/persona_defaults/skills.md +325 -0
- codeagent/persona_defaults/soul.md +38 -0
- codeagent/persona_defaults/tools.md +41 -0
- codeagent/persona_defaults/user.md +18 -0
- codeagent/runtime/__init__.py +11 -0
- codeagent/runtime/agent_context.py +6 -0
- codeagent/runtime/agent_runtime.py +21 -0
- codeagent/runtime/compact_prompt.py +9 -0
- codeagent/runtime/compact_state.py +40 -0
- codeagent/runtime/llm_worker.py +132 -0
- codeagent/runtime/orchestrator.py +161 -0
- codeagent/runtime/prompt_enrichment.py +324 -0
- codeagent/runtime/session_manager.py +30 -0
- codeagent/runtime/task_split.py +38 -0
- codeagent/runtime/turn_loop.py +13 -0
- codeagent/runtime/worker.py +48 -0
- codeagent/server/__init__.py +505 -0
- codeagent/server/app_factory.py +1292 -0
- codeagent/server/attachment_api.py +261 -0
- codeagent/server/hub.py +175 -0
- codeagent/server/icon.png +0 -0
- codeagent/server/self_healing.py +143 -0
- codeagent/server/team_engine.py +243 -0
- codeagent/server/tts_api.py +51 -0
- codeagent/server/webui_api_app.py +3180 -0
- codeagent/skills/__init__.py +2 -0
- codeagent/skills/select.py +144 -0
- codeagent/tools/__init__.py +6 -0
- codeagent/tools/agent_tools.py +10 -0
- codeagent/web/__init__.py +1 -0
- codeagent/web/__pycache__/__init__.cpython-313.pyc +0 -0
- codeagent/web/__pycache__/app.cpython-313.pyc +0 -0
- codeagent/web/__pycache__/auth_impl.cpython-313.pyc +0 -0
- codeagent/web/auth_impl.py +326 -0
- codeagent/web/static/00-marked.min.js +6 -0
- codeagent/web/static/00-mermaid.min.js +2314 -0
- codeagent/web/static/00-purify.min.js +3 -0
- codeagent/web/static/00-storage.js +83 -0
- codeagent/web/static/00-utils.js +51 -0
- codeagent/web/static/01-theme.css +96 -0
- codeagent/web/static/01a-scroll.js +86 -0
- codeagent/web/static/01b-think.js +62 -0
- codeagent/web/static/01c-session-identity.js +81 -0
- codeagent/web/static/01c-session-thinking.js +61 -0
- codeagent/web/static/01c-session-tree-menus.js +349 -0
- codeagent/web/static/01c-session-tree.js +531 -0
- codeagent/web/static/01c-session-utilities.js +83 -0
- codeagent/web/static/01c-vision-model.js +132 -0
- codeagent/web/static/01d-image-gen-model.js +84 -0
- codeagent/web/static/01e-audio-model.js +97 -0
- codeagent/web/static/01f-compose-ui.js +88 -0
- codeagent/web/static/01g-tts-settings.js +128 -0
- codeagent/web/static/01h-music-model.js +85 -0
- codeagent/web/static/01i-model-stack.js +291 -0
- codeagent/web/static/01j-video-gen-model.js +85 -0
- codeagent/web/static/01k-model-stack-bootstrap.js +66 -0
- codeagent/web/static/01k-model-stack-state.js +209 -0
- codeagent/web/static/01q-inflight.js +195 -0
- codeagent/web/static/02-reset.css +22 -0
- codeagent/web/static/02-session-ui.js +201 -0
- codeagent/web/static/03-layout-p1.css +2 -0
- codeagent/web/static/03-layout-p2.css +2 -0
- codeagent/web/static/03a-links-stream.js +88 -0
- codeagent/web/static/03b-stream-text.js +119 -0
- codeagent/web/static/03c-render-bubble.js +520 -0
- codeagent/web/static/03d-agent-bubble.js +126 -0
- codeagent/web/static/03e-tool-trace.js +182 -0
- codeagent/web/static/03f-timeline.js +54 -0
- codeagent/web/static/03g-bubble-tts.js +292 -0
- codeagent/web/static/04-components-p1.css +1 -0
- codeagent/web/static/04-components-p2b.css +1 -0
- codeagent/web/static/04-components-p3.css +2 -0
- codeagent/web/static/04-components-p4.css +1 -0
- codeagent/web/static/04-components-p5.css +1 -0
- codeagent/web/static/04-components-p6.css +58 -0
- codeagent/web/static/04-components-p7.css +4 -0
- codeagent/web/static/04-components-p8.css +1 -0
- codeagent/web/static/04-components-p9.css +1 -0
- codeagent/web/static/04-ws-connect.js +351 -0
- codeagent/web/static/04-ws-helpers.js +116 -0
- codeagent/web/static/05-chat-p1.css +1058 -0
- codeagent/web/static/05-chat-p2.css +1 -0
- codeagent/web/static/05-chat-p3.css +295 -0
- codeagent/web/static/05a-session-history.js +176 -0
- codeagent/web/static/05b-session-list.js +114 -0
- codeagent/web/static/05c-dialog.js +123 -0
- codeagent/web/static/05d-project-create.js +172 -0
- codeagent/web/static/05e-project-remote.js +143 -0
- codeagent/web/static/06-chat.js +552 -0
- codeagent/web/static/06-utilities.css +181 -0
- codeagent/web/static/06b-attachments.js +304 -0
- codeagent/web/static/06c-camera.js +488 -0
- codeagent/web/static/07-activity-bar.css +127 -0
- codeagent/web/static/09-git-p1.css +1 -0
- codeagent/web/static/09-git-p2.css +1 -0
- codeagent/web/static/09a-init-bind.js +110 -0
- codeagent/web/static/09b-panel-toggle.js +80 -0
- codeagent/web/static/09c-api-refresh.js +100 -0
- codeagent/web/static/09d-status-log.js +80 -0
- codeagent/web/static/09e-commit.js +80 -0
- codeagent/web/static/09f-remote.js +93 -0
- codeagent/web/static/11a-cron-parse.js +117 -0
- codeagent/web/static/11b-cron-panel.js +100 -0
- codeagent/web/static/11c-cron-card.js +133 -0
- codeagent/web/static/11d-cron-form.js +105 -0
- codeagent/web/static/11e-env-config.js +168 -0
- codeagent/web/static/11f-env-llm-a-state.js +136 -0
- codeagent/web/static/11f-env-llm-b-form.js +409 -0
- codeagent/web/static/11f-env-llm-c-board.js +322 -0
- codeagent/web/static/11g-env-paths-git.js +973 -0
- codeagent/web/static/11h-env-mcp-a-state.js +161 -0
- codeagent/web/static/11h-env-mcp-b-form.js +229 -0
- codeagent/web/static/11h-env-mcp-c-board.js +565 -0
- codeagent/web/static/12-plugins.js +127 -0
- codeagent/web/static/13a-skills.js +156 -0
- codeagent/web/static/13a-todo-utils.js +140 -0
- codeagent/web/static/13b-todo-fetch.js +106 -0
- codeagent/web/static/13b-tools.js +127 -0
- codeagent/web/static/13c-todo-crud.js +92 -0
- codeagent/web/static/14-activity-bar.js +177 -0
- codeagent/web/static/15-plans.css +194 -0
- codeagent/web/static/15-plans.js +219 -0
- codeagent/web/static/16-mobile.css +612 -0
- codeagent/web/static/16-mobile.js +178 -0
- codeagent/web/static/17-preset-ux.css +287 -0
- codeagent/web/static/18-agent-mgr.css +616 -0
- codeagent/web/static/18-agent-mgr.js +204 -0
- codeagent/web/static/24-health-dashboard.js +68 -0
- codeagent/web/static/99-brand-polish.css +152 -0
- codeagent/web/static/body.html +824 -0
- codeagent/web/static/setup/setup_form.html +171 -0
- codeagent/web/static/setup/setup_head.html +100 -0
- codeagent/web/static/setup/setup_tail.html +264 -0
- codeagent/web/static/setup/setup_test.html +38 -0
- codeagent/web/web_login.html +265 -0
- codeagent/web/webui.html +35 -0
- tagword_codeagent-1.1.6.dist-info/METADATA +273 -0
- tagword_codeagent-1.1.6.dist-info/RECORD +173 -0
- tagword_codeagent-1.1.6.dist-info/WHEEL +5 -0
- tagword_codeagent-1.1.6.dist-info/entry_points.txt +3 -0
- tagword_codeagent-1.1.6.dist-info/licenses/LICENSE +21 -0
- tagword_codeagent-1.1.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
/* ================================================================
|
|
2
|
+
* 06-chat.js
|
|
3
|
+
* - Send / stop / keybinding handlers
|
|
4
|
+
* - Auto-grow textarea
|
|
5
|
+
* - Setup greeting (LLM-based first-time greet)
|
|
6
|
+
* - Top-level tabs dispatcher
|
|
7
|
+
* ================================================================ */
|
|
8
|
+
|
|
9
|
+
// ---------------- Setup greeting (once per browser profile) ----------------
|
|
10
|
+
|
|
11
|
+
(async () => {
|
|
12
|
+
try {
|
|
13
|
+
const qs = new URLSearchParams(location.search || '');
|
|
14
|
+
if (qs.get('setup_done') !== '1') return;
|
|
15
|
+
if (tryGetLS(STORAGE_KEYS.SETUP_GREETED) === '1') return;
|
|
16
|
+
trySetLS(STORAGE_KEYS.SETUP_GREETED, '1');
|
|
17
|
+
const greetSid = sessionId;
|
|
18
|
+
try {
|
|
19
|
+
const r = await fetch('/api/chat', {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: { 'Content-Type': 'application/json' },
|
|
22
|
+
credentials: 'same-origin',
|
|
23
|
+
body: JSON.stringify({
|
|
24
|
+
session_id: greetSid, agent_id: agentId, project_id: projectId,
|
|
25
|
+
message: '请向用户简短打招呼(2-3 句),并说明:① 输入框下方可切换思考模式与主模型;② 点模型 pill 可打开模型栈(识图/生图等需在配置页添加);③ 上下文占用指示器显示 token 用量,超过 30k 会自动压缩;④ 更多设置在左侧「配置」。'
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
const j = await r.json().catch(() => ({}));
|
|
29
|
+
const replySid = (j.session_id || greetSid);
|
|
30
|
+
if (r.ok && j.reply && sessionId === replySid) {
|
|
31
|
+
rememberLocalAgentReply(j.reply);
|
|
32
|
+
if (typeof bubbleAgentWithSplitToolTrace === 'function') {
|
|
33
|
+
bubbleAgentWithSplitToolTrace(j.reply, j.tool_trace || [], { at: Date.now() });
|
|
34
|
+
} else {
|
|
35
|
+
bubble('agent', j.reply, { at: Date.now(), toolTrace: j.tool_trace || [] });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch (_) {}
|
|
39
|
+
} catch (_) {}
|
|
40
|
+
})();
|
|
41
|
+
|
|
42
|
+
// ---------------- Send / stop ----------------
|
|
43
|
+
|
|
44
|
+
async function stopActiveChat() {
|
|
45
|
+
const requestSid = sessionId;
|
|
46
|
+
if (!isSessionRunning(requestSid)) return;
|
|
47
|
+
const btn = (typeof stopBtn !== 'undefined' && stopBtn) || sendBtn;
|
|
48
|
+
try {
|
|
49
|
+
if (btn) btn.disabled = true;
|
|
50
|
+
const r = await fetch('/api/chat/stop', {
|
|
51
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
52
|
+
body: JSON.stringify({ session_id: requestSid, agent_id: agentId })
|
|
53
|
+
});
|
|
54
|
+
const j = await r.json();
|
|
55
|
+
if (!r.ok) throw new Error(j.detail || r.statusText);
|
|
56
|
+
if (sessionId === requestSid) {
|
|
57
|
+
if (j.cancelled) {
|
|
58
|
+
systemMsg('info', '⏳ 正在完成本轮回答…');
|
|
59
|
+
} else {
|
|
60
|
+
systemMsg('info', '当前会话没有正在运行的 agent。');
|
|
61
|
+
setSessionRunning(requestSid, false);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (e) { if (sessionId === requestSid) systemMsg('err', '停止失败:' + String(e)); }
|
|
65
|
+
finally { updateComposerButtons(); }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function buildChatRequestBody(requestSid, text, attachmentIds) {
|
|
69
|
+
return {
|
|
70
|
+
session_id: requestSid, agent_id: agentId, project_id: projectId,
|
|
71
|
+
message: text,
|
|
72
|
+
attachment_ids: attachmentIds.length ? attachmentIds : undefined,
|
|
73
|
+
enable_thinking: getThinkState(),
|
|
74
|
+
reasoning_effort: getThinkState() ? getReasoningEffort() : undefined,
|
|
75
|
+
llm_id: (modelSelect && modelSelect.value !== '__default__') ? modelSelect.value : undefined,
|
|
76
|
+
vision_llm_id: (typeof getSelectedVisionModel === 'function' ? getSelectedVisionModel() : '') || undefined,
|
|
77
|
+
image_gen_llm_id: (typeof getSelectedImageGenModel === 'function' ? getSelectedImageGenModel() : '') || undefined,
|
|
78
|
+
audio_llm_id: (typeof getSelectedAudioModel === 'function' ? getSelectedAudioModel() : '') || undefined,
|
|
79
|
+
music_llm_id: (typeof getSelectedMusicModel === 'function' ? getSelectedMusicModel() : '') || undefined,
|
|
80
|
+
video_gen_llm_id: (typeof getSelectedVideoGenModel === 'function' ? getSelectedVideoGenModel() : '') || undefined
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function submitChatMessage() {
|
|
85
|
+
// 运行中且无输入 → 停止;有输入 → 注入队列(不停止)
|
|
86
|
+
if (isSessionRunning(sessionId) && !composeHasSendPayload()) {
|
|
87
|
+
await stopActiveChat();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const text = msg.value.trim();
|
|
92
|
+
const hasPending = pendingAttachments && pendingAttachments.length > 0;
|
|
93
|
+
if (!text && !hasPending) return;
|
|
94
|
+
|
|
95
|
+
if (hasPending) {
|
|
96
|
+
let needImage = false;
|
|
97
|
+
let needVideo = false;
|
|
98
|
+
let needAudio = false;
|
|
99
|
+
pendingAttachments.forEach(function(p) {
|
|
100
|
+
const m = p.mime || '';
|
|
101
|
+
if (m.startsWith('image/')) needImage = true;
|
|
102
|
+
if (m.startsWith('video/')) needVideo = true;
|
|
103
|
+
if (m.startsWith('audio/')) needAudio = true;
|
|
104
|
+
});
|
|
105
|
+
if (needImage && typeof attachmentImageReady === 'function' && !attachmentImageReady()) {
|
|
106
|
+
systemMsg('err', '请先配置识图:多模态 LLM 或 MiniMax MCP');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (needVideo && typeof attachmentVideoReady === 'function' && !attachmentVideoReady()) {
|
|
110
|
+
systemMsg('err', '请先选择多模态模型(视频分析)');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (needAudio && typeof audioModelReadyForAttachments === 'function' && !audioModelReadyForAttachments()) {
|
|
114
|
+
systemMsg('err', '请先选择音频转写模型');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
msg.value = '';
|
|
120
|
+
if (typeof saveMsgDraft === 'function') saveMsgDraft();
|
|
121
|
+
updateComposerButtons();
|
|
122
|
+
|
|
123
|
+
const requestSid = sessionId;
|
|
124
|
+
scrollLogForce();
|
|
125
|
+
let attachmentIds = [];
|
|
126
|
+
try {
|
|
127
|
+
if (hasPending && typeof uploadPendingAttachments === 'function') {
|
|
128
|
+
attachmentIds = await uploadPendingAttachments();
|
|
129
|
+
}
|
|
130
|
+
} catch (e) {
|
|
131
|
+
systemMsg('err', String(e));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const displayText = text || (attachmentIds.length ? '[附件]' : '');
|
|
136
|
+
bubble('user', displayText, { at: Date.now(), attachmentIds: attachmentIds });
|
|
137
|
+
if (typeof resetAgentReplyDedupe === 'function') resetAgentReplyDedupe();
|
|
138
|
+
|
|
139
|
+
// 运行状态由后端 WS run_started / run_finished 驱动;此处不手动 bump
|
|
140
|
+
try {
|
|
141
|
+
const r = await fetch('/api/chat', {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: { 'Content-Type': 'application/json' },
|
|
144
|
+
body: JSON.stringify(buildChatRequestBody(requestSid, text, attachmentIds))
|
|
145
|
+
});
|
|
146
|
+
const j = await r.json().catch(async () => {
|
|
147
|
+
const respText = await r.text().catch(() => '');
|
|
148
|
+
return { detail: respText || r.statusText };
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (r.status === 202 && j.queued) {
|
|
152
|
+
setSessionRunning(requestSid, true);
|
|
153
|
+
if (sessionId === requestSid) {
|
|
154
|
+
systemMsg('info', '已加入执行队列,将在当前步骤完成后继续处理。');
|
|
155
|
+
}
|
|
156
|
+
if (webuiSessionsEnabled) {
|
|
157
|
+
pinSessionToTopOnce = requestSid;
|
|
158
|
+
await refreshSessionList();
|
|
159
|
+
}
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!r.ok) throw new Error(j.detail || r.statusText);
|
|
164
|
+
|
|
165
|
+
const replySid = (j.session_id || requestSid);
|
|
166
|
+
const reply = j.reply || '';
|
|
167
|
+
const viewingReplySession = sessionId === replySid;
|
|
168
|
+
if (viewingReplySession) {
|
|
169
|
+
rememberLocalAgentReply(reply);
|
|
170
|
+
const hadWsTextDone = typeof consumeWsTextDone === 'function' ? consumeWsTextDone(replySid) : false;
|
|
171
|
+
const hadLiveProgress = consumeLiveProgressSeen(replySid);
|
|
172
|
+
const traceLen = (j.tool_trace || []).length;
|
|
173
|
+
if (!hadWsTextDone && (reply || traceLen) && !hadLiveProgress) {
|
|
174
|
+
if (typeof bubbleAgentWithSplitToolTrace === 'function') {
|
|
175
|
+
bubbleAgentWithSplitToolTrace(reply, j.tool_trace || [], { at: Date.now() });
|
|
176
|
+
} else {
|
|
177
|
+
bubble('agent', reply, { at: Date.now(), toolTrace: j.tool_trace || [] });
|
|
178
|
+
}
|
|
179
|
+
} else if (!hadWsTextDone && hadLiveProgress) {
|
|
180
|
+
if (reply) {
|
|
181
|
+
var finalized = (typeof finalizeStreamBubble === 'function')
|
|
182
|
+
? finalizeStreamBubble(reply)
|
|
183
|
+
: null;
|
|
184
|
+
var remainingDelta = '';
|
|
185
|
+
if (typeof _streamUnconsumedSuffix === 'function') {
|
|
186
|
+
remainingDelta = _streamUnconsumedSuffix(reply);
|
|
187
|
+
} else if (typeof _streamDeltaText === 'function') {
|
|
188
|
+
remainingDelta = _streamDeltaText(reply);
|
|
189
|
+
}
|
|
190
|
+
if (!finalized && remainingDelta && typeof bubbleAgentWithSplitToolTrace === 'function') {
|
|
191
|
+
bubbleAgentWithSplitToolTrace(remainingDelta, [], { at: Date.now(), skipScroll: true });
|
|
192
|
+
}
|
|
193
|
+
if (typeof _advanceStreamConsumedLen === 'function') {
|
|
194
|
+
_advanceStreamConsumedLen(reply);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (typeof syncLiveToolsFromToolTrace === 'function') {
|
|
198
|
+
syncLiveToolsFromToolTrace(j.tool_trace || []);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const _tt = j.tool_trace || [];
|
|
202
|
+
if (j.tools_used && j.tools_used.length && !_tt.length && !hadLiveProgress) {
|
|
203
|
+
systemMsg('tools', '工具:' + j.tools_used.join(', '));
|
|
204
|
+
}
|
|
205
|
+
if (j.cancelled && sessionId === requestSid) {
|
|
206
|
+
systemMsg('info', '✅ 已停止。');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (webuiSessionsEnabled) {
|
|
211
|
+
pinSessionToTopOnce = requestSid;
|
|
212
|
+
await refreshSessionList();
|
|
213
|
+
if (viewingReplySession) {
|
|
214
|
+
const row2 = lastSessionsCache.find((r) => r.session_id === sessionId);
|
|
215
|
+
markSessionReadByCount(sessionId, row2 ? (row2.message_count || 0) : 0);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
window._lastContextUsage = j.context || null;
|
|
220
|
+
window._lastAccumulatedUsage = j.accumulated_usage || null;
|
|
221
|
+
} catch (e) {
|
|
222
|
+
if (sessionId === requestSid) systemMsg('err', String(e));
|
|
223
|
+
} finally {
|
|
224
|
+
var ctx = window._lastContextUsage;
|
|
225
|
+
if (ctx && typeof updateTokenUsage === 'function') {
|
|
226
|
+
var ctxPt = (typeof pickContextUsageTokens === 'function')
|
|
227
|
+
? pickContextUsageTokens(ctx) : (Number(ctx.prompt_tokens) || 0);
|
|
228
|
+
if (ctxPt > 0) updateTokenUsage(ctx);
|
|
229
|
+
window._lastContextUsage = null;
|
|
230
|
+
} else if (typeof recalcTokenUsageFromDom === 'function') {
|
|
231
|
+
setTimeout(recalcTokenUsageFromDom, 50);
|
|
232
|
+
}
|
|
233
|
+
var au = window._lastAccumulatedUsage;
|
|
234
|
+
if (au && typeof updateSidebarCost === 'function') {
|
|
235
|
+
updateSidebarCost(au);
|
|
236
|
+
window._lastAccumulatedUsage = null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ---------------- MCP skill slash suggestions ----------------
|
|
242
|
+
|
|
243
|
+
const mcpSkillSuggest = {
|
|
244
|
+
box: null,
|
|
245
|
+
items: [],
|
|
246
|
+
active: 0,
|
|
247
|
+
servers: null,
|
|
248
|
+
skillsByServer: {},
|
|
249
|
+
loadingKey: '',
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
function ensureMcpSkillSuggestBox() {
|
|
253
|
+
if (mcpSkillSuggest.box) return mcpSkillSuggest.box;
|
|
254
|
+
const host = document.getElementById('composeBox') || (msg && msg.parentElement);
|
|
255
|
+
if (!host) return null;
|
|
256
|
+
if (!document.getElementById('mcpSkillSuggestStyle')) {
|
|
257
|
+
const style = document.createElement('style');
|
|
258
|
+
style.id = 'mcpSkillSuggestStyle';
|
|
259
|
+
style.textContent = [
|
|
260
|
+
'.mcp-skill-suggest{position:absolute;left:12px;right:12px;bottom:calc(100% + 8px);z-index:50;background:var(--bg-panel,#fff);border:1px solid var(--border,#ddd);border-radius:var(--r-md,10px);box-shadow:0 12px 30px rgba(0,0,0,.16);overflow:hidden;max-height:260px;display:none;}',
|
|
261
|
+
'.mcp-skill-suggest__head{padding:7px 10px;font-size:11px;color:var(--text-subtle,#777);border-bottom:1px solid var(--border,#eee);background:var(--bg-subtle,#f7f7f7);}',
|
|
262
|
+
'.mcp-skill-suggest__item{display:flex;gap:8px;align-items:flex-start;width:100%;border:0;background:transparent;text-align:left;padding:9px 10px;cursor:pointer;color:var(--text,#222);}',
|
|
263
|
+
'.mcp-skill-suggest__item:hover,.mcp-skill-suggest__item.is-active{background:var(--accent-dim,#e8f0fe);}',
|
|
264
|
+
'.mcp-skill-suggest__name{font-weight:600;font-size:13px;line-height:1.3;}',
|
|
265
|
+
'.mcp-skill-suggest__meta{font-size:11px;color:var(--text-subtle,#777);line-height:1.35;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
|
|
266
|
+
'.mcp-skill-suggest__badge{font-size:10px;padding:1px 5px;border-radius:999px;background:var(--bg-2,#eee);color:var(--text-subtle,#666);flex-shrink:0;margin-top:1px;}',
|
|
267
|
+
].join('');
|
|
268
|
+
document.head.appendChild(style);
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const cs = window.getComputedStyle(host);
|
|
272
|
+
if (cs.position === 'static') host.style.position = 'relative';
|
|
273
|
+
} catch (_) {}
|
|
274
|
+
const box = document.createElement('div');
|
|
275
|
+
box.className = 'mcp-skill-suggest';
|
|
276
|
+
box.setAttribute('role', 'listbox');
|
|
277
|
+
host.appendChild(box);
|
|
278
|
+
mcpSkillSuggest.box = box;
|
|
279
|
+
return box;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function hideMcpSkillSuggest() {
|
|
283
|
+
const box = mcpSkillSuggest.box;
|
|
284
|
+
mcpSkillSuggest.items = [];
|
|
285
|
+
mcpSkillSuggest.active = 0;
|
|
286
|
+
if (box) {
|
|
287
|
+
box.style.display = 'none';
|
|
288
|
+
box.innerHTML = '';
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function loadMcpSuggestServers() {
|
|
293
|
+
if (mcpSkillSuggest.servers) return mcpSkillSuggest.servers;
|
|
294
|
+
const r = await fetch('/api/ui/mcp', { credentials: 'same-origin' });
|
|
295
|
+
const j = await r.json().catch(function() { return {}; });
|
|
296
|
+
if (!r.ok) throw new Error(j.detail || r.statusText || 'MCP 配置读取失败');
|
|
297
|
+
const servers = (j.config && j.config.servers) ? j.config.servers : {};
|
|
298
|
+
mcpSkillSuggest.servers = Object.keys(servers || {}).sort().map(function(id) {
|
|
299
|
+
return { id: id, config: servers[id] || {} };
|
|
300
|
+
});
|
|
301
|
+
return mcpSkillSuggest.servers;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function loadMcpSuggestSkills(serverId) {
|
|
305
|
+
if (mcpSkillSuggest.skillsByServer[serverId]) return mcpSkillSuggest.skillsByServer[serverId];
|
|
306
|
+
const r = await fetch('/api/ui/mcp/skills?server_id=' + encodeURIComponent(serverId), { credentials: 'same-origin' });
|
|
307
|
+
const j = await r.json().catch(function() { return {}; });
|
|
308
|
+
if (!r.ok) throw new Error(j.detail || r.statusText || 'MCP skill 读取失败');
|
|
309
|
+
const skills = Array.isArray(j.skills) ? j.skills : [];
|
|
310
|
+
mcpSkillSuggest.skillsByServer[serverId] = skills;
|
|
311
|
+
return skills;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function getMcpSlashQuery() {
|
|
315
|
+
if (!msg) return null;
|
|
316
|
+
const value = msg.value || '';
|
|
317
|
+
const caret = msg.selectionStart || 0;
|
|
318
|
+
const before = value.slice(0, caret);
|
|
319
|
+
if (before.indexOf('\n') >= 0) return null;
|
|
320
|
+
const m = before.match(/^\/([A-Za-z][A-Za-z0-9_-]*)(?::([A-Za-z0-9_.:-]*))?(?:\s+([A-Za-z0-9_.:-]*))?$/);
|
|
321
|
+
if (!m || m[1] === 'skill') return null;
|
|
322
|
+
return {
|
|
323
|
+
raw: before,
|
|
324
|
+
serverPrefix: m[1],
|
|
325
|
+
skillPrefix: m[2] !== undefined ? (m[2] || '') : (m[3] || ''),
|
|
326
|
+
hasSkillPart: before.indexOf(':') >= 0 || before.indexOf(' ') >= 0,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function renderMcpSkillSuggest(title, items) {
|
|
331
|
+
const box = ensureMcpSkillSuggestBox();
|
|
332
|
+
if (!box) return;
|
|
333
|
+
mcpSkillSuggest.items = items || [];
|
|
334
|
+
mcpSkillSuggest.active = Math.min(mcpSkillSuggest.active, Math.max(0, mcpSkillSuggest.items.length - 1));
|
|
335
|
+
if (!mcpSkillSuggest.items.length) {
|
|
336
|
+
hideMcpSkillSuggest();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
box.innerHTML = '';
|
|
340
|
+
const head = document.createElement('div');
|
|
341
|
+
head.className = 'mcp-skill-suggest__head';
|
|
342
|
+
head.textContent = title;
|
|
343
|
+
box.appendChild(head);
|
|
344
|
+
mcpSkillSuggest.items.forEach(function(item, idx) {
|
|
345
|
+
const btn = document.createElement('button');
|
|
346
|
+
btn.type = 'button';
|
|
347
|
+
btn.className = 'mcp-skill-suggest__item' + (idx === mcpSkillSuggest.active ? ' is-active' : '');
|
|
348
|
+
btn.setAttribute('role', 'option');
|
|
349
|
+
btn.setAttribute('aria-selected', idx === mcpSkillSuggest.active ? 'true' : 'false');
|
|
350
|
+
const badge = document.createElement('span');
|
|
351
|
+
badge.className = 'mcp-skill-suggest__badge';
|
|
352
|
+
badge.textContent = item.kind === 'server' ? 'MCP' : 'skill';
|
|
353
|
+
const main = document.createElement('span');
|
|
354
|
+
main.style.minWidth = '0';
|
|
355
|
+
const name = document.createElement('div');
|
|
356
|
+
name.className = 'mcp-skill-suggest__name';
|
|
357
|
+
name.textContent = item.label;
|
|
358
|
+
const meta = document.createElement('div');
|
|
359
|
+
meta.className = 'mcp-skill-suggest__meta';
|
|
360
|
+
meta.textContent = item.detail || '';
|
|
361
|
+
main.appendChild(name);
|
|
362
|
+
if (item.detail) main.appendChild(meta);
|
|
363
|
+
btn.appendChild(badge);
|
|
364
|
+
btn.appendChild(main);
|
|
365
|
+
btn.addEventListener('mouseenter', function() {
|
|
366
|
+
mcpSkillSuggest.active = idx;
|
|
367
|
+
renderMcpSkillSuggest(title, mcpSkillSuggest.items);
|
|
368
|
+
});
|
|
369
|
+
btn.addEventListener('mousedown', function(ev) {
|
|
370
|
+
ev.preventDefault();
|
|
371
|
+
applyMcpSkillSuggest(item);
|
|
372
|
+
});
|
|
373
|
+
box.appendChild(btn);
|
|
374
|
+
});
|
|
375
|
+
box.style.display = 'block';
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function applyMcpSkillSuggest(item) {
|
|
379
|
+
if (!item || !msg) return;
|
|
380
|
+
if (item.kind === 'server') {
|
|
381
|
+
msg.value = '/' + item.serverId + ':';
|
|
382
|
+
} else {
|
|
383
|
+
if (item.kind !== 'skill') return;
|
|
384
|
+
msg.value = '/' + item.serverId + ':' + item.skillName + ' ';
|
|
385
|
+
}
|
|
386
|
+
hideMcpSkillSuggest();
|
|
387
|
+
msg.focus();
|
|
388
|
+
msg.selectionStart = msg.selectionEnd = msg.value.length;
|
|
389
|
+
if (typeof saveMsgDraft === 'function') saveMsgDraft();
|
|
390
|
+
updateComposerButtons();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function refreshMcpSkillSuggest() {
|
|
394
|
+
const q = getMcpSlashQuery();
|
|
395
|
+
if (!q) {
|
|
396
|
+
hideMcpSkillSuggest();
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const key = q.serverPrefix + '\n' + q.skillPrefix + '\n' + q.hasSkillPart;
|
|
400
|
+
mcpSkillSuggest.loadingKey = key;
|
|
401
|
+
try {
|
|
402
|
+
const servers = await loadMcpSuggestServers();
|
|
403
|
+
if (mcpSkillSuggest.loadingKey !== key) return;
|
|
404
|
+
const matches = servers.filter(function(s) {
|
|
405
|
+
return s.id.toLowerCase().indexOf(q.serverPrefix.toLowerCase()) === 0;
|
|
406
|
+
});
|
|
407
|
+
if (!matches.length) {
|
|
408
|
+
renderMcpSkillSuggest('没有匹配的 MCP server', []);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (matches.length > 1 && !q.hasSkillPart) {
|
|
412
|
+
renderMcpSkillSuggest('选择 MCP server', matches.slice(0, 8).map(function(s) {
|
|
413
|
+
return {
|
|
414
|
+
kind: 'server',
|
|
415
|
+
serverId: s.id,
|
|
416
|
+
label: '/' + s.id,
|
|
417
|
+
detail: (s.config.transport || 'stdio') + (s.config.url ? ' · ' + s.config.url : ''),
|
|
418
|
+
};
|
|
419
|
+
}));
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const serverId = matches[0].id;
|
|
423
|
+
const skills = await loadMcpSuggestSkills(serverId);
|
|
424
|
+
if (mcpSkillSuggest.loadingKey !== key) return;
|
|
425
|
+
const prefix = (q.skillPrefix || '').toLowerCase();
|
|
426
|
+
const filtered = skills.filter(function(s) {
|
|
427
|
+
return !prefix || String(s.name || '').toLowerCase().indexOf(prefix) === 0;
|
|
428
|
+
}).slice(0, 10);
|
|
429
|
+
renderMcpSkillSuggest('选择 ' + serverId + ' 的 skill', filtered.map(function(s) {
|
|
430
|
+
const args = Array.isArray(s.arguments) && s.arguments.length
|
|
431
|
+
? '参数:' + s.arguments.map(function(a) { return a.name || ''; }).filter(Boolean).join(', ')
|
|
432
|
+
: '';
|
|
433
|
+
return {
|
|
434
|
+
kind: 'skill',
|
|
435
|
+
serverId: serverId,
|
|
436
|
+
skillName: s.name,
|
|
437
|
+
label: s.name,
|
|
438
|
+
detail: [s.description || '', args].filter(Boolean).join(' · '),
|
|
439
|
+
};
|
|
440
|
+
}));
|
|
441
|
+
} catch (e) {
|
|
442
|
+
renderMcpSkillSuggest('MCP skill 读取失败', [{
|
|
443
|
+
kind: 'none',
|
|
444
|
+
label: String(e.message || e),
|
|
445
|
+
detail: '',
|
|
446
|
+
}]);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function mcpSkillSuggestHandleKeydown(e) {
|
|
451
|
+
const box = mcpSkillSuggest.box;
|
|
452
|
+
if (!box || box.style.display !== 'block' || !mcpSkillSuggest.items.length) return false;
|
|
453
|
+
if (e.key === 'ArrowDown') {
|
|
454
|
+
e.preventDefault();
|
|
455
|
+
mcpSkillSuggest.active = (mcpSkillSuggest.active + 1) % mcpSkillSuggest.items.length;
|
|
456
|
+
renderMcpSkillSuggest((box.querySelector('.mcp-skill-suggest__head') || {}).textContent || '', mcpSkillSuggest.items);
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
if (e.key === 'ArrowUp') {
|
|
460
|
+
e.preventDefault();
|
|
461
|
+
mcpSkillSuggest.active = (mcpSkillSuggest.active - 1 + mcpSkillSuggest.items.length) % mcpSkillSuggest.items.length;
|
|
462
|
+
renderMcpSkillSuggest((box.querySelector('.mcp-skill-suggest__head') || {}).textContent || '', mcpSkillSuggest.items);
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
if (e.key === 'Enter' || e.key === 'Tab') {
|
|
466
|
+
e.preventDefault();
|
|
467
|
+
applyMcpSkillSuggest(mcpSkillSuggest.items[mcpSkillSuggest.active]);
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
470
|
+
if (e.key === 'Escape') {
|
|
471
|
+
e.preventDefault();
|
|
472
|
+
hideMcpSkillSuggest();
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (sendBtn) sendBtn.onclick = submitChatMessage;
|
|
479
|
+
|
|
480
|
+
if (typeof stopBtn !== 'undefined' && stopBtn) stopBtn.onclick = stopActiveChat;
|
|
481
|
+
|
|
482
|
+
updateComposerButtons();
|
|
483
|
+
|
|
484
|
+
if (msg) {
|
|
485
|
+
msg.addEventListener('keydown', (e) => {
|
|
486
|
+
if (mcpSkillSuggestHandleKeydown(e)) return;
|
|
487
|
+
if (e.key !== 'Enter') return;
|
|
488
|
+
if (e.shiftKey) return;
|
|
489
|
+
if (e.isComposing || e.keyCode === 229) return;
|
|
490
|
+
if (window.matchMedia('(max-width: 768px)').matches) return;
|
|
491
|
+
e.preventDefault();
|
|
492
|
+
sendBtn.click();
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
msg.addEventListener('input', () => {
|
|
496
|
+
msg.style.height = 'auto';
|
|
497
|
+
msg.style.height = Math.min(msg.scrollHeight, 220) + 'px';
|
|
498
|
+
updateComposerButtons();
|
|
499
|
+
refreshMcpSkillSuggest();
|
|
500
|
+
});
|
|
501
|
+
msg.addEventListener('blur', () => {
|
|
502
|
+
setTimeout(hideMcpSkillSuggest, 120);
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ---------------- Active page persistence (localStorage) ----------------
|
|
507
|
+
|
|
508
|
+
const TAB_KEY = STORAGE_KEYS.SESS_ACTIVE_PAGE;
|
|
509
|
+
const WORKSPACE_PAGE_IDS = ['chat', 'config', 'tasks', 'agent', 'files', 'team', 'hub'];
|
|
510
|
+
|
|
511
|
+
function switchToPage(id) {
|
|
512
|
+
if (!id) return;
|
|
513
|
+
const targetId = 'page-' + id;
|
|
514
|
+
WORKSPACE_PAGE_IDS.forEach((suffix) => {
|
|
515
|
+
const p = document.getElementById('page-' + suffix);
|
|
516
|
+
if (!p) return;
|
|
517
|
+
p.classList.toggle('active', p.id === targetId);
|
|
518
|
+
p.scrollTop = 0;
|
|
519
|
+
});
|
|
520
|
+
var ws = document.querySelector('.workspace');
|
|
521
|
+
if (ws) ws.scrollTop = 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function activatePage(id) {
|
|
525
|
+
var actMode = '';
|
|
526
|
+
try { actMode = document.body.getAttribute('data-activity-mode') || ''; } catch (_) {}
|
|
527
|
+
if (actMode && actMode !== 'chat' && actMode !== 'stats' && actMode !== 'files' && id === 'chat') {
|
|
528
|
+
id = actMode;
|
|
529
|
+
}
|
|
530
|
+
switchToPage(id);
|
|
531
|
+
try { localStorage.setItem(TAB_KEY, id); } catch (_) {}
|
|
532
|
+
if (id === 'config') {
|
|
533
|
+
loadChatEnvConfig();
|
|
534
|
+
loadLlmPresets();
|
|
535
|
+
if (typeof loadMcpEnvConfig === 'function') loadMcpEnvConfig();
|
|
536
|
+
loadConfigPaths();
|
|
537
|
+
loadGitRemoteConfig();
|
|
538
|
+
}
|
|
539
|
+
if (id === 'tasks') { loadCronPanel(); }
|
|
540
|
+
if (id === 'agent') { loadAgentPage(); }
|
|
541
|
+
if (id === 'chat' && webuiSessionsEnabled) {
|
|
542
|
+
refreshSessionList().catch(() => {});
|
|
543
|
+
loadSessionHistoryIntoLog(true);
|
|
544
|
+
}
|
|
545
|
+
if (id === 'chat' || id === 'config') {
|
|
546
|
+
if (typeof refreshMultimodalModelSelects === 'function') {
|
|
547
|
+
refreshMultimodalModelSelects().catch(function() {});
|
|
548
|
+
} else {
|
|
549
|
+
refreshModelSelect().catch(function() {});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|