@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.
Files changed (96) hide show
  1. package/README.md +32 -1
  2. package/package.json +9 -3
  3. package/src/.env.local +4 -0
  4. package/src/app/api/.well-known/agent-card/route.ts +46 -0
  5. package/src/app/api/a2a/route.ts +56 -0
  6. package/src/app/api/a2a/tasks/[taskId]/status/route.ts +49 -0
  7. package/src/app/api/chats/[id]/deploy/route.ts +2 -2
  8. package/src/app/api/openclaw/sync/route.ts +1 -1
  9. package/src/app/api/swarmfeed/channels/route.ts +14 -0
  10. package/src/app/api/swarmfeed/posts/route.ts +60 -0
  11. package/src/app/api/swarmfeed/route.ts +37 -0
  12. package/src/app/protocols/builder/[templateId]/page.tsx +93 -0
  13. package/src/app/protocols/page.tsx +16 -7
  14. package/src/app/swarmfeed/page.tsx +7 -0
  15. package/src/cli/index.js +19 -0
  16. package/src/cli/spec.js +8 -0
  17. package/src/components/agents/agent-avatar.tsx +2 -5
  18. package/src/components/agents/agent-sheet.tsx +10 -0
  19. package/src/components/auth/access-key-gate.tsx +25 -0
  20. package/src/components/layout/sidebar-rail.tsx +52 -0
  21. package/src/components/protocols/builder/edge-editor.tsx +43 -0
  22. package/src/components/protocols/builder/edge-types/branch-edge.tsx +33 -0
  23. package/src/components/protocols/builder/edge-types/default-edge.tsx +18 -0
  24. package/src/components/protocols/builder/edge-types/index.ts +3 -0
  25. package/src/components/protocols/builder/edge-types/loop-edge.tsx +19 -0
  26. package/src/components/protocols/builder/node-inspector.tsx +227 -0
  27. package/src/components/protocols/builder/node-palette.tsx +97 -0
  28. package/src/components/protocols/builder/node-types/branch-node.tsx +34 -0
  29. package/src/components/protocols/builder/node-types/complete-node.tsx +17 -0
  30. package/src/components/protocols/builder/node-types/for-each-node.tsx +21 -0
  31. package/src/components/protocols/builder/node-types/index.ts +9 -0
  32. package/src/components/protocols/builder/node-types/join-node.tsx +18 -0
  33. package/src/components/protocols/builder/node-types/loop-node.tsx +22 -0
  34. package/src/components/protocols/builder/node-types/parallel-node.tsx +31 -0
  35. package/src/components/protocols/builder/node-types/phase-node.tsx +52 -0
  36. package/src/components/protocols/builder/node-types/subflow-node.tsx +23 -0
  37. package/src/components/protocols/builder/node-types/swarm-node.tsx +26 -0
  38. package/src/components/protocols/builder/protocol-builder-canvas.tsx +184 -0
  39. package/src/components/protocols/builder/run-overlay.tsx +29 -0
  40. package/src/components/protocols/builder/template-gallery.tsx +53 -0
  41. package/src/components/protocols/builder/validation-panel.tsx +57 -0
  42. package/src/components/skills/skills-workspace.tsx +1 -9
  43. package/src/features/protocols/builder/hooks/index.ts +2 -0
  44. package/src/features/protocols/builder/hooks/use-canvas-validation.ts +14 -0
  45. package/src/features/protocols/builder/hooks/use-run-overlay.ts +39 -0
  46. package/src/features/protocols/builder/hooks/use-template-sync.ts +45 -0
  47. package/src/features/protocols/builder/protocol-builder-store.ts +233 -0
  48. package/src/features/protocols/builder/utils/node-position-layout.ts +41 -0
  49. package/src/features/protocols/builder/utils/nodes-to-template.test.ts +179 -0
  50. package/src/features/protocols/builder/utils/nodes-to-template.ts +49 -0
  51. package/src/features/protocols/builder/utils/template-to-nodes.test.ts +314 -0
  52. package/src/features/protocols/builder/utils/template-to-nodes.ts +169 -0
  53. package/src/features/protocols/builder/validators/dag-validator.test.ts +150 -0
  54. package/src/features/protocols/builder/validators/dag-validator.ts +119 -0
  55. package/src/features/swarmfeed/agent-social-settings.tsx +277 -0
  56. package/src/features/swarmfeed/compose-post.tsx +139 -0
  57. package/src/features/swarmfeed/feed-page.tsx +136 -0
  58. package/src/features/swarmfeed/post-card.tsx +114 -0
  59. package/src/features/swarmfeed/queries.ts +28 -0
  60. package/src/lib/a2a/agent-card.ts +61 -0
  61. package/src/lib/a2a/auth.ts +54 -0
  62. package/src/lib/a2a/client.ts +133 -0
  63. package/src/lib/a2a/discovery.ts +116 -0
  64. package/src/lib/a2a/handlers.ts +176 -0
  65. package/src/lib/a2a/json-rpc-router.ts +38 -0
  66. package/src/lib/a2a/types.ts +95 -0
  67. package/src/lib/app/navigation.ts +1 -0
  68. package/src/lib/app/view-constants.ts +9 -1
  69. package/src/lib/providers/anthropic.ts +111 -107
  70. package/src/lib/providers/openai.ts +146 -142
  71. package/src/lib/server/agents/main-agent-loop.test.ts +94 -0
  72. package/src/lib/server/agents/main-agent-loop.ts +377 -41
  73. package/src/lib/server/chat-execution/chat-execution.ts +12 -7
  74. package/src/lib/server/extensions.ts +11 -0
  75. package/src/lib/server/openclaw/sync.ts +4 -4
  76. package/src/lib/server/protocols/protocol-a2a-delegate.ts +135 -0
  77. package/src/lib/server/protocols/protocol-normalization.ts +1 -0
  78. package/src/lib/server/protocols/protocol-step-helpers.test.ts +1 -1
  79. package/src/lib/server/protocols/protocol-step-helpers.ts +1 -0
  80. package/src/lib/server/protocols/protocol-step-processors.ts +2 -0
  81. package/src/lib/server/protocols/protocol-types.ts +1 -0
  82. package/src/lib/server/session-tools/delegate.ts +151 -77
  83. package/src/lib/server/storage-auth.ts +10 -2
  84. package/src/lib/server/storage-normalization.ts +11 -0
  85. package/src/lib/server/storage.ts +100 -0
  86. package/src/lib/server/working-state/service.test.ts +2 -3
  87. package/src/lib/server/working-state/service.ts +37 -6
  88. package/src/lib/swarmfeed-client.ts +157 -0
  89. package/src/lib/validation/schemas.ts +1 -1
  90. package/src/stores/slices/data-slice.ts +3 -0
  91. package/src/stores/use-approval-store.ts +4 -1
  92. package/src/types/agent.ts +31 -1
  93. package/src/types/index.ts +1 -0
  94. package/src/types/protocol.ts +19 -0
  95. package/src/types/session.ts +1 -1
  96. package/src/types/swarmfeed.ts +30 -0
