diff --git a/.env.example b/.env.example index 000cd9306..d12e14cb1 100644 --- a/.env.example +++ b/.env.example @@ -54,16 +54,21 @@ ENVIRONMENT=development # docker buildx imagetools inspect ghcr.io/caura-ai/caura-memclaw-core-storage-api:v1.2.3 # CAURA_VERSION=latest -# -- Database (PostgreSQL + pgvector) ---------------------------------------- +# -- Database helpers (PostgreSQL + pgvector) -------------------------------- +# POSTGRES_HOST/PORT/USER/PASSWORD/DB are inputs for migration and development +# helpers only. They do not configure either running API service, and the stock +# Compose file hardcodes its bundled database connection. For a manual or +# custom core-storage-api deployment, set DATABASE_URL directly instead, e.g.: +# DATABASE_URL=postgresql+asyncpg://caura:change-me@db.example:5432/caura POSTGRES_HOST=127.0.0.1 POSTGRES_PORT=5432 POSTGRES_USER=caura -# REQUIRED: change in production POSTGRES_PASSWORD=changeme POSTGRES_DB=caura -# Require TLS on connections to Postgres. Set true in production unless the -# database is reached over a private network you already trust. Encrypts and -# refuses a server that will not upgrade; it does not verify the server +# Require TLS on core-storage-api connections to Postgres. Set true in +# production unless the database is reached over a private network you already +# trust. It encrypts the connection and refuses a server that will not upgrade; +# it does not verify the server # certificate — for that, put ``?ssl=verify-full`` on DATABASE_URL. POSTGRES_REQUIRE_SSL=false diff --git a/AGENT-INSTALL.md b/AGENT-INSTALL.md index e2b071828..a2e2ec6b5 100644 --- a/AGENT-INSTALL.md +++ b/AGENT-INSTALL.md @@ -8,7 +8,7 @@ You need these on your machine: - **Git** (to clone the repo) - **Docker + Docker Compose** (easiest path — handles PostgreSQL + pgvector + Redis) -- OR: **Python 3.11+** and a **PostgreSQL 16 instance with pgvector** (manual path) +- OR: **Python 3.12+** and a **PostgreSQL 16 instance with pgvector** (manual path) ## Option A: Docker (recommended — zero config) @@ -50,12 +50,10 @@ pip install -r requirements.txt # 4. Create .env file cat > .env << 'EOF' ENVIRONMENT=development -POSTGRES_HOST=127.0.0.1 -POSTGRES_PORT=5432 -POSTGRES_USER=caura -POSTGRES_PASSWORD=changeme -POSTGRES_DB=caura +DATABASE_URL=postgresql+asyncpg://caura:changeme@127.0.0.1:5432/caura POSTGRES_REQUIRE_SSL=false +CORE_STORAGE_API_URL=http://127.0.0.1:8002 +CORE_STORAGE_SHARED_SECRET=dev-only-storage-secret-change-me IS_STANDALONE=true EMBEDDING_PROVIDER=fake ENTITY_EXTRACTION_PROVIDER=fake @@ -68,11 +66,15 @@ psql -U postgres -c "CREATE USER caura WITH PASSWORD 'changeme';" psql -U postgres -c "CREATE DATABASE caura OWNER caura;" psql -U caura -d caura -c "CREATE EXTENSION IF NOT EXISTS vector;" -# 6. Run migrations -alembic upgrade head +# 6. Start the storage service. It applies Alembic migrations during startup. +PYTHONPATH=.:core-storage-api/src uvicorn core_storage_api.app:app \ + --host 127.0.0.1 --port 8002 -# 7. Start the server (the FastAPI app lives at core-api/src/core_api/app.py) -PYTHONPATH=core-api/src uvicorn core_api.app:app --host 0.0.0.0 --port 8000 +# 7. In a second terminal, activate the same venv, return to the repo root, +# and start core-api. It talks to the storage service on port 8002. +source venv/bin/activate +PYTHONPATH=.:core-api/src uvicorn core_api.app:app \ + --host 127.0.0.1 --port 8000 # 8. Verify (in another terminal) curl http://localhost:8000/api/v1/health @@ -95,15 +97,20 @@ Restart the server. REST and MCP calls work without `X-API-Key`. Supplying a placeholder such as `X-API-Key: standalone` is harmless and can make the same client configuration easier to reuse with authenticated modes. -**Path 2 — Admin key (multi-tenant, full access).** Set in your `.env`: +**Path 2 — Admin key (multi-tenant, full REST access).** Set in your `.env`: ```env ADMIN_API_KEY=my-long-random-admin-key ``` -Use `my-long-random-admin-key` as `X-API-Key`. You pass `tenant_id` explicitly in request bodies / query params. +Use `my-long-random-admin-key` as `X-API-Key`. You pass `tenant_id` +explicitly in request bodies / query params. Admin/system keys are +intentionally rejected by MCP; use standalone mode, a tenant-scoped key, or +Path 3 for MCP. -**Path 3 — Gate the API with a shared key.** Set `CAURA_API_KEY` in your `.env`. Clients send that key via `X-API-Key` plus `X-Tenant-ID` to pick a tenant. Use this when the OSS API is network-exposed. +**Path 3 — Gate the API with a shared key.** Set `CAURA_API_KEY` in your +`.env`. REST and MCP clients send that key via `X-API-Key` plus `X-Tenant-ID` +to pick a tenant. Use this when the OSS API is network-exposed. > **Note:** There is no `/ui/pricing.html`, `/api/register`, or `scripts/create_key.py` in OSS. Those are enterprise-plane features. For self-install, use Path 1. @@ -124,7 +131,9 @@ Add this to your MCP client configuration (Claude Code, Claude Desktop, Cursor, } ``` -Replace `standalone` with your admin key (Path 2) or the shared gate key (Path 3) as appropriate. +The block above is for Path 1. For Path 3, replace `standalone` with the shared +gate key and add an `X-Tenant-ID` header. Do not use the Path 2 admin key with +MCP; the MCP endpoint rejects admin/system credentials. > **Claude Code** doesn't read MCP servers from `settings.json` — register with `claude mcp add` instead (the block above maps to a project-root `.mcp.json`). Use `-s user` so the server is available in every directory, not just the one you ran the command in: > ```bash @@ -259,14 +268,14 @@ goes through `caura_doc` on the `skills` collection (`op=write` to share, The default `fake` providers skip LLM enrichment — memories are stored but not auto-classified. To enable full enrichment (type, weight, title, summary, tags, PII detection, entity extraction, contradiction detection), add an OpenAI key to your `.env`: ```bash -# Add to .env (or .env.dev for Docker) +# Add to .env (or env.dev for Docker) EMBEDDING_PROVIDER=openai ENTITY_EXTRACTION_PROVIDER=openai USE_LLM_FOR_MEMORY_CREATION=true OPENAI_API_KEY=sk-... ``` -Then restart the server (`docker compose restart app` or re-run uvicorn). +Then restart the server (`docker compose restart core-api` or re-run uvicorn). ## What You Now Have diff --git a/clients/python/README.md b/clients/python/README.md index c775201d0..b55cba2e4 100644 --- a/clients/python/README.md +++ b/clients/python/README.md @@ -127,7 +127,7 @@ with **422** and names it: try: mc.write("a memory", tags=["alpha"]) # `tags` is not a write field except CauraAPIError as exc: - exc.payload["error"]["details"]["unknown_fields"] # ["tags"] + exc.details["unknown_fields"] # ["tags"] ``` This used to return `201` with the field silently discarded, so an integration diff --git a/docs/plugin-upgrade.md b/docs/plugin-upgrade.md index 6787e1572..0afc1819c 100644 --- a/docs/plugin-upgrade.md +++ b/docs/plugin-upgrade.md @@ -108,9 +108,9 @@ For operators with many nodes (and SSH or OpenClaw-agent reach to all of them), Identifying which nodes need re-install: ```bash -curl -s "https://your-caura-server/api/v1/fleet/stats?tenant_id=$TENANT_ID&fleet_id=$FLEET_ID" \ +curl -s "https://your-caura-server/api/v1/fleet/nodes?tenant_id=$TENANT_ID&fleet_id=$FLEET_ID" \ -H "Authorization: Bearer $JWT" \ - | jq '.nodes[] + | jq '.[] | select((.plugin_version // "0") | split(".") | map(tonumber? // 0) | . < [2,6,0]) | {node_name, plugin_version, last_heartbeat}' ``` diff --git a/plugin/skills/caura/SKILL.md b/plugin/skills/caura/SKILL.md index 7b4f6b010..dcd8b1cc3 100644 --- a/plugin/skills/caura/SKILL.md +++ b/plugin/skills/caura/SKILL.md @@ -186,7 +186,9 @@ You auto-register at **trust 1** on your first write. | 3 | admin | all | all, incl. deletes | Operations that escalate the required level: -- browsing / reflecting with `scope="fleet"` or `"all"` → trust 2 +- `caura_list` / `caura_stats` for another fleet, or either tool with + `scope="all"` → trust 2; `scope="fleet"` for your own fleet stays at trust 1 +- `caura_insights` with `scope="fleet"` or `"all"` → trust 2 - reporting outcomes (`caura_evolve`) at `scope="fleet"` / `"all"` → trust 2 (default `scope="agent"` needs only trust 1) - `caura_manage op=delete` → trust 3 @@ -207,9 +209,11 @@ than silently retrying at a narrower scope. **Visibility (on write)** decides who can see a memory: `scope_agent` (private) · `scope_team` *(default — your fleet)* · `scope_org` (all fleets in tenant). -**Scope (on read / `_list` / `_insights`):** `agent` *(default)* · `fleet` -(trust 2) · `all` (trust 2). Prefer `scope_team` on write and `scope=agent` on -read unless you need cross-agent context. *Naming caveat:* writes take +**Scope (on read):** `agent` *(default)* · `fleet` · `all`. For +`caura_list` / `caura_stats`, your own fleet needs trust 1; another fleet or +`all` needs trust 2. `caura_insights` requires trust 2 for `fleet` or `all`. +Prefer `scope_team` on write and `scope=agent` on read unless you need +cross-agent context. *Naming caveat:* writes take `visibility=scope_*`; reads/list/keystone filters take `scope=*` — two axes, similar spelling. @@ -386,7 +390,7 @@ for, and the behaviors that aren't visible in a parameter list. - **`caura_recall`** excludes superseded memories (`status` ∈ {outdated, conflicted}) by default — pass `status` explicitly to walk the chain. - **`caura_write`** can't write `insight` / `outcome` / `rule` types — those are server-generated (via `caura_insights` / `caura_evolve`). `write_mode`: `fast` skips embedding → keyword-only recall afterwards; `strong` forces full LLM enrichment; `auto` is usually right. - **`caura_manage op=transition`** targets: `active · pending · confirmed · cancelled · outdated · conflicted · archived · deleted` (also in `TOOLS.md`). -- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field). Scope the search to a collection when you know it; omit `collection` for the single best match across the tenant. +- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field). Scope the search to a collection when you know it; omit `collection` to return up to `top_k` matches across the tenant (default 5). - **`caura_tune`** persists and reshapes every later recall — change one or two knobs at a time; call with no arguments to read your current profile (`fts_weight` 0 = pure semantic, 1 = pure keyword). - **`caura_insights`** saves findings as `insight` memories; run it at boundaries, not every turn. `focus="divergence"` needs a non-agent scope. - **`caura_stats`** is read-only — use it as a readiness/health probe, never a write-then-delete check. diff --git a/plugin/skills/memclaw/SKILL.md b/plugin/skills/memclaw/SKILL.md index 86c8a4e8b..03a2c98fd 100644 --- a/plugin/skills/memclaw/SKILL.md +++ b/plugin/skills/memclaw/SKILL.md @@ -186,7 +186,9 @@ You auto-register at **trust 1** on your first write. | 3 | admin | all | all, incl. deletes | Operations that escalate the required level: -- browsing / reflecting with `scope="fleet"` or `"all"` → trust 2 +- `caura_list` / `caura_stats` for another fleet, or either tool with + `scope="all"` → trust 2; `scope="fleet"` for your own fleet stays at trust 1 +- `caura_insights` with `scope="fleet"` or `"all"` → trust 2 - reporting outcomes (`caura_evolve`) at `scope="fleet"` / `"all"` → trust 2 (default `scope="agent"` needs only trust 1) - `caura_manage op=delete` → trust 3 @@ -207,9 +209,11 @@ than silently retrying at a narrower scope. **Visibility (on write)** decides who can see a memory: `scope_agent` (private) · `scope_team` *(default — your fleet)* · `scope_org` (all fleets in tenant). -**Scope (on read / `_list` / `_insights`):** `agent` *(default)* · `fleet` -(trust 2) · `all` (trust 2). Prefer `scope_team` on write and `scope=agent` on -read unless you need cross-agent context. *Naming caveat:* writes take +**Scope (on read):** `agent` *(default)* · `fleet` · `all`. For +`caura_list` / `caura_stats`, your own fleet needs trust 1; another fleet or +`all` needs trust 2. `caura_insights` requires trust 2 for `fleet` or `all`. +Prefer `scope_team` on write and `scope=agent` on read unless you need +cross-agent context. *Naming caveat:* writes take `visibility=scope_*`; reads/list/keystone filters take `scope=*` — two axes, similar spelling. @@ -386,7 +390,7 @@ for, and the behaviors that aren't visible in a parameter list. - **`caura_recall`** excludes superseded memories (`status` ∈ {outdated, conflicted}) by default — pass `status` explicitly to walk the chain. - **`caura_write`** can't write `insight` / `outcome` / `rule` types — those are server-generated (via `caura_insights` / `caura_evolve`). `write_mode`: `fast` skips embedding → keyword-only recall afterwards; `strong` forces full LLM enrichment; `auto` is usually right. - **`caura_manage op=transition`** targets: `active · pending · confirmed · cancelled · outdated · conflicted · archived · deleted` (also in `TOOLS.md`). -- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field). Scope the search to a collection when you know it; omit `collection` for the single best match across the tenant. +- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field). Scope the search to a collection when you know it; omit `collection` to return up to `top_k` matches across the tenant (default 5). - **`caura_tune`** persists and reshapes every later recall — change one or two knobs at a time; call with no arguments to read your current profile (`fts_weight` 0 = pure semantic, 1 = pure keyword). - **`caura_insights`** saves findings as `insight` memories; run it at boundaries, not every turn. `focus="divergence"` needs a non-agent scope. - **`caura_stats`** is read-only — use it as a readiness/health probe, never a write-then-delete check. diff --git a/static/docs/integration-guide.md b/static/docs/integration-guide.md index ec81faca1..8e494ed71 100644 --- a/static/docs/integration-guide.md +++ b/static/docs/integration-guide.md @@ -2,7 +2,7 @@ --- -> **For server setup, configuration, endpoints, Web UI, deployment, and smoke tests, see the [README](../../README.md).** +> **For server setup, configuration, endpoints, deployment, and smoke tests, see the [README](../../README.md).** > This guide covers only MCP client setup, OpenClaw plugin installation, agent trust levels, agent prompts, and usage examples. ## 1. Overview @@ -15,7 +15,6 @@ Caura is a shared memory layer for OpenClaw agents. It runs as a separate API se MCP Client → Streamable HTTP → Caura API (/mcp) → Postgres + pgvector OpenClaw Agent → tool call → Caura Plugin → HTTP → Caura API → Postgres + pgvector Plugin → heartbeat (60s) → Caura API ← commands (response) -Browser (UI) → HTTP → Caura API → Postgres + pgvector ``` ### Components @@ -26,7 +25,6 @@ Browser (UI) → HTTP → Caura API → Postgres + pgvector | MCP Server | Same process (`/mcp`) | Streamable HTTP endpoint for any MCP client | | Postgres + pgvector | Anywhere (Docker, VM, managed) | Vector + relational store | | Caura Plugin | OpenClaw gateway VM | Thin adapter forwarding tool calls to the API | -| Web UI | Served at `/ui` | Manage, Prism (with Graph button), Playground, Fleet, MCP Test, Ingest, Admin Dashboard | ### Tools available to agents @@ -37,13 +35,13 @@ Tool descriptions are derived from the tool registry (`core-api/src/core_api/too | `caura_write` | Yes | Yes | Single or batch write. Send `content` for one memory, or `items` (≤100) for a batch — the batch path batches embeddings and parallelizes enrichment. LLM auto-infers type, weight, status, title, summary, tags, temporal dates, PII flags. Contradiction detection auto-marks conflicting memories. `visibility` = `scope_agent` / `scope_team` (default) / `scope_org`. Content >2,000 chars is auto-chunked | | `caura_recall` | Yes | Yes | Hybrid semantic + keyword search with graph-enhanced retrieval (expands through entity relations up to 2 hops). `include_brief=true` adds a `brief` alongside the raw results, whose `summary` is the LLM's answer to your query — it reasons step by step internally and only the final answer is surfaced. Supports `fleet_ids` for multi-fleet queries. Respects visibility. Default `top_k=5`, max 200 | | `caura_manage` | Yes | Yes | Per-memory lifecycle, op-dispatched. `op=read` returns the memory; `op=update` patches fields (re-embeds if content changes); `op=transition` sets status; `op=delete` soft-deletes. Trust-enforced | -| `caura_list` | Yes | Yes | Non-semantic enumeration — filter by type/status/agent/weight/date, sort by `created_at`/`weight`/`recall_count`, cursor-paginate. `scope=agent` (default) trust ≥ 1; `scope=fleet`/`all` trust ≥ 2. Trust 3 unlocks `include_deleted` | +| `caura_list` | Yes | Yes | Non-semantic enumeration — filter by type/status/agent/weight/date, sort by `created_at`/`weight`/`recall_count`, cursor-paginate. `scope=agent` (default) and `scope=fleet` for your own fleet need trust ≥ 1; a different fleet or `scope=all` needs trust ≥ 2. Trust 3 unlocks `include_deleted` | | `caura_doc` | Yes | Yes | Document CRUD, op-dispatched. `op=write` upserts a JSON doc in a named collection (include `data["summary"]` to index it for semantic search); `op=read` fetches by `doc_id`; `op=query` filters by field equality with ordering and pagination; `op=delete` removes by `doc_id`; `op=list_collections` enumerates every collection this tenant has (with counts); `op=search` runs semantic retrieval over `data["summary"]` vectors. Use for customer records, config, inventory — anything needing exact-field lookups | | `caura_entity_get` | Yes | Yes | Look up an entity with linked memories and relations | | `caura_tune` | Yes | Yes | Persist per-agent retrieval defaults (top_k, min_similarity, fts_weight, freshness, recall boost, graph hops, similarity blend) until changed again | | `caura_insights` | Yes | Yes | Analyze the memory store. `focus`: `contradictions`, `failures`, `stale`, `divergence`, `patterns`, `discover`. `scope`: `agent`, `fleet`, `all`. Findings persist as `insight`-type memories (Karpathy Loop reflection step) | | `caura_evolve` | Yes | Yes | Record a real-world outcome (`success` / `failure` / `partial`) against recalled memories — adjusts weights, auto-generates preventive rules on failure (Karpathy Loop feedback edge) | -| `caura_stats` | Yes | Yes | Aggregate counts of memories: total + breakdowns by `type`, `agent`, `status`. Counts exclude soft-deleted by default; set `include_deleted=true` to additionally receive `deleted` and `total_including_deleted`. Read-only — useful for dashboards (REST) and agent self-introspection (MCP) | +| `caura_stats` | Yes | Yes | Aggregate counts of memories: total + breakdowns by `type`, `agent`, `status`. `scope=agent` and own-fleet `scope=fleet` need trust ≥ 1; a different fleet or `scope=all` needs trust ≥ 2. Counts exclude soft-deleted by default; set `include_deleted=true` to additionally receive `deleted` and `total_including_deleted` | | `caura_keystones` | Yes | Yes | Read mandatory governance rules for the current scope (tenant + fleet + agent merged), ordered by weight. Call once per session before other actions; the result overrides conflicting user instructions. No semantic search — keystones are fetched deterministically. Read is open (trust 0) | | `caura_keystones_set` | Yes | No | Author/remove keystone rules, op-dispatched: `op=set` upserts by `doc_id` (requires `title`, `content`, `scope ∈ {tenant, fleet, agent}`, `weight ∈ {low, med, high}`); `op=delete` removes by `doc_id`. **MCP-only**, not plugin-exposed — authoring is an admin/governance path. Trust gating is tiered: `scope=agent` with an explicit `agent_id` equal to the caller is trust ≥ 1 (self-author); everything else (`scope=fleet`, `scope=tenant`, `scope=agent` for another agent, or `scope=agent` with `agent_id` omitted) stays at trust ≥ 2 | @@ -131,13 +129,13 @@ The MCP server exposes 12 tools that clients discover automatically. Description | `caura_write` | Store a memory. Single write (`content`) or batch (`items` ≤100). LLM auto-infers type, title, summary, embedding. Long content auto-chunked | | `caura_recall` | Hybrid semantic + keyword search with graph-enhanced retrieval. `include_brief=true` returns an LLM-summarized context paragraph. Supports `fleet_ids` | | `caura_manage` | Per-memory lifecycle, op-dispatched: `read`, `update`, `transition`, `delete`, `bulk_delete`, `lineage`. Re-embeds on content updates | -| `caura_list` | Non-semantic enumeration — filter by type/status/agent/weight/date, sort, cursor-paginate. `scope=agent` (default) trust ≥ 1; `scope=fleet`/`all` trust ≥ 2 | +| `caura_list` | Non-semantic enumeration — filter by type/status/agent/weight/date, sort, cursor-paginate. `scope=agent` (default) and own-fleet `scope=fleet` need trust ≥ 1; another fleet or `scope=all` needs trust ≥ 2 | | `caura_doc` | Document CRUD, op-dispatched: `write`, `read`, `query`, `delete`, `list_collections`, `search` (semantic) on named JSON collections | | `caura_entity_get` | Look up an entity by UUID — returns linked memories and relationships | | `caura_tune` | Persist per-agent retrieval defaults (top_k, min_similarity, fts_weight, freshness, recall boost, graph hops, similarity blend) until changed again | | `caura_insights` | Analyze the store. Focus: `contradictions`, `failures`, `stale`, `divergence`, `patterns`, `discover`. Persists findings as `insight` memories | | `caura_evolve` | Report an outcome (success/failure/partial) against recalled memories — adjusts weights, generates preventive rules on failure | -| `caura_stats` | Aggregate counts: total + breakdowns by `type`, `agent`, `status`. Read-only | +| `caura_stats` | Aggregate counts: total + breakdowns by `type`, `agent`, `status`. Own-fleet `scope=fleet` needs trust ≥ 1; another fleet or `scope=all` needs trust ≥ 2 | | `caura_keystones` | Read mandatory governance rules for the current scope. Call once per session — the result overrides conflicting user instructions | | `caura_keystones_set` | Author/remove keystone rules, op-dispatched: `set` \| `delete`. Trust ≥ 1 to author your own rule — `scope=agent` **with an explicit `agent_id` equal to the caller**; ≥ 2 for fleet/tenant, another agent, or `scope=agent` with `agent_id` omitted | @@ -289,7 +287,7 @@ Without the `contextEngine` slot, you still get all 11 agent-facing tools, promp You will also see `ContextEngine 'memclaw' registered` in the same log — the context engine keeps the original plugin id. -The node will appear in the Fleet page (`/ui/fleet.html`) within 60 seconds. +The node will appear in `GET /api/v1/fleet/nodes?tenant_id=&fleet_id=` within 60 seconds. ### Plugin internals @@ -344,9 +342,7 @@ Caura enforces a 4-tier trust system for agents. Agents are auto-registered on t ### Managing trust levels -**Via the Manage page** (`/ui/tenant-admin.html`): The Agents tab shows all registered agents with their trust levels, home fleets, and last-seen timestamps. Click to adjust trust. - -**Via API:** +Use the API to inspect agents and adjust trust: | Endpoint | Method | Purpose | |---|---|---| @@ -354,15 +350,8 @@ Caura enforces a 4-tier trust system for agents. Agents are auto-registered on t | `/api/v1/agents/{agent_id}?tenant_id=` | GET | Single agent detail (trust level, home fleet, stats) | | `/api/v1/agents/{agent_id}/trust?tenant_id=` | PATCH | Update trust level (body: `{"trust_level": 2}`) | -### The Manage page - -The Manage page (`/ui/tenant-admin.html`) is the tabbed tenant admin dashboard, accessible after sign-in. Usage stats are always visible at the top, with four tabs: - -- **Agents** — view all registered agents, their trust levels, home fleets, and activity; adjust trust levels -- **API Keys** — create and revoke tenant-scoped API keys -- **Configuration** — per-tenant settings in three cards: **Models** (unified LLM provider/model for enrichment, recall, entity extraction + configurable fallback LLM for automatic failover + embedding provider/model), **Features** (enrichment, entity extraction, recall synthesis, graph retrieval, recall boost, semantic dedup, auto-crystallize, lifecycle automation, auto-chunking, agent approval), and **API Keys** (encrypted at rest). Agents can also self-tune their own search retrieval parameters (top_k, min_similarity, fts_weight, freshness, recall boost, graph hops, etc.) via the `caura_tune` tool -- **Crystallizer** — memory health + crystallization results: overall health score, hygiene issues, coverage metrics, type/status distributions, recall stats, crystallization actions taken, and report history. Run on-demand or nightly -- **Activity** — full audit trail of writes, deletes, and admin actions +The OSS server does not bundle a `/ui` application. Use these REST endpoints, +the MCP/OpenClaw tools, or a separately deployed management client. --- @@ -394,7 +383,9 @@ AFTER completing work: - Contradictions auto-detected: conflicting older memories marked outdated - Long content (>2000 chars) is auto-chunked into atomic facts - Set visibility: "scope_agent" (you only), "scope_team" (default), "scope_org" (all fleets) -- Optionally override memory_type, weight, status +- Optionally override `memory_type` with a caller-writeable type (`fact`, + `episode`, `decision`, `preference`, `task`, `plan`, or `action`), plus + `weight` and `status`. `outcome`, `rule`, and `insight` are server-reserved. - RDF triples (subject_entity_id, predicate, object_value) available via OpenClaw plugin and REST API MANAGING EXISTING MEMORIES: @@ -514,7 +505,12 @@ Returns both the fact and the decision — full context without agents needing t } ``` -Returns per-item results with `created`/`duplicate`/`error` status for each item, plus overall counts. Much faster than 4 individual single-`content` calls — embeddings are batched into a single API call and enrichment runs in parallel. Pass the batch form (`items`) exactly when you have more than one memory; `items` is mutually exclusive with `content`. +Returns per-item results with `created`, `duplicate_attempt`, +`duplicate_content`, or `error` status, plus overall counts. Much faster than 4 +individual single-`content` calls — embeddings are batched into a single API +call and enrichment runs in parallel. Pass the batch form (`items`) exactly +when you have more than one memory; `items` is mutually exclusive with +`content`. ### Example: Entity lookup @@ -582,7 +578,12 @@ The old memory is automatically marked `outdated` with `supersedes_id` pointing ### Memory types -Auto-classified by LLM on every write. Agents can override with `memory_type`. +Auto-classified by LLM on every write. Callers may override `memory_type` with +`fact`, `episode`, `decision`, `preference`, `task`, `plan`, or `action`. +`outcome`, `rule`, and `insight` are server-reserved; use `caura_evolve`, +`caura_keystones_set`, and `caura_insights` respectively. `semantic`, +`intention`, `commitment`, and `cancellation` remain readable for historical +rows but are deprecated on new writes. | Type | Use for | Default status | Example | |---|---|---|---| @@ -673,6 +674,11 @@ Togglable per tenant via `lifecycle_automation_enabled` setting. Available as the batch form of the `caura_write` tool (MCP + OpenClaw plugin, pass `items=[...]`) and the `POST /api/v1/memories/bulk` REST endpoint. Writes up to 100 memories in a single request. Optimized for throughput: +REST callers must send a unique `X-Bulk-Attempt-Id` header (1–128 characters; +letters, digits, `.`, `_`, `:`, and `-`) and reuse it when retrying the same +logical batch. MCP does not expose that header, so the server generates an +attempt id for each MCP batch call. + - **Batch embeddings** — single API call for all texts instead of N calls - **Parallel enrichment** — LLM enrichment runs concurrently (bounded at 10) - **Batch dedup** — one `WHERE content_hash IN (...)` query + intra-batch duplicate detection @@ -708,9 +714,18 @@ Response: } ``` -Each item in `items` supports the same fields as a single-`content` write (memory_type, weight, status, source_uri, entity_links, RDF triples, temporal bounds). `tenant_id`, `fleet_id`, and `agent_id` are set once at the top level. When calling `caura_write`, pass exactly one of `content` (single) or `items` (batch). - -Duplicates (exact content hash match against DB or within the batch) are reported as `"status": "duplicate"` with `duplicate_of` pointing to the existing memory ID. All enrichment, entity extraction, and contradiction detection run the same as single writes. +Each item in `items` supports the same fields as a single-`content` write +(`memory_type`, `weight`, `status`, `source_uri`, entity links, RDF triples, +and temporal bounds). The same caller-writeable `memory_type` restriction +applies. `tenant_id`, `fleet_id`, and `agent_id` are set once at the top level. +When calling `caura_write`, pass exactly one of `content` (single) or `items` +(batch). + +A retry with the same attempt id is reported as +`"status": "duplicate_attempt"`; an exact content match from a different +attempt is `"status": "duplicate_content"` with `duplicate_of` pointing to +the existing memory ID. All enrichment, entity extraction, and contradiction +detection run the same as single writes. ### Deduplication diff --git a/static/skills/caura/SKILL.md b/static/skills/caura/SKILL.md index 824228cd5..79ce22c0e 100644 --- a/static/skills/caura/SKILL.md +++ b/static/skills/caura/SKILL.md @@ -165,7 +165,9 @@ You auto-register at **trust 1** on your first write. | 3 | admin | all | all, incl. deletes | Operations that escalate the required level: -- browsing / reflecting with `scope="fleet"` or `"all"` → trust 2 +- `caura_list` / `caura_stats` for another fleet, or either tool with + `scope="all"` → trust 2; `scope="fleet"` for your own fleet stays at trust 1 +- `caura_insights` with `scope="fleet"` or `"all"` → trust 2 - reporting outcomes (`caura_evolve`) at `scope="fleet"` / `"all"` → trust 2 (default `scope="agent"` needs only trust 1) - authoring your **own** `scope=agent` keystone → trust 1 — you must pass `agent_id=`; authoring `scope=fleet` / `scope=tenant` / another agent's keystone, or a `scope=agent` rule with `agent_id` left out → trust 2 - `caura_manage op=delete` → trust 3 @@ -187,9 +189,11 @@ than silently retrying at a narrower scope. **Visibility (on write)** decides who can see a memory: `scope_agent` (private) · `scope_team` *(default — your fleet)* · `scope_org` (all fleets in tenant). -**Scope (on read / `_list` / `_insights`):** `agent` *(default)* · `fleet` -(trust 2) · `all` (trust 2). Prefer `scope_team` on write and `scope=agent` on -read unless you need cross-agent context. *Naming caveat:* writes take +**Scope (on read):** `agent` *(default)* · `fleet` · `all`. For +`caura_list` / `caura_stats`, your own fleet needs trust 1; another fleet or +`all` needs trust 2. `caura_insights` requires trust 2 for `fleet` or `all`. +Prefer `scope_team` on write and `scope=agent` on read unless you need +cross-agent context. *Naming caveat:* writes take `visibility=scope_*`; reads/list/keystone filters take `scope=*` — two axes, similar spelling. @@ -265,7 +269,7 @@ an unnamed target, and you'll get a 403 about trust rather than the shape. ```text # For yourself (trust >= 1; agent_id= is REQUIRED — omitting it # drops the call to the trust >= 2 tier): -caura_keystones_set op=set scope=agent agent_id= \ +caura_keystones_set op=set scope=agent agent_id= fleet_id= \ title="…" content="…" weight=low|med|high # For the fleet or tenant (trust >= 2; OMIT agent_id for tenant/fleet): @@ -326,7 +330,7 @@ parameter list. - **`caura_recall`** excludes superseded memories (`status` ∈ {outdated, conflicted}) by default — pass `status` explicitly to walk the chain. - **`caura_write`** can't write `insight` / `outcome` / `rule` types — those are server-generated (via `caura_insights` / `caura_evolve`). `write_mode`: `fast` skips embedding → keyword-only recall afterwards; `strong` forces full LLM enrichment; `auto` is usually right. - **`caura_manage op=transition`** targets: `active · pending · confirmed · cancelled · outdated · conflicted · archived · deleted`. -- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field); re-write with one to index it retroactively. Scope the search to a collection when you know it; omit `collection` for the single best match across the tenant. +- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field); re-write with one to index it retroactively. Scope the search to a collection when you know it; omit `collection` to return up to `top_k` matches across the tenant (default 5). - **`caura_tune`** persists and reshapes every later recall — change one or two knobs at a time; call with no arguments to read your current profile (`fts_weight` 0 = pure semantic, 1 = pure keyword). - **`caura_insights`** saves findings as `insight` memories; run it at boundaries, not every turn. `focus="divergence"` needs a non-agent scope. - **`caura_stats`** is read-only — use it as a readiness/health probe, never a write-then-delete check. diff --git a/static/skills/memclaw/SKILL.md b/static/skills/memclaw/SKILL.md index 7fdb8378c..4bb1fce75 100644 --- a/static/skills/memclaw/SKILL.md +++ b/static/skills/memclaw/SKILL.md @@ -165,7 +165,9 @@ You auto-register at **trust 1** on your first write. | 3 | admin | all | all, incl. deletes | Operations that escalate the required level: -- browsing / reflecting with `scope="fleet"` or `"all"` → trust 2 +- `caura_list` / `caura_stats` for another fleet, or either tool with + `scope="all"` → trust 2; `scope="fleet"` for your own fleet stays at trust 1 +- `caura_insights` with `scope="fleet"` or `"all"` → trust 2 - reporting outcomes (`caura_evolve`) at `scope="fleet"` / `"all"` → trust 2 (default `scope="agent"` needs only trust 1) - authoring your **own** `scope=agent` keystone → trust 1 — you must pass `agent_id=`; authoring `scope=fleet` / `scope=tenant` / another agent's keystone, or a `scope=agent` rule with `agent_id` left out → trust 2 - `caura_manage op=delete` → trust 3 @@ -187,9 +189,11 @@ than silently retrying at a narrower scope. **Visibility (on write)** decides who can see a memory: `scope_agent` (private) · `scope_team` *(default — your fleet)* · `scope_org` (all fleets in tenant). -**Scope (on read / `_list` / `_insights`):** `agent` *(default)* · `fleet` -(trust 2) · `all` (trust 2). Prefer `scope_team` on write and `scope=agent` on -read unless you need cross-agent context. *Naming caveat:* writes take +**Scope (on read):** `agent` *(default)* · `fleet` · `all`. For +`caura_list` / `caura_stats`, your own fleet needs trust 1; another fleet or +`all` needs trust 2. `caura_insights` requires trust 2 for `fleet` or `all`. +Prefer `scope_team` on write and `scope=agent` on read unless you need +cross-agent context. *Naming caveat:* writes take `visibility=scope_*`; reads/list/keystone filters take `scope=*` — two axes, similar spelling. @@ -265,7 +269,7 @@ an unnamed target, and you'll get a 403 about trust rather than the shape. ```text # For yourself (trust >= 1; agent_id= is REQUIRED — omitting it # drops the call to the trust >= 2 tier): -caura_keystones_set op=set scope=agent agent_id= \ +caura_keystones_set op=set scope=agent agent_id= fleet_id= \ title="…" content="…" weight=low|med|high # For the fleet or tenant (trust >= 2; OMIT agent_id for tenant/fleet): @@ -339,7 +343,7 @@ parameter list. - **`caura_recall`** excludes superseded memories (`status` ∈ {outdated, conflicted}) by default — pass `status` explicitly to walk the chain. - **`caura_write`** can't write `insight` / `outcome` / `rule` types — those are server-generated (via `caura_insights` / `caura_evolve`). `write_mode`: `fast` skips embedding → keyword-only recall afterwards; `strong` forces full LLM enrichment; `auto` is usually right. - **`caura_manage op=transition`** targets: `active · pending · confirmed · cancelled · outdated · conflicted · archived · deleted`. -- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field); re-write with one to index it retroactively. Scope the search to a collection when you know it; omit `collection` for the single best match across the tenant. +- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field); re-write with one to index it retroactively. Scope the search to a collection when you know it; omit `collection` to return up to `top_k` matches across the tenant (default 5). - **`caura_tune`** persists and reshapes every later recall — change one or two knobs at a time; call with no arguments to read your current profile (`fts_weight` 0 = pure semantic, 1 = pure keyword). - **`caura_insights`** saves findings as `insight` memories; run it at boundaries, not every turn. `focus="divergence"` needs a non-agent scope. - **`caura_stats`** is read-only — use it as a readiness/health probe, never a write-then-delete check.