Skip to content
Open
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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Hook scripts in `src/hooks/` are standalone Node.js scripts (no iii-sdk import).
## Current Stats (v0.9.28)

- 54 MCP tools (8 visible by default, `AGENTMEMORY_TOOLS=all` for all)
- 129 REST endpoints
- 130 REST endpoints
- 6 MCP resources, 3 MCP prompts
- 12 hooks, 15 skills
- 260+ iii functions
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1499,7 +1499,7 @@ Create `~/.agentmemory/.env`:

<h2 id="api"><picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/section-api.svg"><img src="assets/tags/section-api.svg" alt="API" height="32" /></picture></h2>

129 endpoints on port `3111`. The REST API binds to `127.0.0.1` by default. Protected endpoints require `Authorization: Bearer <secret>` when `AGENTMEMORY_SECRET` is set, and mesh sync endpoints require `AGENTMEMORY_SECRET` on both peers.
130 endpoints on port `3111`. The REST API binds to `127.0.0.1` by default. Protected endpoints require `Authorization: Bearer <secret>` when `AGENTMEMORY_SECRET` is set, and mesh sync endpoints require `AGENTMEMORY_SECRET` on both peers.

<details>
<summary>Key endpoints</summary>
Expand Down
39 changes: 39 additions & 0 deletions deploy/docker-compose/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# ==========================================
# AgentMemory Configuration
# ==========================================

# ------------------------------------------
# 1. LLM Provider (Deepseek Flash V4 / openrouter)
# ------------------------------------------
# To use Deepseek, you can use OpenRouter or direct DeepSeek API (OpenAI compatible)
# Uncomment one of the following approaches:

# Approach A: OpenRouter (Recommended for Deepseek)
OPENROUTER_API_KEY=your_openrouter_api_key_here
OPENROUTER_MODEL=deepseek/deepseek-chat

# Approach B: Direct DeepSeek API
# OPENAI_API_KEY=your_deepseek_api_key_here
# OPENAI_BASE_URL=https://api.deepseek.com
# OPENAI_MODEL=deepseek-chat

# ------------------------------------------
# 2. Embeddings Configuration (CPU-only Local)
# ------------------------------------------
# Set embedding provider to local
EMBEDDING_PROVIDER=local

# Default model is Xenova/all-MiniLM-L6-v2. If you change it,
# make sure you update the dimensions to match your model!
# NOTE: The custom model support requires building from the latest modified source.
LOCAL_EMBEDDING_MODEL=Xenova/all-MiniLM-L6-v2
LOCAL_EMBEDDING_DIMENSIONS=384

# ------------------------------------------
# 3. AgentMemory Features
# ------------------------------------------
# Run LLM compression on every observation batch (Warning: uses LLM tokens)
AGENTMEMORY_AUTO_COMPRESS=false

# Inject recalled memories back into agent prompts
AGENTMEMORY_INJECT_CONTEXT=false
39 changes: 39 additions & 0 deletions deploy/docker-compose/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Build stage
FROM node:22-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
RUN npm prune --production

# Final stage
FROM iiidev/iii:0.11.2 AS iii-image

