@swarmclawai/swarmclaw 1.6.0 → 1.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.
Files changed (30) hide show
  1. package/README.md +20 -0
  2. package/package.json +3 -3
  3. package/src/app/.well-known/agent-card.json/route.ts +15 -0
  4. package/src/app/api/.well-known/agent-card/route.ts +6 -37
  5. package/src/app/home/page.tsx +10 -19
  6. package/src/components/auth/setup-wizard/index.tsx +2 -6
  7. package/src/components/auth/setup-wizard/step-next.tsx +39 -46
  8. package/src/components/auth/setup-wizard/step-providers.tsx +142 -76
  9. package/src/components/auth/setup-wizard/types.ts +2 -5
  10. package/src/components/auth/setup-wizard/utils.test.ts +19 -0
  11. package/src/components/auth/setup-wizard/utils.ts +69 -0
  12. package/src/components/home/home-launchpad.tsx +100 -80
  13. package/src/lib/a2a/agent-card.test.ts +94 -0
  14. package/src/lib/a2a/agent-card.ts +41 -1
  15. package/src/lib/home-launchpad.test.ts +31 -1
  16. package/src/lib/home-launchpad.ts +58 -0
  17. package/src/lib/providers/cli-utils.test.ts +10 -0
  18. package/src/lib/providers/cli-utils.ts +31 -0
  19. package/src/lib/providers/generic-cli.test.ts +71 -0
  20. package/src/lib/providers/generic-cli.ts +138 -0
  21. package/src/lib/providers/index.ts +56 -1
  22. package/src/lib/providers/opencode-cli.test.ts +9 -0
  23. package/src/lib/providers/opencode-cli.ts +5 -1
  24. package/src/lib/server/missions/mission-templates.test.ts +17 -0
  25. package/src/lib/server/missions/mission-templates.ts +69 -0
  26. package/src/lib/server/protocols/protocol-service.test.ts +25 -0
  27. package/src/lib/server/protocols/protocol-templates.ts +48 -0
  28. package/src/lib/strip-internal-metadata.test.ts +23 -0
  29. package/src/lib/strip-internal-metadata.ts +136 -7
  30. package/src/types/provider.ts +1 -1
@@ -9,27 +9,156 @@
9
9
  *
10
10
  * Importable from both client and server code.
11
11
  */
12
+ import { z } from 'zod'
12
13
 
13
14
  // ---------------------------------------------------------------------------
14
15
  // Classification JSON
15
16
  // ---------------------------------------------------------------------------
16
17
 
17
18
  const INTERNAL_JSON_KEYS = [
18
- 'isDeliverableTask', 'quality_score', 'isBroadGoal',
19
- 'hasHumanSignals', 'explicitToolRequests', 'isResearchSynthesis', 'confidence',
19
+ 'factsUpsert', 'artifactsUpsert', 'planSteps', 'decisionsAppend',
20
+ 'blockersUpsert', 'questionsUpsert', 'hypothesesUpsert', 'supersedeIds',
21
+ 'taskIntent', 'isLightweightDirectChat', 'isDeliverableTask', 'quality_score',
22
+ 'isBroadGoal', 'hasHumanSignals', 'explicitToolRequests', 'isResearchSynthesis',
23
+ 'confidence', 'isIncomplete',
20
24
  ]
21
25
 
22
26
  export const INTERNAL_KEY_RE = new RegExp(`"(?:${INTERNAL_JSON_KEYS.join('|')})"`)
23
27
 
