diff --git a/CHANGELOG.md b/CHANGELOG.md index 2201195..cb4c498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.25] - 2026-07-29 + +### Fixed + +- Linux systemd updates now open the health port before initializing every + retained resident memory database. Large existing workspaces no longer miss + the updater's bounded startup window and roll back an otherwise healthy + release while the same durable memory initialization continues in the + background. +- Includes the Windows host-terminal, Windows WSL identity, Linux LXC network + recovery, and honest lifecycle/command failure corrections prepared in + `0.0.24`; `0.0.24` was never tagged or published. + ## [0.0.24] - 2026-07-29 ### Fixed @@ -751,7 +764,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 notarization, stapled tickets, Gatekeeper verification, persistent Application Support, and isolated Apple container machines. -[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.24...HEAD +[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.25...HEAD +[0.0.25]: https://github.com/gitcommit90/1Helm/compare/v0.0.23...v0.0.25 [0.0.24]: https://github.com/gitcommit90/1Helm/compare/v0.0.23...v0.0.24 [0.0.23]: https://github.com/gitcommit90/1Helm/compare/v0.0.22...v0.0.23 [0.0.22]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.22 diff --git a/README.md b/README.md index edbf4b1..af26ace 100644 --- a/README.md +++ b/README.md @@ -307,7 +307,7 @@ A fresh data directory opens first-run setup. The source runtime defaults to | `PORT` | `8123` | HTTP/WebSocket control-plane port. | | `CTRL_DATA_DIR` | `./data` | Databases, routing state, uploads, and narrow workspace mirrors. | | `HELM_CHANNEL_COMPUTER_BACKEND` | `apple` on macOS, `lxc` on Linux, `wsl` on Windows | Host isolation backend; `native` and `mock` are explicit development/test overrides. | -| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.24` | Versioned channel-machine image contract. | +| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.25` | Versioned channel-machine image contract. | ### Agent-first JSON CLI diff --git a/package-lock.json b/package-lock.json index 817572b..80d6515 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "1helm", - "version": "0.0.24", + "version": "0.0.25", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "1helm", - "version": "0.0.24", + "version": "0.0.25", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 43752b4..a0fbe63 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "1helm", "productName": "1Helm", - "version": "0.0.24", + "version": "0.0.25", "private": true, "type": "module", "license": "AGPL-3.0-only", diff --git a/src/server/channel-computers.ts b/src/server/channel-computers.ts index 9e2fedc..dac0fe5 100644 --- a/src/server/channel-computers.ts +++ b/src/server/channel-computers.ts @@ -67,7 +67,7 @@ const APPLE_RUNTIME_VERSION = "1.1.0"; export const APPLE_RUNTIME_PACKAGE = `container-${APPLE_RUNTIME_VERSION}-installer-signed.pkg`; export const APPLE_RUNTIME_URL = `https://github.com/apple/container/releases/download/${APPLE_RUNTIME_VERSION}/${APPLE_RUNTIME_PACKAGE}`; export const APPLE_RUNTIME_SHA256 = "0ca1c42a2269c2557efb1d82b1b38ac553e6a3a3da1b1179c439bcee1e7d6714"; -export const DEFAULT_CHANNEL_IMAGE = process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.24"; +export const DEFAULT_CHANNEL_IMAGE = process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.25"; const CONTAINER_CANDIDATES = [process.env.HELM_CONTAINER_CLI, "/usr/local/bin/container", "/opt/homebrew/bin/container", "container"].filter(Boolean) as string[]; const LXC_RUNTIME_VERSION = "1helm-lxc-runtime-v1"; const LXC_HELPER_CANDIDATES = [ diff --git a/src/server/db.ts b/src/server/db.ts index 0a3bc72..4b8f436 100644 --- a/src/server/db.ts +++ b/src/server/db.ts @@ -1009,7 +1009,7 @@ export function migrate(): void { const platformBackend = process.platform === "darwin" ? "apple" : process.platform === "win32" ? "wsl" : "lxc"; const configuredBackend = String(process.env.HELM_CHANNEL_COMPUTER_BACKEND || platformBackend); const backend = ["apple", "lxc", "wsl", "native", "mock"].includes(configuredBackend) ? configuredBackend : platformBackend; - const image = String(process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.24"); + const image = String(process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.25"); // Earlier Linux/Windows releases persisted the compatibility `native` // seam into every channel row. A production host update must actually // move those rows onto the platform isolation backend; changing the unit's diff --git a/src/server/index.ts b/src/server/index.ts index ef58e16..38dc1ce 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -77,7 +77,7 @@ import { markdownToDocx } from "./docx.ts"; import { configurePhoton, continuePhotonConversation, deliverPhotonEvent, photonConversation, photonConversations, photonStatus, registerPhotonDispatcher, startPhotonConnector, stopPhotonConnector } from "./photon.ts"; import { photonSetupStatus, startPhotonSetup } from "./photon-auth.ts"; import { completeGmailConnection, gmailConnectionStatus, saveGmailOAuthClient, startGmailConnection } from "./gmail.ts"; -import { cancelMnemosyneRuntimePreparation, ensureAgentMemory, mnemosyneAvailable, prepareMnemosyneRuntime } from "./memory.ts"; +import { cancelMnemosyneRuntimePreparation, mnemosyneAvailable, prepareMnemosyneRuntime } from "./memory.ts"; import { attachCoworkClient, coworkPresence, coworkViewerUsernames, flushCoworkDocuments, normalizeCoworkFolderPath, normalizeCoworkPath } from "./cowork-collaboration.ts"; import { runImprovementPass, scheduleAgentReview, startImprovementLoop } from "./improvements.ts"; import { runThreadAuditPass, startThreadAuditLoop } from "./thread-audit.ts"; @@ -2245,7 +2245,6 @@ async function bootstrap(): Promise { run("DELETE FROM bot_computers WHERE computer_id=? AND bot_id IN (SELECT bot_id FROM agents WHERE kind='channel')", computerId); for (const channel of q("SELECT id FROM channels WHERE kind='channel' AND status<>'deleted'")) { ensureChannelWorkspace(Number(channel.id)); - const agent = agentForChannel(Number(channel.id)); if (agent) ensureAgentMemory(agent); } startImprovementLoop(); startThreadAuditLoop(); @@ -2260,13 +2259,11 @@ async function bootstrap(): Promise { const address = server.address(); const port = typeof address === "object" && address ? address.port : PORT; console.log(`1Helm on 1Helm → http://${HOST === "0.0.0.0" ? "localhost" : HOST}:${port} (local agent on ${agentPort}) data: ${DATA_DIR}`); - void memoryRuntime.then((ready) => { - if (!ready) return; - for (const channel of q("SELECT id FROM channels WHERE kind='channel' AND status<>'deleted'")) { - const agent = agentForChannel(Number(channel.id)); - if (agent) ensureAgentMemory(agent); - } - }).catch((error) => console.warn(`1Helm could not prepare durable memory: ${(error as Error).message}`)); + // Runtime preparation remains asynchronous. Resident databases initialize + // when agents are created and lazily on their first memory operation; a + // retained fleet must never synchronously serialize Python init work on the + // server event loop before or after the health port opens. + void memoryRuntime.catch((error) => console.warn(`1Helm could not prepare durable memory: ${(error as Error).message}`)); void queueLinuxHostContractMigration(DATA_DIR).catch((error) => console.warn(`1Helm could not queue its Linux host-contract migration: ${(error as Error).message}`)); }); } diff --git a/test/channel-computers.mjs b/test/channel-computers.mjs index 3b68ed5..ae53e77 100644 --- a/test/channel-computers.mjs +++ b/test/channel-computers.mjs @@ -168,7 +168,7 @@ test("Apple channel-computer contract preserves isolation, files, wakes, archive test("runtime digest and packaged image recipe stay pinned", async () => { assert.equal(computers.APPLE_RUNTIME_SHA256, "0ca1c42a2269c2557efb1d82b1b38ac553e6a3a3da1b1179c439bcee1e7d6714"); assert.match(computers.APPLE_RUNTIME_URL, /\/1\.1\.0\/container-1\.1\.0-installer-signed\.pkg$/); - assert.equal(computers.DEFAULT_CHANNEL_IMAGE, "local/1helm-channel-machine:0.0.24"); + assert.equal(computers.DEFAULT_CHANNEL_IMAGE, "local/1helm-channel-machine:0.0.25"); const packaging = await readFile(join(root, "scripts", "package-mac-dmg.cjs"), "utf8"); assert.match(packaging, /container\(\?:\$\|\\\/\)/, "release packaging includes container/ image assets"); const image = await readFile(join(root, "container", "Containerfile"), "utf8"); diff --git a/test/desktop.mjs b/test/desktop.mjs index 7aa9c35..b24cc5f 100644 --- a/test/desktop.mjs +++ b/test/desktop.mjs @@ -110,6 +110,9 @@ test("desktop entrypoint keeps the renderer sandboxed and data on the Mac", asyn assert.match(nativeUpdater, /autoUpdater\.quitAndInstall\(false, true\)/); assert.match(source, /1helm-native-update-ready/, "Electron replaces the app only after the local host runtime has quiesced"); assert.match(server, /shutdown\(true\).*1helm-native-update-ready/s, "the host update API closes server work before handing installation back to Electron"); + const bootstrap = server.slice(server.indexOf("async function bootstrap"), server.indexOf("void bootstrap()")); + assert.doesNotMatch(bootstrap, /ensureAgentMemory\(/, "retained resident memory initialization cannot block the event loop during a bounded host update"); + assert.match(bootstrap, /void memoryRuntime\.catch/, "optional memory-runtime preparation remains asynchronous during startup"); assert.match(server, /\["native-macos", "native-windows"\]\.includes\(update\.mode\)/, "both packaged host updaters quiesce the local server before replacement"); assert.match(nativeUpdater, /update\.electronjs\.org\/gitcommit90\/1Helm\/\$\{feedPlatform\}/); assert.match(nativeUpdater, /win32-x64/, "Windows checks and installs on its host through the native updater"); @@ -200,7 +203,7 @@ test("desktop entrypoint keeps the renderer sandboxed and data on the Mac", asyn assert.match(await readFile(join(root, "src", "client", "app.ts"), "utf8"), /mailto:build@1helm\.com/, "the in-app Feedback surface exposes the company contact address"); assert.match(terminalBrowser, /HELM_CHANNEL_COMPUTER_BACKEND: "native"/, "the terminal browser contract uses the explicit development backend on CI hosts without an installed LXC runtime"); const serverRuntime = await readFile(join(root, "src", "server", "index.ts"), "utf8"); - assert.match(serverRuntime, /const memoryRuntime = prepareMnemosyneRuntime\(\);[\s\S]*server\.listen\([\s\S]*memoryRuntime\.then/, "the HTTP server becomes ready before optional memory installation and initializes agent databases afterward"); + assert.match(serverRuntime, /const memoryRuntime = prepareMnemosyneRuntime\(\);[\s\S]*server\.listen\([\s\S]*void memoryRuntime\.catch/, "the HTTP server becomes ready without synchronously initializing every retained agent database"); const memoryBridge = await readFile(join(root, "scripts", "mnemosyne-bridge.py"), "utf8"); assert.match(memoryBridge, /sys\.version_info < \(3, 10\)/); assert.match(memoryBridge, /zip_longest/);