@swarmclawai/swarmclaw 1.5.62 → 1.5.63
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 +8 -10
- package/package.json +1 -1
- package/src/app/api/chatrooms/[id]/chat/route.test.ts +111 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +47 -28
- package/src/app/api/chatrooms/[id]/members/route.ts +0 -8
- package/src/app/api/chatrooms/[id]/route.ts +0 -11
- package/src/app/api/chatrooms/route.ts +0 -10
- package/src/components/chatrooms/chatroom-sheet.tsx +1 -4
- package/src/lib/server/session-tools/chatroom.ts +0 -11
package/README.md
CHANGED
|
@@ -399,6 +399,14 @@ Operational docs: https://swarmclaw.ai/docs/observability
|
|
|
399
399
|
|
|
400
400
|
## Releases
|
|
401
401
|
|
|
402
|
+
### v1.5.63 Highlights
|
|
403
|
+
|
|
404
|
+
Chatroom fix from @borislavnnikolov: CLI-backed agents (codex-cli, copilot-cli, gemini-cli, and the rest of the `NON_LANGGRAPH_PROVIDER_IDS` set) now work correctly as chatroom members instead of falling through a LangGraph path they cannot run. With the execution path fixed, the worker-only membership blocks are lifted too, so any non-trashed agent can be added to a room.
|
|
405
|
+
|
|
406
|
+
- **Direct provider runtime for CLI chatroom turns.** `src/app/api/chatrooms/[id]/chat/route.ts` now branches on `NON_LANGGRAPH_PROVIDER_IDS` and calls `provider.handler.streamChat()` directly for CLI-backed agents while keeping the LangGraph `streamAgentChat` path for everything else. Streaming, tool events, and persisted messages all flow through unchanged.
|
|
407
|
+
- **Full member selection.** The create, update, members, session-tool, and UI layers (`src/app/api/chatrooms/route.ts`, `src/app/api/chatrooms/[id]/route.ts`, `src/app/api/chatrooms/[id]/members/route.ts`, `src/lib/server/session-tools/chatroom.ts`, `src/components/chatrooms/chatroom-sheet.tsx`) no longer reject or hide worker-only agents. Any non-trashed agent is eligible.
|
|
408
|
+
- **Regression test.** `src/app/api/chatrooms/[id]/chat/route.test.ts` proves a `codex-cli`-backed chatroom turn bypasses `streamAgentChat`, streams a response through the provider handler, and persists one assistant reply.
|
|
409
|
+
|
|
402
410
|
### v1.5.62 Highlights
|
|
403
411
|
|
|
404
412
|
Hardens parallel sub-agent dispatch with a concurrency cap, a quorum join policy, and a cycle check — so a fan-out can't accidentally saturate providers, melt a mission budget, or wedge the runtime on a delegation loop.
|
|
@@ -432,16 +440,6 @@ Viral-loop release. Adds public share links for missions, skills, and sessions,
|
|
|
432
440
|
- **Share-link-based skill install.** `POST /api/skills/import` already accepts an http(s) URL; pointing it at `https://<your-host>/api/s/<token>/raw` now installs a shared skill from another SwarmClaw instance without auth handshakes. Pairs naturally with existing `swarmclaw skills import` CLI.
|
|
433
441
|
- **Share-link repository tests.** `share-link-repository.test.ts` covers mint / list / revoke / lookup-by-token round-trip plus expiry handling against a temporary data dir.
|
|
434
442
|
|
|
435
|
-
### v1.5.58 Highlights
|
|
436
|
-
|
|
437
|
-
This release broadens the built-in evaluation harness so SwarmClaw runs can be benchmarked against named suites, adds two targeted starter kits, exposes live per-session cost data, tightens auto-skill drafting, and ships a zero-setup demo mission template.
|
|
438
|
-
|
|
439
|
-
- **Benchmark-style eval suites.** New `SWEBENCH_LITE_SCENARIOS` and `GAIA_L1_SCENARIOS` in `src/lib/server/eval/scenarios-swebench.ts` and `scenarios-gaia.ts` — curated parallels (not the upstream datasets) sized for a single-agent harness run. The shared `EvalScenario` type now carries an optional `suite: 'core' | 'swe-bench-lite' | 'gaia-l1' | 'tool-use' | 'code-action'` tag. `POST /api/eval/suite` accepts `{ suite: "swe-bench-lite" }` to scope a run. New `GET /api/eval/suites` lists every suite with scenario count, max score, and categories. CLI commands: `swarmclaw eval suites`, and `swarmclaw eval suite` still takes a JSON body now including `suite`. Useful for advertising verifiable numbers against a named benchmark instead of a bespoke scoring rubric.
|
|
440
|
-
- **Two additional starter kits.** `inbox_triage` (single Triager agent over email + memory + documents) and `data_analyst` (single Analyst agent over shell + files + web + documents) join the existing seven kits in `src/lib/setup-defaults.ts`. Both are surfaced on the intent-driven setup path alongside Personal Assistant, Research Copilot, Builder Studio, and Delegate Team.
|
|
441
|
-
- **Live per-session usage API.** New `GET /api/usage/live?sessionId=...` returns a lightweight snapshot — records, tokens in/out, estimated cost, firstAt/lastAt, wallclockMs, turns — so frontends can surface a live cost meter without pulling the full aggregated `/api/usage` payload. Without a `sessionId` the route returns the ten most recently active sessions. Registered in the CLI as `swarmclaw usage live`.
|
|
442
|
-
- **Auto-skill drafting is stricter and rate-limited.** `shouldAutoDraftSkillSuggestion` in `chat-turn-finalization.ts` now requires at least 3 tool events in the completed turn (was 1), and a new per-agent daily cap limits automatic drafts to 3 per day per agent to prevent suggestion-inbox spam. Both thresholds are named constants (`AUTO_DRAFT_MIN_TOOL_EVENTS`, `AUTO_DRAFT_DAILY_LIMIT`). Agents with `autoDraftSkillSuggestions = false` are unaffected (auto-drafting remains opt-in per agent).
|
|
443
|
-
- **Hello World demo mission template.** New `hello-world-demo` entry in `BUILT_IN_MISSION_TEMPLATES` — a bounded, zero-setup mission that reads three files in the working directory and writes a one-paragraph markdown summary to `hello-world-report.md`. Budgets (USD 0.25, 20k tokens, 30 turns, 15 min) are small enough to run on a local Ollama model without cost. Intended as the first thing a new user watches an agent complete end to end.
|
|
444
|
-
|
|
445
443
|
Older releases: https://swarmclaw.ai/docs/release-notes
|
|
446
444
|
|
|
447
445
|
- GitHub releases: https://github.com/swarmclawai/swarmclaw/releases
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmclawai/swarmclaw",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.63",
|
|
4
4
|
"description": "Build and run autonomous AI agents with OpenClaw, Hermes, multiple model providers, orchestration, delegation, memory, skills, schedules, and chat connectors.",
|
|
5
5
|
"main": "electron-dist/main.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -297,3 +297,114 @@ test('chatroom route forwards tool activity and records one reply per participat
|
|
|
297
297
|
assert.deepEqual(output.assistantCounts, { alpha: 1, beta: 1 })
|
|
298
298
|
assert.deepEqual([...new Set(output.agentOrder)].sort(), ['alpha', 'beta'])
|
|
299
299
|
})
|
|
300
|
+
|
|
301
|
+
test('chatroom route uses direct provider runtime for CLI providers', () => {
|
|
302
|
+
const output = runWithTempDataDir<{
|
|
303
|
+
errors: string[]
|
|
304
|
+
streamedText: string
|
|
305
|
+
assistantTexts: string[]
|
|
306
|
+
}>(`
|
|
307
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
308
|
+
const providersMod = await import('@/lib/providers')
|
|
309
|
+
const routeMod = await import('./src/app/api/chatrooms/[id]/chat/route')
|
|
310
|
+
const streamMod = await import('@/lib/server/chat-execution/stream-agent-chat')
|
|
311
|
+
const storage = storageMod.default || storageMod
|
|
312
|
+
const providers = providersMod.default || providersMod
|
|
313
|
+
const route = routeMod.default || routeMod
|
|
314
|
+
const stream = streamMod.default || streamMod
|
|
315
|
+
|
|
316
|
+
const originalHandler = providers.PROVIDERS['codex-cli'].handler
|
|
317
|
+
|
|
318
|
+
const now = Date.now()
|
|
319
|
+
storage.saveAgents({
|
|
320
|
+
alpha: {
|
|
321
|
+
id: 'alpha',
|
|
322
|
+
name: 'Alpha',
|
|
323
|
+
provider: 'codex-cli',
|
|
324
|
+
model: 'gpt-5.3-codex',
|
|
325
|
+
extensions: [],
|
|
326
|
+
createdAt: now,
|
|
327
|
+
updatedAt: now,
|
|
328
|
+
},
|
|
329
|
+
})
|
|
330
|
+
storage.saveChatrooms({
|
|
331
|
+
room_1: {
|
|
332
|
+
id: 'room_1',
|
|
333
|
+
name: 'CLI Room',
|
|
334
|
+
agentIds: ['alpha'],
|
|
335
|
+
messages: [],
|
|
336
|
+
createdAt: now,
|
|
337
|
+
updatedAt: now,
|
|
338
|
+
chatMode: 'sequential',
|
|
339
|
+
autoAddress: true,
|
|
340
|
+
},
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
async function readSse(response) {
|
|
344
|
+
const reader = response.body.getReader()
|
|
345
|
+
const decoder = new TextDecoder()
|
|
346
|
+
let buffer = ''
|
|
347
|
+
const events = []
|
|
348
|
+
while (true) {
|
|
349
|
+
const { done, value } = await reader.read()
|
|
350
|
+
if (done) break
|
|
351
|
+
buffer += decoder.decode(value, { stream: true })
|
|
352
|
+
let idx = buffer.indexOf('\\n\\n')
|
|
353
|
+
while (idx !== -1) {
|
|
354
|
+
const chunk = buffer.slice(0, idx)
|
|
355
|
+
buffer = buffer.slice(idx + 2)
|
|
356
|
+
const line = chunk
|
|
357
|
+
.split('\\n')
|
|
358
|
+
.map((entry) => entry.trim())
|
|
359
|
+
.find((entry) => entry.startsWith('data: '))
|
|
360
|
+
if (line) {
|
|
361
|
+
events.push(JSON.parse(line.slice(6)))
|
|
362
|
+
}
|
|
363
|
+
idx = buffer.indexOf('\\n\\n')
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return events
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
stream.setStreamAgentChatForTest(async () => {
|
|
370
|
+
throw new Error('streamAgentChat should not be called for codex-cli chatroom turns')
|
|
371
|
+
})
|
|
372
|
+
providers.PROVIDERS['codex-cli'].handler = {
|
|
373
|
+
streamChat: async (opts) => {
|
|
374
|
+
const reply = 'Codex CLI answered from direct provider runtime.'
|
|
375
|
+
opts.write('data: ' + JSON.stringify({ t: 'd', text: reply }) + '\\n')
|
|
376
|
+
return reply
|
|
377
|
+
},
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
try {
|
|
381
|
+
const response = await route.POST(
|
|
382
|
+
new Request('http://local/api/chatrooms/room_1/chat', {
|
|
383
|
+
method: 'POST',
|
|
384
|
+
headers: { 'content-type': 'application/json' },
|
|
385
|
+
body: JSON.stringify({ senderId: 'user', text: 'Say hello to the room.' }),
|
|
386
|
+
}),
|
|
387
|
+
{ params: Promise.resolve({ id: 'room_1' }) },
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
const events = await readSse(response)
|
|
391
|
+
const chatroom = storage.loadChatrooms().room_1
|
|
392
|
+
const assistantTexts = chatroom.messages
|
|
393
|
+
.filter((entry) => entry.role === 'assistant')
|
|
394
|
+
.map((entry) => entry.text)
|
|
395
|
+
|
|
396
|
+
console.log(JSON.stringify({
|
|
397
|
+
errors: events.filter((entry) => entry.t === 'err').map((entry) => entry.text),
|
|
398
|
+
streamedText: events.filter((entry) => entry.t === 'd').map((entry) => entry.text).join(''),
|
|
399
|
+
assistantTexts,
|
|
400
|
+
}))
|
|
401
|
+
} finally {
|
|
402
|
+
providers.PROVIDERS['codex-cli'].handler = originalHandler
|
|
403
|
+
stream.setStreamAgentChatForTest(null)
|
|
404
|
+
}
|
|
405
|
+
`, { prefix: 'swarmclaw-chatroom-route-cli-provider-' })
|
|
406
|
+
|
|
407
|
+
assert.equal(output.errors.some((text) => /streamAgentChat should not be called/i.test(text)), false)
|
|
408
|
+
assert.equal(output.streamedText.includes('Codex CLI answered from direct provider runtime.'), true)
|
|
409
|
+
assert.equal(output.assistantTexts.some((text) => text.includes('Codex CLI answered from direct provider runtime.')), true)
|
|
410
|
+
})
|
|
@@ -6,6 +6,7 @@ import { notFound } from '@/lib/server/collection-helpers'
|
|
|
6
6
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
7
7
|
import { streamAgentChat } from '@/lib/server/chat-execution/stream-agent-chat'
|
|
8
8
|
import { getProvider } from '@/lib/providers'
|
|
9
|
+
import { NON_LANGGRAPH_PROVIDER_IDS } from '@/lib/provider-sets'
|
|
9
10
|
import {
|
|
10
11
|
resolveApiKey,
|
|
11
12
|
parseMentions,
|
|
@@ -231,37 +232,55 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
231
232
|
|
|
232
233
|
let fullText = ''
|
|
233
234
|
let agentError = ''
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
fullText += parsed.text
|
|
249
|
-
writeEvent({ t: 'd', text: parsed.text, agentId: agent.id, agentName: agent.name })
|
|
250
|
-
} else if (parsed.t === 'tool_call' || parsed.t === 'tool_result') {
|
|
251
|
-
writeEvent({ ...parsed, agentId: agent.id, agentName: agent.name })
|
|
252
|
-
} else if (parsed.t === 'err' && parsed.text) {
|
|
253
|
-
agentError = parsed.text
|
|
254
|
-
writeEvent({ t: 'err', text: parsed.text, agentId: agent.id, agentName: agent.name })
|
|
255
|
-
}
|
|
256
|
-
} catch {
|
|
257
|
-
// skip malformed lines
|
|
235
|
+
const forwardProviderEvents = (raw: string) => {
|
|
236
|
+
const lines = raw.split('\n').filter(Boolean)
|
|
237
|
+
for (const line of lines) {
|
|
238
|
+
if (!line.startsWith('data: ')) continue
|
|
239
|
+
try {
|
|
240
|
+
const parsed = JSON.parse(line.slice(6).trim())
|
|
241
|
+
if (parsed.t === 'd' && parsed.text) {
|
|
242
|
+
fullText += parsed.text
|
|
243
|
+
writeEvent({ t: 'd', text: parsed.text, agentId: agent.id, agentName: agent.name })
|
|
244
|
+
} else if (parsed.t === 'tool_call' || parsed.t === 'tool_result') {
|
|
245
|
+
writeEvent({ ...parsed, agentId: agent.id, agentName: agent.name })
|
|
246
|
+
} else if (parsed.t === 'err' && parsed.text) {
|
|
247
|
+
agentError = parsed.text
|
|
248
|
+
writeEvent({ t: 'err', text: parsed.text, agentId: agent.id, agentName: agent.name })
|
|
258
249
|
}
|
|
250
|
+
} catch {
|
|
251
|
+
// skip malformed lines
|
|
259
252
|
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
})
|
|
253
|
+
}
|
|
254
|
+
}
|
|
263
255
|
|
|
264
|
-
|
|
256
|
+
let rawResponseText = ''
|
|
257
|
+
if (NON_LANGGRAPH_PROVIDER_IDS.has(syntheticSession.provider)) {
|
|
258
|
+
const provider = getProvider(syntheticSession.provider)
|
|
259
|
+
if (!provider) throw new Error(`Unknown provider: ${syntheticSession.provider}`)
|
|
260
|
+
rawResponseText = await provider.handler.streamChat({
|
|
261
|
+
session: syntheticSession,
|
|
262
|
+
message: messageForAgent,
|
|
263
|
+
imagePath,
|
|
264
|
+
apiKey,
|
|
265
|
+
systemPrompt: fullSystemPrompt,
|
|
266
|
+
write: forwardProviderEvents,
|
|
267
|
+
active: new Map<string, unknown>(),
|
|
268
|
+
loadHistory: () => history,
|
|
269
|
+
})
|
|
270
|
+
if (!rawResponseText) rawResponseText = fullText
|
|
271
|
+
} else {
|
|
272
|
+
const result = await streamAgentChat({
|
|
273
|
+
session: syntheticSession,
|
|
274
|
+
message: messageForAgent,
|
|
275
|
+
imagePath,
|
|
276
|
+
attachedFiles,
|
|
277
|
+
apiKey,
|
|
278
|
+
systemPrompt: fullSystemPrompt,
|
|
279
|
+
write: forwardProviderEvents,
|
|
280
|
+
history,
|
|
281
|
+
})
|
|
282
|
+
rawResponseText = result.finalResponse || result.fullText || fullText
|
|
283
|
+
}
|
|
265
284
|
const responseText = stripAgentReactionTokens(stripHiddenControlTokens(rawResponseText))
|
|
266
285
|
|
|
267
286
|
// Don't persist empty or error-only messages — they pollute chat history
|
|
@@ -4,7 +4,6 @@ import { notify } from '@/lib/server/ws-hub'
|
|
|
4
4
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
5
5
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
6
6
|
import { genId } from '@/lib/id'
|
|
7
|
-
import { isWorkerOnlyAgent, buildWorkerOnlyAgentMessage } from '@/lib/server/agents/agent-availability'
|
|
8
7
|
|
|
9
8
|
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
10
9
|
const { id } = await params
|
|
@@ -18,13 +17,6 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
18
17
|
if (!agentId) return NextResponse.json({ error: 'agentId is required' }, { status: 400 })
|
|
19
18
|
|
|
20
19
|
const agents = loadAgents()
|
|
21
|
-
if (isWorkerOnlyAgent(agents[agentId])) {
|
|
22
|
-
return NextResponse.json(
|
|
23
|
-
{ error: buildWorkerOnlyAgentMessage(agents[agentId], 'join chatrooms') },
|
|
24
|
-
{ status: 400 },
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
20
|
if (!chatroom.agentIds.includes(agentId)) {
|
|
29
21
|
chatroom.agentIds.push(agentId)
|
|
30
22
|
|
|
@@ -4,7 +4,6 @@ import { notify } from '@/lib/server/ws-hub'
|
|
|
4
4
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
5
5
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
6
6
|
import { genId } from '@/lib/id'
|
|
7
|
-
import { isWorkerOnlyAgent } from '@/lib/server/agents/agent-availability'
|
|
8
7
|
import {
|
|
9
8
|
ensureChatroomRoutingGuidance,
|
|
10
9
|
synthesizeRoutingGuidanceFromRules,
|
|
@@ -78,16 +77,6 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
78
77
|
{ status: 400 },
|
|
79
78
|
)
|
|
80
79
|
}
|
|
81
|
-
const cliAgentNames = agentIds
|
|
82
|
-
.filter((agentId) => isWorkerOnlyAgent(agents[agentId]))
|
|
83
|
-
.map((agentId) => agents[agentId]?.name || agentId)
|
|
84
|
-
if (cliAgentNames.length > 0) {
|
|
85
|
-
return NextResponse.json(
|
|
86
|
-
{ error: `CLI-based agents cannot join chatrooms: ${cliAgentNames.join(', ')}. They can only be used for direct chats and delegation.` },
|
|
87
|
-
{ status: 400 },
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
80
|
const oldIds = new Set(chatroom.agentIds)
|
|
92
81
|
const newIds = new Set(agentIds)
|
|
93
82
|
const added = agentIds.filter((aid: string) => !oldIds.has(aid))
|
|
@@ -6,7 +6,6 @@ import { ChatroomCreateSchema, formatZodError } from '@/lib/validation/schemas'
|
|
|
6
6
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
7
7
|
import { z } from 'zod'
|
|
8
8
|
import type { Chatroom, ChatroomMessage } from '@/types'
|
|
9
|
-
import { isWorkerOnlyAgent } from '@/lib/server/agents/agent-availability'
|
|
10
9
|
import {
|
|
11
10
|
ensureChatroomRoutingGuidance,
|
|
12
11
|
synthesizeRoutingGuidanceFromRules,
|
|
@@ -61,15 +60,6 @@ export async function POST(req: Request) {
|
|
|
61
60
|
{ status: 400 },
|
|
62
61
|
)
|
|
63
62
|
}
|
|
64
|
-
const cliAgentNames = requestedAgentIds
|
|
65
|
-
.filter((agentId) => isWorkerOnlyAgent(knownAgents[agentId]))
|
|
66
|
-
.map((agentId) => knownAgents[agentId]?.name || agentId)
|
|
67
|
-
if (cliAgentNames.length > 0) {
|
|
68
|
-
return NextResponse.json(
|
|
69
|
-
{ error: `CLI-based agents cannot join chatrooms: ${cliAgentNames.join(', ')}. They can only be used for direct chats and delegation.` },
|
|
70
|
-
{ status: 400 },
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
63
|
const agentIds: string[] = requestedAgentIds
|
|
74
64
|
const chatMode = body.chatMode === 'parallel' ? 'parallel' : 'sequential'
|
|
75
65
|
const autoAddress = Boolean(body.autoAddress)
|
|
@@ -9,7 +9,6 @@ import { toast } from 'sonner'
|
|
|
9
9
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
10
10
|
import type { Agent } from '@/types'
|
|
11
11
|
import { CheckIcon } from '@/components/shared/check-icon'
|
|
12
|
-
import { WORKER_ONLY_PROVIDER_IDS } from '@/lib/provider-sets'
|
|
13
12
|
|
|
14
13
|
export function ChatroomSheet() {
|
|
15
14
|
const open = useChatroomStore((s) => s.chatroomSheetOpen)
|
|
@@ -105,9 +104,7 @@ export function ChatroomSheet() {
|
|
|
105
104
|
)
|
|
106
105
|
}
|
|
107
106
|
|
|
108
|
-
const agentList = Object.values(agents).filter(
|
|
109
|
-
(a: Agent) => !a.trashedAt && !WORKER_ONLY_PROVIDER_IDS.has(a.provider)
|
|
110
|
-
) as Agent[]
|
|
107
|
+
const agentList = Object.values(agents).filter((a: Agent) => !a.trashedAt) as Agent[]
|
|
111
108
|
|
|
112
109
|
return (
|
|
113
110
|
<BottomSheet open={open} onClose={() => setChatroomSheetOpen(false)}>
|
|
@@ -12,7 +12,6 @@ import { log } from '../logger'
|
|
|
12
12
|
import { debug } from '../debug'
|
|
13
13
|
import { logExecution } from '../execution-log'
|
|
14
14
|
import { logActivity } from '../storage'
|
|
15
|
-
import { WORKER_ONLY_PROVIDER_IDS } from '@/lib/provider-sets'
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Core Chatroom Execution Logic
|
|
@@ -78,12 +77,6 @@ async function executeChatroomAction(args: Record<string, unknown>, context: { a
|
|
|
78
77
|
const agents = loadAgents()
|
|
79
78
|
const requestedAgentIds = agentIds || []
|
|
80
79
|
const validAgentIds = requestedAgentIds.filter((aid: string) => !!agents[aid])
|
|
81
|
-
const cliAgentNames = validAgentIds
|
|
82
|
-
.filter((aid: string) => WORKER_ONLY_PROVIDER_IDS.has(agents[aid]?.provider))
|
|
83
|
-
.map((aid: string) => agents[aid]?.name || aid)
|
|
84
|
-
if (cliAgentNames.length > 0) {
|
|
85
|
-
return `Error: CLI-based agents cannot join chatrooms: ${cliAgentNames.join(', ')}. They can only be used for direct chats and delegation.`
|
|
86
|
-
}
|
|
87
80
|
|
|
88
81
|
const chatroom: Chatroom = {
|
|
89
82
|
id,
|
|
@@ -208,10 +201,6 @@ async function executeChatroomAction(args: Record<string, unknown>, context: { a
|
|
|
208
201
|
|
|
209
202
|
if (action === 'add_agent') {
|
|
210
203
|
if (!agentId) return 'Error: agentId required.'
|
|
211
|
-
const agents = loadAgents()
|
|
212
|
-
if (WORKER_ONLY_PROVIDER_IDS.has(agents[agentId]?.provider)) {
|
|
213
|
-
return `Error: ${agents[agentId]?.name || agentId} is a CLI-based agent and cannot join chatrooms. CLI agents can only be used for direct chats and delegation.`
|
|
214
|
-
}
|
|
215
204
|
if (!chatroom.agentIds.includes(agentId)) {
|
|
216
205
|
chatroom.agentIds.push(agentId)
|
|
217
206
|
chatroom.updatedAt = Date.now()
|