28
+ const WorkingStatePatchLikeSchema = z.object({
29
+ factsUpsert: z.array(z.unknown()).optional(),
30
+ artifactsUpsert: z.array(z.unknown()).optional(),
31
+ planSteps: z.array(z.unknown()).optional(),
32
+ decisionsAppend: z.array(z.unknown()).optional(),
33
+ blockersUpsert: z.array(z.unknown()).optional(),
34
+ questionsUpsert: z.array(z.unknown()).optional(),
35
+ hypothesesUpsert: z.array(z.unknown()).optional(),
36
+ supersedeIds: z.array(z.unknown()).optional(),
37
+ }).passthrough()
38
+
39
+ const MessageClassificationLikeSchema = z.object({
40
+ taskIntent: z.string().optional(),
41
+ isLightweightDirectChat: z.boolean().optional(),
42
+ isDeliverableTask: z.boolean().optional(),
43
+ isBroadGoal: z.boolean().optional(),
44
+ hasHumanSignals: z.boolean().optional(),
45
+ explicitToolRequests: z.array(z.unknown()).optional(),
46
+ isResearchSynthesis: z.boolean().optional(),
47
+ confidence: z.number().optional(),
48
+ }).passthrough()
49
+
50
+ const ResponseCompletenessLikeSchema = z.object({
51
+ isIncomplete: z.boolean(),
52
+ }).passthrough()
53
+
54
+ const QualityScoreLikeSchema = z.object({
55
+ quality_score: z.number(),
56
+ quality_reasoning: z.string().optional(),
57
+ }).passthrough()
58
+
59
+ interface InternalPayloadRule {
60
+ schema: z.ZodType<unknown>
61
+ distinctiveKeys: string[]
62
+ }
63
+
64
+ const INTERNAL_PAYLOAD_RULES: InternalPayloadRule[] = [
65
+ {
66
+ schema: WorkingStatePatchLikeSchema,
67
+ distinctiveKeys: [
68
+ 'factsUpsert',
69
+ 'artifactsUpsert',
70
+ 'planSteps',
71
+ 'decisionsAppend',
72
+ 'blockersUpsert',
73
+ 'questionsUpsert',
74
+ 'hypothesesUpsert',
75
+ 'supersedeIds',
76
+ ],
77
+ },
78
+ {
79
+ schema: MessageClassificationLikeSchema,
80
+ distinctiveKeys: [
81
+ 'isLightweightDirectChat',
82
+ 'isDeliverableTask',
83
+ 'isBroadGoal',
84
+ 'hasHumanSignals',
85
+ 'explicitToolRequests',
86
+ 'isResearchSynthesis',
87
+ ],
88
+ },
89
+ {
90
+ schema: ResponseCompletenessLikeSchema,
91
+ distinctiveKeys: ['isIncomplete'],
92
+ },
93
+ {
94
+ schema: QualityScoreLikeSchema,
95
+ distinctiveKeys: ['quality_score'],
96
+ },
97
+ ]
98
+
99
+ function objectIsInternalMetadata(obj: Record<string, unknown>): boolean {
100
+ for (const { schema, distinctiveKeys } of INTERNAL_PAYLOAD_RULES) {
101
+ if (!distinctiveKeys.some((key) => key in obj)) continue
102
+ if (schema.safeParse(obj).success) return true
103
+ }
104
+ return false
105
+ }
106
+
107
+ function findBalancedJsonObjectEnd(text: string, start: number): number {
108
+ if (text.charAt(start) !== '{') return -1
109
+ let depth = 0
110
+ let inString = false
111
+ let escaped = false
112
+ for (let i = start; i < text.length; i += 1) {
113
+ const c = text.charAt(i)
114
+ if (inString) {
115
+ if (escaped) escaped = false
116
+ else if (c === '\\') escaped = true
117
+ else if (c === '"') inString = false
118
+ continue
119
+ }
120
+ if (c === '"') {
121
+ inString = true
122
+ continue
123
+ }
124
+ if (c === '{') depth += 1
125
+ else if (c === '}') {
126
+ depth -= 1
127
+ if (depth === 0) return i + 1
128
+ }
129
+ }
130
+ return -1
131
+ }
132
+
24
133
  /**
25
134
  * Remove top-level `{ ... }` blocks that contain known internal classification keys.
26
- * Handles multi-line JSON. Only strips blocks where at least one internal key is present.
135
+ * Handles nested and multi-line JSON. Only strips blocks where at least one
136
+ * distinctive internal key is present and the payload passes schema validation.
27
137
  */
28
138
  export function stripInternalJson(text: string): string {
29
- return text.replace(
30
- /\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g,
31
- (match) => INTERNAL_KEY_RE.test(match) ? '' : match,
32
- )
139
+ let out = text || ''
140
+ for (let guard = 0; guard < 32; guard += 1) {
141
+ let removed = false
142
+ for (let i = 0; i < out.length; i += 1) {
143
+ if (out.charAt(i) !== '{') continue
144
+ const end = findBalancedJsonObjectEnd(out, i)
145
+ if (end <= i) continue
146
+ const candidate = out.slice(i, end)
147
+ let parsed: unknown
148
+ try {
149
+ parsed = JSON.parse(candidate)
150
+ } catch {
151
+ continue
152
+ }
153
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) continue
154
+ if (!objectIsInternalMetadata(parsed as Record<string, unknown>)) continue
155
+ out = (out.slice(0, i).replace(/\s+$/, '') + ' ' + out.slice(end).replace(/^\s+/, '')).trim()
156
+ removed = true
157
+ break
158
+ }
159
+ if (!removed) break
160
+ }
161
+ return out
33
162
  }
34
163
 
35
164
  // ---------------------------------------------------------------------------
@@ -1,4 +1,4 @@
1
- export type ProviderType = 'claude-cli' | 'codex-cli' | 'opencode-cli' | 'opencode-web' | 'gemini-cli' | 'copilot-cli' | 'droid-cli' | 'cursor-cli' | 'qwen-code-cli' | 'goose' | 'openai' | 'openrouter' | 'ollama' | 'anthropic' | 'openclaw' | 'hermes' | 'google' | 'deepseek' | 'groq' | 'together' | 'mistral' | 'xai' | 'fireworks' | 'nebius' | 'deepinfra'
1
+ export type ProviderType = 'claude-cli' | 'codex-cli' | 'opencode-cli' | 'opencode-web' | 'gemini-cli' | 'copilot-cli' | 'droid-cli' | 'cursor-cli' | 'qwen-code-cli' | 'goose' | 'aider-cli' | 'amp-cli' | 'augment-cli' | 'adal-cli' | 'bob-cli' | 'cline-cli' | 'codebuddy-cli' | 'command-code-cli' | 'continue-cli' | 'cortex-cli' | 'crush-cli' | 'deepagents-cli' | 'firebender-cli' | 'iflow-cli' | 'junie-cli' | 'kilo-code-cli' | 'kimi-cli' | 'kode-cli' | 'mcpjam-cli' | 'mistral-vibe-cli' | 'mux-cli' | 'neovate-cli' | 'openhands-cli' | 'pochi-cli' | 'qoder-cli' | 'replit-cli' | 'roo-code-cli' | 'trae-cn-cli' | 'warp-cli' | 'windsurf-cli' | 'zencoder-cli' | 'openai' | 'openrouter' | 'ollama' | 'anthropic' | 'openclaw' | 'hermes' | 'google' | 'deepseek' | 'groq' | 'together' | 'mistral' | 'xai' | 'fireworks' | 'nebius' | 'deepinfra'
2
2
  export type ProviderId = ProviderType | (string & {})
3
3
 
4
4
  export interface ProviderInfo {