FROM node:22-slim
WORKDIR /opt/agentmemory
RUN apt-get update \
&& apt-get install -y --no-install-recommends openssl ca-certificates tini gosu curl \
&& rm -rf /var/lib/apt/lists/*

COPY --from=iii-image /app/iii /usr/local/bin/iii
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist

# Symlink so `agentmemory` works globally
RUN ln -s /opt/agentmemory/dist/cli.mjs /usr/local/bin/agentmemory \
&& chmod +x /opt/agentmemory/dist/cli.mjs

ENV AGENTMEMORY_III_VERSION=0.11.2 \
TINI_SUBREAPER=1

COPY deploy/docker-compose/entrypoint.sh /usr/local/bin/agentmemory-entrypoint.sh
RUN chmod 0755 /usr/local/bin/agentmemory-entrypoint.sh

EXPOSE 3111

HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD curl -fsS http://127.0.0.1:3111/agentmemory/livez || exit 1

ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/agentmemory-entrypoint.sh"]
30 changes: 30 additions & 0 deletions deploy/docker-compose/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
services:
agentmemory:
build:
context: ../..
dockerfile: deploy/docker-compose/Dockerfile
restart: unless-stopped
env_file:
- .env
expose:
- "3111"
ports:
- "127.0.0.1:3111:3111"
- "127.0.0.1:3112:3112"
- "127.0.0.1:3113:3113"
volumes:
- agentmemory-data:/data
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:3111/agentmemory/livez || exit 1"]
interval: 30s
timeout: 5s
start_period: 30s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

volumes:
agentmemory-data:
97 changes: 97 additions & 0 deletions deploy/docker-compose/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/bin/sh
# agentmemory first-boot entrypoint.
#
# Runs as root so it can:
# 1. Overwrite the iii-config.yaml with a deploy-tuned version that
# binds 0.0.0.0 and uses absolute /data paths.
# 2. chown the platform-mounted /data volume to the runtime user
# (managed platforms mount volumes root-owned 755 by default).
# 3. Generate the HMAC secret on first boot and persist it to
# /data/.hmac (chmod 600) so the secret survives restarts.
#
# Then it execs the agentmemory CLI under gosu as the unprivileged
# `node` user.

set -eu

DATA_DIR="${AGENTMEMORY_DATA_DIR:-/data}"
HMAC_FILE="${AGENTMEMORY_HMAC_FILE:-/data/.hmac}"
RUN_AS="node:node"
III_CONFIG="/opt/agentmemory/dist/iii-config.yaml"

mkdir -p "$DATA_DIR"
chown -R "$RUN_AS" "$DATA_DIR"

cat > "$III_CONFIG" <<'EOF'
workers:
- name: iii-http
config:
port: 3111
host: 0.0.0.0
default_timeout: 180000
cors:
allowed_origins:
- "http://localhost:3111"
- "http://localhost:3113"
- "http://127.0.0.1:3111"
- "http://127.0.0.1:3113"
allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
- name: iii-state
config:
adapter:
name: kv
config:
store_method: file_based
file_path: /data/state_store.db
- name: iii-queue
config:
adapter:
name: builtin
- name: iii-pubsub
config:
adapter:
name: local
- name: iii-cron
config:
adapter:
name: kv
- name: iii-stream
config:
port: 3112
host: 0.0.0.0
adapter:
name: kv
config:
store_method: file_based
file_path: /data/stream_store
- name: iii-observability
config:
enabled: true
service_name: agentmemory
exporter: memory
sampling_ratio: 1.0
metrics_enabled: true
logs_enabled: true
logs_console_output: true
EOF
chown "$RUN_AS" "$III_CONFIG"

if [ ! -s "$HMAC_FILE" ]; then
SECRET="$(openssl rand -hex 32)"
umask 077
printf '%s\n' "$SECRET" > "$HMAC_FILE"
chmod 600 "$HMAC_FILE"
chown "$RUN_AS" "$HMAC_FILE"
echo "================================================================"
echo "agentmemory: generated HMAC secret on first boot"
echo "AGENTMEMORY_SECRET=$SECRET"
echo "Copy this value now. It will not be printed again."
echo "Stored at: $HMAC_FILE (chmod 600)"
echo "To rotate: delete $HMAC_FILE on the persistent volume and restart."
Comment thread
vinbyte marked this conversation as resolved.
echo "================================================================"
fi

AGENTMEMORY_SECRET="$(cat "$HMAC_FILE")"
export AGENTMEMORY_SECRET

exec gosu "$RUN_AS" agentmemory "$@"
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ async function main() {
`Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`,
);
bootLog(
`REST API: 129 endpoints at http://localhost:${config.restPort}/agentmemory/*`,
`REST API: 130 endpoints at http://localhost:${config.restPort}/agentmemory/*`,
);
bootLog(
`MCP surface (opt-in via \`npx @agentmemory/mcp\`): ${getAllTools().length} tools · 6 resources · 3 prompts`,
Expand Down
18 changes: 16 additions & 2 deletions src/providers/embedding/local.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { EmbeddingProvider } from "../../types.js";
import { getEnvVar } from "../../config.js";

type FeatureExtractor = (
texts: string[],
Expand All @@ -7,8 +8,21 @@ type FeatureExtractor = (

export class LocalEmbeddingProvider implements EmbeddingProvider {
readonly name = "local";
readonly dimensions = 384;
readonly dimensions: number;
private extractor: FeatureExtractor | null = null;
private model: string;

constructor() {
this.model = getEnvVar("LOCAL_EMBEDDING_MODEL") || "Xenova/all-MiniLM-L6-v2";
const dim = getEnvVar("LOCAL_EMBEDDING_DIMENSIONS");
const dimensions = dim ? Number(dim) : 384;
if (!Number.isSafeInteger(dimensions) || dimensions <= 0) {
throw new Error(
"LOCAL_EMBEDDING_DIMENSIONS must be a positive safe integer",
);
}
this.dimensions = dimensions;
}

async embed(text: string): Promise<Float32Array> {
const [result] = await this.embedBatch([text]);
Expand Down Expand Up @@ -39,7 +53,7 @@ export class LocalEmbeddingProvider implements EmbeddingProvider {
}
this.extractor = (await transformers.pipeline(
"feature-extraction",
"Xenova/all-MiniLM-L6-v2",
this.model,
{ dtype: "q8" },
)) as FeatureExtractor;
return this.extractor;
Expand Down