diff --git a/README.md b/README.md index c50d46a..6bcf9c8 100644 --- a/README.md +++ b/README.md @@ -82,37 +82,10 @@ The plugin auto-updates on each session start. To manually update: #### Install from ClawHub (Telegram, TUI, WhatsApp): ``` -openclaw plugins install clawhub:hivemind +openclaw plugins install hivemind ``` -Then type `/hivemind_login` in chat, click the auth link, and sign in. - -#### Commands - -| Command | Description | -|---------|-------------| -| `/hivemind_login` | Sign in via device flow | -| `/hivemind_capture` | Toggle capture on/off | -| `/hivemind_whoami` | Show current org and workspace | -| `/hivemind_orgs` | List organizations | -| `/hivemind_switch_org ` | Switch organization | -| `/hivemind_workspaces` | List workspaces | -| `/hivemind_switch_workspace ` | Switch workspace | -| `/hivemind_update` | Check for plugin updates | - -Auto-recall and auto-capture are enabled by default. Data is stored in the same `sessions` table as Claude Code and Codex. - -#### Coexistence with `memory-core` - -Hivemind runs **alongside** OpenClaw's built-in `memory-core` plugin. It does **not** claim the memory slot, so `memory-core`'s dreaming cron (`"0 3 * * *"`) and other memory-slot-dependent jobs keep working. Hivemind captures session activity and exposes its own commands; `memory-core` keeps owning recall/promotion/dreaming. - -#### Troubleshooting - -- **Hivemind seems slow or unresponsive.** Check the agent model in `~/.openclaw/openclaw.json` under `agents.defaults.model`. Hivemind makes many small tool calls per turn; a large reasoning model like Opus will feel sluggish. Recommended default: `anthropic/claude-haiku-4-5-20251001`. -- **`openclaw model ` says "plugins.allow excludes model".** The `model` plugin CLI is disabled by default. Edit `~/.openclaw/openclaw.json` directly (key `agents.defaults.model`) and restart the gateway: `systemctl --user restart openclaw-gateway.service`. -- **Model switch rejected as "not allowed".** Use the exact dated provider-prefixed ID (`anthropic/claude-haiku-4-5-20251001`, `anthropic/claude-sonnet-4-6`). Legacy IDs like `claude-3-5-haiku-latest` and unprefixed bare IDs are not on OpenClaw's allowlist. -- **Self-update via Telegram fails with "elevated is not available".** `tools.elevated.allowFrom` must include `telegram` before elevated commands work from that channel. Safer alternative: run the upgrade in a local shell with `openclaw plugins update hivemind`. -- **`npm error EACCES` during self-update.** OpenClaw was installed under a root-owned npm prefix (e.g. `/usr/lib/node_modules/openclaw`). Reinstall under a user-writable prefix, or run the update with appropriate privileges locally — not via a channel. +Send a message. The plugin sends you an auth link. Click, sign in, done.
Codex Setup diff --git a/openclaw/README.md b/openclaw/README.md deleted file mode 100644 index ab6f048..0000000 --- a/openclaw/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# Hivemind - -Cloud-backed shared memory for AI agents. Install once, memory persists across sessions, machines, and channels — and is shared with every teammate in your Deeplake org. - -Powered by [Deeplake](https://deeplake.ai/hivemind). - -## Install - -```bash -openclaw plugins install clawhub:hivemind -``` - -Then in chat: - -``` -/hivemind_login -``` - -Click the auth link, sign in, send another message. That's it. - -## What it does - -- **Auto-recall** — before each agent turn, relevant memories surface automatically via keyword search. -- **Auto-capture** — after each turn, the conversation is stored to your Deeplake workspace. -- **Cross-platform** — same memory accessible from Claude Code, Codex CLI, and OpenClaw plugins. -- **Team-wide** — every user in your Deeplake org shares the same memory. - -## Commands - -| Command | What it does | -|---------|--------------| -| `/hivemind_login` | Sign in via device flow | -| `/hivemind_capture` | Toggle conversation capture on/off | -| `/hivemind_whoami` | Show current org and workspace | -| `/hivemind_orgs` | List organizations | -| `/hivemind_switch_org ` | Switch organization | -| `/hivemind_workspaces` | List workspaces | -| `/hivemind_switch_workspace ` | Switch workspace | -| `/hivemind_update` | Check for plugin updates | - -You can also just ask the agent naturally — "switch org to activeloop", "list my orgs", "invite alice@example.com as admin", etc. - -## Privacy & data - -- **What's captured**: every user message and assistant reply, sent to `api.deeplake.ai`. -- **Where credentials live**: a long-lived API token at `~/.deeplake/credentials.json` (file permissions 0600). -- **Where it sends data**: only `api.deeplake.ai`. Nothing else. -- **How to pause**: run `/hivemind_capture` to stop capture; run it again to resume. -- **How to fully sign out**: delete `~/.deeplake/credentials.json` and revoke the token in the Deeplake dashboard. - -The plugin does **not** modify OpenClaw's configuration or replace the built-in memory plugin. It runs alongside `memory-core` via lifecycle hooks, so `memory-core`'s dreaming cron and other memory-slot jobs keep working. - -## Troubleshooting - -**Hivemind feels slow or makes tools hang.** -Check `agents.defaults.model` in `~/.openclaw/openclaw.json`. Memory-heavy workflows issue many small tool calls; a large reasoning model feels sluggish. Recommended default is `anthropic/claude-haiku-4-5-20251001`. - -**Model switch rejected as "not allowed".** -OpenClaw's allowlist wants `/`. Use `anthropic/claude-haiku-4-5-20251001` or `anthropic/claude-sonnet-4-6`. Bare IDs and `-latest` suffixes are rejected. - -**`openclaw model ` fails with "plugins.allow excludes model".** -The CLI is disabled by default. Edit `~/.openclaw/openclaw.json` under `agents.defaults.model` and restart the gateway: `systemctl --user restart openclaw-gateway.service`. - -**Telegram-triggered `sudo npm i -g openclaw@latest` fails with "elevated is not available".** -`tools.elevated.allowFrom.telegram` isn't set. Run the upgrade in a local shell instead. - -## Sharing memory with teammates - -Invite teammates to your Deeplake org: - -``` -invite alice@example.com as admin -``` - -Their agents will see your memory; your agents will see theirs. - -## Source - -[github.com/activeloopai/hivemind](https://github.com/activeloopai/hivemind) diff --git a/openclaw/skills/SKILL.md b/openclaw/skills/SKILL.md index a699256..5dc467f 100644 --- a/openclaw/skills/SKILL.md +++ b/openclaw/skills/SKILL.md @@ -1,10 +1,10 @@ --- name: hivemind description: Cloud-backed shared memory for AI agents. Install once, memory persists across sessions, machines, and channels. -allowed-tools: Read, Bash +allowed-tools: Read --- -# Hivemind +# Hivemind Memory Cloud-backed shared memory powered by Deeplake. @@ -14,26 +14,24 @@ Cloud-backed shared memory powered by Deeplake. ## Authentication -The user types `/hivemind_login` in chat. The plugin returns an auth URL. The user clicks it, signs in, and memory activates on the next message. A long-lived API token is stored at `~/.deeplake/credentials.json`. +The user types `/hivemind_login` in chat. The plugin returns an auth URL. The user clicks it, signs in, and memory activates on the next message. -## What the plugin does +## How it works -- **Captures** every conversation (user + assistant messages) and sends them to `api.deeplake.ai`. Disable anytime with `/hivemind_capture`. -- **Recalls** relevant memories before each agent turn via keyword search. -- **Stores** a long-lived API token at `~/.deeplake/credentials.json` after login. -- **Does NOT** modify OpenClaw configuration or replace the built-in memory plugin. -- All network requests go to `api.deeplake.ai` only. +The plugin automatically: +- **Captures** every conversation (user + assistant messages) to Deeplake cloud +- **Recalls** relevant memories before each agent turn via keyword search +- All data stored as structured rows — searchable, persistent, shared ## Commands -- `/hivemind_login` — sign in via device flow -- `/hivemind_capture` — toggle capture on/off (off = no data sent) +- `/hivemind_login` — sign in +- `/hivemind_capture` — toggle capture on/off - `/hivemind_whoami` — show current org and workspace - `/hivemind_orgs` — list organizations - `/hivemind_switch_org ` — switch organization - `/hivemind_workspaces` — list workspaces - `/hivemind_switch_workspace ` — switch workspace -- `/hivemind_update` — check for plugin updates ## Sharing memory diff --git a/openclaw/src/index.ts b/openclaw/src/index.ts index c15e069..469bc26 100644 --- a/openclaw/src/index.ts +++ b/openclaw/src/index.ts @@ -1,4 +1,8 @@ function definePluginEntry(entry: T): T { return entry; } +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import { homedir } from "node:os"; +import { join } from "node:path"; + // Shared core imports import { loadConfig } from "../../src/config.js"; import { loadCredentials, saveCredentials, requestDeviceCode, pollForToken, listOrgs, switchOrg, listWorkspaces, switchWorkspace } from "../../src/commands/auth.js"; @@ -34,46 +38,6 @@ interface PluginAPI { } const DEFAULT_API_URL = "https://api.deeplake.ai"; -const VERSION_URL = "https://raw.githubusercontent.com/activeloopai/hivemind/main/openclaw/openclaw.plugin.json"; - -async function getInstalledVersion(): Promise { - try { - const [{ readFileSync }, { join }] = await Promise.all([ - import("node:fs"), - import("node:path"), - ]); - const dir = new URL(".", import.meta.url).pathname; - const candidates = [join(dir, "..", "package.json"), join(dir, "package.json")]; - for (const c of candidates) { - try { - const pkg = JSON.parse(readFileSync(c, "utf-8")); - if (pkg.name === "hivemind" && pkg.version) return pkg.version; - } catch {} - } - } catch {} - return null; -} - -function isNewer(latest: string, current: string): boolean { - const parse = (v: string) => v.replace(/-.*$/, "").split(".").map(Number); - const [la, lb, lc] = parse(latest); - const [ca, cb, cc] = parse(current); - return la > ca || (la === ca && lb > cb) || (la === ca && lb === cb && lc > cc); -} - -async function checkForUpdate(logger: PluginLogger): Promise { - try { - const current = await getInstalledVersion(); - if (!current) return; - const res = await fetch(VERSION_URL, { signal: AbortSignal.timeout(3000) }); - if (!res.ok) return; - const manifest = await res.json() as { version?: string }; - const latest = manifest.version ?? null; - if (latest && isNewer(latest, current)) { - logger.info?.(`⬆️ Hivemind update available: ${current} → ${latest}. Run: openclaw plugins update hivemind`); - } - } catch {} -} // --- Auth state --- let authPending = false; @@ -143,6 +107,22 @@ async function requestAuth(): Promise { } } +// --- OpenClaw-specific: ensure plugin is in load.paths for hook wiring --- +function addToLoadPaths(): void { + const ocConfigPath = join(homedir(), ".openclaw", "openclaw.json"); + if (!existsSync(ocConfigPath)) return; + try { + const ocConfig = JSON.parse(readFileSync(ocConfigPath, "utf-8")); + const installPath = ocConfig?.plugins?.installs?.["hivemind"]?.installPath; + if (!installPath) return; + const loadPaths: string[] = ocConfig?.plugins?.load?.paths ?? []; + if (loadPaths.includes(installPath)) return; + if (!ocConfig.plugins.load) ocConfig.plugins.load = {}; + ocConfig.plugins.load.paths = [...loadPaths, installPath]; + writeFileSync(ocConfigPath, JSON.stringify(ocConfig, null, 2)); + } catch {} +} + // --- API instance --- let api: DeeplakeApi | null = null; let sessionsTable = "sessions"; @@ -174,9 +154,12 @@ export default definePluginEntry({ id: "hivemind", name: "Hivemind", description: "Cloud-backed shared memory powered by Deeplake", + kind: "memory", register(pluginApi: PluginAPI) { try { + addToLoadPaths(); + // Login command — works immediately after install, no hook dependency if (pluginApi.registerCommand) { pluginApi.registerCommand({ @@ -272,28 +255,6 @@ export default definePluginEntry({ return { text: `Switched to workspace: ${match.name}` }; }, }); - - pluginApi.registerCommand({ - name: "hivemind_update", - description: "Check for Hivemind updates and show how to upgrade", - handler: async () => { - const current = await getInstalledVersion(); - if (!current) return { text: "Could not determine installed version." }; - try { - const res = await fetch(VERSION_URL, { signal: AbortSignal.timeout(3000) }); - if (!res.ok) return { text: `Current version: ${current}. Could not check for updates.` }; - const pkg = await res.json(); - const latest = typeof pkg.version === "string" ? pkg.version : null; - if (!latest) return { text: `Current version: ${current}. Could not parse latest version.` }; - if (isNewer(latest, current)) { - return { text: `⬆️ Update available: ${current} → ${latest}\n\nRun in your terminal:\n\`openclaw plugins update hivemind\`` }; - } - return { text: `✅ Hivemind v${current} is up to date.` }; - } catch { - return { text: `Current version: ${current}. Could not check for updates.` }; - } - }, - }); } const config = (pluginApi.pluginConfig ?? {}) as PluginConfig; @@ -427,20 +388,14 @@ export default definePluginEntry({ }); } - // Prompt login if not authenticated + // Pre-fetch auth URL during registration const creds = loadCredentials(); - if (!creds?.token) { - logger.info?.("Hivemind installed. Run /hivemind_login to authenticate and activate shared memory."); - if (!authPending) { - requestAuth().catch(err => { - logger.error(`Pre-auth failed: ${err instanceof Error ? err.message : String(err)}`); - }); - } + if (!creds?.token && !authPending) { + requestAuth().catch(err => { + logger.error(`Pre-auth failed: ${err instanceof Error ? err.message : String(err)}`); + }); } - // Non-blocking version check - checkForUpdate(logger).catch(() => {}); - logger.info?.("Hivemind plugin registered"); } catch (err) { pluginApi.logger?.error?.(`Hivemind register failed: ${err instanceof Error ? err.message : String(err)}`);