@swarmclawai/swarmclaw 1.5.35 → 1.5.37
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 +29 -1
- package/package.json +18 -1
- package/public/provider-logos/droid-cli.svg +7 -0
- package/src/app/api/setup/check-provider/route.ts +4 -2
- package/src/app/api/setup/doctor/route.ts +1 -0
- package/src/components/agents/agent-sheet.tsx +3 -1
- package/src/components/agents/inspector-panel.tsx +1 -0
- package/src/components/auth/access-key-gate.tsx +0 -24
- package/src/components/chat/activity-moment.tsx +1 -0
- package/src/components/chat/chat-header.tsx +2 -1
- package/src/components/chat/tool-call-bubble.tsx +5 -0
- package/src/components/layout/sidebar-rail.tsx +0 -47
- package/src/lib/orchestrator-config.ts +1 -0
- package/src/lib/provider-sets.ts +3 -3
- package/src/lib/providers/cli-utils.test.ts +2 -0
- package/src/lib/providers/cli-utils.ts +28 -1
- package/src/lib/providers/droid-cli.ts +220 -0
- package/src/lib/providers/index.ts +11 -1
- package/src/lib/server/agents/agent-availability.test.ts +1 -1
- package/src/lib/server/agents/agent-thread-session.ts +1 -0
- package/src/lib/server/agents/task-session.ts +2 -0
- package/src/lib/server/capability-router.ts +3 -1
- package/src/lib/server/chat-execution/chat-execution-utils.ts +11 -0
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +2 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +1 -0
- package/src/lib/server/chat-execution/prompt-sections.ts +2 -0
- package/src/lib/server/chatrooms/chatroom-helpers.ts +3 -0
- package/src/lib/server/chats/chat-session-service.ts +4 -0
- package/src/lib/server/connectors/session.ts +2 -0
- package/src/lib/server/context-manager.ts +1 -0
- package/src/lib/server/provider-health.ts +4 -2
- package/src/lib/server/provider-model-discovery.test.ts +1 -1
- package/src/lib/server/provider-model-discovery.ts +1 -1
- package/src/lib/server/runtime/daemon-state/core.ts +2 -2
- package/src/lib/server/session-reset-policy.ts +2 -0
- package/src/lib/server/session-tools/context.ts +2 -2
- package/src/lib/server/session-tools/delegate-droid.test.ts +24 -0
- package/src/lib/server/session-tools/delegate.ts +105 -12
- package/src/lib/server/session-tools/index.ts +3 -2
- package/src/lib/server/session-tools/session-info.ts +1 -0
- package/src/lib/server/storage-normalization.ts +3 -0
- package/src/lib/server/tool-aliases.ts +1 -1
- package/src/lib/server/tool-capability-policy.ts +2 -1
- package/src/lib/setup-defaults.ts +21 -0
- package/src/types/misc.ts +1 -1
- package/src/types/provider.ts +1 -1
- package/src/types/session.ts +3 -0
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ Extension tutorial: https://swarmclaw.ai/docs/extension-tutorial
|
|
|
40
40
|
<td align="center"><img src="doc/assets/logos/gemini-cli.svg" width="32" alt="Gemini CLI"><br><sub>Gemini CLI</sub></td>
|
|
41
41
|
<td align="center"><img src="doc/assets/logos/opencode.svg" width="32" alt="OpenCode"><br><sub>OpenCode</sub></td>
|
|
42
42
|
<td align="center"><img src="doc/assets/logos/copilot-cli.svg" width="32" alt="Copilot CLI"><br><sub>Copilot</sub></td>
|
|
43
|
+
<td align="center"><img src="public/provider-logos/droid-cli.svg" width="32" alt="Factory Droid CLI"><br><sub>Droid</sub></td>
|
|
43
44
|
<td align="center"><img src="doc/assets/logos/cursor-cli.svg" width="32" alt="Cursor Agent CLI"><br><sub>Cursor</sub></td>
|
|
44
45
|
<td align="center"><img src="doc/assets/logos/qwen-code-cli.svg" width="32" alt="Qwen Code CLI"><br><sub>Qwen Code</sub></td>
|
|
45
46
|
<td align="center"><img src="doc/assets/logos/goose.svg" width="32" alt="Goose"><br><sub>Goose</sub></td>
|
|
@@ -65,10 +66,23 @@ Extension tutorial: https://swarmclaw.ai/docs/extension-tutorial
|
|
|
65
66
|
- Node.js 22.6+ (`nvm use` will pick up the repo's `.nvmrc`, which matches CI)
|
|
66
67
|
- npm 10+ or another supported package manager
|
|
67
68
|
- Docker Desktop is recommended for sandbox browser execution
|
|
68
|
-
- Optional provider CLIs if you want delegated CLI backends such as Claude Code, Codex, OpenCode, Gemini, Copilot, Cursor Agent, Qwen Code, or Goose
|
|
69
|
+
- Optional provider CLIs if you want delegated CLI backends such as Claude Code, Codex, OpenCode, Gemini, Copilot, Factory Droid, Cursor Agent, Qwen Code, or Goose
|
|
69
70
|
|
|
70
71
|
## Quick Start
|
|
71
72
|
|
|
73
|
+
### Desktop app (recommended for non-technical users)
|
|
74
|
+
|
|
75
|
+
Download the one-click installer from [swarmclaw.ai/downloads](https://swarmclaw.ai/downloads).
|
|
76
|
+
Available for macOS (Apple Silicon & Intel), Windows, and Linux (AppImage + .deb).
|
|
77
|
+
|
|
78
|
+
Current builds are unsigned, so on first launch:
|
|
79
|
+
- **macOS:** right-click the app in Finder → **Open** → **Open** to bypass Gatekeeper.
|
|
80
|
+
- **Windows:** if SmartScreen appears, click **More info** → **Run anyway**.
|
|
81
|
+
- **Linux (AppImage):** `chmod +x` the downloaded file, then run it.
|
|
82
|
+
|
|
83
|
+
Data lives in your OS app-data directory (`~/Library/Application Support/SwarmClaw`,
|
|
84
|
+
`%APPDATA%\SwarmClaw`, or `~/.config/SwarmClaw`), separate from any CLI or Docker install.
|
|
85
|
+
|
|
72
86
|
### Global install
|
|
73
87
|
|
|
74
88
|
```bash
|
|
@@ -375,6 +389,20 @@ Operational docs: https://swarmclaw.ai/docs/observability
|
|
|
375
389
|
|
|
376
390
|
## Releases
|
|
377
391
|
|
|
392
|
+
### v1.5.37 Highlights
|
|
393
|
+
|
|
394
|
+
- **Factory Droid CLI as a provider and delegation backend**: adds [`droid`](https://docs.factory.ai/cli/droid-exec/overview) as a first-class chat provider and `delegate` backend with streaming JSON output, session resume, and a conservative `--auto low` autonomy pin on the delegate path. Install `droid` and sign in via browser (or set `FACTORY_API_KEY`), then pick **Factory Droid CLI** in the setup wizard. Resolves #38.
|
|
395
|
+
- **Desktop Release CI hardening**: v1.5.36's Electron build workflow failed on all three platforms. This release:
|
|
396
|
+
- Adds a proper `author` with email to `package.json` and a `linux.maintainer` entry in `electron-builder.yml` so the Linux `.deb` target stops rejecting the build.
|
|
397
|
+
- Pins `outputFileTracingRoot` in `next.config.ts` to the project root so the Next.js build no longer walks `C:\Users\<user>\Application Data` (a legacy NTFS junction that throws EPERM on Windows runners).
|
|
398
|
+
- Pins Python 3.11 in the desktop-release workflow so `node-gyp` rebuilds of native modules (`node-liblzma`, etc.) succeed on Python 3.12+ runners where `distutils` was removed from the stdlib.
|
|
399
|
+
|
|
400
|
+
### v1.5.36 Highlights
|
|
401
|
+
|
|
402
|
+
- **Desktop app (Electron)**: SwarmClaw now ships as a native desktop app for macOS (Apple Silicon + Intel), Windows, and Linux (AppImage + .deb). Download from [swarmclaw.ai/downloads](https://swarmclaw.ai/downloads). The app wraps the existing standalone server inside an Electron shell, stores data in the OS app-data directory, and auto-updates via GitHub Releases (notify-only on unsigned macOS builds).
|
|
403
|
+
- **Desktop release CI**: new `desktop-release.yml` workflow builds and publishes installers for all three platforms to GitHub Releases on every version tag.
|
|
404
|
+
- **UI cleanup**: removed sibling-product navigation links from the in-app sidebar rail and login gate so the open-source app focuses on SwarmClaw itself. Those links remain in the project README and on swarmclaw.ai.
|
|
405
|
+
|
|
378
406
|
### v1.5.35 Highlights
|
|
379
407
|
|
|
380
408
|
- **Update safety: prevent DB corruption on Linux**: `npm run update:easy`, `swarmclaw update`, and the in-app update endpoint now stop the running server (or checkpoint the SQLite WAL) before rebuilding native modules, preventing the WAL journal corruption that forced some Linux users back to the setup wizard.
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmclawai/swarmclaw",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.37",
|
|
4
4
|
"description": "Build and run autonomous AI agents with OpenClaw, Hermes, multiple model providers, orchestration, delegation, memory, skills, schedules, and chat connectors.",
|
|
5
|
+
"main": "electron-dist/main.js",
|
|
5
6
|
"license": "MIT",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "SwarmClaw",
|
|
9
|
+
"email": "noreply@swarmclaw.ai",
|
|
10
|
+
"url": "https://swarmclaw.ai"
|
|
11
|
+
},
|
|
6
12
|
"publishConfig": {
|
|
7
13
|
"access": "public",
|
|
8
14
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -78,6 +84,13 @@
|
|
|
78
84
|
"test:builder": "tsx --test src/features/protocols/builder/utils/nodes-to-template.test.ts src/features/protocols/builder/utils/template-to-nodes.test.ts src/features/protocols/builder/validators/dag-validator.test.ts",
|
|
79
85
|
"test:e2e": "tsx .workbench/browser-e2e/run.ts",
|
|
80
86
|
"test:mcp:conformance": "node --import tsx ./scripts/mcp-conformance-check.ts",
|
|
87
|
+
"electron:compile": "tsc -p electron/tsconfig.json",
|
|
88
|
+
"electron:dev": "npm run electron:compile && electron electron-dist/main.js",
|
|
89
|
+
"electron:build": "node ./scripts/build-electron.mjs",
|
|
90
|
+
"electron:build:mac": "node ./scripts/build-electron.mjs --mac",
|
|
91
|
+
"electron:build:win": "node ./scripts/build-electron.mjs --win",
|
|
92
|
+
"electron:build:linux": "node ./scripts/build-electron.mjs --linux",
|
|
93
|
+
"electron:build:publish": "node ./scripts/build-electron.mjs --publish",
|
|
81
94
|
"prepack": "npm run build:ci",
|
|
82
95
|
"postinstall": "node ./scripts/postinstall.mjs"
|
|
83
96
|
},
|
|
@@ -114,6 +127,7 @@
|
|
|
114
127
|
"class-variance-authority": "^0.7.1",
|
|
115
128
|
"clsx": "^2.1.1",
|
|
116
129
|
"commander": "^13.1.0",
|
|
130
|
+
"electron-updater": "^6.3.9",
|
|
117
131
|
"cron-parser": "^5.5.0",
|
|
118
132
|
"cronstrue": "^3.12.0",
|
|
119
133
|
"dagre": "^0.8.5",
|
|
@@ -156,7 +170,10 @@
|
|
|
156
170
|
"zustand": "^5.0.11"
|
|
157
171
|
},
|
|
158
172
|
"devDependencies": {
|
|
173
|
+
"@electron/rebuild": "^3.7.2",
|
|
159
174
|
"@types/dagre": "^0.7.54",
|
|
175
|
+
"electron": "^33.3.0",
|
|
176
|
+
"electron-builder": "^25.1.8",
|
|
160
177
|
"eslint": "^9",
|
|
161
178
|
"eslint-config-next": "16.1.7"
|
|
162
179
|
},
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="Factory Droid CLI">
|
|
2
|
+
<rect x="2" y="2" width="60" height="60" rx="14" fill="#0f172a"/>
|
|
3
|
+
<rect x="2" y="2" width="60" height="60" rx="14" fill="none" stroke="#38bdf8" stroke-width="2"/>
|
|
4
|
+
<path d="M20 22 h24 v6 h-9 v16 h-6 v-16 h-9 z" fill="#e2e8f0"/>
|
|
5
|
+
<circle cx="16" cy="48" r="2.5" fill="#38bdf8"/>
|
|
6
|
+
<circle cx="48" cy="48" r="2.5" fill="#38bdf8"/>
|
|
7
|
+
</svg>
|
|
@@ -12,6 +12,7 @@ type SetupProvider =
|
|
|
12
12
|
| 'opencode-cli'
|
|
13
13
|
| 'gemini-cli'
|
|
14
14
|
| 'copilot-cli'
|
|
15
|
+
| 'droid-cli'
|
|
15
16
|
| 'cursor-cli'
|
|
16
17
|
| 'qwen-code-cli'
|
|
17
18
|
| 'goose'
|
|
@@ -31,7 +32,7 @@ type SetupProvider =
|
|
|
31
32
|
| 'openclaw'
|
|
32
33
|
| 'hermes'
|
|
33
34
|
|
|
34
|
-
type CliSetupProvider = 'claude-cli' | 'codex-cli' | 'opencode-cli' | 'gemini-cli' | 'copilot-cli' | 'cursor-cli' | 'qwen-code-cli' | 'goose'
|
|
35
|
+
type CliSetupProvider = 'claude-cli' | 'codex-cli' | 'opencode-cli' | 'gemini-cli' | 'copilot-cli' | 'droid-cli' | 'cursor-cli' | 'qwen-code-cli' | 'goose'
|
|
35
36
|
|
|
36
37
|
interface SetupCheckBody {
|
|
37
38
|
provider?: string
|
|
@@ -285,6 +286,7 @@ function checkCliProvider(provider: CliSetupProvider): { ok: boolean; message: s
|
|
|
285
286
|
'opencode-cli': { binary: 'opencode', backend: 'opencode' as const, label: 'OpenCode CLI' },
|
|
286
287
|
'gemini-cli': { binary: 'gemini', backend: 'gemini' as const, label: 'Gemini CLI' },
|
|
287
288
|
'copilot-cli': { binary: 'copilot', backend: 'copilot' as const, label: 'GitHub Copilot CLI' },
|
|
289
|
+
'droid-cli': { binary: 'droid', backend: 'droid' as const, label: 'Factory Droid CLI' },
|
|
288
290
|
'cursor-cli': { binary: 'cursor-agent', backend: 'cursor' as const, label: 'Cursor Agent CLI' },
|
|
289
291
|
'qwen-code-cli': { binary: 'qwen', backend: 'qwen' as const, label: 'Qwen Code CLI' },
|
|
290
292
|
goose: { binary: 'goose', backend: 'goose' as const, label: 'Goose CLI' },
|
|
@@ -312,7 +314,7 @@ export async function POST(req: Request) {
|
|
|
312
314
|
const credentialId = clean(body.credentialId)
|
|
313
315
|
const endpoint = clean(body.endpoint)
|
|
314
316
|
const model = clean(body.model)
|
|
315
|
-
const CLI_PROVIDERS = new Set<CliSetupProvider>(['claude-cli', 'codex-cli', 'opencode-cli', 'gemini-cli', 'copilot-cli', 'cursor-cli', 'qwen-code-cli', 'goose'])
|
|
317
|
+
const CLI_PROVIDERS = new Set<CliSetupProvider>(['claude-cli', 'codex-cli', 'opencode-cli', 'gemini-cli', 'copilot-cli', 'droid-cli', 'cursor-cli', 'qwen-code-cli', 'goose'])
|
|
316
318
|
|
|
317
319
|
// Resolve credentialId to an API key if no raw key was provided
|
|
318
320
|
if (!apiKey && credentialId) {
|
|
@@ -203,6 +203,7 @@ export async function GET(req: Request) {
|
|
|
203
203
|
{ id: 'opencode-cli', label: 'OpenCode CLI', command: 'opencode' },
|
|
204
204
|
{ id: 'gemini-cli', label: 'Gemini CLI', command: 'gemini' },
|
|
205
205
|
{ id: 'copilot-cli', label: 'GitHub Copilot CLI', command: 'copilot' },
|
|
206
|
+
{ id: 'droid-cli', label: 'Factory Droid CLI', command: 'droid' },
|
|
206
207
|
{ id: 'cursor-cli', label: 'Cursor Agent CLI', command: 'cursor-agent' },
|
|
207
208
|
{ id: 'qwen-code-cli', label: 'Qwen Code CLI', command: 'qwen' },
|
|
208
209
|
{ id: 'goose', label: 'Goose CLI', command: 'goose' },
|
|
@@ -2513,7 +2513,9 @@ export function AgentSheet() {
|
|
|
2513
2513
|
? 'Gemini CLI uses its own built-in tools and runtime — SwarmClaw does not inject local platform tools for it.'
|
|
2514
2514
|
: provider === 'copilot-cli'
|
|
2515
2515
|
? 'GitHub Copilot CLI uses its own built-in tools and runtime — SwarmClaw does not inject local platform tools for it.'
|
|
2516
|
-
: provider === '
|
|
2516
|
+
: provider === 'droid-cli'
|
|
2517
|
+
? 'Factory Droid CLI uses its own built-in tools and autonomy controls — SwarmClaw does not inject local platform tools for it.'
|
|
2518
|
+
: provider === 'cursor-cli'
|
|
2517
2519
|
? 'Cursor Agent CLI runs with its own native tool/runtime layer — SwarmClaw sends prompts directly without injecting local platform tools.'
|
|
2518
2520
|
: provider === 'qwen-code-cli'
|
|
2519
2521
|
? 'Qwen Code CLI uses its own native tools and runtime — SwarmClaw does not inject local platform tools for it.'
|
|
@@ -53,6 +53,7 @@ const PROVIDER_LABELS: Record<string, string> = {
|
|
|
53
53
|
'opencode-cli': 'OpenCode CLI',
|
|
54
54
|
'gemini-cli': 'Gemini CLI',
|
|
55
55
|
'copilot-cli': 'Copilot CLI',
|
|
56
|
+
'droid-cli': 'Droid CLI',
|
|
56
57
|
'cursor-cli': 'Cursor CLI',
|
|
57
58
|
'qwen-code-cli': 'Qwen Code CLI',
|
|
58
59
|
goose: 'Goose',
|
|
@@ -9,11 +9,6 @@ interface AccessKeyGateProps {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const AUTH_CHECK_TIMEOUT_MS = 8_000
|
|
12
|
-
const NETWORK_LINKS = [
|
|
13
|
-
{ href: 'https://www.swarmdock.ai', label: 'SwarmDock' },
|
|
14
|
-
{ href: 'https://swarmrecall.ai', label: 'SwarmRecall' },
|
|
15
|
-
{ href: 'https://swarmrelay.ai', label: 'SwarmRelay' },
|
|
16
|
-
]
|
|
17
12
|
|
|
18
13
|
function isExpectedAuthCheckError(err: unknown): boolean {
|
|
19
14
|
return isAbortError(err) || isTimeoutError(err)
|
|
@@ -427,25 +422,6 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
427
422
|
</>
|
|
428
423
|
)}
|
|
429
424
|
|
|
430
|
-
<div className="mt-10 border-t border-white/[0.06] pt-5" style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.35s both' }}>
|
|
431
|
-
<p className="text-[10px] font-700 uppercase tracking-[0.18em] text-text-3/55">
|
|
432
|
-
Network
|
|
433
|
-
</p>
|
|
434
|
-
<div className="mt-3 flex flex-wrap items-center justify-center gap-2.5">
|
|
435
|
-
{NETWORK_LINKS.map((link) => (
|
|
436
|
-
<a
|
|
437
|
-
key={link.href}
|
|
438
|
-
href={link.href}
|
|
439
|
-
target="_blank"
|
|
440
|
-
rel="noopener noreferrer"
|
|
441
|
-
className="rounded-full border border-white/[0.08] bg-white/[0.03] px-3 py-1.5 text-[12px] text-text-3
|
|
442
|
-
no-underline transition-all duration-200 hover:border-white/[0.14] hover:bg-white/[0.06] hover:text-text"
|
|
443
|
-
>
|
|
444
|
-
{link.label}
|
|
445
|
-
</a>
|
|
446
|
-
))}
|
|
447
|
-
</div>
|
|
448
|
-
</div>
|
|
449
425
|
</div>
|
|
450
426
|
</div>
|
|
451
427
|
)
|
|
@@ -14,6 +14,7 @@ const NOTABLE_TOOLS: Record<string, { label: string; color: string; icon: 'brain
|
|
|
14
14
|
delegate_to_opencode_cli: { label: 'Delegated to OpenCode', color: '#38BDF8', icon: 'delegate' },
|
|
15
15
|
delegate_to_gemini_cli: { label: 'Delegated to Gemini CLI', color: '#38BDF8', icon: 'delegate' },
|
|
16
16
|
delegate_to_cursor_cli: { label: 'Delegated to Cursor CLI', color: '#38BDF8', icon: 'delegate' },
|
|
17
|
+
delegate_to_droid_cli: { label: 'Delegated to Factory Droid', color: '#38BDF8', icon: 'delegate' },
|
|
17
18
|
delegate_to_qwen_code_cli: { label: 'Delegated to Qwen Code', color: '#38BDF8', icon: 'delegate' },
|
|
18
19
|
delegate_to_agent: { label: 'Delegating task', color: '#6366F1', icon: 'delegate' },
|
|
19
20
|
check_delegation_status: { label: 'Checking delegation', color: '#6366F1', icon: 'delegate' },
|
|
@@ -220,10 +220,11 @@ export function ChatHeader({ session, streaming, onStop, onMenuToggle, onBack, m
|
|
|
220
220
|
opencodeSessionId: null,
|
|
221
221
|
geminiSessionId: null,
|
|
222
222
|
copilotSessionId: null,
|
|
223
|
+
droidSessionId: null,
|
|
223
224
|
cursorSessionId: null,
|
|
224
225
|
qwenSessionId: null,
|
|
225
226
|
acpSessionId: null,
|
|
226
|
-
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null, copilot: null, cursor: null, qwen: null },
|
|
227
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null, copilot: null, droid: null, cursor: null, qwen: null },
|
|
227
228
|
})
|
|
228
229
|
await refreshSession(session.id)
|
|
229
230
|
} catch { /* best-effort */ }
|
|
@@ -28,6 +28,7 @@ const TOOL_COLORS: Record<string, string> = {
|
|
|
28
28
|
delegate_to_opencode_cli: '#14B8A6',
|
|
29
29
|
delegate_to_gemini_cli: '#2563EB',
|
|
30
30
|
delegate_to_copilot_cli: '#6366F1',
|
|
31
|
+
delegate_to_droid_cli: '#A855F7',
|
|
31
32
|
delegate_to_cursor_cli: '#F97316',
|
|
32
33
|
delegate_to_qwen_code_cli: '#22C55E',
|
|
33
34
|
whoami_tool: '#8B5CF6',
|
|
@@ -81,6 +82,7 @@ export const TOOL_LABELS: Record<string, string> = {
|
|
|
81
82
|
opencode_cli: 'OpenCode CLI',
|
|
82
83
|
gemini_cli: 'Gemini CLI',
|
|
83
84
|
copilot_cli: 'Copilot CLI',
|
|
85
|
+
droid_cli: 'Factory Droid',
|
|
84
86
|
cursor_cli: 'Cursor CLI',
|
|
85
87
|
qwen_code_cli: 'Qwen Code CLI',
|
|
86
88
|
spawn_subagent: 'Subagent',
|
|
@@ -91,6 +93,7 @@ export const TOOL_LABELS: Record<string, string> = {
|
|
|
91
93
|
delegate_to_opencode_cli: 'OpenCode CLI',
|
|
92
94
|
delegate_to_gemini_cli: 'Gemini CLI',
|
|
93
95
|
delegate_to_copilot_cli: 'Copilot CLI',
|
|
96
|
+
delegate_to_droid_cli: 'Factory Droid',
|
|
94
97
|
delegate_to_cursor_cli: 'Cursor CLI',
|
|
95
98
|
delegate_to_qwen_code_cli: 'Qwen Code CLI',
|
|
96
99
|
whoami_tool: 'Who Am I',
|
|
@@ -129,6 +132,7 @@ export const TOOL_DESCRIPTIONS: Record<string, string> = {
|
|
|
129
132
|
opencode_cli: 'Enable delegation to OpenCode CLI',
|
|
130
133
|
gemini_cli: 'Enable delegation to Gemini CLI',
|
|
131
134
|
copilot_cli: 'Enable delegation to GitHub Copilot CLI',
|
|
135
|
+
droid_cli: 'Enable delegation to Factory Droid CLI',
|
|
132
136
|
cursor_cli: 'Enable delegation to Cursor Agent CLI',
|
|
133
137
|
qwen_code_cli: 'Enable delegation to Qwen Code CLI',
|
|
134
138
|
spawn_subagent: 'Spawn native subagents with lineage tracking and batch support',
|
|
@@ -139,6 +143,7 @@ export const TOOL_DESCRIPTIONS: Record<string, string> = {
|
|
|
139
143
|
delegate_to_opencode_cli: 'Delegate complex coding tasks to OpenCode CLI',
|
|
140
144
|
delegate_to_gemini_cli: 'Delegate complex coding tasks to Gemini CLI',
|
|
141
145
|
delegate_to_copilot_cli: 'Delegate complex coding tasks to GitHub Copilot CLI',
|
|
146
|
+
delegate_to_droid_cli: 'Delegate complex coding tasks to Factory Droid CLI',
|
|
142
147
|
delegate_to_cursor_cli: 'Delegate complex coding tasks to Cursor Agent CLI',
|
|
143
148
|
delegate_to_qwen_code_cli: 'Delegate complex coding tasks to Qwen Code CLI',
|
|
144
149
|
whoami_tool: 'Reveal the current agent and chat context',
|
|
@@ -17,11 +17,6 @@ import type { AppView } from '@/types'
|
|
|
17
17
|
const RAIL_EXPANDED_KEY = 'sc_rail_expanded'
|
|
18
18
|
const GITHUB_REPO_URL = 'https://github.com/swarmclawai/swarmclaw'
|
|
19
19
|
const DISCORD_URL = 'https://discord.gg/sbEavS8cPV'
|
|
20
|
-
const NETWORK_LINKS = [
|
|
21
|
-
{ href: 'https://www.swarmdock.ai', label: 'SwarmDock', abbr: 'DO' },
|
|
22
|
-
{ href: 'https://swarmrecall.ai', label: 'SwarmRecall', abbr: 'RE' },
|
|
23
|
-
{ href: 'https://swarmrelay.ai', label: 'SwarmRelay', abbr: 'RL' },
|
|
24
|
-
]
|
|
25
20
|
|
|
26
21
|
export function SidebarRail({
|
|
27
22
|
onSwitchUser,
|
|
@@ -394,48 +389,6 @@ export function SidebarRail({
|
|
|
394
389
|
|
|
395
390
|
{/* Bottom: Docs + Daemon + Settings + User */}
|
|
396
391
|
<div className={`flex flex-col gap-1 ${railExpanded ? 'px-3' : 'items-center'}`}>
|
|
397
|
-
{railExpanded ? (
|
|
398
|
-
<div className="mb-1">
|
|
399
|
-
<div className="px-3 pb-1 text-[10px] font-700 uppercase tracking-[0.12em] text-text-3/45">Network</div>
|
|
400
|
-
<div className="flex flex-col gap-1">
|
|
401
|
-
{NETWORK_LINKS.map((link) => (
|
|
402
|
-
<a
|
|
403
|
-
key={link.href}
|
|
404
|
-
href={link.href}
|
|
405
|
-
target="_blank"
|
|
406
|
-
rel="noopener noreferrer"
|
|
407
|
-
className="w-full flex items-center gap-2.5 px-3 py-2 rounded-[10px] text-[13px] font-500 cursor-pointer transition-all
|
|
408
|
-
bg-transparent text-text-3 hover:text-text hover:bg-white/[0.04] no-underline"
|
|
409
|
-
style={{ fontFamily: 'inherit' }}
|
|
410
|
-
>
|
|
411
|
-
<span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-[6px] border border-white/[0.08] bg-white/[0.03] text-[10px] font-700 uppercase tracking-[0.08em] text-text-3/80">
|
|
412
|
-
{link.abbr}
|
|
413
|
-
</span>
|
|
414
|
-
{link.label}
|
|
415
|
-
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" className="ml-auto opacity-40">
|
|
416
|
-
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /><polyline points="15 3 21 3 21 9" /><line x1="10" y1="14" x2="21" y2="3" />
|
|
417
|
-
</svg>
|
|
418
|
-
</a>
|
|
419
|
-
))}
|
|
420
|
-
</div>
|
|
421
|
-
</div>
|
|
422
|
-
) : (
|
|
423
|
-
<>
|
|
424
|
-
<div className="my-1 h-px w-6 bg-white/[0.06]" />
|
|
425
|
-
{NETWORK_LINKS.map((link) => (
|
|
426
|
-
<RailTooltip key={link.href} label={link.label} description="Open product site in a new tab">
|
|
427
|
-
<a
|
|
428
|
-
href={link.href}
|
|
429
|
-
target="_blank"
|
|
430
|
-
rel="noopener noreferrer"
|
|
431
|
-
className="rail-btn text-[10px] font-700 uppercase tracking-[0.08em] no-underline"
|
|
432
|
-
>
|
|
433
|
-
{link.abbr}
|
|
434
|
-
</a>
|
|
435
|
-
</RailTooltip>
|
|
436
|
-
))}
|
|
437
|
-
</>
|
|
438
|
-
)}
|
|
439
392
|
{railExpanded ? (
|
|
440
393
|
<a
|
|
441
394
|
href="https://swarmclaw.ai/docs"
|
package/src/lib/provider-sets.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** CLI providers that use their own tool execution outside the shared tool-runtime path. */
|
|
2
|
-
export const NON_LANGGRAPH_PROVIDER_IDS = new Set(['claude-cli', 'codex-cli', 'opencode-cli', 'gemini-cli', 'copilot-cli', 'cursor-cli', 'qwen-code-cli'])
|
|
2
|
+
export const NON_LANGGRAPH_PROVIDER_IDS = new Set(['claude-cli', 'codex-cli', 'opencode-cli', 'gemini-cli', 'copilot-cli', 'droid-cli', 'cursor-cli', 'qwen-code-cli'])
|
|
3
3
|
|
|
4
4
|
/** Providers that manage their own runtime/tool loop even when reached over an API endpoint. */
|
|
5
5
|
export const RUNTIME_MANAGED_PROVIDER_IDS = new Set(['hermes', 'goose'])
|
|
6
6
|
|
|
7
7
|
/** Providers with native tool/capability support (CLI providers + OpenClaw + Hermes). */
|
|
8
|
-
export const NATIVE_CAPABILITY_PROVIDER_IDS = new Set(['claude-cli', 'codex-cli', 'opencode-cli', 'gemini-cli', 'copilot-cli', 'cursor-cli', 'qwen-code-cli', 'goose', 'openclaw', 'hermes'])
|
|
8
|
+
export const NATIVE_CAPABILITY_PROVIDER_IDS = new Set(['claude-cli', 'codex-cli', 'opencode-cli', 'gemini-cli', 'copilot-cli', 'droid-cli', 'cursor-cli', 'qwen-code-cli', 'goose', 'openclaw', 'hermes'])
|
|
9
9
|
|
|
10
10
|
/** Providers that can only act as workers — no coordinator role, no heartbeat, no advanced settings. */
|
|
11
|
-
export const WORKER_ONLY_PROVIDER_IDS = new Set(['claude-cli', 'codex-cli', 'opencode-cli', 'gemini-cli', 'copilot-cli', 'cursor-cli', 'qwen-code-cli', 'goose', 'openclaw', 'hermes'])
|
|
11
|
+
export const WORKER_ONLY_PROVIDER_IDS = new Set(['claude-cli', 'codex-cli', 'opencode-cli', 'gemini-cli', 'copilot-cli', 'droid-cli', 'cursor-cli', 'qwen-code-cli', 'goose', 'openclaw', 'hermes'])
|
|
@@ -94,6 +94,7 @@ describe('isCliProvider', () => {
|
|
|
94
94
|
assert.equal(isCliProvider('opencode-cli'), true)
|
|
95
95
|
assert.equal(isCliProvider('gemini-cli'), true)
|
|
96
96
|
assert.equal(isCliProvider('copilot-cli'), true)
|
|
97
|
+
assert.equal(isCliProvider('droid-cli'), true)
|
|
97
98
|
assert.equal(isCliProvider('cursor-cli'), true)
|
|
98
99
|
assert.equal(isCliProvider('qwen-code-cli'), true)
|
|
99
100
|
assert.equal(isCliProvider('goose'), true)
|
|
@@ -118,6 +119,7 @@ describe('CLI_PROVIDER_CAPABILITIES', () => {
|
|
|
118
119
|
assert.ok('opencode-cli' in CLI_PROVIDER_CAPABILITIES)
|
|
119
120
|
assert.ok('gemini-cli' in CLI_PROVIDER_CAPABILITIES)
|
|
120
121
|
assert.ok('copilot-cli' in CLI_PROVIDER_CAPABILITIES)
|
|
122
|
+
assert.ok('droid-cli' in CLI_PROVIDER_CAPABILITIES)
|
|
121
123
|
assert.ok('cursor-cli' in CLI_PROVIDER_CAPABILITIES)
|
|
122
124
|
assert.ok('qwen-code-cli' in CLI_PROVIDER_CAPABILITIES)
|
|
123
125
|
assert.ok('goose' in CLI_PROVIDER_CAPABILITIES)
|
|
@@ -45,6 +45,13 @@ const KNOWN_BINARY_PATHS: Record<string, string[]> = {
|
|
|
45
45
|
'/opt/homebrew/bin/copilot',
|
|
46
46
|
path.join(os.homedir(), '.npm-global/bin/copilot'),
|
|
47
47
|
],
|
|
48
|
+
droid: [
|
|
49
|
+
path.join(os.homedir(), '.local/bin/droid'),
|
|
50
|
+
'/usr/local/bin/droid',
|
|
51
|
+
'/opt/homebrew/bin/droid',
|
|
52
|
+
path.join(os.homedir(), '.npm-global/bin/droid'),
|
|
53
|
+
path.join(os.homedir(), '.factory/bin/droid'),
|
|
54
|
+
],
|
|
48
55
|
'cursor-agent': [
|
|
49
56
|
path.join(os.homedir(), '.local/bin/cursor-agent'),
|
|
50
57
|
'/usr/local/bin/cursor-agent',
|
|
@@ -166,7 +173,7 @@ export interface AuthProbeResult {
|
|
|
166
173
|
*/
|
|
167
174
|
export function probeCliAuth(
|
|
168
175
|
binary: string,
|
|
169
|
-
backend: 'claude' | 'codex' | 'opencode' | 'gemini' | 'copilot' | 'cursor' | 'qwen' | 'goose',
|
|
176
|
+
backend: 'claude' | 'codex' | 'opencode' | 'gemini' | 'copilot' | 'droid' | 'cursor' | 'qwen' | 'goose',
|
|
170
177
|
env: NodeJS.ProcessEnv,
|
|
171
178
|
cwd?: string,
|
|
172
179
|
): AuthProbeResult {
|
|
@@ -277,6 +284,25 @@ export function probeCliAuth(
|
|
|
277
284
|
return { authenticated: true }
|
|
278
285
|
}
|
|
279
286
|
|
|
287
|
+
if (backend === 'droid') {
|
|
288
|
+
if (process.env.FACTORY_API_KEY || env.FACTORY_API_KEY) {
|
|
289
|
+
return { authenticated: true }
|
|
290
|
+
}
|
|
291
|
+
const configPaths = [
|
|
292
|
+
path.join(os.homedir(), '.factory', 'config.json'),
|
|
293
|
+
path.join(os.homedir(), '.config', 'factory', 'config.json'),
|
|
294
|
+
path.join(os.homedir(), '.factory', 'auth.json'),
|
|
295
|
+
]
|
|
296
|
+
const hasConfig = configPaths.some((p) => fs.existsSync(p))
|
|
297
|
+
if (!hasConfig) {
|
|
298
|
+
return {
|
|
299
|
+
authenticated: false,
|
|
300
|
+
errorMessage: 'Factory Droid CLI is not authenticated. Run `droid` once to sign in via browser, or set FACTORY_API_KEY and try again.',
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return { authenticated: true }
|
|
304
|
+
}
|
|
305
|
+
|
|
280
306
|
if (backend === 'cursor') {
|
|
281
307
|
try {
|
|
282
308
|
const probe = spawnSync(binary, ['status'], {
|
|
@@ -427,6 +453,7 @@ export const CLI_PROVIDER_CAPABILITIES: Record<string, string> = {
|
|
|
427
453
|
'opencode-cli': 'code analysis, generation across multiple LLM backends',
|
|
428
454
|
'gemini-cli': 'code generation, analysis with Gemini models',
|
|
429
455
|
'copilot-cli': 'code generation, analysis, multi-model support via GitHub Copilot',
|
|
456
|
+
'droid-cli': 'code generation, refactoring, and automation via Factory Droid with configurable autonomy',
|
|
430
457
|
'cursor-cli': 'full-agent coding workflows, multi-file edits, project-aware code changes',
|
|
431
458
|
'qwen-code-cli': 'terminal-native coding workflows, code generation, review, and automation',
|
|
432
459
|
goose: 'agentic coding workflows with extensions, tools, and runtime-managed execution',
|