@swarmclawai/swarmclaw 1.5.71 → 1.6.1

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 CHANGED
@@ -399,6 +399,27 @@ Operational docs: https://swarmclaw.ai/docs/observability
399
399
 
400
400
  ## Releases
401
401
 
402
+ ### v1.6.1 Highlights
403
+
404
+ Follow-up release for v1.6 with workflow starts, safer metadata handling, A2A discovery polish, and [#61](https://github.com/swarmclawai/swarmclaw/pull/61) by [@latentwill](https://github.com/latentwill). Thanks latentwill!
405
+
406
+ - **Mission and protocol templates for real work.** New starter paths cover codebase review sprints, research bureau scans, content studio cycles, release readiness panels, synthesis panels, and builder review loops.
407
+ - **Home launchpad paths.** First-run users can choose a self-hosted assistant, visual workflow, or autonomous mission path, with quality actions still one click away.
408
+ - **A2A discovery is easier to integrate.** The canonical `/.well-known/agent-card.json` endpoint now works alongside the legacy API route and hides disabled or trashed agents from public discovery.
409
+ - **Internal metadata stripping is safer.** Side-channel JSON is removed with balanced-object parsing and zod validation so nested payloads are scrubbed without deleting ordinary user JSON.
410
+ - **Browser smoke gate restored.** `npm run test:e2e` now runs a Playwright smoke against health, A2A discovery, `/home`, and `/quality`, either against a live URL or a temporary local dev server.
411
+ - **OpenCode CLI hang fixed.** OpenCode CLI delegation no longer keeps an inherited stdin pipe open, preventing hangs in non-interactive runs.
412
+
413
+ ### v1.6.0 Highlights
414
+
415
+ Operator Quality Center release for builders running autonomous agents in production-like workflows.
416
+
417
+ - **New Quality workspace.** `/quality` brings run health, failed/running counts, pending approvals, latest eval scores, and attention shortcuts into one operator surface.
418
+ - **Eval Lab and Approval Desk.** Existing eval and approval APIs are now exposed through a practical UI for running scenarios/suites, reviewing score evidence, and approving or denying human-loop/tool/connector/skill requests.
419
+ - **Run Review upgrades.** The run history now has source filtering and search across run id, source, errors, results, and ownership fields while keeping the existing replay/evidence sheet.
420
+ - **Release-ready mission templates.** New templates cover Release Candidate QA, Agent Cost Audit, Connector Smoke Test, Failed Run Triage, and Weekly Agent Quality Report using the existing mission budget/report model.
421
+ - **Home launchpad quality actions.** First-run users can jump straight to evals, approvals, failed runs, and release QA missions from the operational launchpad.
422
+
402
423
  ### v1.5.71 Highlights
403
424
 
404
425
  Fast-follow release for [#60](https://github.com/swarmclawai/swarmclaw/pull/60) by [@borislavnnikolov](https://github.com/borislavnnikolov). Thanks Borislav!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmclawai/swarmclaw",
3
- "version": "1.5.71",
3
+ "version": "1.6.1",
4
4
  "description": "Build and run autonomous AI agents with OpenClaw, Hermes, multiple model providers, orchestration, delegation, memory, skills, schedules, and chat connectors.",
5
5
  "main": "electron-dist/main.js",
6
6
  "license": "MIT",
@@ -87,9 +87,9 @@
87
87
  "test:cli": "node --test src/cli/*.test.js bin/*.test.js scripts/postinstall.test.mjs scripts/run-next-build.test.mjs scripts/run-next-typegen.test.mjs",
88
88
  "test:setup": "tsx --test src/app/api/setup/check-provider/route.test.ts src/lib/server/provider-model-discovery.test.ts src/components/auth/setup-wizard/utils.test.ts src/components/auth/setup-wizard/types.test.ts src/hooks/setup-done-detection.test.ts src/lib/setup-defaults.test.ts src/lib/server/storage-auth.test.ts src/lib/server/storage-auth-docker.test.ts",
89
89
  "test:openclaw": "tsx --test src/lib/openclaw/openclaw-agent-id.test.ts src/lib/openclaw/openclaw-endpoint.test.ts src/lib/server/agents/agent-runtime-config.test.ts src/lib/server/build-llm.test.ts src/lib/server/connectors/connector-routing.test.ts src/lib/server/connectors/openclaw.test.ts src/lib/server/connectors/swarmdock.test.ts src/lib/server/gateway/protocol.test.ts src/lib/server/llm-response-cache.test.ts src/lib/server/mcp-conformance.test.ts src/lib/server/openclaw/agent-resolver.test.ts src/lib/server/openclaw/deploy.test.ts src/lib/server/openclaw/skills-normalize.test.ts src/lib/server/session-tools/openclaw-nodes.test.ts src/lib/server/session-tools/swarmdock.test.ts src/lib/server/tasks/task-quality-gate.test.ts src/lib/server/tasks/task-validation.test.ts src/lib/server/tool-capability-policy.test.ts src/lib/providers/openai.test.ts src/lib/providers/openclaw-exports.test.ts src/app/api/openclaw/dashboard-url/route.test.ts",
90
- "test:runtime": "tsx --test src/lib/server/mcp-gateway-runtime.test.ts src/lib/server/mcp-connection-pool.test.ts src/lib/server/knowledge-sources.test.ts src/lib/server/chat-execution/chat-execution-grounding.test.ts src/lib/server/chat-execution/chat-turn-preparation.test.ts src/lib/server/chat-execution/iteration-timers.test.ts src/lib/server/chat-execution/post-stream-finalization.test.ts src/lib/server/chats/clear-undo-snapshots.test.ts src/lib/server/connectors/email.test.ts src/lib/server/protocols/protocol-service.test.ts src/lib/server/runtime/run-ledger.test.ts src/lib/server/observability/otel-config.test.ts src/lib/server/safe-parse-body.test.ts src/lib/app/view-constants.test.ts src/app/api/approvals/route.test.ts src/app/api/agents/agents-route.test.ts src/app/api/tasks/tasks-route.test.ts src/app/api/chats/chat-route.test.ts src/app/api/chats/clear-route.test.ts src/app/api/chats/compact-route.test.ts src/app/api/chats/context-status-route.test.ts src/app/api/connectors/connector-doctor-route.test.ts src/app/api/healthz/route.test.ts src/app/api/logs/route.test.ts src/app/api/providers/[id]/route.test.ts src/app/api/tts/route.test.ts",
90
+ "test:runtime": "tsx --test src/lib/a2a/agent-card.test.ts src/lib/strip-internal-metadata.test.ts src/lib/providers/opencode-cli.test.ts src/lib/server/mcp-gateway-runtime.test.ts src/lib/server/mcp-connection-pool.test.ts src/lib/server/knowledge-sources.test.ts src/lib/server/chat-execution/chat-execution-grounding.test.ts src/lib/server/chat-execution/chat-turn-preparation.test.ts src/lib/server/chat-execution/iteration-timers.test.ts src/lib/server/chat-execution/post-stream-finalization.test.ts src/lib/server/chats/clear-undo-snapshots.test.ts src/lib/server/connectors/email.test.ts src/lib/server/protocols/protocol-service.test.ts src/lib/server/runtime/run-ledger.test.ts src/lib/server/observability/otel-config.test.ts src/lib/server/safe-parse-body.test.ts src/lib/server/missions/mission-templates.test.ts src/lib/app/view-constants.test.ts src/lib/quality/quality-summary.test.ts src/app/api/approvals/route.test.ts src/app/api/agents/agents-route.test.ts src/app/api/tasks/tasks-route.test.ts src/app/api/chats/chat-route.test.ts src/app/api/chats/clear-route.test.ts src/app/api/chats/compact-route.test.ts src/app/api/chats/context-status-route.test.ts src/app/api/connectors/connector-doctor-route.test.ts src/app/api/healthz/route.test.ts src/app/api/logs/route.test.ts src/app/api/providers/[id]/route.test.ts src/app/api/tts/route.test.ts",
91
91
  "test:builder": "tsx --test src/features/protocols/builder/utils/nodes-to-template.test.ts src/features/protocols/builder/utils/template-to-nodes.test.ts src/features/protocols/builder/validators/dag-validator.test.ts",
92
- "test:e2e": "tsx .workbench/browser-e2e/run.ts",
92
+ "test:e2e": "node --import tsx scripts/browser-e2e-smoke.ts",
93
93
  "test:mcp:conformance": "node --import tsx ./scripts/mcp-conformance-check.ts",
94
94
  "electron:compile": "tsc -p electron/tsconfig.json",
95
95
  "electron:dev": "npm run electron:compile && electron electron-dist/main.js",
@@ -0,0 +1,15 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { buildAgentCardDiscoveryPayload } from '@/lib/a2a/agent-card'
3
+
4
+ export const dynamic = 'force-dynamic'
5
+
6
+ /**
7
+ * GET /.well-known/agent-card.json?agentId=xxx
8
+ *
9
+ * Canonical public A2A Agent Card discovery endpoint. If agentId is omitted,
10
+ * returns a directory of discoverable local SwarmClaw agents.
11
+ */
12
+ export async function GET(req: Request) {
13
+ const { body, status } = buildAgentCardDiscoveryPayload(req)
14
+ return NextResponse.json(body, { status })
15
+ }
@@ -1,46 +1,15 @@
1
1
  import { NextResponse } from 'next/server'
2
- import { getAgent, listAgents } from '@/lib/server/agents/agent-repository'
3
- import { generateAgentCard } from '@/lib/a2a/agent-card'
2
+ import { buildAgentCardDiscoveryPayload } from '@/lib/a2a/agent-card'
4
3
 
5
4
  export const dynamic = 'force-dynamic'
6
5
 
7
6
  /**
8
- * GET /.well-known/agent-card.json?agentId=xxx
7
+ * GET /api/.well-known/agent-card?agentId=xxx
9
8
  *
10
- * A2A Agent Card discovery endpoint.
11
- * If agentId is provided, returns the full card for that agent.
12
- * Otherwise, returns a directory of all non-disabled agents.
13
- *
14
- * Publicly accessible per A2A spec — no auth required for discovery.
9
+ * Back-compatible A2A Agent Card discovery endpoint. The canonical public
10
+ * well-known URL is implemented at /.well-known/agent-card.json.
15
11
  */
16
12
  export async function GET(req: Request) {
17
- const { searchParams } = new URL(req.url)
18
- const agentId = searchParams.get('agentId')
19
- const baseUrl = `${new URL(req.url).origin}`
20
-
21
- if (agentId) {
22
- const agent = getAgent(agentId)
23
- if (!agent) {
24
- return NextResponse.json({ error: 'Agent not found' }, { status: 404 })
25
- }
26
- if (agent.disabled) {
27
- return NextResponse.json({ error: 'Agent is disabled' }, { status: 404 })
28
- }
29
- const card = generateAgentCard(agent, baseUrl)
30
- return NextResponse.json(card)
31
- }
32
-
33
- // Return directory of all active agents
34
- const agents = listAgents()
35
- const directory = Object.values(agents)
36
- .filter(a => !a.disabled)
37
- .map(a => ({
38
- name: a.name,
39
- description: a.description || `SwarmClaw agent: ${a.name}`,
40
- agentId: a.id,
41
- apiEndpoint: `${baseUrl}/api/a2a`,
42
- cardUrl: `${baseUrl}/api/.well-known/agent-card?agentId=${a.id}`,
43
- }))
44
-
45
- return NextResponse.json({ agents: directory, protocolVersion: '0.3.0' })
13
+ const { body, status } = buildAgentCardDiscoveryPayload(req)
14
+ return NextResponse.json(body, { status })
46
15
  }
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
 
3
- import { useEffect, useMemo, useState } from 'react'
3
+ import { useEffect, useMemo, useRef, useState } from 'react'
4
4
  import { useRouter } from 'next/navigation'
5
5
  import { AreaChart, Area, ResponsiveContainer, Tooltip } from 'recharts'
6
6
  import { useAppStore } from '@/stores/use-app-store'
@@ -109,6 +109,7 @@ export default function HomePage() {
109
109
  const [localhostBrowser, setLocalhostBrowser] = useState(false)
110
110
  const [pageReady, setPageReady] = useState(false)
111
111
  const [launchpadFlag, setLaunchpadFlag] = useState(false)
112
+ const launchpadFlagConsumedRef = useRef(false)
112
113
  const mountedRef = useMountedRef()
113
114
 
114
115
  useEffect(() => {
@@ -116,9 +117,12 @@ export default function HomePage() {
116
117
  }, [])
117
118
 
118
119
  useEffect(() => {
120
+ if (launchpadFlagConsumedRef.current) return
119
121
  const hasFlag = safeStorageGet(HOME_LAUNCHPAD_AFTER_SETUP_KEY) === '1'
120
- setLaunchpadFlag(hasFlag)
121
- if (hasFlag) safeStorageRemove(HOME_LAUNCHPAD_AFTER_SETUP_KEY)
122
+ if (!hasFlag) return
123
+ launchpadFlagConsumedRef.current = true
124
+ setLaunchpadFlag(true)
125
+ safeStorageRemove(HOME_LAUNCHPAD_AFTER_SETUP_KEY)
122
126
  }, [])
123
127
 
124
128
  const allAgents = Object.values(agents).filter((a) => !a.trashedAt)
@@ -279,6 +283,10 @@ export default function HomePage() {
279
283
  onOpenBuilder={openBuilder}
280
284
  onOpenConnectors={() => navigateTo('connectors')}
281
285
  onOpenUsage={() => navigateTo('usage')}
286
+ onRunEvalSuite={() => navigateTo('quality')}
287
+ onReviewApprovals={() => navigateTo('quality')}
288
+ onInspectFailedRuns={() => navigateTo('quality')}
289
+ onStartReleaseQaMission={() => navigateTo('missions')}
282
290
  />
283
291
  </div>
284
292
  </MainContent>
@@ -0,0 +1,7 @@
1
+ 'use client'
2
+
3
+ import { QualityWorkspace } from '@/components/quality/quality-workspace'
4
+
5
+ export default function QualityPage() {
6
+ return <QualityWorkspace />
7
+ }
@@ -14,6 +14,48 @@ function SnapshotItem({ label, value, hint }: { label: string; value: string; hi
14
14
  )
15
15
  }
16
16
 
17
+ function PathCard({
18
+ kicker,
19
+ title,
20
+ description,
21
+ primaryLabel,
22
+ secondaryLabel,
23
+ onPrimary,
24
+ onSecondary,
25
+ }: {
26
+ kicker: string
27
+ title: string
28
+ description: string
29
+ primaryLabel: string
30
+ secondaryLabel: string
31
+ onPrimary: () => void
32
+ onSecondary: () => void
33
+ }) {
34
+ return (
35
+ <div className="flex min-h-[220px] flex-col rounded-[18px] border border-white/[0.07] bg-white/[0.03] p-5">
36
+ <div className="text-[11px] font-700 uppercase tracking-[0.12em] text-text-3/55">{kicker}</div>
37
+ <div className="mt-3 text-[18px] font-display font-700 tracking-normal text-text">{title}</div>
38
+ <p className="mt-2 flex-1 text-[13px] leading-relaxed text-text-3/72">{description}</p>
39
+ <div className="mt-5 flex flex-wrap gap-2">
40
+ <button
41
+ type="button"
42
+ onClick={onPrimary}
43
+ className="rounded-[10px] bg-accent-bright px-3.5 py-2 text-[12px] font-display font-700 text-black transition-opacity hover:opacity-90"
44
+ >
45
+ {primaryLabel}
46
+ </button>
47
+ <button
48
+ type="button"
49
+ onClick={onSecondary}
50
+ className="rounded-[10px] border border-white/[0.08] bg-white/[0.04] px-3.5 py-2 text-[12px] font-display font-700 text-text-2 transition-colors hover:bg-white/[0.08]"
51
+ >
52
+ {secondaryLabel}
53
+ </button>
54
+ </div>
55
+ </div>
56
+ )
57
+ }
58
+
17
59
  type Props = {
18
60
  firstAgent: Agent | null
19
61
  agentCount: number
@@ -27,6 +69,10 @@ type Props = {
27
69
  onOpenBuilder: () => void
28
70
  onOpenConnectors: () => void
29
71
  onOpenUsage: () => void
72
+ onRunEvalSuite: () => void
73
+ onReviewApprovals: () => void
74
+ onInspectFailedRuns: () => void
75
+ onStartReleaseQaMission: () => void
30
76
  }
31
77
 
32
78
  export function HomeLaunchpad({
@@ -42,20 +88,24 @@ export function HomeLaunchpad({
42
88
  onOpenBuilder,
43
89
  onOpenConnectors,
44
90
  onOpenUsage,
91
+ onRunEvalSuite,
92
+ onReviewApprovals,
93
+ onInspectFailedRuns,
94
+ onStartReleaseQaMission,
45
95
  }: Props) {
46
96
  return (
47
97
  <div className="max-w-[980px] mx-auto px-6 py-10">
48
- <div className="rounded-[24px] border border-white/[0.06] bg-gradient-to-br from-white/[0.05] via-white/[0.02] to-transparent p-6">
98
+ <div className="rounded-[20px] border border-white/[0.06] bg-white/[0.025] p-6">
49
99
  <div className="inline-flex rounded-full border border-white/[0.08] bg-white/[0.03] px-3 py-1 text-[11px] font-700 uppercase tracking-[0.16em] text-text-3/70">
50
- Launchpad
100
+ v1.6 Launchpad
51
101
  </div>
52
102
  <div className="mt-4 flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
53
103
  <div className="max-w-[620px]">
54
- <h1 className="font-display text-[34px] font-700 tracking-[-0.03em] text-text">
55
- Start with the result you want, not the control plane.
104
+ <h1 className="font-display text-[34px] font-700 tracking-normal text-text">
105
+ Pick a path and watch the workspace move.
56
106
  </h1>
57
107
  <p className="mt-3 text-[15px] leading-relaxed text-text-3/72">
58
- SwarmClaw already has the building blocks. Use this workspace to start a live agent chat, launch a bounded session, wire a connector, or move straight into reusable workflows.
108
+ Start with a local assistant, a reusable workflow, or a budgeted autonomous mission. The rest of the control plane stays one click away.
59
109
  </p>
60
110
  </div>
61
111
  <div className="rounded-[18px] border border-white/[0.06] bg-white/[0.03] p-4 min-w-[240px]">
@@ -86,6 +136,36 @@ export function HomeLaunchpad({
86
136
  </div>
87
137
  </div>
88
138
 
139
+ <div className="mt-6 grid gap-3 lg:grid-cols-3">
140
+ <PathCard
141
+ kicker="Self-hosted assistant"
142
+ title={firstAgent ? `Work with ${firstAgent.name}` : 'Create the first agent'}
143
+ description="Open a live agent chat, then add memory, local tools, provider routing, or connector access as the work demands."
144
+ primaryLabel={firstAgent ? 'Open Chat' : 'Open Agents'}
145
+ secondaryLabel="Connect Platform"
146
+ onPrimary={onOpenFirstAgent}
147
+ onSecondary={onOpenConnectors}
148
+ />
149
+ <PathCard
150
+ kicker="Visual workflow"
151
+ title="Shape a reusable run"
152
+ description="Use protocol templates and the builder to turn review, research, planning, or release checks into durable workflows."
153
+ primaryLabel="Open Builder"
154
+ secondaryLabel="Use Templates"
155
+ onPrimary={onOpenBuilder}
156
+ onSecondary={onOpenProtocols}
157
+ />
158
+ <PathCard
159
+ kicker="Autonomous mission"
160
+ title="Run with budgets"
161
+ description="Start a mission template for release QA, research, support triage, cost audit, or failed-run review with reports and caps."
162
+ primaryLabel="Open Missions"
163
+ secondaryLabel="Quality Center"
164
+ onPrimary={onStartReleaseQaMission}
165
+ onSecondary={onRunEvalSuite}
166
+ />
167
+ </div>
168
+
89
169
  <div className="mt-6 grid gap-3 md:grid-cols-2 xl:grid-cols-3">
90
170
  <LaunchActionCard
91
171
  title={firstAgent ? 'Open First Agent Chat' : 'Open Agents'}
@@ -120,6 +200,30 @@ export function HomeLaunchpad({
120
200
  actionLabel="Open Usage"
121
201
  onClick={onOpenUsage}
122
202
  />
203
+ <LaunchActionCard
204
+ title="Run Eval Suite"
205
+ description="Open the Quality Center and run scenario or suite checks against an agent before shipping."
206
+ actionLabel="Open Eval Lab"
207
+ onClick={onRunEvalSuite}
208
+ />
209
+ <LaunchActionCard
210
+ title="Review Approvals"
211
+ description="Clear pending human-loop, tool, connector, skill, agent, and budget requests from one desk."
212
+ actionLabel="Open Approvals"
213
+ onClick={onReviewApprovals}
214
+ />
215
+ <LaunchActionCard
216
+ title="Inspect Failed Runs"
217
+ description="Filter recent run failures and open replay evidence without leaving the operator workflow."
218
+ actionLabel="Open Run Review"
219
+ onClick={onInspectFailedRuns}
220
+ />
221
+ <LaunchActionCard
222
+ title="Start Release QA Mission"
223
+ description="Use a budgeted mission template to collect release readiness evidence and quality notes."
224
+ actionLabel="Open Missions"
225
+ onClick={onStartReleaseQaMission}
226
+ />
123
227
  </div>
124
228
 
125
229
  <div className="mt-8 grid gap-3 md:grid-cols-2 xl:grid-cols-3">
@@ -305,6 +305,12 @@ export function SidebarRail({
305
305
  <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
306
306
  </svg>
307
307
  </NavItem>
308
+ <NavItem view="quality" label="Quality" expanded={railExpanded} isActive={isNavActive('quality')} onClick={() => handleNavClick('quality')}>
309
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
310
+ <path d="M9 12l2 2 4-4" />
311
+ <path d="M12 3l7 4v5c0 4.4-2.9 8.5-7 9-4.1-.5-7-4.6-7-9V7l7-4z" />
312
+ </svg>
313
+ </NavItem>
308
314
  </div>
309
315
 
310
316
  <div className={`flex flex-col gap-0.5 ${railExpanded ? '' : 'items-center'}`}>