Skip to content

Commit a698dd0

Browse files
committed
fix: ship v1.9.37 issue fixes
1 parent d2f80bb commit a698dd0

20 files changed

Lines changed: 603 additions & 52 deletions

File tree

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ openclaw skills install swarmclaw
151151

152152
[Browse on ClawHub](https://clawhub.ai/waydelyle/swarmclaw)
153153

154+
## v1.9.37 Highlights
155+
156+
Theme and memory-pressure release for lighter UI preferences and leaner chat history storage.
157+
158+
- **Light, dark, and system theme modes.** Settings → Appearance now persists a Light/Dark/System selector while keeping the existing hue presets and custom color picker.
159+
- **Lean session history storage.** Legacy transcript blobs migrate into the `session_messages` table and are compacted from session records after persistence is verified, reducing page-load memory pressure on lower-RAM devices.
160+
- **Repo-backed message readers.** Global search, live usage summaries, and OpenClaw history merge now read table-backed messages after transcript compaction.
161+
- **Regression coverage.** Added tests for theme-mode normalization, legacy transcript compaction, and repo-backed message search.
162+
154163
## v1.9.36 Highlights
155164

156165
Protocol builder visibility release for built-in Structured Sessions.
@@ -470,6 +479,15 @@ Operational docs: https://swarmclaw.ai/docs/observability
470479

471480
## Releases
472481

482+
### v1.9.37 Highlights
483+
484+
Theme and memory-pressure release for lighter UI preferences and leaner chat history storage.
485+
486+
- **Light, dark, and system theme modes.** Settings → Appearance now persists a Light/Dark/System selector while keeping the existing hue presets and custom color picker.
487+
- **Lean session history storage.** Legacy transcript blobs migrate into the `session_messages` table and are compacted from session records after persistence is verified, reducing page-load memory pressure on lower-RAM devices.
488+
- **Repo-backed message readers.** Global search, live usage summaries, and OpenClaw history merge now read table-backed messages after transcript compaction.
489+
- **Regression coverage.** Added tests for theme-mode normalization, legacy transcript compaction, and repo-backed message search.
490+
473491
### v1.9.36 Highlights
474492

475493
Protocol builder visibility release for built-in Structured Sessions.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@swarmclawai/swarmclaw",
3-
"version": "1.9.36",
3+
"version": "1.9.37",
44
"description": "Build and run autonomous AI agents with OpenClaw, Hermes, multiple model providers, orchestration, delegation, memory, skills, schedules, and chat connectors.",
55
"main": "electron-dist/main.js",
66
"license": "MIT",
@@ -88,7 +88,7 @@
8888
"test:cli": "node --test src/cli/*.test.js bin/*.test.js scripts/electron-after-pack.test.mjs scripts/electron-signing-config.test.mjs scripts/ensure-sandbox-browser-image.test.mjs scripts/postinstall.test.mjs scripts/run-next-build.test.mjs scripts/run-next-typegen.test.mjs",
8989
"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",
9090
"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/gateways/gateway-topology.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/gateways/topology-route.test.ts src/app/api/openclaw/dashboard-url/route.test.ts",
91-
"test:runtime": "tsx --test src/lib/a2a/agent-card.test.ts src/lib/agent-planning-mode.test.ts src/lib/agent-config-history.test.ts src/lib/autonomy/supervisor-settings.test.ts src/lib/strip-internal-metadata.test.ts src/lib/provider-sets.test.ts src/lib/providers/opencode-cli.test.ts src/lib/providers/cli-provider-metadata.test.ts src/lib/providers/cli-utils.test.ts src/lib/providers/generic-cli.test.ts src/lib/server/agents/delegation-advisory.test.ts src/lib/server/agents/agent-runtime-config.test.ts src/lib/server/autonomy/supervisor-reflection.test.ts src/lib/server/cli-provider-readiness.test.ts src/lib/server/provider-health.test.ts src/lib/server/provider-diagnostics.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/memory/dream-service.test.ts src/lib/server/memory/memory-consolidation.test.ts src/lib/server/extension-managed-resources.test.ts src/lib/server/eval/baseline.test.ts src/lib/server/eval/environment-plan.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/compaction-generation-preference.test.ts src/lib/server/chat-execution/iteration-timers.test.ts src/lib/server/chat-execution/post-stream-finalization.test.ts src/lib/server/chat-execution/prompt-sections.planning-mode.test.ts src/lib/server/chat-execution/reasoning-tag-scrubber.test.ts src/lib/server/chats/clear-undo-snapshots.test.ts src/lib/server/chats/session-context-pack.test.ts src/lib/server/connectors/email.test.ts src/lib/server/connectors/slack.test.ts src/lib/server/protocols/protocol-service.test.ts src/lib/server/runtime/run-ledger.test.ts src/lib/server/runtime/queue-retry-policy.test.ts src/lib/server/runs/run-brief.test.ts src/lib/server/runs/run-handoff.test.ts src/lib/server/operations/operation-pulse.test.ts src/lib/server/schedules/schedule-history.test.ts src/lib/server/schedules/schedule-timing.test.ts src/lib/server/schedules/schedule-preview.test.ts src/lib/quality/release-readiness.test.ts src/lib/quality/architecture-health.test.ts src/lib/server/artifacts/artifact-resolver.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/server/sharing/share-link-repository.test.ts src/lib/server/sharing/share-resolver.test.ts src/lib/server/tasks/task-execution-workspace.test.ts src/lib/server/tasks/task-execution-policy.test.ts src/lib/server/tasks/task-handoff.test.ts src/lib/server/tasks/task-service.test.ts src/lib/server/session-tools/execute.test.ts src/lib/server/session-tools/manage-tasks.test.ts src/lib/server/session-tools/web-crawl.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/tasks/task-workspace-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-pack-route.test.ts src/app/api/chats/context-status-route.test.ts src/app/api/config-versions/config-versions-route.test.ts src/app/api/runs/run-handoff-route.test.ts src/app/api/connectors/connector-doctor-route.test.ts src/app/api/extensions/managed-resources/route.test.ts src/app/api/gateways/control-route.test.ts src/app/api/healthz/route.test.ts src/app/api/logs/route.test.ts src/app/api/portability/export/route.test.ts src/app/api/portability/import/route.test.ts src/app/api/providers/[id]/route.test.ts src/app/api/schedules/preview/route.test.ts src/app/api/schedules/schedule-history-route.test.ts src/app/api/tts/route.test.ts",
91+
"test:runtime": "tsx --test src/lib/a2a/agent-card.test.ts src/lib/agent-planning-mode.test.ts src/lib/agent-config-history.test.ts src/lib/autonomy/supervisor-settings.test.ts src/lib/strip-internal-metadata.test.ts src/lib/provider-sets.test.ts src/lib/providers/opencode-cli.test.ts src/lib/providers/cli-provider-metadata.test.ts src/lib/providers/cli-utils.test.ts src/lib/providers/generic-cli.test.ts src/lib/server/agents/delegation-advisory.test.ts src/lib/server/agents/agent-runtime-config.test.ts src/lib/server/autonomy/supervisor-reflection.test.ts src/lib/server/cli-provider-readiness.test.ts src/lib/server/provider-health.test.ts src/lib/server/provider-diagnostics.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/memory/dream-service.test.ts src/lib/server/memory/memory-consolidation.test.ts src/lib/server/messages/message-repository.test.ts src/lib/server/extension-managed-resources.test.ts src/lib/server/eval/baseline.test.ts src/lib/server/eval/environment-plan.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/compaction-generation-preference.test.ts src/lib/server/chat-execution/iteration-timers.test.ts src/lib/server/chat-execution/post-stream-finalization.test.ts src/lib/server/chat-execution/prompt-sections.planning-mode.test.ts src/lib/server/chat-execution/reasoning-tag-scrubber.test.ts src/lib/server/chats/clear-undo-snapshots.test.ts src/lib/server/chats/session-context-pack.test.ts src/lib/server/connectors/email.test.ts src/lib/server/connectors/slack.test.ts src/lib/server/protocols/protocol-service.test.ts src/lib/server/runtime/run-ledger.test.ts src/lib/server/runtime/queue-retry-policy.test.ts src/lib/server/runs/run-brief.test.ts src/lib/server/runs/run-handoff.test.ts src/lib/server/operations/operation-pulse.test.ts src/lib/server/schedules/schedule-history.test.ts src/lib/server/schedules/schedule-timing.test.ts src/lib/server/schedules/schedule-preview.test.ts src/lib/quality/release-readiness.test.ts src/lib/quality/architecture-health.test.ts src/lib/server/artifacts/artifact-resolver.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/server/sharing/share-link-repository.test.ts src/lib/server/sharing/share-resolver.test.ts src/lib/server/tasks/task-execution-workspace.test.ts src/lib/server/tasks/task-execution-policy.test.ts src/lib/server/tasks/task-handoff.test.ts src/lib/server/tasks/task-service.test.ts src/lib/server/session-tools/execute.test.ts src/lib/server/session-tools/manage-tasks.test.ts src/lib/server/session-tools/web-crawl.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/tasks/task-workspace-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-pack-route.test.ts src/app/api/chats/context-status-route.test.ts src/app/api/config-versions/config-versions-route.test.ts src/app/api/runs/run-handoff-route.test.ts src/app/api/search/route.test.ts src/app/api/settings/settings-route.test.ts src/app/api/connectors/connector-doctor-route.test.ts src/app/api/extensions/managed-resources/route.test.ts src/app/api/gateways/control-route.test.ts src/app/api/healthz/route.test.ts src/app/api/logs/route.test.ts src/app/api/portability/export/route.test.ts src/app/api/portability/import/route.test.ts src/app/api/providers/[id]/route.test.ts src/app/api/schedules/preview/route.test.ts src/app/api/schedules/schedule-history-route.test.ts src/app/api/tts/route.test.ts",
9292
"test:builder": "tsx --test src/features/protocols/builder/utils/builder-template-access.test.ts 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",
9393
"test:e2e": "node --import tsx scripts/browser-e2e-smoke.ts",
9494
"test:mcp:conformance": "node --import tsx ./scripts/mcp-conformance-check.ts",

src/app/api/openclaw/history/route.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { NextResponse } from 'next/server'
22
import { ensureGatewayConnected } from '@/lib/server/openclaw/gateway'
33
import { mergeHistoryMessages, isValidSessionKey } from '@/lib/server/openclaw/history-merge'
4-
import { getSession, saveSession } from '@/lib/server/sessions/session-repository'
4+
import { getSession, patchSession } from '@/lib/server/sessions/session-repository'
5+
import { getMessages, replaceAllMessages } from '@/lib/server/messages/message-repository'
56
import { notify } from '@/lib/server/ws-hub'
67
import type { GatewaySessionPreview } from '@/types'
78
import { errorMessage } from '@/lib/shared-utils'
@@ -95,11 +96,15 @@ export async function POST(req: Request) {
9596
return NextResponse.json({ error: 'Local session not found' }, { status: 404 })
9697
}
9798

98-
const merged = mergeHistoryMessages(session.messages, preview)
99-
const newCount = merged.length - session.messages.length
100-
session.messages = merged
101-
session.lastActiveAt = Date.now()
102-
saveSession(localSessionId, session)
99+
const currentMessages = getMessages(localSessionId)
100+
const merged = mergeHistoryMessages(currentMessages, preview)
101+
const newCount = merged.length - currentMessages.length
102+
replaceAllMessages(localSessionId, merged)
103+
patchSession(localSessionId, (current) => {
104+
if (!current) return null
105+
current.lastActiveAt = Date.now()
106+
return current
107+
})
103108
notify('sessions')
104109

105110
return NextResponse.json({ ok: true, merged: newCount })

src/app/api/preview-server/route.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ const servers: Map<string, PreviewServer> =
4949
// ---------------------------------------------------------------------------
5050

5151
function resolveServeDir(filePath: string): string {
52-
const resolved = path.resolve(filePath)
52+
const resolved = path.resolve(/*turbopackIgnore: true*/ filePath)
5353
try {
54-
return fs.statSync(resolved).isDirectory() ? resolved : path.dirname(resolved)
54+
return fs.statSync(/*turbopackIgnore: true*/ resolved).isDirectory()
55+
? resolved
56+
: path.dirname(/*turbopackIgnore: true*/ resolved)
5557
} catch {
56-
return path.dirname(resolved)
58+
return path.dirname(/*turbopackIgnore: true*/ resolved)
5759
}
5860
}
5961

@@ -91,13 +93,13 @@ function buildFrameworkArgs(framework: string | undefined, port: number): string
9193
}
9294