@@ -244,15 +244,46 @@ export function syncWorkingStateFromMainLoopState(input: {
244
244
  status?: WorkingStateStatus | null
245
245
  nextAction?: string | null
246
246
  planSteps?: string[]
247
+ completedPlanSteps?: string[]
247
248
  blockers?: Array<{ summary: string; kind?: WorkingBlocker['kind'] | null }>
248
249
  facts?: string[]
249
250
  }): SessionWorkingState {
250
- const planSteps = Array.isArray(input.planSteps)
251
- ? input.planSteps.map((step, index) => ({
252
- text: step,
253
- status: index === 0 && input.status !== 'completed' ? 'active' : (input.status === 'completed' ? 'resolved' : 'resolved'),
254
- } satisfies WorkingPlanStepPatch))
255
- : undefined
251
+ const planSteps = (() => {
252
+ const activeSteps = Array.isArray(input.planSteps)
253
+ ? input.planSteps
254
+ .map((step) => cleanText(step, 240))
255
+ .filter((step): step is string => Boolean(step))
256
+ : []
257
+ const completedSteps = Array.isArray(input.completedPlanSteps)
258
+ ? input.completedPlanSteps
259
+ .map((step) => cleanText(step, 240))
260
+ .filter((step): step is string => Boolean(step))
261
+ : []
262
+ const out: WorkingPlanStepPatch[] = []
263
+ const seen = new Set<string>()
264
+
265
+ for (const [index, step] of activeSteps.entries()) {
266
+ const key = step.toLowerCase()
267
+ if (seen.has(key)) continue
268
+ seen.add(key)
269
+ out.push({
270
+ text: step,
271
+ status: index === 0 && input.status !== 'completed' ? 'active' : 'resolved',
272
+ } satisfies WorkingPlanStepPatch)
273
+ }
274
+
275
+ for (const step of completedSteps) {
276
+ const key = step.toLowerCase()
277
+ if (seen.has(key)) continue
278
+ seen.add(key)
279
+ out.push({
280
+ text: step,
281
+ status: 'resolved',
282
+ } satisfies WorkingPlanStepPatch)
283
+ }
284
+
285
+ return out.length > 0 ? out : undefined
286
+ })()
256
287
  return applyWorkingStatePatch(input.sessionId, {
257
288
  objective: cleanMultiline(input.goal, 900) || undefined,
258
289
  summary: cleanMultiline(input.summary, 600) || undefined,
@@ -0,0 +1,157 @@
1
+ import { hmrSingleton } from '@/lib/shared-utils'
2
+ import type { SwarmFeedPost, SwarmFeedChannel, CreatePostInput, FeedType } from '@/types/swarmfeed'
3
+
4
+ interface SwarmFeedConfig {
5
+ apiUrl: string
6
+ }
7
+
8
+ const config = hmrSingleton<SwarmFeedConfig>('swarmfeed_config', () => ({
9
+ apiUrl: process.env.SWARMFEED_API_URL || 'http://localhost:3700',
10
+ }))
11
+
12
+ /**
13
+ * Internal fetch helper for SwarmFeed API.
14
+ * @param agentApiKey - Per-agent API key (sf_live_*) for authenticated requests. Omit for public endpoints.
15
+ */
16
+ async function sfFetch<T>(path: string, agentApiKey?: string, init?: RequestInit): Promise<T> {
17
+ const url = `${config.apiUrl}${path}`
18
+ const headers: Record<string, string> = {
19
+ 'Content-Type': 'application/json',
20
+ ...(agentApiKey ? { Authorization: `Bearer ${agentApiKey}` } : {}),
21
+ }
22
+ const res = await fetch(url, {
23
+ ...init,
24
+ headers: { ...headers, ...(init?.headers as Record<string, string> | undefined) },
25
+ })
26
+ if (!res.ok) {
27
+ const text = await res.text().catch(() => 'Unknown error')
28
+ throw new Error(`SwarmFeed API error ${res.status}: ${text}`)
29
+ }
30
+ return res.json() as Promise<T>
31
+ }
32
+
33
+ // --- Feed (public, no auth needed) ---
34
+
35
+ const FEED_TYPE_URL: Record<FeedType, string> = {
36
+ for_you: 'for-you',
37
+ following: 'following',
38
+ channel: 'channel',
39
+ trending: 'trending',
40
+ }
41
+
42
+ export async function getFeed(
43
+ type: FeedType,
44
+ params?: { channelId?: string; cursor?: string; limit?: number },
45
+ agentApiKey?: string,
46
+ ): Promise<{ posts: SwarmFeedPost[]; nextCursor?: string }> {
47
+ const urlSegment = FEED_TYPE_URL[type] ?? type
48
+
49
+ // Channel feed uses path param: /feed/channel/:channelId
50
+ if (type === 'channel' && params?.channelId) {
51
+ const searchParams = new URLSearchParams()
52
+ if (params.cursor) searchParams.set('cursor', params.cursor)
53
+ if (params.limit) searchParams.set('limit', String(params.limit))
54
+ const qs = searchParams.toString()
55
+ return sfFetch(`/api/v1/feed/channel/${params.channelId}${qs ? `?${qs}` : ''}`, agentApiKey)
56
+ }
57
+
58
+ const searchParams = new URLSearchParams()
59
+ if (params?.cursor) searchParams.set('cursor', params.cursor)
60
+ if (params?.limit) searchParams.set('limit', String(params.limit))
61
+ const qs = searchParams.toString()
62
+ return sfFetch(`/api/v1/feed/${urlSegment}${qs ? `?${qs}` : ''}`, agentApiKey)
63
+ }
64
+
65
+ // --- Posts (auth required for writes) ---
66
+
67
+ export async function createPost(agentApiKey: string, input: CreatePostInput): Promise<SwarmFeedPost> {
68
+ return sfFetch('/api/v1/posts', agentApiKey, {
69
+ method: 'POST',
70
+ body: JSON.stringify({
71
+ content: input.content,
72
+ channelId: input.channelId,
73
+ parentId: input.parentId,
74
+ }),
75
+ })
76
+ }
77
+
78
+ // --- Reactions (auth required) ---
79
+
80
+ export async function likePost(agentApiKey: string, postId: string): Promise<unknown> {
81
+ return sfFetch(`/api/v1/posts/${postId}/like`, agentApiKey, {
82
+ method: 'POST',
83
+ body: JSON.stringify({ reactionType: 'like' }),
84
+ })
85
+ }
86
+
87
+ export async function repostPost(agentApiKey: string, postId: string): Promise<unknown> {
88
+ return sfFetch(`/api/v1/posts/${postId}/like`, agentApiKey, {
89
+ method: 'POST',
90
+ body: JSON.stringify({ reactionType: 'repost' }),
91
+ })
92
+ }
93
+
94
+ // --- Channels (public reads) ---
95
+
96
+ export async function getChannels(): Promise<SwarmFeedChannel[]> {
97
+ const result = await sfFetch<{ channels: SwarmFeedChannel[] }>('/api/v1/channels')
98
+ return result.channels
99
+ }
100
+
101
+ // --- Agent Registration ---
102
+
103
+ interface RegisterResult {
104
+ agentId: string
105
+ apiKey: string
106
+ challenge: string
107
+ challengeExpiresAt: string
108
+ }
109
+
110
+ /**
111
+ * Register a SwarmClaw agent on SwarmFeed.
112
+ * Generates an Ed25519 keypair, registers, verifies, and returns the API key.
113
+ */
114
+ export async function registerAgent(agent: {
115
+ name: string
116
+ description?: string
117
+ framework?: string
118
+ model?: string
119
+ avatar?: string
120
+ bio?: string
121
+ }): Promise<{ agentId: string; apiKey: string }> {
122
+ // Dynamic import tweetnacl (available in SwarmClaw's deps)
123
+ const naclModule = await import('tweetnacl')
124
+ const nacl = naclModule.default ?? naclModule
125
+ const keypair = nacl.sign.keyPair()
126
+ const publicKeyHex = Buffer.from(keypair.publicKey).toString('hex')
127
+
128
+ // Step 1: Register
129
+ const reg = await sfFetch<RegisterResult>('/api/v1/register', undefined, {
130
+ method: 'POST',
131
+ body: JSON.stringify({
132
+ publicKey: publicKeyHex,
133
+ name: agent.name,
134
+ description: agent.description,
135
+ framework: agent.framework,
136
+ modelName: agent.model,
137
+ avatarUrl: agent.avatar,
138
+ bio: agent.bio,
139
+ }),
140
+ })
141
+
142
+ // Step 2: Sign the challenge and verify
143
+ const messageBytes = new TextEncoder().encode(reg.challenge)
144
+ const signature = nacl.sign.detached(messageBytes, keypair.secretKey)
145
+ const signatureHex = Buffer.from(signature).toString('hex')
146
+
147
+ await sfFetch('/api/v1/register/verify', undefined, {
148
+ method: 'POST',
149
+ body: JSON.stringify({
150
+ publicKey: publicKeyHex,
151
+ challenge: reg.challenge,
152
+ signature: signatureHex,
153
+ }),
154
+ })
155
+
156
+ return { agentId: reg.agentId, apiKey: reg.apiKey }
157
+ }
@@ -147,7 +147,7 @@ export const ConnectorCreateSchema = z.object({
147
147
  export const ExternalAgentRegisterSchema = z.object({
148
148
  id: z.string().optional(),
149
149
  name: z.string().min(1, 'External agent name is required'),
150
- sourceType: z.enum(['codex', 'claude', 'opencode', 'openclaw', 'custom']).default('custom'),
150
+ sourceType: z.enum(['codex', 'claude', 'opencode', 'openclaw', 'custom', 'a2a']).default('custom'),
151
151
  status: z.enum(['online', 'idle', 'offline', 'stale']).optional().default('online'),
152
152
  provider: z.string().nullable().optional().default(null),
153
153
  model: z.string().nullable().optional().default(null),
@@ -159,6 +159,7 @@ export const createDataSlice: StateCreator<AppState, [], [], DataSlice> = (set,
159
159
  const notifications = get().notifications.map((n) =>
160
160
  n.id === id ? { ...n, read: true } : n,
161
161
  )
162
+ invalidateFingerprint('notifications')
162
163
  set({
163
164
  notifications,
164
165
  unreadNotificationCount: notifications.filter((n) => !n.read).length,
@@ -174,6 +175,7 @@ export const createDataSlice: StateCreator<AppState, [], [], DataSlice> = (set,
174
175
  if (!unreadIds.length) return
175
176
  const originalNotifications = get().notifications
176
177
  const notifications = originalNotifications.map((n) => ({ ...n, read: true }))
178
+ invalidateFingerprint('notifications')
177
179
  set({ notifications, unreadNotificationCount: 0 })
178
180
  try {
179
181
  await Promise.all(unreadIds.map((id) => api('PUT', `/notifications/${id}`, { read: true })))
@@ -185,6 +187,7 @@ export const createDataSlice: StateCreator<AppState, [], [], DataSlice> = (set,
185
187
  clearReadNotifications: async () => {
186
188
  const original = get().notifications
187
189
  const kept = original.filter((n) => !n.read)
190
+ invalidateFingerprint('notifications')
188
191
  set({ notifications: kept, unreadNotificationCount: kept.length })
189
192
  try {
190
193
  await api('DELETE', '/notifications')
@@ -4,6 +4,7 @@ import { create } from 'zustand'
4
4
  import type { PendingExecApproval, ExecApprovalDecision } from '@/types'
5
5
  import { api } from '@/lib/app/api-client'
6
6
  import { errorMessage } from '@/lib/shared-utils'
7
+ import { setIfChanged, invalidateFingerprint } from './set-if-changed'
7
8
 
8
9
  interface ApprovalState {
9
10
  approvals: Record<string, PendingExecApproval>
@@ -20,10 +21,12 @@ export const useApprovalStore = create<ApprovalState>((set) => ({
20
21
  resolvedIds: new Set<string>(),
21
22
 
22
23
  addApproval: (approval) => {
24
+ invalidateFingerprint('approvals')
23
25
  set((s) => ({ approvals: { ...s.approvals, [approval.id]: approval } }))
24
26
  },
25
27
 
26
28
  removeApproval: (id) => {
29
+ invalidateFingerprint('approvals')
27
30
  set((s) => {
28
31
  const next = { ...s.approvals }
29
32
  delete next[id]
@@ -91,7 +94,7 @@ export const useApprovalStore = create<ApprovalState>((set) => ({
91
94
  const result = await api<PendingExecApproval[]>('GET', '/openclaw/approvals')
92
95
  const approvals: Record<string, PendingExecApproval> = {}
93
96
  for (const a of result) approvals[a.id] = a
94
- set({ approvals, resolvedIds: new Set<string>() })
97
+ setIfChanged<ApprovalState>(set, 'approvals', approvals)
95
98
  } catch {
96
99
  // ignore — gateway may be offline
97
100
  }
@@ -3,6 +3,17 @@ import type { SessionResetMode, IdentityContinuityState } from './session'
3
3
  import type { SkillAllowlistMode } from './skill'
4
4
  import type { DreamConfig } from './dream'
5
5
 
6
+ // --- SwarmFeed Heartbeat ---
7
+
8
+ export interface SwarmFeedHeartbeatConfig {
9
+ enabled: boolean
10
+ browseFeed: boolean
11
+ postFrequency: 'every_cycle' | 'daily' | 'on_task_completion' | 'manual_only'
12
+ autoReply: boolean
13
+ autoFollow: boolean
14
+ channelsToMonitor: string[]
15
+ }
16
+
6
17
  // --- Agent / Delegation ---
7
18
 
8
19
  export type AgentRole = 'worker' | 'coordinator'
@@ -180,6 +191,18 @@ export interface Agent {
180
191
  orchestratorLastWakeAt?: number | null
181
192
  orchestratorCycleCount?: number
182
193
 
194
+ // SwarmFeed (social network integration)
195
+ swarmfeedEnabled?: boolean
196
+ swarmfeedJoinedAt?: number | null
197
+ swarmfeedBio?: string | null
198
+ swarmfeedPinnedPostId?: string | null
199
+ swarmfeedAutoPost?: boolean
200
+ swarmfeedAutoPostChannels?: string[]
201
+ swarmfeedApiKey?: string | null
202
+ swarmfeedAgentId?: string | null
203
+ origin?: 'swarmdock' | 'swarmfeed' | 'swarmclaw' | 'external'
204
+ swarmfeedHeartbeat?: SwarmFeedHeartbeatConfig | null
205
+
183
206
  createdAt: number
184
207
  updatedAt: number
185
208
  }
@@ -246,7 +269,7 @@ export interface AgentPackManifest {
246
269
 
247
270
  // --- External Agents ---
248
271
 
249
- export type ExternalAgentSourceType = 'codex' | 'claude' | 'opencode' | 'openclaw' | 'custom'
272
+ export type ExternalAgentSourceType = 'codex' | 'claude' | 'opencode' | 'openclaw' | 'custom' | 'a2a'
250
273
  export type ExternalAgentStatus = 'online' | 'idle' | 'offline' | 'stale'
251
274
 
252
275
  export interface ExternalAgentRuntime {
@@ -274,6 +297,13 @@ export interface ExternalAgentRuntime {
274
297
  outputTokens?: number
275
298
  totalTokens?: number
276
299
  } | null
300
+ a2aCard?: {
301
+ protocolVersion?: string | null
302
+ apiEndpoint?: string | null
303
+ capabilities?: Array<{ name: string; methods?: string[]; description?: string | null }>
304
+ supportsStreaming?: boolean
305
+ supportsAsync?: boolean
306
+ } | null
277
307
  lastHeartbeatAt?: number | null
278
308
  lastSeenAt?: number | null
279
309
  createdAt: number
@@ -16,3 +16,4 @@ export * from './misc'
16
16
  export * from './goal'
17
17
  export * from './swarmdock'
18
18
  export * from './dream'
19
+ export * from './swarmfeed'
@@ -16,6 +16,7 @@ export type ProtocolPhaseKind =
16
16
  | 'wait'
17
17
  | 'dispatch_task'
18
18
  | 'dispatch_delegation'
19
+ | 'a2a_delegate'
19
20
 
20
21
  export interface ProtocolPhaseDefinition {
21
22
  id: string
@@ -26,6 +27,15 @@ export interface ProtocolPhaseDefinition {
26
27
  completionCriteria?: string | null
27
28
  taskConfig?: { agentId?: string; title: string; description: string } | null
28
29
  delegationConfig?: { agentId: string; message: string } | null
30
+ a2aDelegateConfig?: {
31
+ targetUrl?: string | null
32
+ targetExternalAgentId?: string | null
33
+ taskName: string
34
+ taskMessage: string
35
+ timeoutMs?: number | null
36
+ credentialId?: string | null
37
+ onFailure?: 'fail' | 'advance_with_warning'
38
+ } | null
29
39
  }
30
40
 
31
41
  export type ProtocolConditionDefinition =
@@ -90,6 +100,15 @@ export interface ProtocolStepDefinition {
90
100
  completionCriteria?: string | null
91
101
  taskConfig?: { agentId?: string; title: string; description: string } | null
92
102
  delegationConfig?: { agentId: string; message: string } | null
103
+ a2aDelegateConfig?: {
104
+ targetUrl?: string | null
105
+ targetExternalAgentId?: string | null
106
+ taskName: string
107
+ taskMessage: string
108
+ timeoutMs?: number | null
109
+ credentialId?: string | null
110
+ onFailure?: 'fail' | 'advance_with_warning'
111
+ } | null
93
112
  nextStepId?: string | null
94
113
  branchCases?: ProtocolBranchCase[]
95
114
  defaultNextStepId?: string | null
@@ -214,4 +214,4 @@ export type SessionTool =
214
214
  | 'crawl'
215
215
 
216
216
  export type SessionType = 'human'
217
- export type AppView = 'home' | 'agents' | 'org_chart' | 'inbox' | 'chatrooms' | 'protocols' | 'schedules' | 'memory' | 'tasks' | 'secrets' | 'wallets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'extensions' | 'usage' | 'runs' | 'autonomy' | 'logs' | 'settings' | 'projects' | 'activity'
217
+ export type AppView = 'home' | 'agents' | 'org_chart' | 'inbox' | 'chatrooms' | 'protocols' | 'schedules' | 'memory' | 'tasks' | 'secrets' | 'wallets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'extensions' | 'usage' | 'runs' | 'autonomy' | 'logs' | 'settings' | 'projects' | 'activity' | 'swarmfeed'
@@ -0,0 +1,30 @@
1
+ export interface SwarmFeedPost {
2
+ id: string
3
+ agentId: string
4
+ content: string
5
+ channelId?: string
6
+ parentId?: string
7
+ likeCount: number
8
+ replyCount: number
9
+ repostCount: number
10
+ bookmarkCount: number
11
+ createdAt: string
12
+ agent?: { id: string; name: string; avatar?: string }
13
+ }
14
+
15
+ export interface SwarmFeedChannel {
16
+ id: string
17
+ handle: string
18
+ displayName: string
19
+ description?: string
20
+ memberCount: number
21
+ postCount: number
22
+ }
23
+
24
+ export interface CreatePostInput {
25
+ content: string
26
+ channelId?: string
27
+ parentId?: string
28
+ }
29
+
30
+ export type FeedType = 'for_you' | 'following' | 'channel' | 'trending'