From 8cb118f0d68e4631a54869e32b6796219511dd83 Mon Sep 17 00:00:00 2001 From: joshavant <830519+joshavant@users.noreply.github.com> Date: Fri, 1 May 2026 22:46:08 -0500 Subject: [PATCH] chore(kiloclaw): update OpenClaw to 2026.4.23 --- .../(app)/claw/components/changelog-data.ts | 6 ++++++ services/kiloclaw/.dev.vars.example | 2 +- services/kiloclaw/Dockerfile | 6 +++--- .../controller/src/config-writer.test.ts | 13 +++++++++++++ .../kiloclaw/controller/src/config-writer.ts | 19 +++++++++++++++++++ .../kiloclaw/controller/src/pairing-cache.ts | 7 +++---- services/kiloclaw/e2e/docker-image-testing.md | 4 ++-- .../kiloclaw/plugins/kilo-chat/package.json | 2 +- .../durable-objects/kiloclaw-instance.test.ts | 12 ++++++------ .../kiloclaw-instance/pairing.ts | 8 ++++---- .../kiloclaw/src/lib/rollout-bucket.test.ts | 6 +++--- .../kiloclaw/src/lib/version-rollout.test.ts | 2 +- 12 files changed, 62 insertions(+), 25 deletions(-) diff --git a/apps/web/src/app/(app)/claw/components/changelog-data.ts b/apps/web/src/app/(app)/claw/components/changelog-data.ts index d0b824fea0..c5941e0835 100644 --- a/apps/web/src/app/(app)/claw/components/changelog-data.ts +++ b/apps/web/src/app/(app)/claw/components/changelog-data.ts @@ -10,6 +10,12 @@ export type ChangelogEntry = { // Newest entries first. Developers add new entries to the top of this array. export const CHANGELOG_ENTRIES: ChangelogEntry[] = [ + { + date: '2026-05-01', + description: 'Updated OpenClaw to 2026.4.23.', + category: 'feature', + deployHint: 'redeploy_suggested', + }, { date: '2026-04-28', description: diff --git a/services/kiloclaw/.dev.vars.example b/services/kiloclaw/.dev.vars.example index 11812eac3a..1a4336b0d7 100644 --- a/services/kiloclaw/.dev.vars.example +++ b/services/kiloclaw/.dev.vars.example @@ -119,7 +119,7 @@ FLY_IMAGE_DIGEST= # Used by the worker to self-register the version → image tag mapping in KV, # enabling per-user version tracking. Without this, version tracking fields # will be null and instances fall back to FLY_IMAGE_TAG directly. -OPENCLAW_VERSION=2026.4.15 +OPENCLAW_VERSION=2026.4.23 # Legacy fallback for existing instances without per-user apps. # New instances get per-user apps (acct-{hash}) created automatically. diff --git a/services/kiloclaw/Dockerfile b/services/kiloclaw/Dockerfile index 1911fd8732..e4944dbf88 100644 --- a/services/kiloclaw/Dockerfile +++ b/services/kiloclaw/Dockerfile @@ -55,7 +55,7 @@ RUN npm install -g pnpm # Patch: bump model discovery timeout from 5s to 60s — shared-cpu machines # under boot load routinely need 27-35s for the first TLS+fetch to complete. # Remove this sed patch once openclaw exposes a configurable timeout. -RUN npm install -g openclaw@2026.4.15 \ +RUN npm install -g openclaw@2026.4.23 \ && OC_DIST=/usr/local/lib/node_modules/openclaw/dist \ && FOUND_FILES=$(find "$OC_DIST" -name 'provider-models-*.js' | wc -l | tr -d ' ') \ && if [ "$FOUND_FILES" -eq 0 ]; then echo "ERROR: provider-models-*.js not found in openclaw dist" >&2; exit 1; fi \ @@ -228,8 +228,8 @@ RUN mkdir -p /root/.openclaw \ && mkdir -p /root/clawd/skills # Copy helper scripts (used at runtime by the controller/gateway) -# Build cache bust: 2026-04-24-v66-openclaw-2026.4.15-add-kilo-chat-plugin -RUN echo "12" +# Build cache bust: 2026-05-01-v67-openclaw-2026.4.23 +RUN echo "13" COPY openclaw-pairing-list.js /usr/local/bin/openclaw-pairing-list.js COPY openclaw-device-pairing-list.js /usr/local/bin/openclaw-device-pairing-list.js diff --git a/services/kiloclaw/controller/src/config-writer.test.ts b/services/kiloclaw/controller/src/config-writer.test.ts index bc8189a5f0..7076de6b3a 100644 --- a/services/kiloclaw/controller/src/config-writer.test.ts +++ b/services/kiloclaw/controller/src/config-writer.test.ts @@ -955,6 +955,7 @@ describe('generateBaseConfig', () => { expect(config.hooks.enabled).toBe(true); expect(config.hooks.token).toBe('test-hooks-token'); expect(config.hooks.path).toBe('/hooks'); + expect(config.hooks.allowedSessionKeyPrefixes).toEqual(['hook:', 'inbound-email:']); expect(config.hooks.presets).toBeUndefined(); expect(config.hooks.mappings).toContainEqual({ id: 'cloudflare-email-inbound', @@ -1018,6 +1019,18 @@ describe('generateBaseConfig', () => { expect(config.hooks.mappings).toContainEqual( expect.objectContaining({ id: 'cloudflare-email-inbound', wakeMode: 'now' }) ); + expect(config.hooks.allowedSessionKeyPrefixes).toEqual(['hook:', 'inbound-email:']); + }); + + it('preserves existing hook session key prefixes without duplicating inbound email', () => { + const existing = JSON.stringify({ + hooks: { allowedSessionKeyPrefixes: ['custom:', 'hook:', 'inbound-email:'] }, + }); + const { deps } = fakeDeps(existing); + const env = { ...minimalEnv(), KILOCLAW_HOOKS_TOKEN: 'test-hooks-token' }; + const config = generateBaseConfig(env, '/tmp/openclaw.json', deps); + + expect(config.hooks.allowedSessionKeyPrefixes).toEqual(['custom:', 'hook:', 'inbound-email:']); }); it('adds gmail preset when Gog credentials are configured', () => { diff --git a/services/kiloclaw/controller/src/config-writer.ts b/services/kiloclaw/controller/src/config-writer.ts index 0d392e1b6e..bb459c8091 100644 --- a/services/kiloclaw/controller/src/config-writer.ts +++ b/services/kiloclaw/controller/src/config-writer.ts @@ -105,6 +105,8 @@ type ConfigObject = Record; type EnvLike = Record; const INBOUND_EMAIL_HOOK_ID = 'cloudflare-email-inbound'; +const DEFAULT_HOOK_SESSION_KEY_PREFIX = 'hook:'; +const INBOUND_EMAIL_SESSION_KEY_PREFIX = 'inbound-email:'; function migrateHookMapping(mapping: ConfigObject): ConfigObject { if (mapping.id === INBOUND_EMAIL_HOOK_ID) { @@ -541,6 +543,23 @@ export function generateBaseConfig( config.hooks.enabled = true; config.hooks.token = env.KILOCLAW_HOOKS_TOKEN; config.hooks.path = '/hooks'; + config.hooks.allowedSessionKeyPrefixes = Array.isArray(config.hooks.allowedSessionKeyPrefixes) + ? config.hooks.allowedSessionKeyPrefixes + : []; + if ( + !(config.hooks.allowedSessionKeyPrefixes as string[]).includes( + DEFAULT_HOOK_SESSION_KEY_PREFIX + ) + ) { + (config.hooks.allowedSessionKeyPrefixes as string[]).push(DEFAULT_HOOK_SESSION_KEY_PREFIX); + } + if ( + !(config.hooks.allowedSessionKeyPrefixes as string[]).includes( + INBOUND_EMAIL_SESSION_KEY_PREFIX + ) + ) { + (config.hooks.allowedSessionKeyPrefixes as string[]).push(INBOUND_EMAIL_SESSION_KEY_PREFIX); + } config.hooks.mappings = Array.isArray(config.hooks.mappings) ? config.hooks.mappings.map((mapping: ConfigObject) => migrateHookMapping(mapping)) diff --git a/services/kiloclaw/controller/src/pairing-cache.ts b/services/kiloclaw/controller/src/pairing-cache.ts index 692ff38285..2282edfa07 100644 --- a/services/kiloclaw/controller/src/pairing-cache.ts +++ b/services/kiloclaw/controller/src/pairing-cache.ts @@ -72,10 +72,9 @@ export const DEBOUNCE_DELAY_MS = 2_000; export const FAILURE_RETRY_BASE_MS = 30_000; export const FAILURE_RETRY_MAX_MS = 300_000; -// TEMPORARY: bumped from 45_000 to 180_000 because openclaw 2026.4.15 CLI -// startup takes ~65s (CPU profile shows ~55% in jiti normalizeAliases/createJiti -// from per-plugin loader churn). Revert to 45_000 once openclaw upstream -// reduces startup time. Tracking: . +// TEMPORARY: bumped from 45_000 to 180_000 because OpenClaw CLI startup can +// exceed 60s on shared-cpu instances. Revert to 45_000 once upstream startup +// is consistently below the old timeout on full KiloClaw images. export const APPROVE_TIMEOUT_MS = 180_000; export const CONFIG_PATH = '/root/.openclaw/openclaw.json'; diff --git a/services/kiloclaw/e2e/docker-image-testing.md b/services/kiloclaw/e2e/docker-image-testing.md index 1760c72017..f70574ff64 100644 --- a/services/kiloclaw/e2e/docker-image-testing.md +++ b/services/kiloclaw/e2e/docker-image-testing.md @@ -140,8 +140,8 @@ docker rm kiloclaw-gateway ```bash # Check versions -docker run --rm kiloclaw:test node --version # v24.14.1 -docker run --rm kiloclaw:test openclaw --version # 2026.4.15 +docker run --rm kiloclaw:test node --version # v24.15.0 +docker run --rm kiloclaw:test openclaw --version # 2026.4.23 # Check directories docker run --rm kiloclaw:test ls -la /root/.openclaw diff --git a/services/kiloclaw/plugins/kilo-chat/package.json b/services/kiloclaw/plugins/kilo-chat/package.json index c3aba08897..82fba47593 100644 --- a/services/kiloclaw/plugins/kilo-chat/package.json +++ b/services/kiloclaw/plugins/kilo-chat/package.json @@ -28,7 +28,7 @@ "test": "vitest run" }, "peerDependencies": { - "openclaw": "2026.4.15" + "openclaw": "2026.4.23" }, "devDependencies": { "esbuild": "^0.25.2", diff --git a/services/kiloclaw/src/durable-objects/kiloclaw-instance.test.ts b/services/kiloclaw/src/durable-objects/kiloclaw-instance.test.ts index 9b1d2d7217..24e70e4815 100644 --- a/services/kiloclaw/src/durable-objects/kiloclaw-instance.test.ts +++ b/services/kiloclaw/src/durable-objects/kiloclaw-instance.test.ts @@ -7303,9 +7303,9 @@ describe('applyPinnedVersion', () => { }); (selectImageVersionForInstance as Mock).mockResolvedValueOnce({ - openclawVersion: '2026.4.15', + openclawVersion: '2026.4.23', variant: 'default', - imageTag: '2026-04-15', + imageTag: '2026-04-23', imageDigest: 'sha256:latest', publishedAt: new Date().toISOString(), rolloutPercent: 100, @@ -7314,11 +7314,11 @@ describe('applyPinnedVersion', () => { const applied = await instance.applyPinnedVersion(null); - expect(applied.imageTag).toBe('2026-04-15'); - expect(applied.openclawVersion).toBe('2026.4.15'); + expect(applied.imageTag).toBe('2026-04-23'); + expect(applied.openclawVersion).toBe('2026.4.23'); expect(selectImageVersionForInstance).toHaveBeenCalledOnce(); expect(resolveVersionByTag).not.toHaveBeenCalled(); - expect(storage._store.get('trackedImageTag')).toBe('2026-04-15'); + expect(storage._store.get('trackedImageTag')).toBe('2026-04-23'); }); it('when cleared, passes currentImageTag=null to the selector so non-cohort users can fall off the pinned candidate', async () => { @@ -7335,7 +7335,7 @@ describe('applyPinnedVersion', () => { // ignoreCurrentImageTag, it should instead be invoked with // currentImageTag=null and return :latest. (selectImageVersionForInstance as Mock).mockResolvedValueOnce({ - openclawVersion: '2026.4.15', + openclawVersion: '2026.4.23', variant: 'default', imageTag: 'latest-tag', imageDigest: 'sha256:latest', diff --git a/services/kiloclaw/src/durable-objects/kiloclaw-instance/pairing.ts b/services/kiloclaw/src/durable-objects/kiloclaw-instance/pairing.ts index c3dacddf13..3d3c9b2a7b 100644 --- a/services/kiloclaw/src/durable-objects/kiloclaw-instance/pairing.ts +++ b/services/kiloclaw/src/durable-objects/kiloclaw-instance/pairing.ts @@ -184,8 +184,8 @@ export async function approvePairingRequest( 'POST', ControllerPairingApproveResponseSchema, { channel, code }, - // TEMPORARY: 180s timeout — openclaw 2026.4.15 CLI startup is ~65s - // (jiti loader churn). Revert to default 30s once openclaw startup is fixed. + // TEMPORARY: 180s timeout while OpenClaw CLI startup can exceed 60s on + // shared-cpu instances. Revert once full-image startup is consistently fast. { timeoutMs: 180_000 } ); } catch (error) { @@ -389,8 +389,8 @@ export async function approveDevicePairingRequest( 'POST', ControllerPairingApproveResponseSchema, { requestId }, - // TEMPORARY: 180s timeout — openclaw 2026.4.15 CLI startup is ~65s - // (jiti loader churn). Revert to default 30s once openclaw startup is fixed. + // TEMPORARY: 180s timeout while OpenClaw CLI startup can exceed 60s on + // shared-cpu instances. Revert once full-image startup is consistently fast. { timeoutMs: 180_000 } ); } catch (error) { diff --git a/services/kiloclaw/src/lib/rollout-bucket.test.ts b/services/kiloclaw/src/lib/rollout-bucket.test.ts index 25ef3c8df3..f812a5e90a 100644 --- a/services/kiloclaw/src/lib/rollout-bucket.test.ts +++ b/services/kiloclaw/src/lib/rollout-bucket.test.ts @@ -11,7 +11,7 @@ describe('rolloutBucket', () => { }); it('is deterministic for the same key', async () => { - const key = 'tag:kiloclaw-2026.4.15-abc:instance:550e8400-e29b-41d4-a716-446655440000'; + const key = 'tag:kiloclaw-2026.4.23-abc:instance:550e8400-e29b-41d4-a716-446655440000'; const a = await rolloutBucket(key); const b = await rolloutBucket(key); expect(a).toBe(b); @@ -37,8 +37,8 @@ describe('rolloutBucket', () => { it('changes the bucket for a fixed instanceId when the salt (imageTag) changes', async () => { const instanceId = '550e8400-e29b-41d4-a716-446655440001'; - const a = await rolloutBucket(`tag:kiloclaw-2026.4.15-aaa:instance:${instanceId}`); - const b = await rolloutBucket(`tag:kiloclaw-2026.4.15-bbb:instance:${instanceId}`); + const a = await rolloutBucket(`tag:kiloclaw-2026.4.23-aaa:instance:${instanceId}`); + const b = await rolloutBucket(`tag:kiloclaw-2026.4.23-bbb:instance:${instanceId}`); // Two different salts (rebuilds of the same upstream version) must produce // independent draws so the same instance is not always the canary. expect(a).not.toBe(b); diff --git a/services/kiloclaw/src/lib/version-rollout.test.ts b/services/kiloclaw/src/lib/version-rollout.test.ts index e6647db9d5..f6bc484491 100644 --- a/services/kiloclaw/src/lib/version-rollout.test.ts +++ b/services/kiloclaw/src/lib/version-rollout.test.ts @@ -28,7 +28,7 @@ function createJsonKV(): KVNamespace & { _store: Map } { function entry(imageTag: string, rolloutPercent: number, isLatest = false): ImageVersionEntry { return { - openclawVersion: '2026.4.15', + openclawVersion: '2026.4.23', variant: 'default', imageTag, imageDigest: null,