@swarmclawai/swarmclaw 0.6.7 → 0.7.0
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 +82 -39
- package/next.config.ts +31 -6
- package/package.json +3 -2
- package/src/app/api/agents/[id]/thread/route.ts +1 -0
- package/src/app/api/agents/route.ts +19 -5
- package/src/app/api/approvals/route.ts +22 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/eval/run/route.ts +37 -0
- package/src/app/api/eval/scenarios/route.ts +24 -0
- package/src/app/api/eval/suite/route.ts +29 -0
- package/src/app/api/mcp-servers/[id]/conformance/route.ts +26 -0
- package/src/app/api/mcp-servers/[id]/invoke/route.ts +81 -0
- package/src/app/api/memory/graph/route.ts +46 -0
- package/src/app/api/memory/route.ts +36 -5
- package/src/app/api/notifications/route.ts +3 -0
- package/src/app/api/plugins/install/route.ts +57 -5
- package/src/app/api/plugins/marketplace/route.ts +73 -22
- package/src/app/api/plugins/route.ts +61 -1
- package/src/app/api/plugins/ui/route.ts +34 -0
- package/src/app/api/sessions/[id]/checkpoints/route.ts +31 -0
- package/src/app/api/sessions/[id]/restore/route.ts +36 -0
- package/src/app/api/settings/route.ts +62 -0
- package/src/app/api/setup/doctor/route.ts +22 -5
- package/src/app/api/souls/[id]/route.ts +65 -0
- package/src/app/api/souls/route.ts +70 -0
- package/src/app/api/tasks/[id]/approve/route.ts +4 -3
- package/src/app/api/tasks/[id]/route.ts +16 -3
- package/src/app/api/tasks/route.ts +10 -2
- package/src/app/api/usage/route.ts +9 -2
- package/src/app/globals.css +27 -0
- package/src/app/page.tsx +10 -5
- package/src/cli/index.js +37 -0
- package/src/components/activity/activity-feed.tsx +9 -2
- package/src/components/agents/agent-avatar.tsx +5 -1
- package/src/components/agents/agent-card.tsx +55 -9
- package/src/components/agents/agent-sheet.tsx +112 -34
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/agents/soul-library-picker.tsx +84 -13
- package/src/components/auth/access-key-gate.tsx +63 -54
- package/src/components/auth/user-picker.tsx +37 -32
- package/src/components/chat/activity-moment.tsx +2 -0
- package/src/components/chat/chat-area.tsx +11 -0
- package/src/components/chat/chat-header.tsx +69 -25
- package/src/components/chat/chat-tool-toggles.tsx +2 -2
- package/src/components/chat/checkpoint-timeline.tsx +112 -0
- package/src/components/chat/code-block.tsx +3 -1
- package/src/components/chat/exec-approval-card.tsx +8 -1
- package/src/components/chat/message-bubble.tsx +164 -4
- package/src/components/chat/message-list.tsx +46 -4
- package/src/components/chat/session-approval-card.tsx +80 -0
- package/src/components/chat/session-debug-panel.tsx +106 -84
- package/src/components/chat/streaming-bubble.tsx +6 -5
- package/src/components/chat/task-approval-card.tsx +78 -0
- package/src/components/chat/thinking-indicator.tsx +48 -12
- package/src/components/chat/tool-call-bubble.tsx +3 -0
- package/src/components/chat/tool-request-banner.tsx +39 -20
- package/src/components/chatrooms/chatroom-list.tsx +11 -4
- package/src/components/chatrooms/chatroom-sheet.tsx +7 -2
- package/src/components/connectors/connector-list.tsx +33 -11
- package/src/components/connectors/connector-sheet.tsx +37 -7
- package/src/components/home/home-view.tsx +54 -24
- package/src/components/input/chat-input.tsx +22 -1
- package/src/components/knowledge/knowledge-list.tsx +17 -18
- package/src/components/knowledge/knowledge-sheet.tsx +9 -5
- package/src/components/layout/app-layout.tsx +87 -19
- package/src/components/mcp-servers/mcp-server-list.tsx +352 -50
- package/src/components/mcp-servers/mcp-server-sheet.tsx +25 -9
- package/src/components/memory/memory-browser.tsx +73 -45
- package/src/components/memory/memory-graph-view.tsx +203 -0
- package/src/components/memory/memory-list.tsx +20 -13
- package/src/components/plugins/plugin-list.tsx +214 -60
- package/src/components/plugins/plugin-sheet.tsx +119 -24
- package/src/components/projects/project-list.tsx +17 -9
- package/src/components/providers/provider-list.tsx +21 -6
- package/src/components/providers/provider-sheet.tsx +42 -25
- package/src/components/runs/run-list.tsx +17 -13
- package/src/components/schedules/schedule-card.tsx +10 -3
- package/src/components/schedules/schedule-list.tsx +2 -2
- package/src/components/schedules/schedule-sheet.tsx +28 -9
- package/src/components/secrets/secret-sheet.tsx +7 -2
- package/src/components/secrets/secrets-list.tsx +18 -5
- package/src/components/sessions/new-session-sheet.tsx +183 -376
- package/src/components/sessions/session-card.tsx +10 -2
- package/src/components/settings/gateway-connection-panel.tsx +9 -8
- package/src/components/shared/command-palette.tsx +13 -5
- package/src/components/shared/empty-state.tsx +20 -8
- package/src/components/shared/hint-tip.tsx +31 -0
- package/src/components/shared/notification-center.tsx +134 -86
- package/src/components/shared/profile-sheet.tsx +4 -0
- package/src/components/shared/settings/plugin-manager.tsx +360 -135
- package/src/components/shared/settings/section-capability-policy.tsx +3 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +149 -4
- package/src/components/skills/clawhub-browser.tsx +1 -0
- package/src/components/skills/skill-list.tsx +31 -12
- package/src/components/skills/skill-sheet.tsx +20 -7
- package/src/components/tasks/approvals-panel.tsx +224 -0
- package/src/components/tasks/task-board.tsx +20 -12
- package/src/components/tasks/task-card.tsx +21 -7
- package/src/components/tasks/task-column.tsx +4 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +130 -1
- package/src/components/ui/dialog.tsx +1 -0
- package/src/components/ui/sheet.tsx +1 -0
- package/src/components/usage/metrics-dashboard.tsx +72 -48
- package/src/components/wallets/wallet-panel.tsx +65 -41
- package/src/components/wallets/wallet-section.tsx +9 -3
- package/src/components/webhooks/webhook-list.tsx +21 -12
- package/src/components/webhooks/webhook-sheet.tsx +13 -3
- package/src/lib/approval-display.test.ts +45 -0
- package/src/lib/approval-display.ts +62 -0
- package/src/lib/clipboard.ts +38 -0
- package/src/lib/memory.ts +8 -0
- package/src/lib/providers/claude-cli.ts +5 -3
- package/src/lib/providers/index.ts +67 -21
- package/src/lib/runtime-loop.ts +3 -2
- package/src/lib/server/approvals.ts +150 -0
- package/src/lib/server/chat-execution.ts +319 -74
- package/src/lib/server/chatroom-helpers.ts +63 -5
- package/src/lib/server/chatroom-orchestration.ts +74 -0
- package/src/lib/server/clawhub-client.ts +82 -6
- package/src/lib/server/connectors/manager.ts +27 -1
- package/src/lib/server/context-manager.ts +132 -50
- package/src/lib/server/cost.test.ts +73 -0
- package/src/lib/server/cost.ts +165 -34
- package/src/lib/server/daemon-state.ts +112 -1
- package/src/lib/server/data-dir.ts +18 -1
- package/src/lib/server/eval/runner.ts +126 -0
- package/src/lib/server/eval/scenarios.ts +218 -0
- package/src/lib/server/eval/scorer.ts +96 -0
- package/src/lib/server/eval/store.ts +37 -0
- package/src/lib/server/eval/types.ts +48 -0
- package/src/lib/server/execution-log.ts +12 -8
- package/src/lib/server/guardian.ts +34 -0
- package/src/lib/server/heartbeat-service.ts +53 -1
- package/src/lib/server/integrity-monitor.ts +208 -0
- package/src/lib/server/langgraph-checkpoint.ts +10 -0
- package/src/lib/server/link-understanding.ts +55 -0
- package/src/lib/server/llm-response-cache.test.ts +102 -0
- package/src/lib/server/llm-response-cache.ts +227 -0
- package/src/lib/server/main-agent-loop.ts +115 -16
- package/src/lib/server/main-session.ts +6 -3
- package/src/lib/server/mcp-conformance.test.ts +18 -0
- package/src/lib/server/mcp-conformance.ts +233 -0
- package/src/lib/server/memory-db.ts +193 -19
- package/src/lib/server/memory-retrieval.test.ts +56 -0
- package/src/lib/server/mmr.ts +73 -0
- package/src/lib/server/orchestrator-lg.ts +7 -1
- package/src/lib/server/orchestrator.ts +4 -3
- package/src/lib/server/plugins.ts +662 -132
- package/src/lib/server/process-manager.ts +18 -0
- package/src/lib/server/query-expansion.ts +57 -0
- package/src/lib/server/queue.ts +280 -11
- package/src/lib/server/runtime-settings.ts +9 -0
- package/src/lib/server/session-run-manager.test.ts +23 -0
- package/src/lib/server/session-run-manager.ts +32 -2
- package/src/lib/server/session-tools/canvas.ts +85 -50
- package/src/lib/server/session-tools/chatroom.ts +130 -127
- package/src/lib/server/session-tools/connector.ts +233 -454
- package/src/lib/server/session-tools/context-mgmt.ts +87 -105
- package/src/lib/server/session-tools/crud.ts +84 -7
- package/src/lib/server/session-tools/delegate.ts +351 -752
- package/src/lib/server/session-tools/discovery.ts +198 -0
- package/src/lib/server/session-tools/edit_file.ts +82 -0
- package/src/lib/server/session-tools/file-send.test.ts +39 -0
- package/src/lib/server/session-tools/file.ts +257 -425
- package/src/lib/server/session-tools/git.ts +87 -47
- package/src/lib/server/session-tools/http.ts +95 -33
- package/src/lib/server/session-tools/index.ts +217 -138
- package/src/lib/server/session-tools/memory.ts +154 -239
- package/src/lib/server/session-tools/monitor.ts +126 -0
- package/src/lib/server/session-tools/normalize-tool-args.test.ts +61 -0
- package/src/lib/server/session-tools/normalize-tool-args.ts +48 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +82 -99
- package/src/lib/server/session-tools/openclaw-workspace.ts +103 -93
- package/src/lib/server/session-tools/platform.ts +86 -0
- package/src/lib/server/session-tools/plugin-creator.ts +239 -0
- package/src/lib/server/session-tools/sample-ui.ts +97 -0
- package/src/lib/server/session-tools/sandbox.ts +175 -148
- package/src/lib/server/session-tools/schedule.ts +78 -0
- package/src/lib/server/session-tools/session-info.ts +104 -410
- package/src/lib/server/session-tools/shell-normalize.test.ts +43 -0
- package/src/lib/server/session-tools/shell.ts +171 -143
- package/src/lib/server/session-tools/subagent.ts +77 -77
- package/src/lib/server/session-tools/wallet.ts +182 -106
- package/src/lib/server/session-tools/web.ts +181 -327
- package/src/lib/server/storage.ts +36 -0
- package/src/lib/server/stream-agent-chat.ts +348 -242
- package/src/lib/server/task-quality-gate.test.ts +44 -0
- package/src/lib/server/task-quality-gate.ts +67 -0
- package/src/lib/server/task-validation.test.ts +78 -0
- package/src/lib/server/task-validation.ts +67 -2
- package/src/lib/server/tool-aliases.ts +68 -0
- package/src/lib/server/tool-capability-policy.ts +24 -5
- package/src/lib/server/tool-retry.ts +62 -0
- package/src/lib/server/transcript-repair.ts +72 -0
- package/src/lib/setup-defaults.ts +1 -0
- package/src/lib/tasks.ts +7 -1
- package/src/lib/tool-definitions.ts +24 -23
- package/src/lib/validation/schemas.ts +13 -0
- package/src/lib/view-routes.ts +2 -23
- package/src/stores/use-app-store.ts +23 -1
- package/src/types/index.ts +155 -10
|
@@ -32,11 +32,13 @@ export function streamClaudeCliChat({ session, message, imagePath, systemPrompt,
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const args = ['--print', '--output-format', 'stream-json', '--verbose', '--dangerously-skip-permissions']
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const resumeSessionId = typeof session.claudeSessionId === 'string' ? session.claudeSessionId : ''
|
|
36
|
+
const selectedModel = typeof session.model === 'string' ? session.model : ''
|
|
37
|
+
if (resumeSessionId) args.push('--resume', resumeSessionId)
|
|
38
|
+
if (selectedModel) args.push('--model', selectedModel)
|
|
37
39
|
|
|
38
40
|
// Inject agent system prompt
|
|
39
|
-
if (systemPrompt && !
|
|
41
|
+
if (systemPrompt && !resumeSessionId) {
|
|
40
42
|
args.push('--system-prompt', systemPrompt)
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -5,7 +5,7 @@ import { streamOpenAiChat } from './openai'
|
|
|
5
5
|
import { streamOllamaChat } from './ollama'
|
|
6
6
|
import { streamAnthropicChat } from './anthropic'
|
|
7
7
|
import { streamOpenClawChat } from './openclaw'
|
|
8
|
-
import type { ProviderInfo, ProviderConfig as CustomProviderConfig } from '../../types'
|
|
8
|
+
import type { ProviderInfo, ProviderConfig as CustomProviderConfig, ProviderType } from '../../types'
|
|
9
9
|
|
|
10
10
|
const RETRYABLE_STATUS_CODES = [401, 429, 500, 502, 503]
|
|
11
11
|
|
|
@@ -19,14 +19,16 @@ export interface StreamChatUsage {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export interface StreamChatOptions {
|
|
22
|
-
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
+
session: Record<string, any> & { id: string }
|
|
23
24
|
message: string
|
|
24
25
|
imagePath?: string
|
|
25
26
|
imageUrl?: string
|
|
26
27
|
apiKey?: string | null
|
|
27
28
|
systemPrompt?: string
|
|
28
29
|
write: (data: string) => void
|
|
29
|
-
active: Map<string,
|
|
30
|
+
active: Map<string, unknown>
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
32
|
loadHistory: (sessionId: string) => any[]
|
|
31
33
|
onUsage?: (usage: StreamChatUsage) => void
|
|
32
34
|
/** Abort signal from the caller — providers should use this to cancel in-flight requests. */
|
|
@@ -37,7 +39,7 @@ interface BuiltinProviderConfig extends ProviderInfo {
|
|
|
37
39
|
handler: ProviderHandler
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
const PROVIDERS: Record<string, BuiltinProviderConfig> = {
|
|
42
|
+
export const PROVIDERS: Record<string, BuiltinProviderConfig> = {
|
|
41
43
|
'claude-cli': {
|
|
42
44
|
id: 'claude-cli',
|
|
43
45
|
name: 'Claude Code CLI',
|
|
@@ -232,6 +234,7 @@ const PROVIDERS: Record<string, BuiltinProviderConfig> = {
|
|
|
232
234
|
/** Merge built-in providers with custom providers from storage */
|
|
233
235
|
function getCustomProviders(): Record<string, CustomProviderConfig> {
|
|
234
236
|
try {
|
|
237
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
235
238
|
const { loadProviderConfigs } = require('../server/storage')
|
|
236
239
|
return loadProviderConfigs() as Record<string, CustomProviderConfig>
|
|
237
240
|
} catch {
|
|
@@ -241,6 +244,7 @@ function getCustomProviders(): Record<string, CustomProviderConfig> {
|
|
|
241
244
|
|
|
242
245
|
function getModelOverrides(): Record<string, string[]> {
|
|
243
246
|
try {
|
|
247
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
244
248
|
const { loadModelOverrides } = require('../server/storage')
|
|
245
249
|
return loadModelOverrides()
|
|
246
250
|
} catch {
|
|
@@ -257,41 +261,80 @@ export function getProviderList(): ProviderInfo[] {
|
|
|
257
261
|
models: overrides[info.id] || info.models,
|
|
258
262
|
defaultModels: info.models,
|
|
259
263
|
}))
|
|
260
|
-
|
|
264
|
+
|
|
265
|
+
const customs: ProviderInfo[] = Object.values(getCustomProviders())
|
|
261
266
|
.filter((c) => c.isEnabled)
|
|
262
267
|
.map((c) => ({
|
|
263
|
-
id: c.id as
|
|
268
|
+
id: c.id as ProviderType,
|
|
264
269
|
name: c.name,
|
|
265
270
|
models: c.models,
|
|
266
271
|
defaultModels: c.models,
|
|
267
272
|
requiresApiKey: c.requiresApiKey,
|
|
268
|
-
requiresEndpoint: false,
|
|
273
|
+
requiresEndpoint: false as boolean,
|
|
269
274
|
defaultEndpoint: c.baseUrl,
|
|
270
275
|
}))
|
|
271
|
-
|
|
276
|
+
|
|
277
|
+
let pluginProviders: ProviderInfo[] = []
|
|
278
|
+
try {
|
|
279
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
280
|
+
const { getPluginManager } = require('../server/plugins')
|
|
281
|
+
pluginProviders = getPluginManager().getProviders().map((p: Record<string, unknown>) => ({
|
|
282
|
+
id: String(p.id) as ProviderType,
|
|
283
|
+
name: String(p.name),
|
|
284
|
+
models: p.models as string[],
|
|
285
|
+
defaultModels: p.models as string[],
|
|
286
|
+
requiresApiKey: Boolean(p.requiresApiKey),
|
|
287
|
+
requiresEndpoint: Boolean(p.requiresEndpoint),
|
|
288
|
+
defaultEndpoint: p.defaultEndpoint as string | undefined,
|
|
289
|
+
}))
|
|
290
|
+
} catch { /* ignore if running somewhere plugins aren't available */ }
|
|
291
|
+
|
|
292
|
+
return [...builtins, ...customs, ...pluginProviders]
|
|
272
293
|
}
|
|
273
294
|
|
|
274
295
|
export function getProvider(id: string): BuiltinProviderConfig | null {
|
|
275
296
|
if (PROVIDERS[id]) return PROVIDERS[id]
|
|
276
|
-
|
|
297
|
+
|
|
298
|
+
// Check custom providers
|
|
277
299
|
const customs = getCustomProviders()
|
|
278
300
|
const custom = customs[id]
|
|
279
301
|
if (custom?.isEnabled) {
|
|
280
302
|
return {
|
|
281
|
-
id: custom.id as
|
|
303
|
+
id: custom.id as ProviderType,
|
|
282
304
|
name: custom.name,
|
|
283
305
|
models: custom.models,
|
|
284
306
|
requiresApiKey: custom.requiresApiKey,
|
|
285
307
|
requiresEndpoint: false,
|
|
286
308
|
handler: {
|
|
287
|
-
streamChat: (opts) => {
|
|
288
|
-
// Custom providers use OpenAI handler with custom baseUrl
|
|
309
|
+
streamChat: async (opts) => {
|
|
289
310
|
const patchedSession = { ...opts.session, apiEndpoint: custom.baseUrl }
|
|
311
|
+
const { streamOpenAiChat } = await import('./openai')
|
|
290
312
|
return streamOpenAiChat({ ...opts, session: patchedSession })
|
|
291
313
|
},
|
|
292
314
|
},
|
|
293
315
|
}
|
|
294
316
|
}
|
|
317
|
+
|
|
318
|
+
// Check Plugin Providers
|
|
319
|
+
try {
|
|
320
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
321
|
+
const { getPluginManager } = require('../server/plugins')
|
|
322
|
+
const pluginProviders = getPluginManager().getProviders()
|
|
323
|
+
const found = pluginProviders.find((p: Record<string, unknown>) => p.id === id)
|
|
324
|
+
if (found) {
|
|
325
|
+
return {
|
|
326
|
+
id: found.id as ProviderType,
|
|
327
|
+
name: found.name,
|
|
328
|
+
models: found.models,
|
|
329
|
+
requiresApiKey: found.requiresApiKey,
|
|
330
|
+
requiresEndpoint: found.requiresEndpoint,
|
|
331
|
+
handler: {
|
|
332
|
+
streamChat: found.streamChat
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch { /* ignore */ }
|
|
337
|
+
|
|
295
338
|
return null
|
|
296
339
|
}
|
|
297
340
|
|
|
@@ -315,7 +358,7 @@ export async function streamChatWithFailover(
|
|
|
315
358
|
return provider.handler.streamChat(opts)
|
|
316
359
|
}
|
|
317
360
|
|
|
318
|
-
let lastError:
|
|
361
|
+
let lastError: unknown = null
|
|
319
362
|
|
|
320
363
|
for (let i = 0; i < credentialIds.length; i++) {
|
|
321
364
|
const credId = credentialIds[i]
|
|
@@ -324,6 +367,7 @@ export async function streamChatWithFailover(
|
|
|
324
367
|
let apiKey: string | null = opts.apiKey || null
|
|
325
368
|
if (credId && i > 0) {
|
|
326
369
|
// Need to decrypt fallback credential
|
|
370
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
327
371
|
const { loadCredentials, decryptKey } = require('../server/storage')
|
|
328
372
|
const creds = loadCredentials()
|
|
329
373
|
const cred = creds[credId]
|
|
@@ -337,21 +381,23 @@ export async function streamChatWithFailover(
|
|
|
337
381
|
apiKey,
|
|
338
382
|
})
|
|
339
383
|
return result // success
|
|
340
|
-
} catch (err:
|
|
384
|
+
} catch (err: unknown) {
|
|
341
385
|
lastError = err
|
|
342
|
-
const
|
|
386
|
+
const errObj = err as Record<string, unknown>
|
|
387
|
+
const statusCode = (typeof errObj?.status === 'number' ? errObj.status : typeof errObj?.statusCode === 'number' ? errObj.statusCode : 0) as number
|
|
388
|
+
const errMessage = err instanceof Error ? err.message : String(err)
|
|
343
389
|
const isRetryable = RETRYABLE_STATUS_CODES.includes(statusCode)
|
|
344
|
-
||
|
|
345
|
-
||
|
|
346
|
-
||
|
|
347
|
-
||
|
|
390
|
+
|| errMessage?.includes('rate limit')
|
|
391
|
+
|| errMessage?.includes('Rate limit')
|
|
392
|
+
|| errMessage?.includes('429')
|
|
393
|
+
|| errMessage?.includes('401')
|
|
348
394
|
|
|
349
395
|
if (isRetryable && i < credentialIds.length - 1) {
|
|
350
|
-
console.log(`[failover] Credential ${credId} failed (${statusCode ||
|
|
396
|
+
console.log(`[failover] Credential ${credId} failed (${statusCode || errMessage}), trying fallback...`)
|
|
351
397
|
// Send a metadata event to inform the client
|
|
352
398
|
opts.write(`data: ${JSON.stringify({
|
|
353
399
|
t: 'md',
|
|
354
|
-
text: JSON.stringify({ failover: { from: credId, reason:
|
|
400
|
+
text: JSON.stringify({ failover: { from: credId, reason: errMessage?.slice(0, 100) } }),
|
|
355
401
|
})}\n\n`)
|
|
356
402
|
// Exponential backoff for rate-limit / server errors (skip for auth rotation)
|
|
357
403
|
if (statusCode !== 401) {
|
package/src/lib/runtime-loop.ts
CHANGED
|
@@ -3,11 +3,12 @@ import type { LoopMode } from '@/types'
|
|
|
3
3
|
export const DEFAULT_LOOP_MODE: LoopMode = 'bounded'
|
|
4
4
|
|
|
5
5
|
// Loop limits
|
|
6
|
-
export const DEFAULT_AGENT_LOOP_RECURSION_LIMIT =
|
|
7
|
-
export const DEFAULT_ORCHESTRATOR_LOOP_RECURSION_LIMIT =
|
|
6
|
+
export const DEFAULT_AGENT_LOOP_RECURSION_LIMIT = 30
|
|
7
|
+
export const DEFAULT_ORCHESTRATOR_LOOP_RECURSION_LIMIT = 40
|
|
8
8
|
export const DEFAULT_LEGACY_ORCHESTRATOR_MAX_TURNS = 10
|
|
9
9
|
export const DEFAULT_ONGOING_LOOP_MAX_ITERATIONS = 250
|
|
10
10
|
export const DEFAULT_ONGOING_LOOP_MAX_RUNTIME_MINUTES = 60
|
|
11
|
+
export const DEFAULT_DELEGATION_MAX_DEPTH = 3
|
|
11
12
|
|
|
12
13
|
// Tool/process timeouts
|
|
13
14
|
export const DEFAULT_SHELL_COMMAND_TIMEOUT_SEC = 30
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { genId } from '@/lib/id'
|
|
4
|
+
import { loadApprovals, upsertApproval, loadSessions, saveSessions } from './storage'
|
|
5
|
+
import type { ApprovalRequest, ApprovalCategory } from '@/types'
|
|
6
|
+
import { notify } from './ws-hub'
|
|
7
|
+
import { DATA_DIR } from './data-dir'
|
|
8
|
+
import { log } from './logger'
|
|
9
|
+
|
|
10
|
+
function getApprovalTargetId(data: Record<string, unknown>): string | null {
|
|
11
|
+
const toolId = typeof data.toolId === 'string' ? data.toolId.trim() : ''
|
|
12
|
+
if (toolId) return toolId
|
|
13
|
+
const pluginId = typeof data.pluginId === 'string' ? data.pluginId.trim() : ''
|
|
14
|
+
return pluginId || null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function requestApproval(params: {
|
|
18
|
+
category: ApprovalCategory
|
|
19
|
+
title: string
|
|
20
|
+
description?: string
|
|
21
|
+
data: Record<string, unknown>
|
|
22
|
+
agentId?: string | null
|
|
23
|
+
sessionId?: string | null
|
|
24
|
+
taskId?: string | null
|
|
25
|
+
}): ApprovalRequest {
|
|
26
|
+
const targetId = getApprovalTargetId(params.data)
|
|
27
|
+
if (params.category === 'tool_access' && !targetId) {
|
|
28
|
+
throw new Error('tool_access approvals require a toolId or pluginId')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const normalizedData = { ...params.data }
|
|
32
|
+
if (params.category === 'tool_access' && targetId) {
|
|
33
|
+
normalizedData.toolId = targetId
|
|
34
|
+
normalizedData.pluginId = targetId
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const id = genId(8)
|
|
38
|
+
const now = Date.now()
|
|
39
|
+
const request: ApprovalRequest = {
|
|
40
|
+
id,
|
|
41
|
+
...params,
|
|
42
|
+
title: params.category === 'tool_access' && targetId ? `Enable Plugin: ${targetId}` : params.title,
|
|
43
|
+
data: normalizedData,
|
|
44
|
+
status: 'pending',
|
|
45
|
+
createdAt: now,
|
|
46
|
+
updatedAt: now,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
upsertApproval(id, request)
|
|
50
|
+
|
|
51
|
+
notify('approvals')
|
|
52
|
+
return request
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
export async function submitDecision(id: string, approved: boolean): Promise<void> {
|
|
57
|
+
const approvals = loadApprovals() as Record<string, ApprovalRequest>
|
|
58
|
+
const request = approvals[id]
|
|
59
|
+
if (!request) throw new Error('Approval request not found')
|
|
60
|
+
|
|
61
|
+
request.status = approved ? 'approved' : 'rejected'
|
|
62
|
+
request.updatedAt = Date.now()
|
|
63
|
+
upsertApproval(id, request)
|
|
64
|
+
|
|
65
|
+
// Handle specific side effects based on category
|
|
66
|
+
if (approved) {
|
|
67
|
+
if (request.category === 'tool_access' && request.sessionId) {
|
|
68
|
+
const sessions = loadSessions()
|
|
69
|
+
const session = sessions[request.sessionId]
|
|
70
|
+
if (session) {
|
|
71
|
+
const toolId = getApprovalTargetId(request.data)
|
|
72
|
+
const currentTools = session.tools || []
|
|
73
|
+
if (toolId && !currentTools.includes(toolId)) {
|
|
74
|
+
session.tools = [...currentTools, toolId]
|
|
75
|
+
saveSessions(sessions)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (request.category === 'plugin_scaffold') {
|
|
81
|
+
const filename = typeof request.data.filename === 'string' ? request.data.filename : ''
|
|
82
|
+
const code = typeof request.data.code === 'string' ? request.data.code : ''
|
|
83
|
+
if (filename && code) {
|
|
84
|
+
const pluginsDir = path.join(DATA_DIR, 'plugins')
|
|
85
|
+
if (!fs.existsSync(pluginsDir)) fs.mkdirSync(pluginsDir, { recursive: true })
|
|
86
|
+
fs.writeFileSync(path.join(pluginsDir, filename), code, 'utf8')
|
|
87
|
+
const { getPluginManager } = await import('./plugins')
|
|
88
|
+
getPluginManager().reload()
|
|
89
|
+
|
|
90
|
+
// Store creator agent metadata
|
|
91
|
+
const createdByAgentId = typeof request.data.createdByAgentId === 'string' ? request.data.createdByAgentId : request.agentId
|
|
92
|
+
if (createdByAgentId) {
|
|
93
|
+
getPluginManager().setMeta(filename, { createdByAgentId })
|
|
94
|
+
}
|
|
95
|
+
log.info('approvals', `Plugin scaffolded: ${filename}`)
|
|
96
|
+
|
|
97
|
+
// Auto-enable the new plugin for the creating agent's session
|
|
98
|
+
if (request.sessionId) {
|
|
99
|
+
const sessions = loadSessions()
|
|
100
|
+
const session = sessions[request.sessionId]
|
|
101
|
+
if (session) {
|
|
102
|
+
const currentTools = session.tools || []
|
|
103
|
+
if (!currentTools.includes(filename)) {
|
|
104
|
+
session.tools = [...currentTools, filename]
|
|
105
|
+
saveSessions(sessions)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
notify('plugins')
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (request.category === 'plugin_install') {
|
|
114
|
+
const url = typeof request.data.url === 'string' ? request.data.url : ''
|
|
115
|
+
if (url) {
|
|
116
|
+
try {
|
|
117
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(15000) })
|
|
118
|
+
if (res.ok) {
|
|
119
|
+
const code = await res.text()
|
|
120
|
+
const pluginId = typeof request.data.pluginId === 'string' ? request.data.pluginId : ''
|
|
121
|
+
const safeName = (pluginId || url.split('/').pop() || 'plugin').replace(/[^a-zA-Z0-9._-]/g, '_')
|
|
122
|
+
const filename = safeName.endsWith('.js') ? safeName : `${safeName}.js`
|
|
123
|
+
const pluginsDir = path.join(DATA_DIR, 'plugins')
|
|
124
|
+
if (!fs.existsSync(pluginsDir)) fs.mkdirSync(pluginsDir, { recursive: true })
|
|
125
|
+
fs.writeFileSync(path.join(pluginsDir, filename), code, 'utf8')
|
|
126
|
+
const { getPluginManager } = await import('./plugins')
|
|
127
|
+
getPluginManager().reload()
|
|
128
|
+
log.info('approvals', `Plugin installed from URL: ${filename}`)
|
|
129
|
+
notify('plugins')
|
|
130
|
+
}
|
|
131
|
+
} catch (err: unknown) {
|
|
132
|
+
log.error('approvals', 'Plugin install failed after approval', {
|
|
133
|
+
url,
|
|
134
|
+
error: err instanceof Error ? err.message : String(err),
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
notify('approvals')
|
|
142
|
+
if (request.sessionId) notify(`session:${request.sessionId}`)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function listPendingApprovals(): ApprovalRequest[] {
|
|
146
|
+
const approvals = loadApprovals() as Record<string, ApprovalRequest>
|
|
147
|
+
return Object.values(approvals)
|
|
148
|
+
.filter(a => a.status === 'pending')
|
|
149
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
150
|
+
}
|