myagent-ai 1.6.7 → 1.6.8
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/package.json
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Example Skill
|
|
2
|
+
|
|
3
|
+
A demonstration skill showing the SKILL.md format used by GLM Agent Engine.
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
This is an example skill that demonstrates the expected structure and format for skills in the GLM Agent ecosystem. Skills are self-contained modules that extend the agent's capabilities with specialized functionality.
|
|
8
|
+
|
|
9
|
+
## Capability
|
|
10
|
+
|
|
11
|
+
The agent can invoke this skill when users need demonstration or testing of the skill system. It serves as both documentation and a working example for skill developers.
|
|
12
|
+
|
|
13
|
+
## Instructions
|
|
14
|
+
|
|
15
|
+
When this skill is loaded, the agent should:
|
|
16
|
+
1. Acknowledge that the example skill has been invoked
|
|
17
|
+
2. Explain the skill system architecture to the user
|
|
18
|
+
3. Guide the user on how to create their own custom skills
|
|
19
|
+
|
|
20
|
+
## Skill Structure
|
|
21
|
+
|
|
22
|
+
Each skill should contain:
|
|
23
|
+
- `SKILL.md` - This metadata file (required)
|
|
24
|
+
- Supporting scripts, configs, or data files as needed
|
|
25
|
+
- Any language-specific setup (package.json, requirements.txt, etc.)
|
|
26
|
+
|
|
27
|
+
## Creating Custom Skills
|
|
28
|
+
|
|
29
|
+
To create a new skill:
|
|
30
|
+
1. Create a directory under `/home/z/my-project/skills/`
|
|
31
|
+
2. Add a `SKILL.md` file with proper metadata
|
|
32
|
+
3. Include any necessary scripts or configurations
|
|
33
|
+
4. The skill will be automatically detected by the agent engine
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Example skill execution script
|
|
3
|
+
# This demonstrates how skills can include executable logic
|
|
4
|
+
|
|
5
|
+
echo "Example Skill executed at: $(date)"
|
|
6
|
+
echo "Arguments: $@"
|
|
7
|
+
echo "Project directory: ${CLAWHUB_WORKDIR:-/home/z/my-project}"
|
|
8
|
+
|
|
9
|
+
# Your skill logic goes here
|
|
10
|
+
# Exit 0 for success, non-zero for failure
|
|
11
|
+
exit 0
|
package/web/api_server.py
CHANGED
|
@@ -454,7 +454,11 @@ class ApiServer:
|
|
|
454
454
|
# 支持 path 格式 (如 "coder/python-expert")
|
|
455
455
|
agent_path = data.get("agent_path", agent_name)
|
|
456
456
|
raw_session_id = data.get("session_id", "") or "web_default"
|
|
457
|
-
session_id
|
|
457
|
+
# Avoid double-prefixing: if the session_id already starts with agent_path_, use it directly
|
|
458
|
+
if raw_session_id.startswith(f"{agent_path}_"):
|
|
459
|
+
session_id = raw_session_id
|
|
460
|
+
else:
|
|
461
|
+
session_id = f"{agent_path}_{raw_session_id}"
|
|
458
462
|
chat_mode = data.get("mode", "") # "exec" = 执行模式
|
|
459
463
|
escalated = data.get("escalated", False) # 临时提权到 local
|
|
460
464
|
|
|
@@ -541,7 +545,11 @@ class ApiServer:
|
|
|
541
545
|
|
|
542
546
|
agent_path = data.get("agent_path", data.get("agent_name", "default")) or "default"
|
|
543
547
|
raw_session_id = data.get("session_id", "") or "web_default"
|
|
544
|
-
session_id
|
|
548
|
+
# Avoid double-prefixing: if the session_id already starts with agent_path_, use it directly
|
|
549
|
+
if raw_session_id.startswith(f"{agent_path}_"):
|
|
550
|
+
session_id = raw_session_id
|
|
551
|
+
else:
|
|
552
|
+
session_id = f"{agent_path}_{raw_session_id}"
|
|
545
553
|
chat_mode = data.get("mode", "")
|
|
546
554
|
escalated = data.get("escalated", False)
|
|
547
555
|
|
|
@@ -592,16 +600,7 @@ class ApiServer:
|
|
|
592
600
|
agent_path=agent_path, agent_system_prompt=agent_system_prompt,
|
|
593
601
|
chat_mode=chat_mode, stream_response=response,
|
|
594
602
|
)
|
|
595
|
-
elif self.core.main_agent and self.core.llm:
|
|
596
|
-
# model_chain is empty (no model_id configured), but LLM is available
|
|
597
|
-
# Use _stream_process_message for true token-by-token streaming
|
|
598
|
-
full_response = await self._stream_process_message(
|
|
599
|
-
clean_message, session_id, response,
|
|
600
|
-
agent_path=agent_path, agent_system_prompt=agent_system_prompt,
|
|
601
|
-
chat_mode=chat_mode,
|
|
602
|
-
)
|
|
603
603
|
else:
|
|
604
|
-
# No LLM at all — non-streaming fallback
|
|
605
604
|
full_response = await self.core.process_message(clean_message, session_id)
|
|
606
605
|
await response.write(("data: " + json.dumps({"type": "text", "content": full_response}) + "\n\n").encode())
|
|
607
606
|
|
|
@@ -649,12 +648,6 @@ class ApiServer:
|
|
|
649
648
|
agent_path=agent_path, agent_system_prompt=agent_system_prompt_q,
|
|
650
649
|
chat_mode=chat_mode, stream_response=response,
|
|
651
650
|
)
|
|
652
|
-
elif self.core.main_agent and self.core.llm:
|
|
653
|
-
full_response = await self._stream_process_message(
|
|
654
|
-
clean_message_q, session_id, response,
|
|
655
|
-
agent_path=agent_path, agent_system_prompt=agent_system_prompt_q,
|
|
656
|
-
chat_mode=chat_mode,
|
|
657
|
-
)
|
|
658
651
|
else:
|
|
659
652
|
full_response = await self.core.process_message(clean_message_q, session_id)
|
|
660
653
|
await response.write(("data: " + json.dumps({"type": "text", "content": full_response}) + "\n\n").encode())
|
|
@@ -3885,7 +3878,6 @@ class ApiServer:
|
|
|
3885
3878
|
# 获取agent的显示信息
|
|
3886
3879
|
avatar = "🤖"
|
|
3887
3880
|
display_name = agent_path
|
|
3888
|
-
agent_color = "var(--accent)"
|
|
3889
3881
|
if agent_cfg:
|
|
3890
3882
|
avatar = agent_cfg.get("avatar_emoji", "🤖") or "🤖"
|
|
3891
3883
|
display_name = agent_cfg.get("name", agent_path)
|
|
@@ -3895,7 +3887,6 @@ class ApiServer:
|
|
|
3895
3887
|
"agent_path": agent_path,
|
|
3896
3888
|
"name": display_name,
|
|
3897
3889
|
"avatar": avatar,
|
|
3898
|
-
"agent_color": agent_color,
|
|
3899
3890
|
"response": response,
|
|
3900
3891
|
}
|
|
3901
3892
|
except Exception as e:
|
package/web/ui/chat.html
CHANGED
|
@@ -149,6 +149,26 @@ input,textarea,select{font:inherit}
|
|
|
149
149
|
.session-item:hover .session-actions{opacity:1}
|
|
150
150
|
.session-actions{opacity:0;display:flex;gap:2px;flex-shrink:0}
|
|
151
151
|
.session-actions .session-delete:hover{background:var(--danger);color:#fff}
|
|
152
|
+
.session-more-btn{
|
|
153
|
+
width:24px;height:24px;border-radius:4px;display:grid;place-items:center;
|
|
154
|
+
background:var(--bg2);transition:var(--transition);font-size:16px;color:var(--text3);
|
|
155
|
+
flex-shrink:0;cursor:pointer;border:none;line-height:1;
|
|
156
|
+
}
|
|
157
|
+
.session-more-btn:hover{background:var(--bg3);color:var(--text)}
|
|
158
|
+
.session-context-menu{
|
|
159
|
+
position:fixed;z-index:1000;background:var(--card);border:1px solid var(--border);
|
|
160
|
+
border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.15);padding:4px;min-width:140px;
|
|
161
|
+
animation:menuFadeIn .15s ease-out;
|
|
162
|
+
}
|
|
163
|
+
@keyframes menuFadeIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}
|
|
164
|
+
.session-context-menu button{
|
|
165
|
+
display:flex;align-items:center;gap:8px;width:100%;padding:8px 12px;border:none;
|
|
166
|
+
background:none;color:var(--text);font-size:13px;cursor:pointer;border-radius:6px;
|
|
167
|
+
transition:var(--transition);text-align:left;
|
|
168
|
+
}
|
|
169
|
+
.session-context-menu button:hover{background:var(--bg3)}
|
|
170
|
+
.session-context-menu button.danger{color:var(--danger)}
|
|
171
|
+
.session-context-menu button.danger:hover{background:rgba(239,68,68,.1)}
|
|
152
172
|
.session-rename{
|
|
153
173
|
opacity:0;width:24px;height:24px;border-radius:4px;display:grid;place-items:center;
|
|
154
174
|
background:var(--bg2);transition:var(--transition);font-size:12px;color:var(--text3);
|
|
@@ -3072,6 +3092,64 @@ async function loadSessions() {
|
|
|
3072
3092
|
}
|
|
3073
3093
|
}
|
|
3074
3094
|
|
|
3095
|
+
// ── Session context menu (three dots) ──
|
|
3096
|
+
let _sessionMenuEl = null;
|
|
3097
|
+
function closeSessionMenu() {
|
|
3098
|
+
if (_sessionMenuEl) { _sessionMenuEl.remove(); _sessionMenuEl = null; }
|
|
3099
|
+
}
|
|
3100
|
+
function toggleSessionMenu(event, sessionId) {
|
|
3101
|
+
event.preventDefault();
|
|
3102
|
+
event.stopPropagation();
|
|
3103
|
+
closeSessionMenu();
|
|
3104
|
+
const menu = document.createElement('div');
|
|
3105
|
+
menu.className = 'session-context-menu';
|
|
3106
|
+
menu.innerHTML = `
|
|
3107
|
+
<button onclick="event.stopPropagation();closeSessionMenu();startRenameSession('${escapeHtml(sessionId)}')">
|
|
3108
|
+
<span>✏️</span> 重命名
|
|
3109
|
+
</button>
|
|
3110
|
+
<button onclick="event.stopPropagation();closeSessionMenu();clearSessionFromMenu('${escapeHtml(sessionId)}')">
|
|
3111
|
+
<span>🗑️</span> 清空消息
|
|
3112
|
+
</button>
|
|
3113
|
+
<button class="danger" onclick="event.stopPropagation();closeSessionMenu();deleteSession('${escapeHtml(sessionId)}')">
|
|
3114
|
+
<span>✕</span> 删除对话
|
|
3115
|
+
</button>
|
|
3116
|
+
`;
|
|
3117
|
+
// Position near the button
|
|
3118
|
+
const btn = event.currentTarget || event.target;
|
|
3119
|
+
const rect = btn.getBoundingClientRect();
|
|
3120
|
+
menu.style.top = rect.bottom + 4 + 'px';
|
|
3121
|
+
menu.style.right = (window.innerWidth - rect.right) + 'px';
|
|
3122
|
+
document.body.appendChild(menu);
|
|
3123
|
+
_sessionMenuEl = menu;
|
|
3124
|
+
// Close on outside click
|
|
3125
|
+
setTimeout(() => {
|
|
3126
|
+
document.addEventListener('click', closeSessionMenu, { once: true });
|
|
3127
|
+
}, 10);
|
|
3128
|
+
}
|
|
3129
|
+
async function clearSessionFromMenu(id) {
|
|
3130
|
+
if (!id || id === '__new__') return;
|
|
3131
|
+
if (!confirm('确定清空此对话的消息?会话本身不会被删除。')) return;
|
|
3132
|
+
try {
|
|
3133
|
+
const resp = await fetch(`/api/sessions/${encodeURIComponent(id)}/messages`, {
|
|
3134
|
+
method: 'DELETE',
|
|
3135
|
+
headers: { 'Content-Type': 'application/json' }
|
|
3136
|
+
});
|
|
3137
|
+
if (!resp.ok) {
|
|
3138
|
+
const errData = await resp.json().catch(() => ({}));
|
|
3139
|
+
throw new Error(errData.error || 'HTTP ' + resp.status);
|
|
3140
|
+
}
|
|
3141
|
+
if (state.activeSessionId === id) {
|
|
3142
|
+
state.messages = [];
|
|
3143
|
+
renderMessages();
|
|
3144
|
+
}
|
|
3145
|
+
toast('消息已清空', 'success');
|
|
3146
|
+
await loadSessions();
|
|
3147
|
+
} catch (e) {
|
|
3148
|
+
console.error('Clear session error:', e);
|
|
3149
|
+
toast('清空失败: ' + e.message, 'error');
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3075
3153
|
function formatSessionName(id) {
|
|
3076
3154
|
if (id.startsWith('web_')) return id.replace('web_', '').replace(/_/g, ' ');
|
|
3077
3155
|
if (id.startsWith('cli_')) return 'CLI: ' + id.replace('cli_', '');
|
|
@@ -3096,8 +3174,7 @@ function renderSessions(filter = '') {
|
|
|
3096
3174
|
</div>
|
|
3097
3175
|
${s.id !== '__new__' ? `
|
|
3098
3176
|
<div class="session-actions">
|
|
3099
|
-
<button class="session-
|
|
3100
|
-
<button class="session-delete" onclick="event.stopPropagation();deleteSession('${escapeHtml(s.id)}')" title="删除">✕</button>
|
|
3177
|
+
<button class="session-more-btn" onclick="event.stopPropagation();toggleSessionMenu(event,'${escapeHtml(s.id)}')" title="更多操作">⋮</button>
|
|
3101
3178
|
</div>
|
|
3102
3179
|
` : ''}
|
|
3103
3180
|
</div>
|
|
@@ -4952,22 +5029,7 @@ async function selectGroup(gid) {
|
|
|
4952
5029
|
|
|
4953
5030
|
// Load messages
|
|
4954
5031
|
var msgsData = await getGroupMessages(gid);
|
|
4955
|
-
|
|
4956
|
-
groupMessages = (msgsData || []).map(function(m) {
|
|
4957
|
-
if (m.sender) {
|
|
4958
|
-
return {
|
|
4959
|
-
role: m.sender === 'user' ? 'user' : 'assistant',
|
|
4960
|
-
agent: m.agent_path || '',
|
|
4961
|
-
agent_name: m.sender_name || '',
|
|
4962
|
-
agent_emoji: m.sender_avatar || '🤖',
|
|
4963
|
-
agent_color: m.agent_color || '',
|
|
4964
|
-
content: m.content || '',
|
|
4965
|
-
time: m.timestamp ? new Date(m.timestamp * 1000).toISOString() : (m.time || ''),
|
|
4966
|
-
type: m.msg_type === 'system' ? 'system' : '',
|
|
4967
|
-
};
|
|
4968
|
-
}
|
|
4969
|
-
return m;
|
|
4970
|
-
});
|
|
5032
|
+
groupMessages = msgsData || [];
|
|
4971
5033
|
renderGroupMessages();
|
|
4972
5034
|
} catch (e) {
|
|
4973
5035
|
toast('加载群聊失败: ' + e.message, 'error');
|
|
@@ -5026,11 +5088,11 @@ function renderGroupMessages() {
|
|
|
5026
5088
|
+ '<div><div class="message-bubble">' + renderMarkdown(msg.content || '') + '</div>'
|
|
5027
5089
|
+ (msg.time ? '<div class="message-time" style="text-align:right">' + formatTime(msg.time) + '</div>' : '')
|
|
5028
5090
|
+ '</div></div>';
|
|
5029
|
-
} else if (msg.role === 'assistant' || msg.agent) {
|
|
5030
|
-
var agentName = msg.agent_name || msg.agent || 'Agent';
|
|
5031
|
-
var agentEmoji = msg.agent_emoji || '🤖';
|
|
5032
|
-
var agentColor = msg.agent_color || 'var(--accent)';
|
|
5033
|
-
var agentRole = msg.agent_role || '';
|
|
5091
|
+
} else if (msg.role === 'assistant' || msg.sender === 'agent' || msg.agent) {
|
|
5092
|
+
var agentName = msg.agent_name || msg.sender_name || msg.name || msg.agent || 'Agent';
|
|
5093
|
+
var agentEmoji = msg.agent_emoji || msg.sender_avatar || msg.avatar || '🤖';
|
|
5094
|
+
var agentColor = msg.agent_color || msg.color || 'var(--accent)';
|
|
5095
|
+
var agentRole = msg.agent_role || msg.role_detail || '';
|
|
5034
5096
|
html += '<div class="group-msg-row">'
|
|
5035
5097
|
+ '<div class="group-msg-avatar" style="background:' + agentColor + ';color:#fff">' + agentEmoji + '</div>'
|
|
5036
5098
|
+ '<div>'
|
|
@@ -5044,10 +5106,9 @@ function renderGroupMessages() {
|
|
|
5044
5106
|
// Multiple agent responses (broadcast response)
|
|
5045
5107
|
for (var j = 0; j < msg.responses.length; j++) {
|
|
5046
5108
|
var r = msg.responses[j];
|
|
5047
|
-
|
|
5048
|
-
var
|
|
5049
|
-
var
|
|
5050
|
-
var rColor = r.agent_color || 'var(--accent)';
|
|
5109
|
+
var rName = r.agent_name || r.sender_name || r.name || r.agent || 'Agent';
|
|
5110
|
+
var rEmoji = r.agent_emoji || r.sender_avatar || r.avatar || '🤖';
|
|
5111
|
+
var rColor = r.agent_color || r.color || 'var(--accent)';
|
|
5051
5112
|
html += '<div class="group-msg-row">'
|
|
5052
5113
|
+ '<div class="group-msg-avatar" style="background:' + rColor + ';color:#fff">' + rEmoji + '</div>'
|
|
5053
5114
|
+ '<div>'
|
|
Binary file
|
|
Binary file
|