@swarmclawai/swarmclaw 1.3.6 → 1.4.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 +32 -1
- package/package.json +9 -3
- package/src/.env.local +4 -0
- package/src/app/api/.well-known/agent-card/route.ts +46 -0
- package/src/app/api/a2a/route.ts +56 -0
- package/src/app/api/a2a/tasks/[taskId]/status/route.ts +49 -0
- package/src/app/api/chats/[id]/deploy/route.ts +2 -2
- package/src/app/api/openclaw/sync/route.ts +1 -1
- package/src/app/api/swarmfeed/channels/route.ts +14 -0
- package/src/app/api/swarmfeed/posts/route.ts +60 -0
- package/src/app/api/swarmfeed/route.ts +37 -0
- package/src/app/protocols/builder/[templateId]/page.tsx +93 -0
- package/src/app/protocols/page.tsx +16 -7
- package/src/app/swarmfeed/page.tsx +7 -0
- package/src/cli/index.js +19 -0
- package/src/cli/spec.js +8 -0
- package/src/components/agents/agent-avatar.tsx +2 -5
- package/src/components/agents/agent-sheet.tsx +10 -0
- package/src/components/auth/access-key-gate.tsx +25 -0
- package/src/components/layout/sidebar-rail.tsx +52 -0
- package/src/components/protocols/builder/edge-editor.tsx +43 -0
- package/src/components/protocols/builder/edge-types/branch-edge.tsx +33 -0
- package/src/components/protocols/builder/edge-types/default-edge.tsx +18 -0
- package/src/components/protocols/builder/edge-types/index.ts +3 -0
- package/src/components/protocols/builder/edge-types/loop-edge.tsx +19 -0
- package/src/components/protocols/builder/node-inspector.tsx +227 -0
- package/src/components/protocols/builder/node-palette.tsx +97 -0
- package/src/components/protocols/builder/node-types/branch-node.tsx +34 -0
- package/src/components/protocols/builder/node-types/complete-node.tsx +17 -0
- package/src/components/protocols/builder/node-types/for-each-node.tsx +21 -0
- package/src/components/protocols/builder/node-types/index.ts +9 -0
- package/src/components/protocols/builder/node-types/join-node.tsx +18 -0
- package/src/components/protocols/builder/node-types/loop-node.tsx +22 -0
- package/src/components/protocols/builder/node-types/parallel-node.tsx +31 -0
- package/src/components/protocols/builder/node-types/phase-node.tsx +52 -0
- package/src/components/protocols/builder/node-types/subflow-node.tsx +23 -0
- package/src/components/protocols/builder/node-types/swarm-node.tsx +26 -0
- package/src/components/protocols/builder/protocol-builder-canvas.tsx +184 -0
- package/src/components/protocols/builder/run-overlay.tsx +29 -0
- package/src/components/protocols/builder/template-gallery.tsx +53 -0
- package/src/components/protocols/builder/validation-panel.tsx +57 -0
- package/src/components/skills/skills-workspace.tsx +1 -9
- package/src/features/protocols/builder/hooks/index.ts +2 -0
- package/src/features/protocols/builder/hooks/use-canvas-validation.ts +14 -0
- package/src/features/protocols/builder/hooks/use-run-overlay.ts +39 -0
- package/src/features/protocols/builder/hooks/use-template-sync.ts +45 -0
- package/src/features/protocols/builder/protocol-builder-store.ts +233 -0
- package/src/features/protocols/builder/utils/node-position-layout.ts +41 -0
- package/src/features/protocols/builder/utils/nodes-to-template.test.ts +179 -0
- package/src/features/protocols/builder/utils/nodes-to-template.ts +49 -0
- package/src/features/protocols/builder/utils/template-to-nodes.test.ts +314 -0
- package/src/features/protocols/builder/utils/template-to-nodes.ts +169 -0
- package/src/features/protocols/builder/validators/dag-validator.test.ts +150 -0
- package/src/features/protocols/builder/validators/dag-validator.ts +119 -0
- package/src/features/swarmfeed/agent-social-settings.tsx +277 -0
- package/src/features/swarmfeed/compose-post.tsx +139 -0
- package/src/features/swarmfeed/feed-page.tsx +136 -0
- package/src/features/swarmfeed/post-card.tsx +114 -0
- package/src/features/swarmfeed/queries.ts +28 -0
- package/src/lib/a2a/agent-card.ts +61 -0
- package/src/lib/a2a/auth.ts +54 -0
- package/src/lib/a2a/client.ts +133 -0
- package/src/lib/a2a/discovery.ts +116 -0
- package/src/lib/a2a/handlers.ts +176 -0
- package/src/lib/a2a/json-rpc-router.ts +38 -0
- package/src/lib/a2a/types.ts +95 -0
- package/src/lib/app/navigation.ts +1 -0
- package/src/lib/app/view-constants.ts +9 -1
- package/src/lib/providers/anthropic.ts +111 -107
- package/src/lib/providers/openai.ts +146 -142
- package/src/lib/server/agents/main-agent-loop.test.ts +94 -0
- package/src/lib/server/agents/main-agent-loop.ts +377 -41
- package/src/lib/server/chat-execution/chat-execution.ts +12 -7
- package/src/lib/server/extensions.ts +11 -0
- package/src/lib/server/openclaw/sync.ts +4 -4
- package/src/lib/server/protocols/protocol-a2a-delegate.ts +135 -0
- package/src/lib/server/protocols/protocol-normalization.ts +1 -0
- package/src/lib/server/protocols/protocol-step-helpers.test.ts +1 -1
- package/src/lib/server/protocols/protocol-step-helpers.ts +1 -0
- package/src/lib/server/protocols/protocol-step-processors.ts +2 -0
- package/src/lib/server/protocols/protocol-types.ts +1 -0
- package/src/lib/server/session-tools/delegate.ts +151 -77
- package/src/lib/server/storage-auth.ts +10 -2
- package/src/lib/server/storage-normalization.ts +11 -0
- package/src/lib/server/storage.ts +100 -0
- package/src/lib/server/working-state/service.test.ts +2 -3
- package/src/lib/server/working-state/service.ts +37 -6
- package/src/lib/swarmfeed-client.ts +157 -0
- package/src/lib/validation/schemas.ts +1 -1
- package/src/stores/slices/data-slice.ts +3 -0
- package/src/stores/use-approval-store.ts +4 -1
- package/src/types/agent.ts +31 -1
- package/src/types/index.ts +1 -0
- package/src/types/protocol.ts +19 -0
- package/src/types/session.ts +1 -1
- package/src/types/swarmfeed.ts +30 -0
|
@@ -874,6 +874,17 @@ class ExtensionManager {
|
|
|
874
874
|
try {
|
|
875
875
|
const parsed = JSON.parse(fs.readFileSync(EXTENSION_FAILURES, 'utf8')) as Record<string, ExtensionFailureRecord>
|
|
876
876
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {}
|
|
877
|
+
// Prune records older than 7 days
|
|
878
|
+
const maxAgeMs = 7 * 24 * 60 * 60 * 1000
|
|
879
|
+
const now = Date.now()
|
|
880
|
+
let pruned = false
|
|
881
|
+
for (const key of Object.keys(parsed)) {
|
|
882
|
+
if (now - (parsed[key].lastFailedAt || 0) > maxAgeMs) {
|
|
883
|
+
delete parsed[key]
|
|
884
|
+
pruned = true
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
if (pruned) this.writeFailureState(parsed)
|
|
877
888
|
return parsed
|
|
878
889
|
} catch {
|
|
879
890
|
return {}
|
|
@@ -381,7 +381,7 @@ export function syncExtensionsFromOpenClaw(): { imported: number } {
|
|
|
381
381
|
const openclawExtensionDir = path.join(config.workspacePath, 'plugins')
|
|
382
382
|
if (!fs.existsSync(openclawExtensionDir)) return { imported: 0 }
|
|
383
383
|
|
|
384
|
-
const localExtensionDir = path.join(DATA_DIR, '
|
|
384
|
+
const localExtensionDir = path.join(DATA_DIR, 'extensions')
|
|
385
385
|
ensureDir(localExtensionDir)
|
|
386
386
|
|
|
387
387
|
const files = fs.readdirSync(openclawExtensionDir).filter((f) => f.endsWith('.js'))
|
|
@@ -432,7 +432,7 @@ export function setSharedDeviceToken(token: string): void {
|
|
|
432
432
|
|
|
433
433
|
// --- Unified Sync Entry Point ---
|
|
434
434
|
|
|
435
|
-
export type SyncType = 'memory' | 'workspace' | 'schedules' | 'credentials' | '
|
|
435
|
+
export type SyncType = 'memory' | 'workspace' | 'schedules' | 'credentials' | 'extensions'
|
|
436
436
|
|
|
437
437
|
export interface SyncResult {
|
|
438
438
|
type: SyncType
|
|
@@ -467,7 +467,7 @@ export async function runSync(params: {
|
|
|
467
467
|
case 'credentials':
|
|
468
468
|
results.push({ type, action: 'push', result: pushCredentialsToOpenClaw() })
|
|
469
469
|
break
|
|
470
|
-
case '
|
|
470
|
+
case 'extensions':
|
|
471
471
|
// Extensions only pull from OpenClaw
|
|
472
472
|
break
|
|
473
473
|
}
|
|
@@ -492,7 +492,7 @@ export async function runSync(params: {
|
|
|
492
492
|
case 'credentials':
|
|
493
493
|
results.push({ type, action: 'pull', result: await pullCredentialsFromOpenClaw() })
|
|
494
494
|
break
|
|
495
|
-
case '
|
|
495
|
+
case 'extensions':
|
|
496
496
|
results.push({ type, action: 'pull', result: syncExtensionsFromOpenClaw() })
|
|
497
497
|
break
|
|
498
498
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { genId } from '@/lib/id'
|
|
2
|
+
import { log } from '@/lib/server/logger'
|
|
3
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
4
|
+
import { upsertTask } from '@/lib/server/tasks/task-repository'
|
|
5
|
+
import { notify } from '@/lib/server/ws-hub'
|
|
6
|
+
import { callA2AAgent } from '@/lib/a2a/client'
|
|
7
|
+
import { loadExternalAgents } from '@/lib/server/storage'
|
|
8
|
+
import { appendProtocolEvent, persistRun } from '@/lib/server/protocols/protocol-agent-turn'
|
|
9
|
+
import { now } from '@/lib/server/protocols/protocol-types'
|
|
10
|
+
import type { ProtocolRunDeps } from '@/lib/server/protocols/protocol-types'
|
|
11
|
+
import type { ProtocolPhaseDefinition, ProtocolRun, ProtocolRunPhaseState } from '@/types/protocol'
|
|
12
|
+
import type { BoardTask } from '@/types/task'
|
|
13
|
+
|
|
14
|
+
const TAG = 'protocol-a2a-delegate'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Process an a2a_delegate phase: call a remote A2A agent and wait for the result.
|
|
18
|
+
*
|
|
19
|
+
* Follows the same pattern as processDispatchDelegationPhase:
|
|
20
|
+
* 1. Create a BoardTask for tracking (with protocolRunId so wakeProtocolRunFromTaskCompletion fires)
|
|
21
|
+
* 2. Call the remote agent via HTTP
|
|
22
|
+
* 3. Set protocol run to 'waiting'
|
|
23
|
+
* 4. When the HTTP call completes, update the task → wake machinery resumes the run
|
|
24
|
+
*/
|
|
25
|
+
export function processA2ADelegatePhase(
|
|
26
|
+
run: ProtocolRun,
|
|
27
|
+
phase: ProtocolPhaseDefinition,
|
|
28
|
+
deps?: ProtocolRunDeps,
|
|
29
|
+
): ProtocolRun {
|
|
30
|
+
const config = phase.a2aDelegateConfig
|
|
31
|
+
if (!config?.taskName || !config?.taskMessage) {
|
|
32
|
+
appendProtocolEvent(run.id, {
|
|
33
|
+
type: 'failed',
|
|
34
|
+
phaseId: phase.id,
|
|
35
|
+
summary: `a2a_delegate phase "${phase.label}" missing a2aDelegateConfig`,
|
|
36
|
+
}, deps)
|
|
37
|
+
return persistRun({
|
|
38
|
+
...run,
|
|
39
|
+
status: 'failed',
|
|
40
|
+
lastError: `a2a_delegate phase "${phase.label}" missing a2aDelegateConfig`,
|
|
41
|
+
endedAt: run.endedAt || now(deps),
|
|
42
|
+
updatedAt: now(deps),
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Resolve target URL
|
|
47
|
+
let targetUrl = config.targetUrl
|
|
48
|
+
if (!targetUrl && config.targetExternalAgentId) {
|
|
49
|
+
const externalAgents = loadExternalAgents()
|
|
50
|
+
const ea = externalAgents[config.targetExternalAgentId]
|
|
51
|
+
if (ea?.endpoint) {
|
|
52
|
+
targetUrl = ea.endpoint
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!targetUrl) {
|
|
57
|
+
appendProtocolEvent(run.id, {
|
|
58
|
+
type: 'failed',
|
|
59
|
+
phaseId: phase.id,
|
|
60
|
+
summary: `a2a_delegate phase "${phase.label}" — no target URL resolved`,
|
|
61
|
+
}, deps)
|
|
62
|
+
return persistRun({
|
|
63
|
+
...run,
|
|
64
|
+
status: 'failed',
|
|
65
|
+
lastError: `a2a_delegate phase "${phase.label}" — could not resolve target A2A agent URL`,
|
|
66
|
+
endedAt: run.endedAt || now(deps),
|
|
67
|
+
updatedAt: now(deps),
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Create a BoardTask for tracking
|
|
72
|
+
const taskId = genId()
|
|
73
|
+
const taskData: BoardTask = {
|
|
74
|
+
id: taskId,
|
|
75
|
+
title: `A2A: ${config.taskName}`,
|
|
76
|
+
description: config.taskMessage,
|
|
77
|
+
status: 'queued',
|
|
78
|
+
agentId: run.facilitatorAgentId || run.participantAgentIds?.[0] || '',
|
|
79
|
+
protocolRunId: run.id,
|
|
80
|
+
sourceType: 'delegation',
|
|
81
|
+
externalSource: { source: 'a2a', id: taskId },
|
|
82
|
+
queuedAt: now(deps),
|
|
83
|
+
createdAt: now(deps),
|
|
84
|
+
updatedAt: now(deps),
|
|
85
|
+
}
|
|
86
|
+
upsertTask(taskId, taskData)
|
|
87
|
+
notify('tasks')
|
|
88
|
+
|
|
89
|
+
appendProtocolEvent(run.id, {
|
|
90
|
+
type: 'delegation_dispatched',
|
|
91
|
+
summary: `Dispatched A2A delegation to ${targetUrl}: ${config.taskName}`,
|
|
92
|
+
phaseId: phase.id,
|
|
93
|
+
taskId,
|
|
94
|
+
}, deps)
|
|
95
|
+
|
|
96
|
+
log.info(TAG, `Calling remote A2A agent at ${targetUrl}`, { taskName: config.taskName, taskId })
|
|
97
|
+
|
|
98
|
+
// Fire the HTTP call asynchronously — when it completes, update the task
|
|
99
|
+
// The existing wakeProtocolRunFromTaskCompletion machinery will resume the run
|
|
100
|
+
const resolvedUrl = targetUrl
|
|
101
|
+
callA2AAgent(resolvedUrl, 'executeTask', {
|
|
102
|
+
taskId,
|
|
103
|
+
taskName: config.taskName,
|
|
104
|
+
message: config.taskMessage,
|
|
105
|
+
}, {
|
|
106
|
+
timeout: config.timeoutMs ?? 300_000,
|
|
107
|
+
credentialId: config.credentialId,
|
|
108
|
+
}).then(result => {
|
|
109
|
+
const resultStr = typeof result === 'string' ? result : JSON.stringify(result)
|
|
110
|
+
upsertTask(taskId, { ...taskData, status: 'completed', result: resultStr, updatedAt: Date.now(), completedAt: Date.now() })
|
|
111
|
+
notify('tasks')
|
|
112
|
+
log.info(TAG, `A2A delegation completed for task ${taskId}`)
|
|
113
|
+
// Dynamic import to break circular dependency (protocol-step-processors → protocol-a2a-delegate → protocol-run-lifecycle → protocol-step-processors)
|
|
114
|
+
import('@/lib/server/protocols/protocol-run-lifecycle').then(m => m.wakeProtocolRunFromTaskCompletion(taskId))
|
|
115
|
+
}).catch(err => {
|
|
116
|
+
log.error(TAG, `A2A delegation failed for task ${taskId}: ${errorMessage(err)}`)
|
|
117
|
+
if (config.onFailure === 'advance_with_warning') {
|
|
118
|
+
upsertTask(taskId, { ...taskData, status: 'completed', result: `A2A delegation failed: ${errorMessage(err)}`, error: errorMessage(err), updatedAt: Date.now(), completedAt: Date.now() })
|
|
119
|
+
} else {
|
|
120
|
+
upsertTask(taskId, { ...taskData, status: 'failed', error: errorMessage(err), updatedAt: Date.now() })
|
|
121
|
+
}
|
|
122
|
+
notify('tasks')
|
|
123
|
+
import('@/lib/server/protocols/protocol-run-lifecycle').then(m => m.wakeProtocolRunFromTaskCompletion(taskId))
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const createdTaskIds = [...(run.createdTaskIds || []), taskId]
|
|
127
|
+
return persistRun({
|
|
128
|
+
...run,
|
|
129
|
+
status: 'waiting',
|
|
130
|
+
waitingReason: `Waiting for A2A delegation: ${config.taskName}`,
|
|
131
|
+
createdTaskIds,
|
|
132
|
+
phaseState: { ...(run.phaseState || { phaseId: phase.id }), dispatchedTaskId: taskId } as ProtocolRunPhaseState,
|
|
133
|
+
updatedAt: now(deps),
|
|
134
|
+
})
|
|
135
|
+
}
|
|
@@ -48,7 +48,7 @@ describe('protocol-step-helpers', () => {
|
|
|
48
48
|
const kinds = [
|
|
49
49
|
'present', 'collect_independent_inputs', 'round_robin',
|
|
50
50
|
'compare', 'decide', 'summarize', 'emit_tasks', 'wait',
|
|
51
|
-
'dispatch_task', 'dispatch_delegation',
|
|
51
|
+
'dispatch_task', 'dispatch_delegation', 'a2a_delegate',
|
|
52
52
|
]
|
|
53
53
|
for (const kind of kinds) {
|
|
54
54
|
const step = { id: `step-${kind}`, kind, label: kind } as never
|
|
@@ -58,6 +58,7 @@ export function phaseFromStep(step: ProtocolStepDefinition): ProtocolPhaseDefini
|
|
|
58
58
|
completionCriteria: step.completionCriteria || null,
|
|
59
59
|
taskConfig: step.taskConfig || null,
|
|
60
60
|
delegationConfig: step.delegationConfig || null,
|
|
61
|
+
a2aDelegateConfig: step.a2aDelegateConfig || null,
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -23,6 +23,7 @@ import type * as ProtocolRunLifecycle from '@/lib/server/protocols/protocol-run-
|
|
|
23
23
|
import { processForEachStep } from '@/lib/server/protocols/protocol-foreach'
|
|
24
24
|
import { processSubflowStep } from '@/lib/server/protocols/protocol-subflow'
|
|
25
25
|
import { processSwarmStep } from '@/lib/server/protocols/protocol-swarm'
|
|
26
|
+
import { processA2ADelegatePhase } from '@/lib/server/protocols/protocol-a2a-delegate'
|
|
26
27
|
import { findRunStep } from '@/lib/server/protocols/protocol-normalization'
|
|
27
28
|
import {
|
|
28
29
|
appendProtocolEvent,
|
|
@@ -708,6 +709,7 @@ export async function stepProtocolRun(run: ProtocolRun, deps?: ProtocolRunDeps):
|
|
|
708
709
|
if (phase.kind === 'emit_tasks') return processEmitTasksPhase(started, phase, deps)
|
|
709
710
|
if (phase.kind === 'dispatch_task') return processDispatchTaskPhase(started, phase, deps)
|
|
710
711
|
if (phase.kind === 'dispatch_delegation') return processDispatchDelegationPhase(started, phase, deps)
|
|
712
|
+
if (phase.kind === 'a2a_delegate') return processA2ADelegatePhase(started, phase, deps)
|
|
711
713
|
return processWaitPhase(started, phase, deps)
|
|
712
714
|
}
|
|
713
715
|
if (step.kind === 'branch') return processBranchStep(run, step, deps)
|