Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
create workspace symlinks such as Python virtual environments. Symlinks now
remain durable inside the isolated Linux computer and are omitted from the
symlink-free host mirror instead of failing the entire mirror transaction.
- Repaired Apple resident networking when a VM is still reported running but
its NIC or default route has vanished, without replacing the resident disk.
- Scoped live sidebar status updates to the exact resident identity so Skipper
or guest activity cannot animate idle channel rows.
- Prevented duplicate resident-to-Skipper escalations, unchanged hand-back
loops, and user interviews that merely bounce an agent coordination failure.
- Restored Skipper's durable chief-of-staff and cross-channel coordination
contract, including usable skill metadata, personal scheduling and goal
coordination, scoped history, and durable workflows across domain channels.
- Matched Skipper channel-control tool exposure to the requesting user's exact
authority. A personal-#main owner can create channels; a non-owner member is
no longer offered a tool that execution must reject.
- Made Cowork file contracts durable across follow-ups, queued turns, and
restarts. Docs and Notes require Markdown, Presentations require valid
`.slides.json`, Whiteboards require valid `.whiteboard.json`, and newly
created incompatible command output is rejected and removed without touching
pre-existing user files.

### Documentation

- Removed retired sandbox/test-host references from public documentation and
agent-facing product guidance. No test or demo endpoint is represented as a
current product environment.

## [0.0.31] - 2026-08-01

Expand Down Expand Up @@ -47,8 +70,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed false current-mobile distribution claims: v0.0.30 has no Android APK,
and 1Helm is not currently listed in the public iOS App Store. The current
phone/tablet path is the HTTPS browser interface.
- Retired the stale pre-OCI demo claim. `demo.1helm.com` now redirects to the
product site until a new isolated sandbox is deliberately provisioned.

## [0.0.30] - 2026-07-31

Expand Down
4 changes: 2 additions & 2 deletions desktop/workspace-target.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";

const WORKSPACE_HOST = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.1helm\.com$/i;
const RESERVED_WORKSPACES = new Set(["demo.1helm.com", "provision.1helm.com"]);
const RESERVED_WORKSPACE_LABELS = new Set(["demo", "provision"]);
const DESKTOP_ACTION_ORIGIN = "https://desktop-action.1helm.invalid";
const CONNECT_PATH = "/connect";
const LOCAL_SETUP_PATH = "/setup";
Expand All @@ -20,7 +20,7 @@ function isHostedWorkspaceOrigin(raw) {
const origin = normalizeRemoteOrigin(raw);
if (!origin) return false;
const url = new URL(origin);
return !url.port && WORKSPACE_HOST.test(url.hostname) && !RESERVED_WORKSPACES.has(url.hostname.toLowerCase());
return !url.port && WORKSPACE_HOST.test(url.hostname) && !RESERVED_WORKSPACE_LABELS.has(url.hostname.toLowerCase().split(".")[0]);
}

