@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
|
@@ -8,13 +8,11 @@ import {
|
|
|
8
8
|
buildExternalWalletExecutionBlock,
|
|
9
9
|
buildToolDisciplineLines,
|
|
10
10
|
getExplicitRequiredToolNames,
|
|
11
|
-
isNarrowDirectMemoryWriteTurn,
|
|
12
11
|
isWalletSimulationResult,
|
|
13
12
|
looksLikeOpenEndedDeliverableTask,
|
|
14
13
|
resolveContinuationAssistantText,
|
|
15
14
|
resolveFinalStreamResponseText,
|
|
16
|
-
|
|
17
|
-
shouldAllowToolForCurrentThreadRecall,
|
|
15
|
+
shouldSkipToolSummaryForShortResponse,
|
|
18
16
|
shouldForceAttachmentFollowthrough,
|
|
19
17
|
shouldForceRecoverableToolErrorFollowthrough,
|
|
20
18
|
shouldTerminateOnSuccessfulMemoryMutation,
|
|
@@ -55,72 +53,66 @@ describe('buildToolDisciplineLines', () => {
|
|
|
55
53
|
assert.ok(lines.every((line) => !line.includes('Do not substitute `manage_platform`')))
|
|
56
54
|
})
|
|
57
55
|
|
|
58
|
-
it('includes concrete files-tool examples for
|
|
56
|
+
it('includes concrete files-tool examples for file work', () => {
|
|
59
57
|
const lines = buildToolDisciplineLines(['files'])
|
|
60
58
|
|
|
61
59
|
assert.ok(lines.some((line) => line.includes('{"action":"read","filePath":"path/to/file.md"}')))
|
|
62
|
-
assert.ok(lines.some((line) => line.includes('
|
|
63
|
-
assert.ok(lines.some((line) => line.includes('
|
|
60
|
+
assert.ok(lines.some((line) => line.includes('{"action":"list","dirPath":"."}')))
|
|
61
|
+
assert.ok(lines.some((line) => line.includes('{"action":"write","files":[{"path":"path/to/file.md","content":"..."}]}')))
|
|
64
62
|
})
|
|
65
63
|
|
|
66
|
-
it('
|
|
64
|
+
it('tells the agent to use direct schedule tools when manage_platform is absent', () => {
|
|
67
65
|
const lines = buildToolDisciplineLines(['manage_schedules', 'schedule_wake'])
|
|
68
66
|
|
|
69
|
-
assert.ok(lines.some((line) => line.includes('
|
|
70
|
-
assert.ok(lines.some((line) => line.includes('
|
|
71
|
-
assert.ok(lines.some((line) => line.includes('prefer `schedule_wake` over creating a recurring schedule')))
|
|
67
|
+
assert.ok(lines.some((line) => line.includes('Use direct platform tools exactly as named (`manage_schedules`)')))
|
|
68
|
+
assert.ok(lines.some((line) => line.includes('Do not substitute `manage_platform` unless it is explicitly enabled.')))
|
|
72
69
|
})
|
|
73
70
|
|
|
74
|
-
it('warns browser
|
|
71
|
+
it('warns browser-capable sessions to use current supported tool inputs and sequencing', () => {
|
|
75
72
|
const lines = buildToolDisciplineLines(['web_search', 'web_fetch', 'browser', 'manage_connectors', 'http_request', 'email', 'ask_human', 'manage_secrets'])
|
|
76
73
|
|
|
77
74
|
assert.ok(lines.some((line) => line.includes('Do not invent placeholder URLs')))
|
|
78
75
|
assert.ok(lines.some((line) => line.includes('A shorthand `form` object keyed by input id/name also works')))
|
|
79
|
-
assert.ok(lines.some((line) => line.includes('prefer `fill_form` and `submit_form`')))
|
|
80
76
|
assert.ok(lines.some((line) => line.includes('For current events, breaking news, or "latest" requests, start with `web_search`')))
|
|
81
77
|
assert.ok(lines.some((line) => line.includes('Use `browser` when the user asks for screenshots')))
|
|
82
|
-
assert.ok(lines.some((line) => line.includes('do not capture screenshots') && line.includes('`browser`')))
|
|
83
78
|
assert.ok(lines.some((line) => line.includes('connector_message_tool') && line.includes('list_running')))
|
|
84
79
|
assert.ok(lines.some((line) => line.includes('connector/channel setup is missing')))
|
|
85
|
-
assert.ok(lines.some((line) => line.includes('capture the artifact first with `browser`') && line.includes('`connector_message_tool`')))
|
|
86
80
|
assert.ok(lines.some((line) => line.includes('Keep JSON request bodies as raw JSON strings')))
|
|
87
|
-
assert.ok(lines.some((line) => line.includes('
|
|
88
|
-
assert.ok(lines.some((line) => line.includes('
|
|
81
|
+
assert.ok(lines.some((line) => line.includes('gather sources first, then capture')))
|
|
82
|
+
assert.ok(lines.some((line) => line.includes('If one research path is blocked, try another') && line.includes('`http_request`') && line.includes('`browser`')))
|
|
89
83
|
assert.ok(lines.some((line) => line.includes('{"action":"send","to":"user@example.com","subject":"...","body":"..."}')))
|
|
90
84
|
assert.ok(lines.some((line) => line.includes('do not guess or keep re-submitting blank forms')))
|
|
91
|
-
assert.ok(lines.some((line) => line.includes('
|
|
92
|
-
assert.ok(lines.some((line) => line.includes('Use `manage_secrets` only for sensitive credentials or tokens')))
|
|
85
|
+
assert.ok(lines.some((line) => line.includes('Store secrets (passwords, API keys, tokens) with `manage_secrets`')))
|
|
93
86
|
})
|
|
94
87
|
|
|
95
88
|
it('adds bounded execution guidance for wallet-connected external-service tasks', () => {
|
|
96
89
|
const lines = buildToolDisciplineLines(['wallet', 'browser', 'http_request', 'manage_capabilities'])
|
|
97
90
|
|
|
98
|
-
assert.ok(lines.some((line) => line.includes('inspect the
|
|
99
|
-
assert.ok(lines.some((line) => line.includes('
|
|
100
|
-
assert.ok(lines.some((line) => line.includes('
|
|
101
|
-
assert.ok(lines.some((line) => line.includes('If a direct tool for the job is already enabled in this session, call that tool immediately')))
|
|
91
|
+
assert.ok(lines.some((line) => line.includes('inspect the wallet first with `wallet_tool`')))
|
|
92
|
+
assert.ok(lines.some((line) => line.includes('Use a bounded loop: verify, attempt one reversible step, then execute or state the blocker.')))
|
|
93
|
+
assert.ok(lines.some((line) => line.includes('stop venue-shopping') && line.includes('call_contract')))
|
|
102
94
|
})
|
|
103
95
|
|
|
104
|
-
it('
|
|
96
|
+
it('includes concrete local coding tool guidance when coding tools are already available', () => {
|
|
105
97
|
const lines = buildToolDisciplineLines(['files', 'shell', 'delegate'])
|
|
106
98
|
|
|
107
|
-
assert.ok(lines.some((line) => line.includes('
|
|
99
|
+
assert.ok(lines.some((line) => line.includes('{"action":"read","filePath":"path/to/file.md"}')))
|
|
100
|
+
assert.ok(lines.some((line) => line.includes('For `shell`, use `{"action":"execute","command":"..."}`')))
|
|
108
101
|
})
|
|
109
102
|
|
|
110
|
-
it('adds explicit
|
|
103
|
+
it('adds explicit ask_human request and wait guidance when ask_human is enabled', () => {
|
|
111
104
|
const lines = buildToolDisciplineLines(['browser', 'ask_human'])
|
|
112
105
|
|
|
113
|
-
assert.ok(lines.some((line) => line.includes('request_input') && line.includes('wait_for_reply') && line.includes('
|
|
114
|
-
assert.ok(lines.some((line) => line.includes('
|
|
115
|
-
assert.ok(lines.some((line) => line.includes('Do not loop on `status` without a `watchJobId`')))
|
|
106
|
+
assert.ok(lines.some((line) => line.includes('request_input') && line.includes('wait_for_reply') && line.includes('correlationId')))
|
|
107
|
+
assert.ok(lines.some((line) => line.includes('do not guess or keep re-submitting blank forms')))
|
|
116
108
|
})
|
|
117
109
|
|
|
118
|
-
it('tells agents to
|
|
110
|
+
it('tells agents how to send email and write files when those tools are enabled', () => {
|
|
119
111
|
const lines = buildToolDisciplineLines(['files', 'email', 'spawn_subagent'])
|
|
120
112
|
|
|
121
|
-
assert.ok(lines.some((line) => line.includes('
|
|
122
|
-
assert.ok(lines.some((line) => line.includes('
|
|
123
|
-
assert.ok(lines.some((line) => line.includes('
|
|
113
|
+
assert.ok(lines.some((line) => line.includes('For `email`, send mail with `{"action":"send","to":"user@example.com","subject":"...","body":"..."}`')))
|
|
114
|
+
assert.ok(lines.some((line) => line.includes('If delivery depends on SMTP setup, check `{"action":"status"}` before claiming success.')))
|
|
115
|
+
assert.ok(lines.some((line) => line.includes('{"action":"write","files":[{"path":"path/to/file.md","content":"..."}]}')))
|
|
124
116
|
})
|
|
125
117
|
|
|
126
118
|
it('does not force capability-inferred tools — trusts the LLM to select tools', () => {
|
|
@@ -186,7 +178,7 @@ describe('buildToolDisciplineLines', () => {
|
|
|
186
178
|
assert.ok(streamAgentChatSource.includes('If a task explicitly names an enabled tool, use that tool before declaring success.'))
|
|
187
179
|
assert.ok(streamAgentChatSource.includes('collect required human input through the tool'))
|
|
188
180
|
assert.ok(streamAgentChatSource.includes('## Attachments'))
|
|
189
|
-
assert.ok(
|
|
181
|
+
assert.ok(streamSources.includes('Do not claim that you cannot use images, attachments, or external tools when they are available in this session.'))
|
|
190
182
|
assert.ok(streamSources.includes('You have not yet completed the required explicit tool step(s):'))
|
|
191
183
|
assert.ok(streamSources.includes('attachment_followthrough'))
|
|
192
184
|
assert.ok(streamSources.includes('unfinished_tool_followthrough'))
|
|
@@ -208,57 +200,16 @@ describe('buildToolDisciplineLines', () => {
|
|
|
208
200
|
assert.ok(streamAgentChatSource.includes('did not start the required workspace tool step'))
|
|
209
201
|
})
|
|
210
202
|
|
|
211
|
-
it('adds
|
|
203
|
+
it('adds current-thread recall guidance and immediate memory routes in the system prompt', () => {
|
|
212
204
|
assert.ok(streamAgentChatSource.includes('## Current Thread Recall'))
|
|
213
205
|
assert.ok(streamAgentChatSource.includes('## Immediate Memory Routes'))
|
|
214
|
-
assert.ok(streamAgentChatSource.includes('## Direct Memory Write'))
|
|
215
206
|
assert.ok(streamAgentChatSource.includes('call `memory_store` or `memory_update` immediately before any planning, delegation, task creation, or agent management'))
|
|
216
|
-
assert.ok(streamAgentChatSource.includes('Do not inspect skills, browse the workspace, request capabilities, manage tasks, manage agents, or delegate before the direct memory write is complete.'))
|
|
217
207
|
assert.ok(streamAgentChatSource.includes('Do NOT call memory tools, web search, or session-history tools'))
|
|
218
|
-
assert.ok(streamAgentChatSource.includes('const currentThreadRecallRequest =
|
|
219
|
-
assert.ok(streamAgentChatSource.includes('const directMemoryWriteOnlyTurn = isNarrowDirectMemoryWriteTurn(message)'))
|
|
220
|
-
assert.ok(streamAgentChatSource.includes('shouldAllowToolForDirectMemoryWrite(toolName)'))
|
|
221
|
-
assert.ok(streamAgentChatSource.includes('shouldAllowToolForCurrentThreadRecall(toolName)'))
|
|
208
|
+
assert.ok(streamAgentChatSource.includes('const currentThreadRecallRequest = isCurrentThreadRecallRequest(message)'))
|
|
222
209
|
assert.ok(streamSources.includes('Preserve hard structural constraints from the original request'))
|
|
223
210
|
assert.ok(streamAgentChatSource.includes('## Exact Structural Constraints'))
|
|
224
211
|
})
|
|
225
212
|
|
|
226
|
-
it('blocks memory, session-history, web, and context tools during same-thread recall turns', () => {
|
|
227
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('memory_tool'), false)
|
|
228
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('memory_search'), false)
|
|
229
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('memory_get'), false)
|
|
230
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('memory_store'), false)
|
|
231
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('memory_update'), false)
|
|
232
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('search_history_tool'), false)
|
|
233
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('sessions_tool'), false)
|
|
234
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('web_search'), false)
|
|
235
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('context_status'), false)
|
|
236
|
-
assert.equal(shouldAllowToolForCurrentThreadRecall('files'), true)
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
it('only allows direct memory write tools during pure remember/store turns', () => {
|
|
240
|
-
assert.equal(shouldAllowToolForDirectMemoryWrite('memory_store'), true)
|
|
241
|
-
assert.equal(shouldAllowToolForDirectMemoryWrite('memory_update'), true)
|
|
242
|
-
assert.equal(shouldAllowToolForDirectMemoryWrite('memory_tool'), false)
|
|
243
|
-
assert.equal(shouldAllowToolForDirectMemoryWrite('manage_capabilities'), false)
|
|
244
|
-
assert.equal(shouldAllowToolForDirectMemoryWrite('files'), false)
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
it('treats long remember-and-confirm turns as narrow direct memory writes', () => {
|
|
248
|
-
assert.equal(
|
|
249
|
-
isNarrowDirectMemoryWriteTurn('Remember that my favorite programming language is Rust and I prefer functional programming patterns. Then confirm what you just stored.'),
|
|
250
|
-
true,
|
|
251
|
-
)
|
|
252
|
-
assert.equal(
|
|
253
|
-
isNarrowDirectMemoryWriteTurn('Remember these facts for future conversations: My favorite programming language is Rust. My deploy target is Fly.io. My team size is 7 people. The project is codenamed "Neptune".'),
|
|
254
|
-
true,
|
|
255
|
-
)
|
|
256
|
-
assert.equal(
|
|
257
|
-
isNarrowDirectMemoryWriteTurn('Remember that my favorite programming language is Rust, then write a file summarizing it and send it to me.'),
|
|
258
|
-
false,
|
|
259
|
-
)
|
|
260
|
-
})
|
|
261
|
-
|
|
262
213
|
it('canonicalizes required tool names when checking completion', () => {
|
|
263
214
|
// The requiredToolsPending filter must canonicalize tool names so that
|
|
264
215
|
// alias names (e.g. ask_human) match canonical names from LangGraph events.
|
|
@@ -308,6 +259,46 @@ describe('isWalletSimulationResult', () => {
|
|
|
308
259
|
})
|
|
309
260
|
})
|
|
310
261
|
|
|
262
|
+
describe('shouldSkipToolSummaryForShortResponse', () => {
|
|
263
|
+
it('skips forced tool-summary continuation for short responses after pure use_skill calls', () => {
|
|
264
|
+
assert.equal(
|
|
265
|
+
shouldSkipToolSummaryForShortResponse({
|
|
266
|
+
fullText: 'HAL2K_RELEASE_LIVE_OK',
|
|
267
|
+
toolEvents: [
|
|
268
|
+
{ name: 'use_skill', input: '{"action":"list"}', output: '{"ok":true}' },
|
|
269
|
+
{ name: 'use_skill', input: '{"action":"load"}', output: '{"loaded":true}' },
|
|
270
|
+
],
|
|
271
|
+
}),
|
|
272
|
+
true,
|
|
273
|
+
)
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
it('does not skip tool-summary continuation when substantive tools also ran', () => {
|
|
277
|
+
assert.equal(
|
|
278
|
+
shouldSkipToolSummaryForShortResponse({
|
|
279
|
+
fullText: 'Done.',
|
|
280
|
+
toolEvents: [
|
|
281
|
+
{ name: 'use_skill', input: '{"action":"load"}', output: '{"loaded":true}' },
|
|
282
|
+
{ name: 'web', input: '{"q":"latest"}', output: 'results' },
|
|
283
|
+
],
|
|
284
|
+
}),
|
|
285
|
+
false,
|
|
286
|
+
)
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
it('does not skip tool-summary continuation for empty text', () => {
|
|
290
|
+
assert.equal(
|
|
291
|
+
shouldSkipToolSummaryForShortResponse({
|
|
292
|
+
fullText: '',
|
|
293
|
+
toolEvents: [
|
|
294
|
+
{ name: 'use_skill', input: '{"action":"load"}', output: '{"loaded":true}' },
|
|
295
|
+
],
|
|
296
|
+
}),
|
|
297
|
+
false,
|
|
298
|
+
)
|
|
299
|
+
})
|
|
300
|
+
})
|
|
301
|
+
|
|
311
302
|
describe('looksLikeOpenEndedDeliverableTask', () => {
|
|
312
303
|
it('detects open-ended deliverable prompts', () => {
|
|
313
304
|
assert.equal(
|