@swarmclawai/swarmclaw 0.9.2 → 0.9.4
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 +12 -10
- package/bundled-skills/google-workspace/SKILL.md +2 -0
- package/package.json +1 -1
- package/src/app/agents/page.tsx +2 -1
- package/src/app/api/chatrooms/[id]/chat/route.ts +1 -1
- package/src/app/api/clawhub/install/route.ts +2 -0
- package/src/app/api/skills/[id]/route.ts +4 -0
- package/src/app/api/skills/route.ts +4 -0
- package/src/app/globals.css +28 -0
- package/src/app/home/page.tsx +11 -0
- package/src/app/settings/page.tsx +12 -5
- package/src/components/agents/agent-sheet.tsx +5 -5
- package/src/components/connectors/connector-list.tsx +2 -5
- package/src/components/logs/log-list.tsx +2 -5
- package/src/components/providers/provider-list.tsx +2 -5
- package/src/components/runs/run-list.tsx +2 -6
- package/src/components/schedules/schedule-list.tsx +7 -1
- package/src/components/ui/full-screen-loader.tsx +0 -29
- package/src/components/ui/page-loader.tsx +69 -0
- package/src/lib/runtime/runtime-loop.ts +21 -1
- package/src/lib/server/agents/agent-thread-session.test.ts +64 -0
- package/src/lib/server/agents/agent-thread-session.ts +1 -1
- package/src/lib/server/agents/main-agent-loop-advanced.test.ts +77 -0
- package/src/lib/server/agents/main-agent-loop.ts +259 -0
- package/src/lib/server/agents/orchestrator-lg.ts +12 -8
- package/src/lib/server/agents/orchestrator.ts +11 -7
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +11 -10
- package/src/lib/server/chat-execution/chat-execution-session-sync.test.ts +116 -3
- package/src/lib/server/chat-execution/chat-execution-utils.test.ts +56 -0
- package/src/lib/server/chat-execution/chat-execution-utils.ts +24 -0
- package/src/lib/server/chat-execution/chat-execution.ts +116 -29
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +1 -38
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +67 -76
- package/src/lib/server/chat-execution/stream-agent-chat.ts +119 -110
- package/src/lib/server/chat-execution/stream-continuation.ts +1 -1
- package/src/lib/server/chatrooms/chatroom-helpers.test.ts +26 -0
- package/src/lib/server/chatrooms/chatroom-helpers.ts +11 -8
- package/src/lib/server/connectors/contact-boundaries.ts +101 -0
- package/src/lib/server/connectors/manager.test.ts +504 -73
- package/src/lib/server/connectors/manager.ts +41 -10
- package/src/lib/server/connectors/session-consolidation.ts +2 -0
- package/src/lib/server/connectors/session-kind.ts +7 -0
- package/src/lib/server/connectors/session.test.ts +104 -0
- package/src/lib/server/connectors/session.ts +5 -2
- package/src/lib/server/identity-continuity.test.ts +4 -3
- package/src/lib/server/identity-continuity.ts +8 -4
- package/src/lib/server/memory/memory-policy.test.ts +5 -15
- package/src/lib/server/memory/memory-policy.ts +11 -41
- package/src/lib/server/memory/session-archive-memory.ts +2 -1
- package/src/lib/server/runtime/heartbeat-service.test.ts +46 -0
- package/src/lib/server/runtime/heartbeat-service.ts +5 -1
- package/src/lib/server/runtime/runtime-settings.test.ts +4 -4
- package/src/lib/server/runtime/runtime-settings.ts +4 -0
- package/src/lib/server/runtime/session-run-manager.ts +2 -0
- package/src/lib/server/session-reset-policy.test.ts +17 -3
- package/src/lib/server/session-reset-policy.ts +4 -2
- package/src/lib/server/session-tools/connector.ts +11 -10
- package/src/lib/server/session-tools/crud.ts +41 -7
- package/src/lib/server/session-tools/delegate.ts +3 -3
- package/src/lib/server/session-tools/index.ts +2 -0
- package/src/lib/server/session-tools/manage-skills.test.ts +194 -0
- package/src/lib/server/session-tools/memory.ts +209 -48
- package/src/lib/server/session-tools/skill-runtime.test.ts +175 -0
- package/src/lib/server/session-tools/skill-runtime.ts +382 -0
- package/src/lib/server/session-tools/skills.ts +575 -0
- package/src/lib/server/skills/runtime-skill-resolver.test.ts +162 -0
- package/src/lib/server/skills/runtime-skill-resolver.ts +750 -0
- package/src/lib/server/skills/skill-discovery.ts +4 -0
- package/src/lib/server/skills/skills-normalize.test.ts +28 -0
- package/src/lib/server/skills/skills-normalize.ts +93 -1
- package/src/lib/server/storage.ts +1 -1
- package/src/lib/server/tasks/task-followups.test.ts +124 -0
- package/src/lib/server/tasks/task-followups.ts +88 -13
- package/src/types/index.ts +30 -2
- package/src/views/settings/section-runtime-loop.tsx +38 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import test from 'node:test'
|
|
6
|
+
import type { Skill } from '@/types'
|
|
7
|
+
import {
|
|
8
|
+
buildRuntimeSkillPromptBlocks,
|
|
9
|
+
recommendRuntimeSkillsForTask,
|
|
10
|
+
resolveRuntimeSkills,
|
|
11
|
+
} from './runtime-skill-resolver'
|
|
12
|
+
|
|
13
|
+
function makeSkill(id: string, overrides: Partial<Skill> = {}): Skill {
|
|
14
|
+
return {
|
|
15
|
+
id,
|
|
16
|
+
name: id,
|
|
17
|
+
filename: `${id}.md`,
|
|
18
|
+
content: `# ${id}\nUse ${id}.`,
|
|
19
|
+
createdAt: Date.now(),
|
|
20
|
+
updatedAt: Date.now(),
|
|
21
|
+
...overrides,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test('resolveRuntimeSkills prefers project-local skills over stored skills with the same key', () => {
|
|
26
|
+
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-skill-resolver-'))
|
|
27
|
+
try {
|
|
28
|
+
const skillDir = path.join(cwd, 'skills', 'github-sync')
|
|
29
|
+
fs.mkdirSync(skillDir, { recursive: true })
|
|
30
|
+
fs.writeFileSync(path.join(skillDir, 'SKILL.md'), `---
|
|
31
|
+
name: github-sync
|
|
32
|
+
description: Project-local GitHub flow.
|
|
33
|
+
metadata:
|
|
34
|
+
openclaw:
|
|
35
|
+
toolNames: [shell]
|
|
36
|
+
---
|
|
37
|
+
# Project Skill
|
|
38
|
+
|
|
39
|
+
Prefer the project workflow.
|
|
40
|
+
`)
|
|
41
|
+
|
|
42
|
+
const storedSkills = {
|
|
43
|
+
stored_github_sync: makeSkill('stored_github_sync', {
|
|
44
|
+
name: 'github-sync',
|
|
45
|
+
description: 'Stored GitHub flow.',
|
|
46
|
+
content: '# Stored Skill\nUse the stored workflow.',
|
|
47
|
+
toolNames: ['http_request'],
|
|
48
|
+
}),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const snapshot = resolveRuntimeSkills({
|
|
52
|
+
cwd,
|
|
53
|
+
enabledPlugins: ['shell'],
|
|
54
|
+
storedSkills,
|
|
55
|
+
agentSkillIds: ['stored_github_sync'],
|
|
56
|
+
})
|
|
57
|
+
const githubSkill = snapshot.skills.find((skill) => skill.key === 'github_sync')
|
|
58
|
+
|
|
59
|
+
assert.ok(githubSkill)
|
|
60
|
+
assert.equal(githubSkill?.source, 'project')
|
|
61
|
+
assert.equal(githubSkill?.attached, true, 'attachment survives precedence merge')
|
|
62
|
+
assert.match(githubSkill?.content || '', /Project Skill/)
|
|
63
|
+
} finally {
|
|
64
|
+
fs.rmSync(cwd, { recursive: true, force: true })
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('resolveRuntimeSkills auto-matches skills from explicit tool metadata and reports missing config', () => {
|
|
69
|
+
const storedSkills = {
|
|
70
|
+
weather_skill: makeSkill('weather_skill', {
|
|
71
|
+
name: 'weather-helper',
|
|
72
|
+
toolNames: ['google_workspace', 'gws'],
|
|
73
|
+
capabilities: ['weather', 'forecast'],
|
|
74
|
+
skillRequirements: { config: ['nonexistent.skill.path'] },
|
|
75
|
+
}),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const snapshot = resolveRuntimeSkills({
|
|
79
|
+
enabledPlugins: ['google_workspace'],
|
|
80
|
+
storedSkills,
|
|
81
|
+
})
|
|
82
|
+
const skill = snapshot.skills.find((entry) => entry.name === 'weather-helper')
|
|
83
|
+
|
|
84
|
+
assert.equal(skill?.autoMatch, true)
|
|
85
|
+
assert.equal(skill?.eligible, false)
|
|
86
|
+
assert.deepEqual(skill?.missing, ['config nonexistent.skill.path'])
|
|
87
|
+
assert.ok(skill?.matchReasons.some((reason) => /matches tools/i.test(reason)))
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('recommendRuntimeSkillsForTask ranks matching local skills and prompt blocks include auto-matched skills', () => {
|
|
91
|
+
const storedSkills = {
|
|
92
|
+
gws_skill: makeSkill('gws_skill', {
|
|
93
|
+
name: 'google-workspace-helper',
|
|
94
|
+
description: 'Automate Google Workspace docs and sheets.',
|
|
95
|
+
toolNames: ['google_workspace', 'gws'],
|
|
96
|
+
capabilities: ['docs', 'sheets', 'workspace'],
|
|
97
|
+
}),
|
|
98
|
+
generic_skill: makeSkill('generic_skill', {
|
|
99
|
+
name: 'generic-notes',
|
|
100
|
+
description: 'Store notes.',
|
|
101
|
+
}),
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const snapshot = resolveRuntimeSkills({
|
|
105
|
+
enabledPlugins: ['google_workspace'],
|
|
106
|
+
storedSkills,
|
|
107
|
+
})
|
|
108
|
+
const recommended = recommendRuntimeSkillsForTask(snapshot.skills, 'Update the Google Docs and Sheets workspace report', ['google_workspace'])
|
|
109
|
+
|
|
110
|
+
assert.equal(recommended[0]?.skill.name, 'google-workspace-helper')
|
|
111
|
+
const blocks = buildRuntimeSkillPromptBlocks(snapshot).join('\n')
|
|
112
|
+
assert.match(blocks, /Skill Runtime/)
|
|
113
|
+
assert.match(blocks, /Available Skills/)
|
|
114
|
+
assert.match(blocks, /google-workspace-helper/)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test('buildRuntimeSkillPromptBlocks only inlines pinned skills before explicit selection', () => {
|
|
118
|
+
const storedSkills = {
|
|
119
|
+
pinned_skill: makeSkill('pinned_skill', {
|
|
120
|
+
name: 'Pinned Workflow',
|
|
121
|
+
}),
|
|
122
|
+
generic_skill: makeSkill('generic_skill', {
|
|
123
|
+
name: 'Generic Helper',
|
|
124
|
+
description: 'Useful fallback workflow.',
|
|
125
|
+
}),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const snapshot = resolveRuntimeSkills({
|
|
129
|
+
storedSkills,
|
|
130
|
+
agentSkillIds: ['pinned_skill'],
|
|
131
|
+
})
|
|
132
|
+
const blocks = buildRuntimeSkillPromptBlocks(snapshot).join('\n')
|
|
133
|
+
|
|
134
|
+
assert.match(blocks, /Pinned Skills/)
|
|
135
|
+
assert.match(blocks, /discoverable by default/i)
|
|
136
|
+
assert.match(blocks, /Pinned Workflow/)
|
|
137
|
+
assert.match(blocks, /Generic Helper/)
|
|
138
|
+
assert.doesNotMatch(blocks, /### Generic Helper/)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test('resolveRuntimeSkills marks the selected skill and loads it into the prompt separately', () => {
|
|
142
|
+
const storedSkills = {
|
|
143
|
+
pinned_skill: makeSkill('pinned_skill', {
|
|
144
|
+
name: 'Pinned Workflow',
|
|
145
|
+
}),
|
|
146
|
+
selected_skill: makeSkill('selected_skill', {
|
|
147
|
+
name: 'Selected Workflow',
|
|
148
|
+
}),
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const snapshot = resolveRuntimeSkills({
|
|
152
|
+
storedSkills,
|
|
153
|
+
agentSkillIds: ['pinned_skill'],
|
|
154
|
+
selectedSkillId: 'selected_skill',
|
|
155
|
+
})
|
|
156
|
+
const blocks = buildRuntimeSkillPromptBlocks(snapshot).join('\n')
|
|
157
|
+
|
|
158
|
+
assert.equal(snapshot.selectedSkill?.name, 'Selected Workflow')
|
|
159
|
+
assert.match(blocks, /Active Selected Skill/)
|
|
160
|
+
assert.match(blocks, /Selected Workflow/)
|
|
161
|
+
assert.match(blocks, /### Selected Workflow/)
|
|
162
|
+
})
|