@swarmclawai/swarmclaw 1.9.24 → 1.9.26
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 +17 -6
- package/package.json +2 -2
- package/src/cli/index.js +17 -0
- package/src/cli/index.test.js +30 -0
- package/src/cli/spec.js +5 -0
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +2 -3
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +2 -2
- package/src/lib/server/chat-execution/chat-execution-tool-events.ts +0 -4
package/README.md
CHANGED
|
@@ -151,13 +151,14 @@ clawhub install swarmclaw
|
|
|
151
151
|
|
|
152
152
|
[Browse on ClawHub](https://clawhub.ai/skills/swarmclaw)
|
|
153
153
|
|
|
154
|
-
## v1.9.
|
|
154
|
+
## v1.9.26 Highlights
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
Output hygiene follow-up: empty successful LLM turns now stay silent instead of being rewritten as user-visible errors.
|
|
157
157
|
|
|
158
|
-
- **
|
|
159
|
-
- **
|
|
160
|
-
- **
|
|
158
|
+
- **Silent empty completions.** Blank successful runs no longer become `Error: Run completed...` assistant messages.
|
|
159
|
+
- **Connector-safe final text.** Slack and other connectors no longer receive synthetic error text for intentional silence or quiet no-op turns.
|
|
160
|
+
- **Real errors preserved.** Explicit provider failures and streamed provider errors still surface as terminal errors.
|
|
161
|
+
- **Regression coverage.** Chat-execution tests now lock the distinction between empty success and real failure.
|
|
161
162
|
|
|
162
163
|
## Hosted Deploys
|
|
163
164
|
|
|
@@ -409,13 +410,23 @@ Operational docs: https://swarmclaw.ai/docs/observability
|
|
|
409
410
|
|
|
410
411
|
## Releases
|
|
411
412
|
|
|
412
|
-
### v1.9.
|
|
413
|
+
### v1.9.26 Highlights
|
|
414
|
+
|
|
415
|
+
Output hygiene follow-up: empty successful LLM turns now stay silent instead of being rewritten as user-visible errors.
|
|
416
|
+
|
|
417
|
+
- **Silent empty completions.** Blank successful runs no longer become `Error: Run completed...` assistant messages.
|
|
418
|
+
- **Connector-safe final text.** Slack and other connectors no longer receive synthetic error text for intentional silence or quiet no-op turns.
|
|
419
|
+
- **Real errors preserved.** Explicit provider failures and streamed provider errors still surface as terminal errors.
|
|
420
|
+
- **Regression coverage.** Chat-execution tests now lock the distinction between empty success and real failure.
|
|
421
|
+
|
|
422
|
+
### v1.9.25 Highlights
|
|
413
423
|
|
|
414
424
|
Gateway lifecycle release: saved OpenClaw gateways now have explicit operator lifecycle controls, automatic routing avoids gateways that should not receive new work, and Slack peer-agent messages flow through the existing connector policy gates.
|
|
415
425
|
|
|
416
426
|
- **Gateway lifecycle controls.** Providers can activate, drain, cordon, and request restart for saved OpenClaw gateway profiles.
|
|
417
427
|
- **Routing guardrails.** OpenClaw route selection skips draining and cordoned profiles, including default, preferred, and pinned gateway paths.
|
|
418
428
|
- **Operations Pulse awareness.** Cordoned and draining gateways now appear as operator attention items before they surprise a handoff or release check.
|
|
429
|
+
- **CLI lifecycle access.** `swarmclaw gateways activate`, `drain`, `cordon`, and `restart` now post the matching lifecycle action for automation and release scripts.
|
|
419
430
|
- **Slack peer collaboration.** Slack peer-bot messages are no longer dropped before group policy, mention, and self-loop protections run.
|
|
420
431
|
|
|
421
432
|
### v1.9.23 Highlights
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmclawai/swarmclaw",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.26",
|
|
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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"node": ">=22.6.0"
|
|
40
40
|
},
|
|
41
41
|
"bin": {
|
|
42
|
-
"swarmclaw": "
|
|
42
|
+
"swarmclaw": "bin/swarmclaw.js"
|
|
43
43
|
},
|
|
44
44
|
"files": [
|
|
45
45
|
"bin/",
|
package/src/cli/index.js
CHANGED
|
@@ -278,6 +278,23 @@ const COMMAND_GROUPS = [
|
|
|
278
278
|
cmd('create', 'POST', '/gateways', 'Create a gateway profile', { expectsJsonBody: true }),
|
|
279
279
|
cmd('update', 'PUT', '/gateways/:id', 'Update a gateway profile', { expectsJsonBody: true }),
|
|
280
280
|
cmd('delete', 'DELETE', '/gateways/:id', 'Delete a gateway profile'),
|
|
281
|
+
cmd('control', 'POST', '/gateways/:id/control', 'Run a gateway lifecycle control action', { expectsJsonBody: true }),
|
|
282
|
+
cmd('activate', 'POST', '/gateways/:id/control', 'Return a gateway to active routing', {
|
|
283
|
+
expectsJsonBody: true,
|
|
284
|
+
defaultBody: { action: 'activate' },
|
|
285
|
+
}),
|
|
286
|
+
cmd('drain', 'POST', '/gateways/:id/control', 'Drain a gateway from new automatic work', {
|
|
287
|
+
expectsJsonBody: true,
|
|
288
|
+
defaultBody: { action: 'drain' },
|
|
289
|
+
}),
|
|
290
|
+
cmd('cordon', 'POST', '/gateways/:id/control', 'Cordon a gateway from automatic work', {
|
|
291
|
+
expectsJsonBody: true,
|
|
292
|
+
defaultBody: { action: 'cordon' },
|
|
293
|
+
}),
|
|
294
|
+
cmd('restart', 'POST', '/gateways/:id/control', 'Request a gateway restart', {
|
|
295
|
+
expectsJsonBody: true,
|
|
296
|
+
defaultBody: { action: 'restart' },
|
|
297
|
+
}),
|
|
281
298
|
cmd('health', 'GET', '/gateways/:id/health', 'Run a gateway health check'),
|
|
282
299
|
cmd('topology', 'GET', '/gateways/:id/topology', 'Refresh and return one gateway topology snapshot'),
|
|
283
300
|
cmd('environments', 'GET', '/gateways/:id/environments', 'List OpenClaw gateway execution environments'),
|
package/src/cli/index.test.js
CHANGED
|
@@ -225,6 +225,36 @@ test('tasks execution-policy-decision posts policy decisions', async () => {
|
|
|
225
225
|
assert.equal(stderr.toString(), '')
|
|
226
226
|
})
|
|
227
227
|
|
|
228
|
+
test('gateways drain command posts a lifecycle control action', async () => {
|
|
229
|
+
const stdout = makeWritable()
|
|
230
|
+
const stderr = makeWritable()
|
|
231
|
+
const calls = []
|
|
232
|
+
|
|
233
|
+
const fetchImpl = async (url, init) => {
|
|
234
|
+
calls.push({ url: String(url), init })
|
|
235
|
+
return jsonResponse({ ok: true })
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const exitCode = await runCli(
|
|
239
|
+
['gateways', 'drain', 'gateway-1', '--json'],
|
|
240
|
+
{
|
|
241
|
+
fetchImpl,
|
|
242
|
+
stdout,
|
|
243
|
+
stderr,
|
|
244
|
+
env: {
|
|
245
|
+
SWARMCLAW_API_KEY: 'test-key',
|
|
246
|
+
},
|
|
247
|
+
cwd: process.cwd(),
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
assert.equal(exitCode, 0)
|
|
252
|
+
assert.equal(calls.length, 1)
|
|
253
|
+
assert.match(calls[0].url, /\/api\/gateways\/gateway-1\/control$/)
|
|
254
|
+
assert.equal(calls[0].init.method, 'POST')
|
|
255
|
+
assert.deepEqual(JSON.parse(calls[0].init.body), { action: 'drain' })
|
|
256
|
+
})
|
|
257
|
+
|
|
228
258
|
test('openclaw deploy bundle command merges action with provided JSON body', async () => {
|
|
229
259
|
const stdout = makeWritable()
|
|
230
260
|
const stderr = makeWritable()
|
package/src/cli/spec.js
CHANGED
|
@@ -219,6 +219,11 @@ const COMMAND_GROUPS = {
|
|
|
219
219
|
create: { description: 'Create a gateway profile', method: 'POST', path: '/gateways' },
|
|
220
220
|
update: { description: 'Update a gateway profile', method: 'PUT', path: '/gateways/:id', params: ['id'] },
|
|
221
221
|
delete: { description: 'Delete a gateway profile', method: 'DELETE', path: '/gateways/:id', params: ['id'] },
|
|
222
|
+
control: { description: 'Run a gateway lifecycle control action', method: 'POST', path: '/gateways/:id/control', params: ['id'] },
|
|
223
|
+
activate: { description: 'Return a gateway to active routing', method: 'POST', path: '/gateways/:id/control', params: ['id'] },
|
|
224
|
+
drain: { description: 'Drain a gateway from new automatic work', method: 'POST', path: '/gateways/:id/control', params: ['id'] },
|
|
225
|
+
cordon: { description: 'Cordon a gateway from automatic work', method: 'POST', path: '/gateways/:id/control', params: ['id'] },
|
|
226
|
+
restart: { description: 'Request a gateway restart', method: 'POST', path: '/gateways/:id/control', params: ['id'] },
|
|
222
227
|
health: { description: 'Run a gateway health check', method: 'GET', path: '/gateways/:id/health', params: ['id'] },
|
|
223
228
|
topology: { description: 'Refresh and return one gateway topology snapshot', method: 'GET', path: '/gateways/:id/topology', params: ['id'] },
|
|
224
229
|
environments: { description: 'List OpenClaw gateway execution environments', method: 'GET', path: '/gateways/:id/environments', params: ['id'] },
|
|
@@ -240,15 +240,14 @@ describe('deriveTerminalRunError advanced', () => {
|
|
|
240
240
|
assert.equal(err, undefined)
|
|
241
241
|
})
|
|
242
242
|
|
|
243
|
-
it('
|
|
243
|
+
it('returns undefined for empty successful non-internal runs', () => {
|
|
244
244
|
const err = deriveTerminalRunError({
|
|
245
245
|
fullResponse: '',
|
|
246
246
|
streamErrors: [],
|
|
247
247
|
toolEvents: [],
|
|
248
248
|
internal: false,
|
|
249
249
|
})
|
|
250
|
-
assert.
|
|
251
|
-
assert.ok(err.includes('Check the provider configuration'))
|
|
250
|
+
assert.equal(err, undefined)
|
|
252
251
|
})
|
|
253
252
|
|
|
254
253
|
it('uses errorMessage directly when provided', () => {
|
|
@@ -184,7 +184,7 @@ describe('deriveTerminalRunError', () => {
|
|
|
184
184
|
)
|
|
185
185
|
})
|
|
186
186
|
|
|
187
|
-
it('
|
|
187
|
+
it('does not convert empty successful runs into a visible assistant error', () => {
|
|
188
188
|
assert.equal(
|
|
189
189
|
deriveTerminalRunError({
|
|
190
190
|
errorMessage: undefined,
|
|
@@ -193,7 +193,7 @@ describe('deriveTerminalRunError', () => {
|
|
|
193
193
|
toolEvents: [],
|
|
194
194
|
internal: false,
|
|
195
195
|
}),
|
|
196
|
-
|
|
196
|
+
undefined,
|
|
197
197
|
)
|
|
198
198
|
})
|
|
199
199
|
|
|
@@ -108,9 +108,5 @@ export function deriveTerminalRunError(params: {
|
|
|
108
108
|
return params.streamErrors[params.streamErrors.length - 1]
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
if (!params.internal && !params.fullResponse.trim() && params.toolEvents.length === 0) {
|
|
112
|
-
return 'Run completed without any response text, tool calls, or explicit error details. Check the provider configuration and try again.'
|
|
113
|
-
}
|
|
114
|
-
|
|
115
111
|
return undefined
|
|
116
112
|
}
|