@swarmclawai/swarmclaw 0.7.1 → 0.7.3
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 +155 -150
- package/package.json +1 -1
- package/src/app/api/agents/[id]/route.ts +26 -0
- package/src/app/api/agents/[id]/thread/route.ts +37 -9
- package/src/app/api/agents/route.ts +13 -2
- package/src/app/api/auth/route.ts +76 -7
- package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
- package/src/app/api/{sessions → chats}/[id]/browser/route.ts +5 -1
- package/src/app/api/{sessions → chats}/[id]/chat/route.ts +7 -3
- package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
- package/src/app/api/chats/[id]/main-loop/route.ts +13 -0
- package/src/app/api/{sessions → chats}/[id]/messages/route.ts +19 -13
- package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
- package/src/app/api/{sessions → chats}/[id]/route.ts +22 -52
- package/src/app/api/{sessions → chats}/[id]/stop/route.ts +6 -1
- package/src/app/api/{sessions → chats}/route.ts +21 -7
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/skills/route.ts +11 -3
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +6 -26
- package/src/app/api/plugins/settings/route.ts +40 -0
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/settings/route.ts +49 -7
- package/src/app/api/tasks/[id]/route.ts +15 -6
- package/src/app/api/tasks/bulk/route.ts +2 -2
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/usage/route.ts +30 -0
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +9 -2
- package/src/cli/index.js +39 -33
- package/src/cli/index.ts +43 -49
- package/src/cli/spec.js +29 -27
- package/src/components/agents/agent-card.tsx +16 -13
- package/src/components/agents/agent-chat-list.tsx +104 -4
- package/src/components/agents/agent-list.tsx +54 -22
- package/src/components/agents/agent-sheet.tsx +209 -18
- package/src/components/agents/cron-job-form.tsx +3 -3
- package/src/components/agents/inspector-panel.tsx +110 -50
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +5 -38
- package/src/components/chat/chat-area.tsx +39 -27
- package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +7 -23
- package/src/components/chat/chat-header.tsx +299 -314
- package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +11 -14
- package/src/components/chat/chat-tool-toggles.tsx +26 -17
- package/src/components/chat/checkpoint-timeline.tsx +4 -4
- package/src/components/chat/message-bubble.tsx +4 -1
- package/src/components/chat/message-list.tsx +5 -3
- package/src/components/chat/session-debug-panel.tsx +1 -1
- package/src/components/chat/tool-request-banner.tsx +3 -3
- package/src/components/chatrooms/agent-hover-card.tsx +3 -3
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +2 -2
- package/src/components/chatrooms/chatroom-view.tsx +347 -205
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +218 -1
- package/src/components/home/home-view.tsx +129 -5
- package/src/components/layout/app-layout.tsx +392 -182
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/plugins/plugin-list.tsx +487 -254
- package/src/components/plugins/plugin-sheet.tsx +236 -13
- package/src/components/projects/project-detail.tsx +183 -0
- package/src/components/settings/gateway-connection-panel.tsx +1 -1
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/command-palette.tsx +111 -25
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +78 -1
- package/src/components/shared/settings/section-orchestrator.tsx +3 -3
- package/src/components/shared/settings/section-providers.tsx +1 -1
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +244 -56
- package/src/components/tasks/approvals-panel.tsx +205 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/usage/metrics-dashboard.tsx +147 -1
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +8 -8
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/chat.ts +1 -1
- package/src/lib/{sessions.ts → chats.ts} +28 -18
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/providers/claude-cli.ts +1 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +205 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +36 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/capability-router.ts +10 -8
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
- package/src/lib/server/chat-execution.ts +285 -165
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +67 -2
- package/src/lib/server/chatroom-helpers.ts +48 -8
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +948 -112
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +188 -9
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/cost.ts +34 -1
- package/src/lib/server/daemon-state.ts +61 -3
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/heartbeat-service.ts +14 -40
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +28 -1103
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +5 -6
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +20 -9
- package/src/lib/server/orchestrator.ts +7 -7
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +207 -0
- package/src/lib/server/plugins.ts +927 -66
- package/src/lib/server/provider-health.ts +38 -6
- package/src/lib/server/queue.ts +13 -28
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -82
- package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
- package/src/lib/server/session-tools/calendar.ts +366 -0
- package/src/lib/server/session-tools/canvas.ts +1 -1
- package/src/lib/server/session-tools/chatroom.ts +4 -2
- package/src/lib/server/session-tools/connector.ts +114 -10
- package/src/lib/server/session-tools/context.ts +21 -5
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +74 -28
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +497 -24
- package/src/lib/server/session-tools/discovery.ts +24 -6
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/edit_file.ts +4 -2
- package/src/lib/server/session-tools/email.ts +320 -0
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +241 -25
- package/src/lib/server/session-tools/git.ts +1 -1
- package/src/lib/server/session-tools/http.ts +1 -1
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +380 -0
- package/src/lib/server/session-tools/index.ts +130 -50
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/memory.ts +172 -3
- package/src/lib/server/session-tools/monitor.ts +151 -8
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.ts +1 -1
- package/src/lib/server/session-tools/openclaw-workspace.ts +1 -1
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +148 -7
- package/src/lib/server/session-tools/plugin-creator.ts +89 -26
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +301 -0
- package/src/lib/server/session-tools/sample-ui.ts +1 -1
- package/src/lib/server/session-tools/sandbox.ts +4 -2
- package/src/lib/server/session-tools/schedule.ts +24 -12
- package/src/lib/server/session-tools/session-info.ts +43 -7
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
- package/src/lib/server/session-tools/shell.ts +5 -2
- package/src/lib/server/session-tools/subagent.ts +194 -28
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +42 -12
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +926 -91
- package/src/lib/server/storage.ts +255 -16
- package/src/lib/server/stream-agent-chat.ts +116 -268
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -10
- package/src/lib/server/tool-aliases.ts +66 -18
- package/src/lib/server/tool-capability-policy.test.ts +9 -9
- package/src/lib/server/tool-capability-policy.ts +38 -27
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/tool-definitions.ts +4 -0
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +10 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +5 -11
- package/src/stores/use-chat-store.ts +38 -9
- package/src/types/index.ts +352 -47
- package/src/app/api/sessions/[id]/main-loop/route.ts +0 -94
- package/src/components/sessions/new-session-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -24
- package/src/lib/server/session-run-manager.test.ts +0 -23
- /package/src/app/api/{sessions → chats}/[id]/clear/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/deploy/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/devserver/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/edit-resend/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/fork/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/mailbox/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/retry/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
|
@@ -6,7 +6,6 @@ import Database from 'better-sqlite3'
|
|
|
6
6
|
|
|
7
7
|
import { DATA_DIR, WORKSPACE_DIR } from './data-dir'
|
|
8
8
|
import type { Message } from '@/types'
|
|
9
|
-
import { ensureMainSessionFlag } from './main-session'
|
|
10
9
|
export const UPLOAD_DIR = path.join(DATA_DIR, 'uploads')
|
|
11
10
|
|
|
12
11
|
// --- LRU Cache ---
|
|
@@ -157,8 +156,13 @@ const COLLECTIONS = [
|
|
|
157
156
|
'souls',
|
|
158
157
|
'benchmarks',
|
|
159
158
|
'approvals',
|
|
159
|
+
'browser_sessions',
|
|
160
|
+
'watch_jobs',
|
|
161
|
+
'delegation_jobs',
|
|
160
162
|
] as const
|
|
161
163
|
|
|
164
|
+
export type StorageCollection = (typeof COLLECTIONS)[number]
|
|
165
|
+
|
|
162
166
|
for (const table of COLLECTIONS) {
|
|
163
167
|
db.exec(`CREATE TABLE IF NOT EXISTS ${table} (id TEXT PRIMARY KEY, data TEXT NOT NULL)`)
|
|
164
168
|
}
|
|
@@ -186,12 +190,34 @@ function getCollectionRawCache(table: string): LRUMap<string, string> {
|
|
|
186
190
|
return loaded
|
|
187
191
|
}
|
|
188
192
|
|
|
193
|
+
function normalizeStoredRecord(table: string, value: any): any {
|
|
194
|
+
if (table !== 'sessions') return value
|
|
195
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return value
|
|
196
|
+
|
|
197
|
+
const session = value as Record<string, any>
|
|
198
|
+
if (session.sessionType !== 'human') session.sessionType = 'human'
|
|
199
|
+
const isLegacyShortcut = (
|
|
200
|
+
(typeof session.id === 'string' && session.id.startsWith('agent-thread-'))
|
|
201
|
+
|| (typeof session.name === 'string' && session.name.startsWith('agent-thread:'))
|
|
202
|
+
)
|
|
203
|
+
if (
|
|
204
|
+
isLegacyShortcut
|
|
205
|
+
&& typeof session.agentId === 'string'
|
|
206
|
+
&& session.agentId.trim()
|
|
207
|
+
&& (!session.shortcutForAgentId || session.shortcutForAgentId !== session.agentId)
|
|
208
|
+
) {
|
|
209
|
+
session.shortcutForAgentId = session.agentId
|
|
210
|
+
}
|
|
211
|
+
if ('mainLoopState' in session) delete session.mainLoopState
|
|
212
|
+
return session
|
|
213
|
+
}
|
|
214
|
+
|
|
189
215
|
function loadCollection(table: string): Record<string, any> {
|
|
190
216
|
const raw = getCollectionRawCache(table)
|
|
191
217
|
const result: Record<string, any> = {}
|
|
192
218
|
for (const [id, data] of raw.entries()) {
|
|
193
219
|
try {
|
|
194
|
-
result[id] = JSON.parse(data)
|
|
220
|
+
result[id] = normalizeStoredRecord(table, JSON.parse(data))
|
|
195
221
|
} catch {
|
|
196
222
|
// Ignore malformed records instead of crashing list endpoints.
|
|
197
223
|
}
|
|
@@ -206,7 +232,8 @@ function saveCollection(table: string, data: Record<string, any>) {
|
|
|
206
232
|
const toDelete: string[] = []
|
|
207
233
|
|
|
208
234
|
for (const [id, val] of Object.entries(data)) {
|
|
209
|
-
const
|
|
235
|
+
const normalized = normalizeStoredRecord(table, val)
|
|
236
|
+
const serialized = JSON.stringify(normalized)
|
|
210
237
|
if (typeof serialized !== 'string') continue
|
|
211
238
|
next.set(id, serialized)
|
|
212
239
|
if (current.get(id) !== serialized) {
|
|
@@ -252,7 +279,7 @@ function deleteCollectionItem(table: string, id: string) {
|
|
|
252
279
|
* concurrent processes are modifying different items.
|
|
253
280
|
*/
|
|
254
281
|
function upsertCollectionItem(table: string, id: string, value: any) {
|
|
255
|
-
const serialized = JSON.stringify(value)
|
|
282
|
+
const serialized = JSON.stringify(normalizeStoredRecord(table, value))
|
|
256
283
|
db.prepare(`INSERT OR REPLACE INTO ${table} (id, data) VALUES (?, ?)`).run(id, serialized)
|
|
257
284
|
// Update the in-memory cache
|
|
258
285
|
const cached = collectionCache.get(table)
|
|
@@ -261,6 +288,51 @@ function upsertCollectionItem(table: string, id: string, value: any) {
|
|
|
261
288
|
}
|
|
262
289
|
}
|
|
263
290
|
|
|
291
|
+
function loadCollectionItem(table: string, id: string): any | null {
|
|
292
|
+
const row = db.prepare(`SELECT data FROM ${table} WHERE id = ?`).get(id) as { data: string } | undefined
|
|
293
|
+
if (!row) return null
|
|
294
|
+
try {
|
|
295
|
+
return normalizeStoredRecord(table, JSON.parse(row.data))
|
|
296
|
+
} catch {
|
|
297
|
+
return null
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function upsertCollectionItems(table: string, entries: Array<[string, any]>): void {
|
|
302
|
+
if (!entries.length) return
|
|
303
|
+
const prepared = entries
|
|
304
|
+
.map(([id, value]) => [id, JSON.stringify(normalizeStoredRecord(table, value))] as const)
|
|
305
|
+
.filter(([, serialized]) => typeof serialized === 'string')
|
|
306
|
+
if (!prepared.length) return
|
|
307
|
+
|
|
308
|
+
const transaction = db.transaction(() => {
|
|
309
|
+
const upsert = db.prepare(`INSERT OR REPLACE INTO ${table} (id, data) VALUES (?, ?)`)
|
|
310
|
+
for (const [id, serialized] of prepared) {
|
|
311
|
+
upsert.run(id, serialized)
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
transaction()
|
|
315
|
+
|
|
316
|
+
const cached = collectionCache.get(table)
|
|
317
|
+
if (cached) {
|
|
318
|
+
for (const [id, serialized] of prepared) {
|
|
319
|
+
cached.set(id, serialized)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function loadStoredItem(table: StorageCollection, id: string): any | null {
|
|
325
|
+
return loadCollectionItem(table, id)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function upsertStoredItem(table: StorageCollection, id: string, value: any): void {
|
|
329
|
+
upsertCollectionItem(table, id, value)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function upsertStoredItems(table: StorageCollection, entries: Array<[string, any]>): void {
|
|
333
|
+
upsertCollectionItems(table, entries)
|
|
334
|
+
}
|
|
335
|
+
|
|
264
336
|
function loadSingleton(table: string, fallback: any): any {
|
|
265
337
|
const row = db.prepare(`SELECT data FROM ${table} WHERE id = 1`).get() as { data: string } | undefined
|
|
266
338
|
return row ? JSON.parse(row.data) : fallback
|
|
@@ -393,7 +465,7 @@ if (!IS_BUILD_BOOTSTRAP) {
|
|
|
393
465
|
|
|
394
466
|
## Platform
|
|
395
467
|
|
|
396
|
-
- **Agents** — Create specialized AI agents (Agents tab → "+") with a provider, model, system prompt, and tools. "Generate with AI" scaffolds agents from a description.
|
|
468
|
+
- **Agents** — Create specialized AI agents (Agents tab → "+") with a provider, model, system prompt, and tools. "Generate with AI" scaffolds agents from a description. Enable cross-agent delegation when an agent should assign work to others.
|
|
397
469
|
- **Providers** — Configure LLM backends in Settings → Providers: Claude Code CLI, OpenAI Codex CLI, OpenCode CLI, Anthropic, OpenAI, Google Gemini, DeepSeek, Groq, Together AI, Mistral AI, xAI (Grok), Fireworks AI, Ollama, OpenClaw, or custom OpenAI-compatible endpoints.
|
|
398
470
|
- **Tasks** — The Task Board tracks work items. Assign agents and they'll execute autonomously.
|
|
399
471
|
- **Schedules** — Cron-based recurring jobs that run agents or tasks automatically.
|
|
@@ -420,8 +492,8 @@ Use your platform management tools proactively:
|
|
|
420
492
|
You have opinions about good agent design. You suggest creative approaches, warn about common pitfalls, and get excited when someone gets something cool working. You're not a manual — you're a collaborator.
|
|
421
493
|
|
|
422
494
|
Be concise but not curt. Warmth doesn't require verbosity. When someone asks "how do I...?", give them the direct steps. Offer to do things rather than just explaining — if someone wants an agent created, create it. Use your tools when actions speak louder than words. If you don't know something, say so honestly.`,
|
|
423
|
-
isOrchestrator:
|
|
424
|
-
|
|
495
|
+
isOrchestrator: true,
|
|
496
|
+
plugins: defaultStarterTools,
|
|
425
497
|
heartbeatEnabled: true,
|
|
426
498
|
platformAssignScope: 'all',
|
|
427
499
|
skillIds: [],
|
|
@@ -435,11 +507,21 @@ Be concise but not curt. Warmth doesn't require verbosity. When someone asks "ho
|
|
|
435
507
|
if (row?.data) {
|
|
436
508
|
try {
|
|
437
509
|
const existing = JSON.parse(row.data) as Record<string, unknown>
|
|
438
|
-
const
|
|
439
|
-
const
|
|
440
|
-
if (JSON.stringify(
|
|
441
|
-
existing.
|
|
510
|
+
const existingPlugins = Array.isArray(existing.plugins) ? existing.plugins : Array.isArray(existing.tools) ? existing.tools : []
|
|
511
|
+
const mergedPlugins = Array.from(new Set([...existingPlugins, ...defaultStarterTools])).filter((t) => t !== 'delete_file')
|
|
512
|
+
if (JSON.stringify(existingPlugins) !== JSON.stringify(mergedPlugins)) {
|
|
513
|
+
existing.plugins = mergedPlugins
|
|
514
|
+
delete existing.tools
|
|
442
515
|
existing.updatedAt = Date.now()
|
|
516
|
+
}
|
|
517
|
+
if (existing.platformAssignScope === 'all' || existing.platformAssignScope === 'self') {
|
|
518
|
+
const derivedIsOrchestrator = existing.platformAssignScope === 'all'
|
|
519
|
+
if (existing.isOrchestrator !== derivedIsOrchestrator) {
|
|
520
|
+
existing.isOrchestrator = derivedIsOrchestrator
|
|
521
|
+
existing.updatedAt = Date.now()
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (JSON.stringify(JSON.parse(row.data)) !== JSON.stringify(existing)) {
|
|
443
525
|
db.prepare('UPDATE agents SET data = ? WHERE id = ?').run(JSON.stringify(existing), 'default')
|
|
444
526
|
}
|
|
445
527
|
} catch {
|
|
@@ -516,15 +598,19 @@ export function loadSessions(): Record<string, any> {
|
|
|
516
598
|
changed = true
|
|
517
599
|
}
|
|
518
600
|
|
|
519
|
-
const beforeMainFlag = session.mainSession === true
|
|
520
|
-
ensureMainSessionFlag(session)
|
|
521
|
-
if (!beforeMainFlag && session.mainSession === true) changed = true
|
|
522
601
|
|
|
523
602
|
const agentId = typeof session.agentId === 'string' ? session.agentId.trim() : ''
|
|
524
603
|
if (agentId && !Object.prototype.hasOwnProperty.call(agents, agentId)) {
|
|
525
604
|
session.agentId = null
|
|
526
605
|
changed = true
|
|
527
606
|
}
|
|
607
|
+
|
|
608
|
+
// Migrate tools → plugins
|
|
609
|
+
if (Array.isArray(session.tools) && !Array.isArray(session.plugins)) {
|
|
610
|
+
session.plugins = session.tools
|
|
611
|
+
delete session.tools
|
|
612
|
+
changed = true
|
|
613
|
+
}
|
|
528
614
|
}
|
|
529
615
|
|
|
530
616
|
if (changed) saveCollection('sessions', sessions)
|
|
@@ -596,8 +682,24 @@ export function decryptKey(encrypted: string): string {
|
|
|
596
682
|
}
|
|
597
683
|
|
|
598
684
|
// --- Agents ---
|
|
685
|
+
|
|
686
|
+
/** Migrate legacy `tools` field → `plugins` on agent objects. */
|
|
687
|
+
function migrateAgentPlugins(agents: Record<string, Record<string, unknown>>): boolean {
|
|
688
|
+
let changed = false
|
|
689
|
+
for (const agent of Object.values(agents)) {
|
|
690
|
+
if (!agent || typeof agent !== 'object') continue
|
|
691
|
+
if (Array.isArray(agent.tools) && !Array.isArray(agent.plugins)) {
|
|
692
|
+
agent.plugins = agent.tools
|
|
693
|
+
delete agent.tools
|
|
694
|
+
changed = true
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return changed
|
|
698
|
+
}
|
|
699
|
+
|
|
599
700
|
export function loadAgents(opts?: { includeTrashed?: boolean }): Record<string, any> {
|
|
600
701
|
const all = loadCollection('agents')
|
|
702
|
+
if (migrateAgentPlugins(all)) saveCollection('agents', all)
|
|
601
703
|
if (opts?.includeTrashed) return all
|
|
602
704
|
const result: Record<string, any> = {}
|
|
603
705
|
for (const [id, agent] of Object.entries(all)) {
|
|
@@ -665,12 +767,106 @@ export function saveQueue(q: string[]) {
|
|
|
665
767
|
}
|
|
666
768
|
|
|
667
769
|
// --- Settings ---
|
|
770
|
+
const APP_SETTINGS_SECRET_FIELDS = [
|
|
771
|
+
'elevenLabsApiKey',
|
|
772
|
+
'tavilyApiKey',
|
|
773
|
+
'braveApiKey',
|
|
774
|
+
] as const
|
|
775
|
+
|
|
776
|
+
const ENCRYPTED_APP_SETTINGS_KEY = '__encryptedAppSettings'
|
|
777
|
+
|
|
778
|
+
type PersistedSettingsRecord = Record<string, any> & {
|
|
779
|
+
[ENCRYPTED_APP_SETTINGS_KEY]?: Record<string, string>
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function cloneRecord<T extends Record<string, any>>(value: T): T {
|
|
783
|
+
return JSON.parse(JSON.stringify(value || {})) as T
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function isPlainRecord(value: unknown): value is Record<string, any> {
|
|
787
|
+
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function getEncryptedAppSettings(settings: PersistedSettingsRecord): Record<string, string> {
|
|
791
|
+
return isPlainRecord(settings[ENCRYPTED_APP_SETTINGS_KEY])
|
|
792
|
+
? { ...(settings[ENCRYPTED_APP_SETTINGS_KEY] as Record<string, string>) }
|
|
793
|
+
: {}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function isClearedSecretValue(value: unknown): boolean {
|
|
797
|
+
return value === null || (typeof value === 'string' && value.trim() === '')
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function isProvidedSecretValue(value: unknown): value is string {
|
|
801
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function buildPersistedSettings(input: Record<string, any>, existing?: PersistedSettingsRecord): PersistedSettingsRecord {
|
|
805
|
+
const next = cloneRecord(input) as PersistedSettingsRecord
|
|
806
|
+
const encrypted = {
|
|
807
|
+
...(existing ? getEncryptedAppSettings(existing) : {}),
|
|
808
|
+
...getEncryptedAppSettings(next),
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
delete next[ENCRYPTED_APP_SETTINGS_KEY]
|
|
812
|
+
|
|
813
|
+
for (const field of APP_SETTINGS_SECRET_FIELDS) {
|
|
814
|
+
const raw = next[field]
|
|
815
|
+
if (isClearedSecretValue(raw)) {
|
|
816
|
+
delete encrypted[field]
|
|
817
|
+
delete next[field]
|
|
818
|
+
continue
|
|
819
|
+
}
|
|
820
|
+
if (isProvidedSecretValue(raw)) {
|
|
821
|
+
encrypted[field] = encryptKey(raw)
|
|
822
|
+
delete next[field]
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (Object.keys(encrypted).length > 0) next[ENCRYPTED_APP_SETTINGS_KEY] = encrypted
|
|
827
|
+
return next
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function resolveSettingsSecrets(settings: PersistedSettingsRecord): Record<string, any> {
|
|
831
|
+
const resolved = cloneRecord(settings)
|
|
832
|
+
delete resolved[ENCRYPTED_APP_SETTINGS_KEY]
|
|
833
|
+
|
|
834
|
+
const encrypted = getEncryptedAppSettings(settings)
|
|
835
|
+
for (const field of APP_SETTINGS_SECRET_FIELDS) {
|
|
836
|
+
if (isProvidedSecretValue(resolved[field])) continue
|
|
837
|
+
const value = encrypted[field]
|
|
838
|
+
if (typeof value !== 'string' || !value) continue
|
|
839
|
+
try {
|
|
840
|
+
resolved[field] = decryptKey(value)
|
|
841
|
+
} catch {
|
|
842
|
+
// Ignore malformed encrypted settings instead of breaking all settings reads.
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
return resolved
|
|
847
|
+
}
|
|
848
|
+
|
|
668
849
|
export function loadSettings(): Record<string, any> {
|
|
669
|
-
|
|
850
|
+
const persisted = loadSingleton('settings', {}) as PersistedSettingsRecord
|
|
851
|
+
const normalized = buildPersistedSettings(persisted, persisted)
|
|
852
|
+
if (JSON.stringify(persisted) !== JSON.stringify(normalized)) {
|
|
853
|
+
saveSingleton('settings', normalized)
|
|
854
|
+
}
|
|
855
|
+
return resolveSettingsSecrets(normalized)
|
|
670
856
|
}
|
|
671
857
|
|
|
672
858
|
export function saveSettings(s: Record<string, any>) {
|
|
673
|
-
|
|
859
|
+
const existing = loadSingleton('settings', {}) as PersistedSettingsRecord
|
|
860
|
+
saveSingleton('settings', buildPersistedSettings(s, existing))
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
export function loadPublicSettings(): Record<string, any> {
|
|
864
|
+
const settings = cloneRecord(loadSettings())
|
|
865
|
+
for (const field of APP_SETTINGS_SECRET_FIELDS) {
|
|
866
|
+
settings[`${field}Configured`] = isProvidedSecretValue(settings[field])
|
|
867
|
+
settings[field] = null
|
|
868
|
+
}
|
|
869
|
+
return settings
|
|
674
870
|
}
|
|
675
871
|
|
|
676
872
|
// --- Secrets (service keys for orchestrators) ---
|
|
@@ -1006,6 +1202,49 @@ export function loadApprovals(): Record<string, unknown> {
|
|
|
1006
1202
|
return loadCollection('approvals')
|
|
1007
1203
|
}
|
|
1008
1204
|
|
|
1205
|
+
// --- Browser Sessions ---
|
|
1206
|
+
export function loadBrowserSessions(): Record<string, unknown> {
|
|
1207
|
+
return loadCollection('browser_sessions')
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
export function upsertBrowserSession(id: string, data: unknown) {
|
|
1211
|
+
upsertCollectionItem('browser_sessions', id, data)
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
export function deleteBrowserSession(id: string) {
|
|
1215
|
+
deleteCollectionItem('browser_sessions', id)
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// --- Watch Jobs ---
|
|
1219
|
+
export function loadWatchJobs(): Record<string, unknown> {
|
|
1220
|
+
return loadCollection('watch_jobs')
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
export function upsertWatchJob(id: string, data: unknown) {
|
|
1224
|
+
upsertCollectionItem('watch_jobs', id, data)
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
export function upsertWatchJobs(entries: Array<[string, unknown]>) {
|
|
1228
|
+
upsertCollectionItems('watch_jobs', entries)
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
export function deleteWatchJob(id: string) {
|
|
1232
|
+
deleteCollectionItem('watch_jobs', id)
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// --- Delegation Jobs ---
|
|
1236
|
+
export function loadDelegationJobs(): Record<string, unknown> {
|
|
1237
|
+
return loadCollection('delegation_jobs')
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
export function upsertDelegationJob(id: string, data: unknown) {
|
|
1241
|
+
upsertCollectionItem('delegation_jobs', id, data)
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
export function deleteDelegationJob(id: string) {
|
|
1245
|
+
deleteCollectionItem('delegation_jobs', id)
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1009
1248
|
export function upsertApproval(id: string, approval: unknown) {
|
|
1010
1249
|
upsertCollectionItem('approvals', id, approval)
|
|
1011
1250
|
}
|