diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6dcf0a7..333b2c2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [0.0.6] - 2026-07-24
+
+### Added
+
+- Linux systemd hosts now give every ordinary channel a persistent
+ unprivileged LXC computer with private networking, subordinate UID/GID maps,
+ exact owner markers, managed CPU/RAM, host-mirrored files, and no host-home
+ mount. The installer pins and verifies the Ubuntu Noble image payloads and
+ installs a narrow root-owned lifecycle boundary.
+- The accepted Windows 11 implementation now has a native x64 desktop and one
+ private WSL 2 Ubuntu distribution per ordinary channel. Windows-drive
+ automount and process interop are disabled, Ubuntu root filesystems are
+ immutable-version and SHA-256 pinned for x64 and arm64, and setup/removal
+ verify exact ownership. Its public installer is withheld until Authenticode
+ signing is available.
+- Durable feedback is always visible, accepts optional attachments and
+ diagnostics, retries central delivery, and provides a Captain feedback inbox.
+- Residents can search their own authoritative raw channel transcripts by
+ meaning, exact text/date, or recency and hydrate a complete prior session on
+ demand. Each channel keeps a separate Mnemosyne index; guests and other
+ channel residents cannot access it.
+- SkillsMD discovery now queries the open registry directly and displays every
+ returned result. Revision pinning, bounds, scanning, hashing, and runtime
+ wrapping remain enforced when the user chooses to install.
+
+### Changed
+
+- Linux and Windows are production isolation backends; the in-process native
+ computer remains an explicit development/test seam rather than the default.
+- Linux update requests are executed entirely by the host. The verified root
+ updater migrates runtime files and systemd units transactionally, refuses
+ downgrades, health-checks for up to one minute, and reports rollback complete
+ only after the restored host answers its health endpoint.
+- The website and documentation describe the macOS, Linux/LXC, and Windows/WSL
+ product contracts without claiming an unsigned Windows artifact is public.
+
+### Fixed
+
+- Fresh hosts now open the 1Helm server while the optional Mnemosyne Python
+ and embedding runtime is prepared in the background, instead of making
+ first launch and health checks wait on virtual-environment package installs.
+- Newly created and newly assigned skills now appear immediately in already
+ open Arsenal and Channel Settings views without a page refresh.
+- Linux upgrades migrate existing compatibility computer records to LXC while
+ retaining channels, workspaces, obligations, and durable application state.
+- Fresh/repeat Linux installs reject unsafe rollback symlinks and install
+ Python venv support required by long-term Mnemosyne memory.
+- App removal, fleet inspection, lifecycle commands, terminal execution,
+ workspace synchronization, and ownership refusal now cover Apple, LXC, and
+ WSL backends consistently.
+
## [0.0.5] - 2026-07-24
### Changed
@@ -133,7 +184,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.5...HEAD
+[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.6...HEAD
+[0.0.6]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.6
[0.0.5]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.5
[0.0.4]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.4
[0.0.3]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.3
diff --git a/README.md b/README.md
index 06da916..ab14b8c 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ release acceptance tests.
Apple Silicon native34 complete playbooks
- Focused SkillsMD catalog
+ Open SkillsMD searchSigned + notarized
@@ -102,7 +102,7 @@ The model receives a compact inventory of the arsenal—not all 34 procedures in
every prompt. It can inspect metadata and load one complete skill when useful.
It can also:
-- search the focused SkillsMD catalog of ready GitHub-backed repositories;
+- search SkillsMD directly without a 1Helm-curated subset, then inspect and install a selected GitHub-backed skill;
- install ready skills only after immutable revision pinning, bounds,
scanning, hashing, provenance storage, and runtime-authority wrapping;
- route sources without a ready repository-specific procedure through the
@@ -154,6 +154,11 @@ does not run on the laptop or phone viewing the web UI.
- Exactly one resident for every ordinary channel and one Skipper in `#main`.
- A persistent Apple `container machine` Linux VM per ordinary channel, with
`home-mount=none`, on supported Apple Silicon Macs.
+- A persistent unprivileged LXC per ordinary channel on supported Linux
+ systemd hosts, with subordinate UID/GID mapping and exact ownership checks.
+- A private WSL 2 Ubuntu distribution per ordinary channel in the accepted
+ Windows implementation, with Windows-drive mounts and interop disabled. Its
+ public installer remains withheld until Authenticode signing is available.
- Shared channel `/workspace` for the agent command surface and human Terminal.
- Durable files, threads, curated memory, Mnemosyne long-term recall,
corrections, follow-ups, and recurring workflows.
@@ -176,12 +181,12 @@ does not run on the laptop or phone viewing the web UI.
| Platform | Current contract |
|---|---|
| **Apple Silicon macOS 26** | Native desktop product and real isolated Linux computer per resident. |
-| **Linux / CI** | Durable headless compatibility backend; not per-resident VM isolation. |
-| **Windows + WSL** | Headless compatibility path; not a native Windows app. |
+| **Linux / CI** | Supported headless systemd host with one unprivileged LXC per resident; CI may select an explicit test backend. |
+| **Windows + WSL** | Native x64 Electron and private WSL 2 worlds have passed real-host acceptance; the public installer awaits Authenticode signing. |
-Not yet shipped: native Windows and Linux desktop packages, mobile clients,
-Linux resident VM isolation, a hosted control plane, rich Photon attachment
-fidelity, or blind execution of community skills.
+Not yet shipped: a signed public Windows installer, a native Linux desktop
+shell, mobile clients, a hosted control plane, rich Photon attachment fidelity,
+or blind execution of community skills.
## Install on Apple Silicon
@@ -212,8 +217,8 @@ checkouts remain operator-managed and never send a Mac installer to the browser.
## Run the source workspace
-The native Mac app is the complete consumer product. For development and
-headless compatibility deployments, use Node 22:
+For development or a source deployment outside the verified platform
+installers, use Node 22:
```bash
PUPPETEER_SKIP_DOWNLOAD=1 npm install
@@ -230,8 +235,8 @@ 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, `native` elsewhere | Explicit development/test backend override. |
-| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.5` | Versioned Apple channel-machine image. |
+| `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.6` | Versioned channel-machine image contract. |
### Agent-first JSON CLI
@@ -249,8 +254,9 @@ npm run helm -- audit-verify
## Architecture
-1Helm is a compact Node/TypeScript control plane hosted by Electron on macOS.
-It does not need an external database or a server transpilation step.
+1Helm is a compact Node/TypeScript control plane hosted by Electron on macOS
+and in the accepted Windows implementation, or by systemd on Linux. It does not
+need an external database or a server transpilation step.
| Layer | Implementation |
|---|---|
@@ -258,11 +264,11 @@ It does not need an external database or a server transpilation step.
| Control plane | `node:http`, WebSocket, additive SQLite migrations. |
| Client | Vanilla TypeScript bundled with esbuild and Tailwind CSS. |
| Model routing | Embedded ReRouted headless engine, private internal gateway, account pools, retries, routes, quotas, and logs. |
-| Computers | Defensive argv-only Apple `container machine` backend; explicit compatibility backend elsewhere. |
+| Computers | Defensive argv-only Apple `container machine`, narrow root-owned unprivileged LXC, and private WSL 2 backends; explicit `native`/`mock` test seams. |
| Terminal | `node-pty`; ordinary terminals enter their channel VM while Skipper remains native. |
| Memory | Curated records with provenance plus an isolated Mnemosyne SQLite store per identity. |
| Scheduling | Durable obligations, wake reconciliation, lifecycle safety, repair, update, and pressure-aware sizing. |
-| Desktop | Sandboxed Electron renderer, ephemeral loopback server, persistent Application Support, native wake agent. |
+| Desktop | Sandboxed Electron renderer, ephemeral loopback server, persistent host data, and native wake/update integration on supported desktop hosts. |
Start with [`docs/VISION.md`](docs/VISION.md) for the product record and
[`docs/architecture`](https://1helm.com/docs/architecture) for the readable
@@ -286,7 +292,9 @@ or a complete security score.
## Security boundary
-- Resident Macs use separate Linux VMs with no native Mac home mount.
+- Residents use separate Linux worlds: Apple machines with no Mac home mount,
+ unprivileged LXC with subordinate host IDs, or private WSL 2 distributions
+ with Windows-drive mounts and interop disabled.
- Skipper's host tools require Captain-authorized provenance.
- Workspace file mirrors are channel-scoped, size-bounded, and symlink-contained.
- Provider and connection credentials stay in host-owned storage.
diff --git a/cloudflare/migrations/0001_initial.sql b/cloudflare/migrations/0001_initial.sql
new file mode 100644
index 0000000..783631b
--- /dev/null
+++ b/cloudflare/migrations/0001_initial.sql
@@ -0,0 +1,39 @@
+CREATE TABLE IF NOT EXISTS workspaces (
+ slug TEXT PRIMARY KEY,
+ hostname TEXT NOT NULL UNIQUE,
+ installation_id TEXT NOT NULL UNIQUE,
+ workspace_name TEXT NOT NULL,
+ management_secret_hash TEXT NOT NULL,
+ tunnel_id TEXT NOT NULL DEFAULT '',
+ connector_secret_cipher TEXT NOT NULL DEFAULT '',
+ status TEXT NOT NULL DEFAULT 'provisioning',
+ enabled INTEGER NOT NULL DEFAULT 1,
+ error TEXT NOT NULL DEFAULT '',
+ created_at INTEGER NOT NULL,
+ updated_at INTEGER NOT NULL
+);
+
+CREATE INDEX IF NOT EXISTS idx_workspaces_installation ON workspaces(installation_id);
+
+CREATE TABLE IF NOT EXISTS feedback_reports (
+ public_id TEXT PRIMARY KEY,
+ installation_id TEXT NOT NULL,
+ workspace_name TEXT NOT NULL DEFAULT '',
+ comment TEXT NOT NULL,
+ diagnostics TEXT NOT NULL DEFAULT '{}',
+ attachment_count INTEGER NOT NULL DEFAULT 0,
+ created_at INTEGER NOT NULL,
+ received_at INTEGER NOT NULL
+);
+
+CREATE INDEX IF NOT EXISTS idx_feedback_received ON feedback_reports(received_at DESC);
+
+CREATE TABLE IF NOT EXISTS feedback_attachments (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ report_id TEXT NOT NULL REFERENCES feedback_reports(public_id) ON DELETE CASCADE,
+ name TEXT NOT NULL,
+ mime TEXT NOT NULL,
+ size INTEGER NOT NULL,
+ data TEXT NOT NULL,
+ created_at INTEGER NOT NULL
+);
diff --git a/cloudflare/schema.sql b/cloudflare/schema.sql
index 38f12ee..4480b45 100644
--- a/cloudflare/schema.sql
+++ b/cloudflare/schema.sql
@@ -14,3 +14,25 @@ CREATE TABLE IF NOT EXISTS workspaces (
);
CREATE INDEX IF NOT EXISTS idx_workspaces_installation ON workspaces(installation_id);
+
+CREATE TABLE IF NOT EXISTS feedback_reports (
+ public_id TEXT PRIMARY KEY,
+ installation_id TEXT NOT NULL,
+ workspace_name TEXT NOT NULL DEFAULT '',
+ comment TEXT NOT NULL,
+ diagnostics TEXT NOT NULL DEFAULT '{}',
+ attachment_count INTEGER NOT NULL DEFAULT 0,
+ created_at INTEGER NOT NULL,
+ received_at INTEGER NOT NULL
+);
+CREATE INDEX IF NOT EXISTS idx_feedback_received ON feedback_reports(received_at DESC);
+
+CREATE TABLE IF NOT EXISTS feedback_attachments (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ report_id TEXT NOT NULL REFERENCES feedback_reports(public_id) ON DELETE CASCADE,
+ name TEXT NOT NULL,
+ mime TEXT NOT NULL,
+ size INTEGER NOT NULL,
+ data TEXT NOT NULL,
+ created_at INTEGER NOT NULL
+);
diff --git a/cloudflare/src/worker.ts b/cloudflare/src/worker.ts
index 603558a..3c281df 100644
--- a/cloudflare/src/worker.ts
+++ b/cloudflare/src/worker.ts
@@ -4,6 +4,8 @@ interface Env {
CLOUDFLARE_ZONE_ID: string;
CLOUDFLARE_RUNTIME_TOKEN: string;
PROVISION_LIMIT?: RateLimit;
+ FEEDBACK_LIMIT?: RateLimit;
+ FEEDBACK_ADMIN_TOKEN?: string;
}
type WorkspaceRow = {
@@ -151,11 +153,76 @@ async function workspaceAction(request: Request, env: Env, slug: string): Promis
return json({ workspace: { slug, hostname: workspace.hostname, status: workspace.status, enabled } });
}
+async function feedbackIntake(request: Request, env: Env): Promise {
+ const address = request.headers.get("cf-connecting-ip") || "unknown";
+ if (env.FEEDBACK_LIMIT && !(await env.FEEDBACK_LIMIT.limit({ key: address })).success) {
+ return json({ error: "Too many feedback reports. Try again shortly." }, 429);
+ }
+ const body = await request.json().catch(() => ({})) as Record;
+ const publicId = String(body.public_id || "");
+ const installationId = String(body.installation_id || "");
+ const workspaceName = String(body.workspace_name || "").trim().slice(0, 100);
+ const comment = String(body.comment || "").trim().slice(0, 10_000);
+ const diagnostics = body.diagnostics && typeof body.diagnostics === "object" && !Array.isArray(body.diagnostics) ? body.diagnostics : {};
+ const attachments = Array.isArray(body.attachments) ? body.attachments.slice(0, 3) as Array> : [];
+ if (!/^fb_[a-f0-9]{24}$/.test(publicId) || !/^[a-f0-9]{16}$/.test(installationId)) {
+ return json({ error: "Feedback source could not be verified." }, 400);
+ }
+ if (!comment && !attachments.length) return json({ error: "Feedback is empty." }, 400);
+ if (JSON.stringify(diagnostics).length > 64 * 1024) return json({ error: "Diagnostics are too large." }, 413);
+ let total = 0;
+ for (const attachment of attachments) {
+ const size = Number(attachment.size || 0);
+ const data = String(attachment.data || "");
+ const validBase64 = data.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(data);
+ const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
+ const decodedSize = data.length ? (data.length / 4) * 3 - padding : 0;
+ if (!Number.isSafeInteger(size) || !validBase64 || decodedSize !== size
+ || size < 0 || size > 5 * 1024 * 1024 || data.length > 7 * 1024 * 1024) {
+ return json({ error: "A feedback attachment is too large." }, 413);
+ }
+ total += size;
+ }
+ if (total > 10 * 1024 * 1024) return json({ error: "Feedback attachments are too large." }, 413);
+ const timestamp = Date.now();
+ await env.REGISTRY.prepare(`INSERT OR IGNORE INTO feedback_reports
+ (public_id,installation_id,workspace_name,comment,diagnostics,attachment_count,created_at,received_at)
+ VALUES (?,?,?,?,?,?,?,?)`).bind(publicId, installationId, workspaceName, comment, JSON.stringify(diagnostics), attachments.length, timestamp, timestamp).run();
+ const exists = await env.REGISTRY.prepare("SELECT 1 FROM feedback_attachments WHERE report_id=? LIMIT 1").bind(publicId).first();
+ if (!exists) {
+ for (const attachment of attachments) await env.REGISTRY.prepare(`INSERT INTO feedback_attachments
+ (report_id,name,mime,size,data,created_at) VALUES (?,?,?,?,?,?)`).bind(
+ publicId,
+ String(attachment.name || "attachment").slice(0, 255),
+ String(attachment.mime || "application/octet-stream").slice(0, 120),
+ Number(attachment.size || 0),
+ String(attachment.data || ""),
+ timestamp,
+ ).run();
+ }
+ return json({ id: publicId }, 202);
+}
+
+async function feedbackInbox(request: Request, env: Env): Promise {
+ const token = request.headers.get("authorization")?.replace(/^Bearer\s+/i, "") || "";
+ if (!env.FEEDBACK_ADMIN_TOKEN || token !== env.FEEDBACK_ADMIN_TOKEN) return json({ error: "Not found" }, 404);
+ const results = await env.REGISTRY.prepare(`SELECT public_id,installation_id,workspace_name,comment,diagnostics,
+ attachment_count,created_at created,received_at FROM feedback_reports ORDER BY received_at DESC LIMIT 500`).all>();
+ return json({ reports: (results.results || []).map((report) => ({
+ ...report,
+ diagnostics: JSON.parse(String(report.diagnostics || "{}")),
+ state: "delivered",
+ attachments: [],
+ })) });
+}
+
export default {
async fetch(request: Request, env: Env): Promise {
if (request.method === "OPTIONS") return json({ ok: true });
const url = new URL(request.url);
if (url.pathname === "/health") return json({ ok: true });
+ if (url.pathname === "/v1/feedback" && request.method === "POST") return feedbackIntake(request, env);
+ if (url.pathname === "/v1/feedback" && request.method === "GET") return feedbackInbox(request, env);
const availability = url.pathname.match(/^\/v1\/slugs\/([a-z0-9-]+)$/);
if (availability && request.method === "GET") {
const slug = availability[1].toLowerCase();
diff --git a/cloudflare/wrangler.jsonc b/cloudflare/wrangler.jsonc
index b8f3ad9..07bedc6 100644
--- a/cloudflare/wrangler.jsonc
+++ b/cloudflare/wrangler.jsonc
@@ -11,6 +11,11 @@
"name": "PROVISION_LIMIT",
"namespace_id": "1001",
"simple": { "limit": 20, "period": 60 }
+ },
+ {
+ "name": "FEEDBACK_LIMIT",
+ "namespace_id": "1002",
+ "simple": { "limit": 30, "period": 60 }
}
],
"d1_databases": [
diff --git a/deploy/1helm-lxc-unprivileged.conf b/deploy/1helm-lxc-unprivileged.conf
new file mode 100644
index 0000000..d8f256b
--- /dev/null
+++ b/deploy/1helm-lxc-unprivileged.conf
@@ -0,0 +1,14 @@
+# 1Helm channel computers are user-namespaced Ubuntu containers. Root inside a
+# channel maps to an unprivileged host subuid/subgid and each container receives
+# only the private LXC bridge—not a host directory or device passthrough.
+lxc.include = /usr/share/lxc/config/ubuntu.userns.conf
+lxc.idmap = u 0 100000 65536
+lxc.idmap = g 0 100000 65536
+lxc.apparmor.profile = generated
+lxc.apparmor.allow_nesting = 0
+lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
+lxc.net.0.type = veth
+lxc.net.0.link = lxcbr0
+lxc.net.0.flags = up
+lxc.net.0.name = eth0
+lxc.start.auto = 0
diff --git a/desktop/main.cjs b/desktop/main.cjs
index 64ca7ea..d7c8f45 100644
--- a/desktop/main.cjs
+++ b/desktop/main.cjs
@@ -17,6 +17,26 @@ let quitting = false;
let hostUpdateService = null;
const remoteWorkspacePath = () => path.join(app.getPath("userData"), "remote-workspace");
+function handleSquirrelEvent() {
+ if (process.platform !== "win32") return false;
+ const event = process.argv[1];
+ if (!["--squirrel-install", "--squirrel-updated", "--squirrel-uninstall", "--squirrel-obsolete"].includes(event)) return false;
+ const appFolder = path.resolve(process.execPath, "..");
+ const updateExe = path.resolve(appFolder, "..", "Update.exe");
+ const exe = path.basename(process.execPath);
+ if (event === "--squirrel-install" || event === "--squirrel-updated") {
+ spawnSync(updateExe, ["--createShortcut", exe], { stdio: "ignore", windowsHide: true });
+ } else if (event === "--squirrel-uninstall") {
+ const dataRoot = path.join(String(process.env.APPDATA || ""), "1Helm");
+ const wslRoot = path.join(path.dirname(dataRoot), "1Helm-WSL");
+ const cleanup = path.resolve(__dirname, "..", "scripts", "windows-removal.cjs");
+ spawnSync(process.execPath, [cleanup, dataRoot, wslRoot], { env: { ...process.env, ELECTRON_RUN_AS_NODE: "1" }, stdio: "ignore", windowsHide: true, timeout: 10 * 60_000 });
+ spawnSync(updateExe, ["--removeShortcut", exe], { stdio: "ignore", windowsHide: true });
+ }
+ setTimeout(() => app.quit(), 1000);
+ return true;
+}
+
function preferredWorkspaceOrigin() {
try {
const value = fs.readFileSync(remoteWorkspacePath(), "utf8").trim();
@@ -74,7 +94,7 @@ async function startLocalRuntime() {
process.env.HELM_HOST = LOOPBACK;
process.env.PORT = String(port);
process.env.CTRL_DATA_DIR = app.getPath("userData");
- process.env.SHELL ||= "/bin/zsh";
+ if (process.platform !== "win32") process.env.SHELL ||= "/bin/zsh";
process.env.HELM_INTERNAL_WAKE_TOKEN ||= crypto.randomBytes(32).toString("hex");
process.chdir(appRoot);
localOrigin = `http://${LOOPBACK}:${port}`;
@@ -97,6 +117,13 @@ function keepSkipperAvailable() {
app.setLoginItemSettings({ openAtLogin: true, type: "mainAppService" });
}
+function prepareWindowsWslDataRoot() {
+ if (process.platform !== "win32") return;
+ // Per-channel virtual disks stay outside the replaceable application
+ // directory and beside the durable Electron userData directory.
+ fs.mkdirSync(path.join(path.dirname(app.getPath("userData")), "1Helm-WSL"), { recursive: true });
+}
+
function allowedLocalUrl(raw) {
try {
const url = new URL(raw);
@@ -181,7 +208,10 @@ function createWindow(showWhenReady = true) {
mainWindow = window;
}
-if (!app.requestSingleInstanceLock()) {
+if (handleSquirrelEvent()) {
+ // Squirrel install/update/uninstall work must exit before the application
+ // acquires its normal single-instance lock or starts the local server.
+} else if (!app.requestSingleInstanceLock()) {
app.quit();
} else {
app.on("second-instance", (_event, argv) => {
@@ -193,6 +223,7 @@ if (!app.requestSingleInstanceLock()) {
});
app.whenReady().then(async () => {
+ if (process.platform === "win32") app.setAppUserModelId("com.squirrel.1Helm.1Helm");
session.defaultSession.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false));
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
if (!allowedLocalUrl(details.url)) {
@@ -211,6 +242,7 @@ if (!app.requestSingleInstanceLock()) {
try {
removeLegacyWakeLaunchAgent();
keepSkipperAvailable();
+ prepareWindowsWslDataRoot();
hostUpdateService = createNativeUpdateService({ app, autoUpdater });
hostUpdateService.initialize();
globalThis[Symbol.for("1helm.nativeUpdater")] = {
@@ -227,7 +259,7 @@ if (!app.requestSingleInstanceLock()) {
await dialog.showMessageBox({
type: "error",
title: "1Helm could not start",
- message: "The local 1Helm runtime could not start on this Mac.",
+ message: `The local 1Helm runtime could not start on this ${process.platform === "win32" ? "Windows PC" : "Mac"}.`,
detail: error instanceof Error ? error.stack || error.message : String(error),
});
app.quit();
diff --git a/desktop/updater.cjs b/desktop/updater.cjs
index 61e21a7..9e2ba29 100644
--- a/desktop/updater.cjs
+++ b/desktop/updater.cjs
@@ -21,22 +21,24 @@ function createNativeUpdateService({ app, autoUpdater, platform = process.platfo
let busy = false;
let initialTimer = null;
let intervalTimer = null;
+ const nativeMode = platform === "win32" ? "native-windows" : "native-macos";
let state = {
- mode: "native-macos",
+ mode: nativeMode,
status: "idle",
current_version: app.getVersion(),
version: null,
checked_at: null,
error: null,
- message: "Check for a signed 1Helm update on this Mac.",
+ message: `Check for a signed 1Helm update on this ${platform === "win32" ? "Windows PC" : "Mac"}.`,
};
let inApplications = true;
if (platform === "darwin" && typeof app.isInApplicationsFolder === "function") {
try { inApplications = app.isInApplicationsFolder(); } catch { inApplications = false; }
}
- const supported = platform === "darwin" && arch === "arm64" && app.isPackaged === true && inApplications;
- const feedUrl = `https://update.electronjs.org/gitcommit90/1Helm/darwin-arm64/${encodeURIComponent(app.getVersion())}`;
+ const feedPlatform = platform === "win32" && arch === "x64" ? "win32-x64" : "darwin-arm64";
+ const supported = app.isPackaged === true && ((platform === "darwin" && arch === "arm64" && inApplications) || (platform === "win32" && arch === "x64"));
+ const feedUrl = `https://update.electronjs.org/gitcommit90/1Helm/${feedPlatform}/${encodeURIComponent(app.getVersion())}`;
const snapshot = () => ({ ...state });
const setState = (patch) => { state = { ...state, ...patch }; };
@@ -51,7 +53,7 @@ function createNativeUpdateService({ app, autoUpdater, platform = process.platfo
? "Move 1Helm to Applications to enable host updates."
: null,
message: app.isPackaged
- ? "Signed automatic updates are available for Apple Silicon macOS hosts."
+ ? "Signed automatic updates are available for supported macOS and Windows hosts."
: "Development builds are updated from their source checkout.",
});
return false;
@@ -120,7 +122,7 @@ function createNativeUpdateService({ app, autoUpdater, platform = process.platfo
if (state.status !== "ready") {
return { ...snapshot(), error: "No downloaded host update is ready." };
}
- setState({ status: "installing", error: null, message: "1Helm is restarting this Mac host to install the verified update…" });
+ setState({ status: "installing", error: null, message: `1Helm is restarting this ${platform === "win32" ? "Windows" : "Mac"} host to install the verified update…` });
process.env.HELM_UPDATE_INSTALLING = "1";
return snapshot();
}
diff --git a/docs/RELIABILITY.md b/docs/RELIABILITY.md
index 28f376d..17cbcba 100644
--- a/docs/RELIABILITY.md
+++ b/docs/RELIABILITY.md
@@ -23,6 +23,9 @@ ownership, recovery, and tests rather than depend on a prompt alone.
- The prompt names the size and categories of the skill arsenal. Skill metadata
is available through `list_skills`; a complete procedure enters context only
when the model calls `read_skill` for that skill.
+- A resident can search its own raw prior-session transcripts and hydrate a
+ returned session in full. These are factual tools, not an injected rule about
+ when the model must recall history; cross-channel access is server-rejected.
- Channel memory and agent recall are provenance-bearing reference data, never
higher-priority instructions.
- `ask_user` is a validated tool for evidenced human judgment, credentials,
diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md
index e60ee97..5ec479c 100644
--- a/docs/USER_GUIDE.md
+++ b/docs/USER_GUIDE.md
@@ -105,18 +105,23 @@ backgrounding the app or changing networks does not print disconnect noise or
discard the shell's working directory, exported variables, running process, or
scrollback. If the host confirms that the underlying terminal session itself no
longer exists, 1Helm opens a fresh one.
-Ordinary residents cannot select or enter the Captain's native Mac. On supported
-Apple Silicon Macs, each resident runs inside its own Apple `container machine`
-with `home-mount=none`; source/CI compatibility backends do not claim equivalent
-VM isolation.
+Ordinary residents cannot select or enter the Captain's native host. On
+supported Apple Silicon Macs, each resident runs inside its own Apple
+`container machine` with `home-mount=none`; Linux systemd hosts use one
+unprivileged LXC per resident; the accepted Windows implementation uses one
+private WSL 2 distribution per resident with Windows-drive mounts and interop
+disabled. `native` and `mock` remain explicit source/CI test seams.

## Memory, Activity, and audit
**Memory** contains curated facts, decisions, corrections, preferences, and
-procedures with provenance—not a transcript dump. Each resident and Skipper also
-has an isolated Mnemosyne store for longer-term recall.
+procedures with provenance—not a transcript dump. Raw messages remain the
+authoritative session archive. Each resident can search its own channel's prior
+sessions semantically, by exact text/date, or by recency and then read one
+returned session in full. Each resident and Skipper has an isolated Mnemosyne
+store; guests and other channel residents cannot use that index.
**Activity** presents outcome-first operational rows that mutate from working to
complete or failed. Expand a row for the retained input and outcome evidence.
@@ -136,7 +141,7 @@ sidebar; smaller layouts use a compact non-scrolling grid.
- **Admin** — workspace name, theme, image, and release/update information.
- **Agents** — Skipper and resident identities, status, models, capabilities,
and channel ownership.
-- **Skills** — built-in arsenal, focused SkillsMD catalog, installed skills,
+- **Skills** — built-in arsenal, direct SkillsMD search, installed skills,
and Learn a new skill.
- **Workflows** — recurring resident work, next run, pause/resume, run counts,
and failures.
@@ -198,10 +203,10 @@ sees a compact inventory, can list the available skills, and loads the full
procedure for one skill only when it chooses to use it. The procedures are not
injected wholesale into every turn.
-
+
-Settings searches the focused SkillsMD catalog of ready GitHub-backed
-repositories. A catalog install resolves an immutable revision, selects bounded
+Settings searches the open SkillsMD registry directly and shows every result it
+returns. A catalog install resolves an immutable revision, selects bounded
skill documentation, scans it, stores a SHA-256 digest and provenance, and wraps
it beneath 1Helm runtime authority. Unsafe content is blocked. When a ready
repository-specific procedure is unavailable, choose **Learn a new skill** so
@@ -280,7 +285,7 @@ messages, and WebSocket fan-out.
## Updates, removal, and recovery
-Signed desktop releases are unique patch versions. Profile → Check for updates
+Signed Mac releases are unique patch versions. Profile → Check for updates
always operates on the machine hosting the active 1Helm instance. In the native
Mac app, Electron downloads and verifies a notarized update ZIP on that Mac and
offers **Restart & install** only when it is ready. It does not navigate the
@@ -350,7 +355,8 @@ fallback.
## Security summary
-- Per-resident Apple Linux VMs have no Mac home mount.
+- Per-resident Apple Linux VMs have no Mac home mount; Linux LXC uses
+ subordinate IDs; private WSL worlds disable Windows-drive mounts and interop.
- Credentials and connectors remain host-owned and minimally brokered.
- Membership scopes data and live events; private coworker channels are not
Captain-readable without invitation.
diff --git a/docs/VISION.md b/docs/VISION.md
index 47619e6..f68fab5 100644
--- a/docs/VISION.md
+++ b/docs/VISION.md
@@ -2,8 +2,9 @@
1Helm gives an AI employee a place to work, not merely a chat box in which to
describe work. Every ordinary channel owns exactly one resident identity, one
-durable workspace, one memory namespace, and—on supported Apple Silicon
-Macs—one persistent private Linux computer. The human is the Captain. Skipper
+durable workspace, one memory namespace, and one persistent private Linux
+computer: an Apple container machine, unprivileged LXC, or private WSL 2
+distribution according to the host platform. The human is the Captain. Skipper
is the workspace-wide operator for host, fleet, credentials, connections, and
cross-channel boundaries.
@@ -49,12 +50,22 @@ instructions that weaken the runtime are rejected. Corrections, curated memory,
skills, artifacts, and obligations compound without binding the identity to one
model provider.
+Raw prior-session transcripts remain an on-demand resident capability rather
+than prompt baggage. Each resident can search its own channel archive by
+meaning, exact text/date, or recency and hydrate a selected session in full.
+The message database remains authoritative; the resident's separate Mnemosyne
+database is a scoped semantic index. Models receive the tools and choose when
+they are relevant without a behavioral decision tree.
+
## Computer and connection boundaries
On supported Macs, each resident computer is a real Apple container machine
-with the Mac home directory unmounted. The resident's command tools and the
-channel Terminal share the same `/workspace`. Skipper manages CPU, memory,
-disk, wake/sleep, repair, update, archive, restore, and deletion safety.
+with the Mac home directory unmounted. Linux systemd hosts use an unprivileged
+LXC with subordinate host IDs and private networking. The accepted Windows
+implementation uses one private WSL 2 distribution with Windows-drive mounts
+and interop disabled. The resident's command tools and the channel Terminal
+share the same `/workspace`. Skipper manages CPU, memory, disk, wake/sleep,
+repair, update, archive, restore, and deletion safety.
Provider accounts and native connections remain host-owned brokers. Residents
receive narrow operations, never raw OAuth tokens, Photon project secrets, or a
@@ -68,13 +79,14 @@ verification.
- `https://1helm.com` is the standalone product and documentation site.
- `https://demo.1helm.com` is a separate public sandbox, not the product site
and not a dependency of an installed workspace.
-- The native consumer product targets Apple Silicon macOS and provides one
+- Apple Silicon macOS and Linux systemd are public host products with one
isolated Linux computer per ordinary resident.
-- Linux and Windows + WSL run a durable systemd compatibility deployment today.
- They do not yet claim per-resident VM isolation or native desktop packaging.
+- The native Windows x64 + WSL implementation and real-host lifecycle are
+ accepted, but its public installer is withheld until an Authenticode signing
+ identity is available.
- 1Helm is self-hosted and open source. A hosted control plane, mobile clients,
- blind community-skill execution, and native Windows/Linux desktop apps are
- not shipped.
+ blind community-skill execution, a signed public Windows installer, and a
+ native Linux desktop shell are not shipped.
## What 1Helm borrows—and what it does not
diff --git a/package-lock.json b/package-lock.json
index 68ab38c..1c3c7bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "1helm",
- "version": "0.0.5",
+ "version": "0.0.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "1helm",
- "version": "0.0.5",
+ "version": "0.0.6",
"hasInstallScript": true,
"dependencies": {
"@gitcommit90/rerouted": "https://github.com/gitcommit90/rerouted/releases/download/v0.5.7/ReRouted-0.5.7-linux-node.tgz",
@@ -25,7 +25,9 @@
"@xterm/addon-fit": "^0.11.0",
"@xterm/xterm": "^6.0.0",
"electron": "43.1.1",
+ "electron-winstaller": "^5.4.4",
"esbuild": "^0.28.1",
+ "png-to-ico": "^3.0.1",
"puppeteer": "^25.3.0",
"sharp": "^0.35.3",
"typescript": "^7.0.2",
@@ -3561,6 +3563,13 @@
"node": "^12.20.0 || >=14"
}
},
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/cookie": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
@@ -3575,6 +3584,14 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/cross-dirname": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz",
+ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -3756,6 +3773,171 @@
"node": ">= 22.12.0"
}
},
+ "node_modules/electron-winstaller": {
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.4.tgz",
+ "integrity": "sha512-j9ETcBGJaXxAY/b6UBpR7LZfjdU4BAO+yvr4ifqHEdyuc3UNCy91PDGkWKY5UQ4coHNYfnwFggrqD6QPeFGAlg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@electron/asar": "^3.2.1",
+ "debug": "^4.1.1",
+ "fs-extra": "^7.0.1",
+ "lodash": "^4.17.21",
+ "semver": "^7.6.3",
+ "temp": "^0.9.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@electron/windows-sign": "^1.1.2"
+ }
+ },
+ "node_modules/electron-winstaller/node_modules/@electron/asar": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz",
+ "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^5.0.0",
+ "glob": "^7.1.6",
+ "minimatch": "^3.0.4"
+ },
+ "bin": {
+ "asar": "bin/asar.js"
+ },
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
+ "node_modules/electron-winstaller/node_modules/@electron/windows-sign": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz",
+ "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "dependencies": {
+ "cross-dirname": "^0.1.0",
+ "debug": "^4.3.4",
+ "fs-extra": "^11.1.1",
+ "minimist": "^1.2.8",
+ "postject": "^1.0.0-alpha.6"
+ },
+ "bin": {
+ "electron-windows-sign": "bin/electron-windows-sign.js"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/electron-winstaller/node_modules/@electron/windows-sign/node_modules/fs-extra": {
+ "version": "11.4.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz",
+ "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/electron-winstaller/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-winstaller/node_modules/brace-expansion": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/electron-winstaller/node_modules/commander": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
+ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/electron-winstaller/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/electron-winstaller/node_modules/jsonfile": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/electron-winstaller/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/electron-winstaller/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
"node_modules/electron/node_modules/@types/node": {
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
@@ -4007,6 +4189,28 @@
"license": "MIT",
"optional": true
},
+ "node_modules/fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -4158,12 +4362,24 @@
"license": "BSD-3-Clause",
"optional": true
},
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "license": "ISC",
- "optional": true
+ "devOptional": true,
+ "license": "ISC"
},
"node_modules/ini": {
"version": "1.3.8",
@@ -4244,6 +4460,16 @@
"jiti": "lib/jiti-cli.mjs"
}
},
+ "node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "license": "MIT",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
"node_modules/junk": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz",
@@ -4551,6 +4777,13 @@
"url": "https://github.com/sponsors/antonk52"
}
},
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@@ -4687,8 +4920,8 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "devOptional": true,
"license": "MIT",
- "optional": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -4710,6 +4943,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
@@ -4816,8 +5062,8 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "devOptional": true,
"license": "ISC",
- "optional": true,
"dependencies": {
"wrappy": "1"
}
@@ -4886,6 +5132,16 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -4977,6 +5233,51 @@
"node": ">=10.4.0"
}
},
+ "node_modules/png-to-ico": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/png-to-ico/-/png-to-ico-3.0.1.tgz",
+ "integrity": "sha512-S8BOAoaGd9gT5uaemQ62arIY3Jzco7Uc7LwUTqRyqJDTsKqOAiyfyN4dSdT0D+Zf8XvgztgpRbM5wnQd7EgYwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^22.10.3",
+ "minimist": "^1.2.8",
+ "pngjs": "^7.0.0"
+ },
+ "bin": {
+ "png-to-ico": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/png-to-ico/node_modules/@types/node": {
+ "version": "22.20.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz",
+ "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/png-to-ico/node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pngjs": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
+ "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.19.0"
+ }
+ },
"node_modules/postject": {
"version": "1.0.0-alpha.6",
"resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz",
@@ -5197,6 +5498,73 @@
"node": ">= 4"
}
},
+ "node_modules/rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/rimraf/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rimraf/node_modules/brace-expansion": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -5669,6 +6037,20 @@
"node": ">=6"
}
},
+ "node_modules/temp": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz",
+ "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mkdirp": "^0.5.1",
+ "rimraf": "~2.6.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -5776,6 +6158,16 @@
"pathe": "^2.0.3"
}
},
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -5929,8 +6321,8 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "license": "ISC",
- "optional": true
+ "devOptional": true,
+ "license": "ISC"
},
"node_modules/ws": {
"version": "8.21.1",
diff --git a/package.json b/package.json
index 6e1cd12..4300e8c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "1helm",
"productName": "1Helm",
- "version": "0.0.5",
+ "version": "0.0.6",
"private": true,
"type": "module",
"description": "1Helm is the self-hosted home for durable AI employees: one resident, one private computer, compounding memory and skills, and Skipper for every boundary.",
@@ -27,17 +27,20 @@
"test:brief-browser": "node test/brief-regressions-browser.mjs",
"test:production-browser": "node test/production-browser.mjs",
"test:terminal-browser": "node --test test/terminal-reconnect-browser.mjs",
+ "test:feedback-browser": "node --test test/feedback-browser.mjs",
"test:site": "node --test test/site.mjs",
"benchmark:autonomy": "node scripts/autonomy-benchmark.mjs",
"helm": "node scripts/1helm-cli.mjs",
"test:live": "node test/live-smoke.mjs",
- "test": "node test/native-world.mjs && node --test test/routing.mjs test/routing-disabled-account.mjs test/desktop.mjs test/update-service.mjs test/channel-computers.mjs test/cloudflare-worker.mjs test/connectors.mjs test/chatgpt-image.mjs test/autonomy-platform.mjs test/gmail.mjs test/photon.mjs test/site.mjs test/terminal-reconnect-contract.mjs test/terminal-reconnect-browser.mjs test/web-research.mjs test/workflows.mjs",
+ "test": "node scripts/run-test-suite.mjs",
"ci": "npm run typecheck && npm run build && npm test",
"test:pipeline": "node test/pipeline.mjs",
"desktop": "electron .",
"package:mac": "node scripts/package-mac-dmg.cjs",
"package:dmg:release": "HELM_REQUIRE_NOTARIZATION=1 node scripts/package-mac-dmg.cjs",
"package:linux": "node scripts/package-linux-host.mjs",
+ "package:windows": "node scripts/package-windows.cjs",
+ "package:windows:release": "set HELM_REQUIRE_WINDOWS_SIGNATURE=1&& node scripts/package-windows.cjs",
"test:desktop": "node --test test/desktop.mjs",
"test:channel-computers:mac": "node scripts/mac-channel-computer-acceptance.mjs"
},
@@ -58,7 +61,9 @@
"@xterm/addon-fit": "^0.11.0",
"@xterm/xterm": "^6.0.0",
"electron": "43.1.1",
+ "electron-winstaller": "^5.4.4",
"esbuild": "^0.28.1",
+ "png-to-ico": "^3.0.1",
"puppeteer": "^25.3.0",
"sharp": "^0.35.3",
"typescript": "^7.0.2",
diff --git a/public/index.html b/public/index.html
index 60264bb..82bb140 100644
--- a/public/index.html
+++ b/public/index.html
@@ -29,11 +29,11 @@
document.querySelectorAll('meta[name="theme-color"]').forEach(function (m) { m.setAttribute("content", color); });
})();
-
+
-
+