9395
function detectProject(dir: string): ProjectInfo {
94-
const pkgPath = path.join(dir, 'package.json')
95-
if (!fs.existsSync(pkgPath)) {
96+
const pkgPath = path.join(/*turbopackIgnore: true*/ dir, 'package.json')
97+
if (!fs.existsSync(/*turbopackIgnore: true*/ pkgPath)) {
9698
return { type: 'static' }
9799
}
98100

99101
try {
100-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
102+
const pkg = JSON.parse(fs.readFileSync(/*turbopackIgnore: true*/ pkgPath, 'utf-8'))
101103
const scripts = pkg.scripts || {}
102104
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) }
103105

@@ -156,16 +158,22 @@ function createStaticServer(dir: string): http.Server {
156158
]
157159

158160
for (const candidate of candidates) {
159-
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
161+
if (
162+
fs.existsSync(/*turbopackIgnore: true*/ candidate)
163+
&& fs.statSync(/*turbopackIgnore: true*/ candidate).isFile()
164+
) {
160165
const ext = path.extname(candidate).toLowerCase()
161166
res.writeHead(200, { 'Content-Type': MIME_MAP[ext] || 'application/octet-stream' })
162-
fs.createReadStream(candidate).pipe(res)
167+
fs.createReadStream(/*turbopackIgnore: true*/ candidate).pipe(res)
163168
return
164169
}
165170
}
166171

167-
if (fs.existsSync(normalizedFile) && fs.statSync(normalizedFile).isDirectory()) {
168-
const files = fs.readdirSync(normalizedFile)
172+
if (
173+
fs.existsSync(/*turbopackIgnore: true*/ normalizedFile)
174+
&& fs.statSync(/*turbopackIgnore: true*/ normalizedFile).isDirectory()
175+
) {
176+
const files = fs.readdirSync(/*turbopackIgnore: true*/ normalizedFile)
169177
const links = files.map((f) => `<li><a href="${reqPath.replace(/\/$/, '')}/${f}">${f}</a></li>`).join('\n')
170178
res.writeHead(200, { 'Content-Type': 'text/html' })
171179
res.end(`<!DOCTYPE html><html><head><title>Index of ${reqPath}</title><style>body{font-family:monospace;padding:20px;background:#1a1a2e;color:#e0e0e0}a{color:#60a5fa}</style></head><body><h2>Index of ${reqPath}</h2><ul>${links}</ul></body></html>`)
@@ -183,7 +191,7 @@ function createStaticServer(dir: string): http.Server {
183191

184192
async function startNpmServer(dir: string, command: string[], port: number, framework?: string): Promise<PreviewServer> {
185193
// Install deps if node_modules missing
186-
if (!fs.existsSync(path.join(dir, 'node_modules'))) {
194+
if (!fs.existsSync(/*turbopackIgnore: true*/ path.join(/*turbopackIgnore: true*/ dir, 'node_modules'))) {
187195
log.info(TAG, `Installing dependencies in ${dir}`)
188196
await new Promise<void>((resolve, reject) => {
189197
const install = spawn('npm', ['install'], { cwd: dir, stdio: 'pipe' })
@@ -290,7 +298,7 @@ export async function POST(req: Request) {
290298
return NextResponse.json(buildResponse(servers.get(key)!))
291299
}
292300

293-
if (!fs.existsSync(dir)) {
301+
if (!fs.existsSync(/*turbopackIgnore: true*/ dir)) {
294302
return NextResponse.json({ error: 'Directory not found' }, { status: 404 })
295303
}
296304

src/app/api/search/route.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import assert from 'node:assert/strict'
2+
import test from 'node:test'
3+
4+
import { runWithTempDataDir } from '@/lib/server/test-utils/run-with-temp-data-dir'
5+
6+
test('global search finds repo-backed session messages after blob compaction', () => {
7+
const output = runWithTempDataDir<{
8+
messageTitles: string[]
9+
messageIndexes: number[]
10+
}>(`
11+
const storageMod = await import('@/lib/server/storage')
12+
const repoMod = await import('@/lib/server/messages/message-repository')
13+
const routeMod = await import('./src/app/api/search/route')
14+
const storage = storageMod.default || storageMod
15+
const repo = repoMod.default || repoMod
16+
const route = routeMod.default || routeMod
17+
18+
storage.saveAgents({
19+
'agent-search': {
20+
id: 'agent-search',
21+
name: 'Search Agent',
22+
description: 'Search fixture',
23+
provider: 'openai',
24+
model: 'gpt-5',
25+
createdAt: Date.now(),
26+
updatedAt: Date.now(),
27+
},
28+
})
29+
storage.saveSessions({
30+
'sess-search': {
31+
id: 'sess-search',
32+
name: 'Search Session',
33+
cwd: process.env.WORKSPACE_DIR,
34+
user: 'tester',
35+
provider: 'openai',
36+
model: 'gpt-5',
37+
agentId: 'agent-search',
38+
claudeSessionId: null,
39+
codexThreadId: null,
40+
opencodeSessionId: null,
41+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
42+
messages: [],
43+
createdAt: Date.now(),
44+
lastActiveAt: Date.now(),
45+
},
46+
})
47+
48+
repo.appendMessage('sess-search', { role: 'user', text: 'ordinary setup note', time: 10 })
49+
repo.appendMessage('sess-search', { role: 'assistant', text: 'needle-backed answer from the message table', time: 20 })
50+
51+
const response = await route.GET(new Request('http://local/api/search?q=needle-backed'))
52+
const payload = await response.json()
53+
const messageResults = payload.results.filter((result) => result.type === 'message')
54+
55+
console.log(JSON.stringify({
56+
messageTitles: messageResults.map((result) => result.title),
57+
messageIndexes: messageResults.map((result) => result.messageIndex),
58+
}))
59+
`, { prefix: 'swarmclaw-search-repo-messages-' })
60+
61+
assert.deepEqual(output.messageTitles, ['needle-backed answer from the message table'])
62+
assert.deepEqual(output.messageIndexes, [1])
63+
})

0 commit comments

Comments
 (0)