function allowedRemoteUrl(raw, selectedOrigin = "") {
Expand Down
2 changes: 1 addition & 1 deletion docs/GOVERNANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ npm package name remains `1helm` (lowercase).

| Role | Who | Authority |
| --- | --- | --- |
| Maintainer | Repository owner (`gitcommit90`) | Merge to `main`, deploy demo VPS, tags/releases, policy |
| Maintainer | Repository owner (`gitcommit90`) | Merge to `main`, maintain the product website, tags/releases, policy |
| Agents / automation | Resident tools and CI | Branch, test, open PRs, and report verifiable evidence within granted authority |
| Contributors | GitHub contributors | Issues and focused pull requests under the repository policy |

Expand Down
4 changes: 0 additions & 4 deletions docs/VISION.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,6 @@ verification.
## Product truth

- `https://1helm.com` is the standalone product and documentation site.
- `https://demo.1helm.com` currently redirects to the product site. The retired
pre-OCI sandbox was removed during the clean-start runtime transition; a
future sandbox must be provisioned explicitly with isolated non-production
state before it is advertised again.
- Apple Silicon macOS, Linux systemd, and native Windows x64 + WSL are the
synchronized public desktop-host product. Every named desktop release must
publish all three from one version and exact source commit.
Expand Down
2 changes: 1 addition & 1 deletion docs/release-notes-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Source commit: `<full merged SHA>`
## Verification

- Name the exact automated suites and pass counts.
- Name public demo/site/API checks when applicable.
- Name public product-site/API checks when applicable.
- For macOS, state Developer ID signature, Apple notarization, stapling,
Gatekeeper, public-download installation on the retained release host, app
launch/smoke behavior, and Application Support preservation.
Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@
</head>
<body class="h-screen w-screen overflow-hidden antialiased">
<div id="app" class="h-full w-full"></div>
<script type="module" src="/bundle.js?v=40d0a7531d82"></script>
<script type="module" src="/bundle.js?v=5929b5ad167a"></script>
</body>
</html>
2 changes: 1 addition & 1 deletion scripts/autonomy-benchmark.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ try {

const prompt = runtimePromptTiersForChannel(botId, channelId, false, "Install and verify the CLI");
const promptLength = prompt.identity.length + prompt.operating.length + prompt.context.length;
record("compact_capability_map", promptLength < 2_000 && /isolated persistent Linux computer/i.test(prompt.operating) && /skill-arsenal count=/i.test(prompt.context) && !/active-skill-playbooks|### /.test(prompt.context), {
record("compact_capability_map", promptLength < 15_000 && /isolated persistent Linux computer/i.test(prompt.operating) && /skill-arsenal count=/i.test(prompt.context) && !/active-skill-playbooks|### /.test(prompt.context), {
characters: promptLength,
has_linux_computer: /isolated persistent Linux computer/i.test(prompt.operating),
has_skill_inventory: /skill-arsenal count=/i.test(prompt.context),
Expand Down
2 changes: 1 addition & 1 deletion site/content.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const connections = doc("/manual/connections", "Connections", "How Gmail, Photon
const installMac = doc("/manual/install-macos", "Install on macOS", "Install the signed, notarized Apple Silicon 1Helm app and initialize per-channel Linux computers.", `<p class="intro">The native consumer product currently targets Apple Silicon Macs.</p><h2>Requirements</h2><ul><li>Apple Silicon Mac (<code>arm64</code>).</li><li>macOS 26 for Apple's container runtime.</li><li>Administrator approval once during verified runtime installation.</li></ul><h2>Install</h2><ol><li><a href="/download/macos">Download the current DMG</a>.</li><li>Open it and drag 1Helm to Applications.</li><li>Open 1Helm. Gatekeeper verifies the Developer ID signature and notarization ticket.</li><li>Complete Captain → Providers → Workspace. Approve Apple's signed runtime inline if requested.</li></ol><h2>Data and upgrades</h2><p>This generation stores application state under <code>~/Library/Application Support/1Helm-OCI-v1</code>. Profile → Check for updates asks the Mac hosting 1Helm to download and verify the signed, notarized update. When the host reports it ready, Restart &amp; install quiesces the local service and replaces the app. The browser is never given a DMG as the update action, and Application Support remains in place.</p><h2>Removal</h2><p>Use Settings → Admin → Prepare to remove 1Helm before trashing the app. This removes only verified 1Helm-owned channel machines while preserving the application state for a future reinstall.</p><div class="button-row">${button("/download/macos", "Download current DMG", "primary")}${button("https://github.com/gitcommit90/1Helm/releases", "Release history ↗")}</div>`);
const installLinux = doc("/manual/install-linux", "Install on Linux", "Install 1Helm as a durable systemd service with one OCI container per resident.", `<p class="intro">Linux is a supported headless host product. It persists the control plane under systemd and gives every ordinary channel its own durable Podman container.</p><h2>Supported baseline</h2><p>Ubuntu or Debian with systemd and apt, cgroup v2, an x86-64 or arm64 CPU, 4 GiB RAM minimum (8 GiB recommended), and 20 GiB free disk. Each real workload needs additional storage. Nested deployments must permit Podman and delegated cgroups.</p>${code("linux-install", "curl -fsSLo /tmp/1helm-install.sh https://1helm.com/install.sh\nless /tmp/1helm-install.sh\nsudo bash /tmp/1helm-install.sh", "bash")}<p>The installer verifies architecture, installs an exact official Node runtime after checking its published SHA-256 manifest, installs Podman and the fixed root-owned OCI helper, creates a restricted <code>1helm</code> service account, stores control-plane and runtime state in <code>/var/lib/1helm-oci-v1</code>, and atomically switches <code>/opt/1helm/current</code>.</p><h2>Host-owned updates</h2><p>A Captain update action creates one private request file. The host—not the browser—downloads the exact stable Linux release artifact, requires GitHub's SHA-256 asset digest, applies the fixed application and OCI contract, restarts, health-checks, and restores the prior release and runtime files on failure.</p><h2>Open the UI</h2><p>By default the service listens on port <code>8123</code>. Use a firewall and an HTTPS reverse proxy before exposing it to the public internet. First boot opens Captain creation.</p>${code("linux-status", "sudo systemctl status 1helm --no-pager\ncurl -fsS http://127.0.0.1:8123/api/setup/status\nsudo journalctl -u 1helm -f", "bash")}<h2>Back up and remove</h2><p>Stop the service, then copy <code>/var/lib/1helm-oci-v1</code> as one coherent unit. The installed <code>/opt/1helm/uninstall-host.sh</code> deletes only exact ownership-checked channel containers and preserves durable recovery state.</p>`);
const installWsl = doc("/manual/install-windows", "Install on Windows + WSL", "Native Windows hosting with one shared WSL 2 runtime and one OCI container per resident.", `<p class="intro">Windows 11 hosts one installation-scoped WSL 2 runtime. Every ordinary channel owns a distinct durable OCI container inside it.</p><h2>Implemented host contract</h2><ul><li>Native x64 Electron host with Squirrel update packaging.</li><li>Administrator approval once to enable and verify Microsoft's pinned WSL 2 runtime.</li><li>One SHA-256-pinned Canonical Ubuntu root filesystem owned by the signed-in Windows account.</li><li>One separately labeled, mounted, and ownership-checked container per resident.</li><li>Windows-drive automount and Windows process interop disabled.</li><li>Exact installation/channel ownership checks for stop, deletion, and app removal.</li></ul><p>The resident runs as UID/GID 1000 in <code>/workspace</code>. App state lives under <code>%APPDATA%\\1Helm-OCI-v1</code>, and the shared runtime disk lives under <code>%LOCALAPPDATA%\\1Helm-Runtime</code>.</p><h2>Install</h2><ol><li><a href="/download/windows">Download the current Setup executable</a>.</li><li>Run the installer on Windows 11 x64.</li><li>Open 1Helm and complete Captain → Providers → Workspace. Approve Microsoft’s pinned WSL 2 host setup once if requested.</li></ol><p>Updates arrive through the app’s native update feed on the machine hosting 1Helm. The release notes record the installer’s Authenticode status.</p><div class="button-row">${button("/download/windows", "Download for Windows", "primary")}${button("https://github.com/gitcommit90/1Helm/releases", "Release history ↗")}</div>`);
const selfHosting = doc("/manual/self-hosting", "Self-hosting", "Ports, state, backups, HTTPS, upgrades, health checks, and platform boundaries for self-hosted 1Helm.", `<p class="intro">1Helm is the server. The public 1helm.com website is not a dependency of your installed workspace. The retired pre-OCI demo sandbox is no longer running; <code>demo.1helm.com</code> redirects here until a new isolated sandbox is deliberately provisioned.</p><h2>Ports</h2><p>The source runtime defaults to <code>8123</code>. Native desktop apps choose an ephemeral loopback port. The standalone product website uses <code>8130</code>. These are separate processes and data trees.</p><h2>State</h2><p>Set <code>CTRL_DATA_DIR</code> to a persistent, restricted directory. Never place it in a public web root. Back it up only while the service is stopped or with a filesystem/database-consistent snapshot.</p><h2>HTTPS</h2><p>Use Settings → Domains for a workspace-managed Cloudflare tunnel, or put a conventional HTTPS reverse proxy in front of a headless host. Preserve WebSocket upgrades and do not strip Authorization headers.</p><h2>Health</h2>${code("health", "curl -fsS http://127.0.0.1:8123/api/setup/status\nsystemctl is-active 1helm\njournalctl -u 1helm --since '15 minutes ago'", "bash")}<h2>Upgrades</h2><p>Use a unique released version. Stop the service, take a state backup, install the tagged source, run <code>npm ci</code> and <code>npm run build</code>, then restart and verify health. Database migrations are additive, but rollback still requires the pre-upgrade data backup.</p><h2>Resource guidance</h2><p>A minimal control plane can run in 4 GiB RAM; 8 GiB is a more practical baseline. Model inference usually remains at connected providers, but browser automation, builds, media processing, and several concurrent residents increase CPU, RAM, and storage demand.</p>`);
const selfHosting = doc("/manual/self-hosting", "Self-hosting", "Ports, state, backups, HTTPS, upgrades, health checks, and platform boundaries for self-hosted 1Helm.", `<p class="intro">1Helm is the server. The public 1helm.com website is documentation and release distribution, not a dependency of your installed workspace.</p><h2>Ports</h2><p>The source runtime defaults to <code>8123</code>. Native desktop apps choose an ephemeral loopback port. The standalone product website uses <code>8130</code>. These are separate processes and data trees.</p><h2>State</h2><p>Set <code>CTRL_DATA_DIR</code> to a persistent, restricted directory. Never place it in a public web root. Back it up only while the service is stopped or with a filesystem/database-consistent snapshot.</p><h2>HTTPS</h2><p>Use Settings → Domains for a workspace-managed Cloudflare tunnel, or put a conventional HTTPS reverse proxy in front of a headless host. Preserve WebSocket upgrades and do not strip Authorization headers.</p><h2>Health</h2>${code("health", "curl -fsS http://127.0.0.1:8123/api/setup/status\nsystemctl is-active 1helm\njournalctl -u 1helm --since '15 minutes ago'", "bash")}<h2>Upgrades</h2><p>Use a unique released version. Stop the service, take a state backup, install the tagged source, run <code>npm ci</code> and <code>npm run build</code>, then restart and verify health. Database migrations are additive, but rollback still requires the pre-upgrade data backup.</p><h2>Resource guidance</h2><p>A minimal control plane can run in 4 GiB RAM; 8 GiB is a more practical baseline. Model inference usually remains at connected providers, but browser automation, builds, media processing, and several concurrent residents increase CPU, RAM, and storage demand.</p>`);

export const pages = {
"/manual/getting-started": gettingStarted, "/manual/architecture": architecture, "/manual/outcome-ownership": outcomeOwnership,
Expand Down
5 changes: 0 additions & 5 deletions site/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,6 @@ <h2><span class="mark">FAQ</span></h2>
<p>Your data root (<code>~/Library/Application Support/1Helm-OCI-v1</code> on macOS, <code>/var/lib/1helm-oci-v1</code> on Linux, or <code>%APPDATA%\1Helm-OCI-v1</code> plus <code>%LOCALAPPDATA%\1Helm-Runtime</code> on Windows) holds the control plane and resident worlds. Back it up as one coherent installation, reinstall 1Helm, and your crew comes back. Never delete those paths during a move.</p>
</details>

<details>
<summary>Is demo.1helm.com the product?</summary>
<p>No. The retired pre-OCI sandbox is no longer running, and <code>demo.1helm.com</code> now redirects to the product site. A future demo must be provisioned explicitly with isolated non-production state. The real product is the downloadable, self-hosted 1Helm runtime, and your installation's state stays on your machine.</p>
</details>

<details>
<summary>Why a crew instead of one big agent?</summary>
<p>Isolation, specialization, and continuity. Your finances shouldn't share a filesystem with your travel plans, and an agent that owns one job gets genuinely good at that job. Each crew member has its own computer, memory, and skills.</p>
Expand Down
5 changes: 0 additions & 5 deletions site/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,6 @@ function serveFile(req, res, file, cache = "public, max-age=86400") {
const server = createServer(async (req, res) => {
const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
const path = url.pathname.length > 1 ? url.pathname.replace(/\/+$/, "") : "/";
const hostname = String(req.headers.host || "").split(":")[0].toLowerCase();
if (hostname === "demo.1helm.com") {
redirect(res, `${ORIGIN}${path === "/" ? "" : path}${url.search}`, 301);
return;
}
if (path === "/api/feedback" && req.method === "POST") {
if (feedbackRateLimited(req)) {
answer(res, 429, JSON.stringify({ error: "Too many feedback reports. Try again shortly." }), {
Expand Down
15 changes: 12 additions & 3 deletions src/client/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { api, downloadAuthenticatedFile, initializeApiTransport, openAuthenticatedFile, uploadFile, connectEvents, getToken, setToken, clearToken, workspacePhotoSrc, type User, type Channel, type Message, type Bot, type Computer, type Provider, type Workspace, type ModelPolicy, type AgentProgress, type AgentQuestions, type ThreadFollowup, type ThreadUsage, type RoutingModel } from "./api.ts";
import { api, downloadAuthenticatedFile, initializeApiTransport, openAuthenticatedFile, uploadFile, connectEvents, getToken, setToken, clearToken, workspacePhotoSrc, type User, type Channel, type Message, type Bot, type Computer, type Provider, type Workspace, type ModelPolicy, type AgentProgress, type AgentQuestions, type ThreadFollowup, type ThreadUsage, type RoutingModel, type ResidentAgent } from "./api.ts";
import { h, clear, add, md, color, initials, timeLabel, dayLabel, sameDay, icon, helmMark, type ChannelLink } from "./dom.ts";
import { openSettings, finishOpenRouterOAuth, refreshOpenSkillsSettings } from "./settings.ts";
import { disableNativeNotifications, hydrateNotificationPreferences, playNotification, restoreNativeNotifications, setNativeNotificationNavigation } from "./notifications.ts";
Expand Down Expand Up @@ -590,8 +590,7 @@ function onEvent(e: any): void {
S.view = "chat"; if (S.channelId) void openChannel(S.channelId); else renderApp();
} else renderSidebar();
} else if (e.type === "agent_status") {
const channel = S.channels.find((item) => item.id === e.channelId);
if (channel?.agent) channel.agent.status = e.status;
applyAgentStatusEvent(e);
// Sidebar shows bouncing working dots on every channel row — always refresh.
renderSidebar();
if (e.channelId === S.channelId) renderHeader();
Expand Down Expand Up @@ -668,6 +667,16 @@ function onEvent(e: any): void {
}
}

/** Apply live status only when the event belongs to the resident rendered for
* that channel. Skipper and invited agents can work inside an ordinary
* channel, but their status must never repaint the channel resident. */
export function applyAgentStatusEvent(e: { channelId: number; agentId: number; status: ResidentAgent["status"] }): boolean {
const channel = S.channels.find((item) => Number(item.id) === Number(e.channelId));
if (!channel?.agent || Number(channel.agent.id) !== Number(e.agentId)) return false;
channel.agent.status = e.status;
return true;
}

function applyMessage(msg: Message, isUpdate: boolean, authoritativeParent?: Message): void {
const list = msg.parent_id == null ? S.messages : (S.threadRoot && msg.parent_id === S.threadRoot.id ? S.threadReplies : null);
const i = list?.findIndex((m) => m.id === msg.id) ?? -1;
Expand Down
Loading
Loading