Skip to content

Commit 224323c

Browse files
committed
feat: v12.4.0 — full functionality through every install path
1 parent f6f1b30 commit 224323c

19 files changed

Lines changed: 1222 additions & 63 deletions

.github/workflows/docker.yml

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
name: docker
2+
3+
# Build and publish multi-arch images to ghcr.io on every git tag (v*).
4+
#
5+
# Tags produced for ``v12.3.0``:
6+
# ghcr.io/vbcherepanov/total-agent-memory:12.3.0
7+
# ghcr.io/vbcherepanov/total-agent-memory:12.3
8+
# ghcr.io/vbcherepanov/total-agent-memory:12
9+
# ghcr.io/vbcherepanov/total-agent-memory:latest
10+
#
11+
# Also runnable manually via workflow_dispatch with an explicit tag — used
12+
# to backfill missing ghcr tags (e.g. 12.1, 12.2) without cutting a new
13+
# git tag.
14+
#
15+
# Smoke step: pull the freshly-pushed amd64 image, run it, hit /healthz,
16+
# fail the workflow if 200 doesn't come back within 60s.
17+
18+
on:
19+
push:
20+
tags:
21+
- 'v*'
22+
workflow_dispatch:
23+
inputs:
24+
tag:
25+
description: 'Version to publish (e.g. 12.3.0). Defaults to git ref tag.'
26+
required: false
27+
default: ''
28+
29+
permissions:
30+
contents: read
31+
packages: write
32+
33+
env:
34+
REGISTRY: ghcr.io
35+
IMAGE_NAME: ${{ github.repository_owner }}/total-agent-memory
36+
37+
jobs:
38+
build:
39+
runs-on: ubuntu-latest
40+
outputs:
41+
version: ${{ steps.version.outputs.version }}
42+
steps:
43+
- uses: actions/checkout@v4
44+
45+
- name: Resolve version
46+
id: version
47+
run: |
48+
if [ -n "${{ inputs.tag }}" ]; then
49+
v="${{ inputs.tag }}"
50+
else
51+
v="${GITHUB_REF_NAME#v}"
52+
fi
53+
if ! echo "$v" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then
54+
echo "::error::expected semver MAJOR.MINOR.PATCH, got '$v'"
55+
exit 1
56+
fi
57+
echo "version=$v" >> "$GITHUB_OUTPUT"
58+
echo "major=$(echo "$v" | cut -d. -f1)" >> "$GITHUB_OUTPUT"
59+
echo "minor=$(echo "$v" | cut -d. -f1-2)" >> "$GITHUB_OUTPUT"
60+
61+
- name: Set up QEMU (for arm64 cross-build)
62+
uses: docker/setup-qemu-action@v3
63+
64+
- name: Set up Buildx
65+
uses: docker/setup-buildx-action@v3
66+
67+
- name: Login to ghcr
68+
uses: docker/login-action@v3
69+
with:
70+
registry: ${{ env.REGISTRY }}
71+
username: ${{ github.actor }}
72+
password: ${{ secrets.GITHUB_TOKEN }}
73+
74+
- name: Build + push (amd64 + arm64)
75+
uses: docker/build-push-action@v5
76+
with:
77+
context: .
78+
file: ./Dockerfile
79+
platforms: linux/amd64,linux/arm64
80+
push: true
81+
provenance: false
82+
tags: |
83+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
84+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.minor }}
85+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.major }}
86+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
87+
labels: |
88+
org.opencontainers.image.title=total-agent-memory
89+
org.opencontainers.image.description=Persistent memory MCP server for AI coding agents
90+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
91+
org.opencontainers.image.version=${{ steps.version.outputs.version }}
92+
org.opencontainers.image.licenses=MIT
93+
org.opencontainers.image.url=https://totalmemory.dev
94+
cache-from: type=gha
95+
cache-to: type=gha,mode=max
96+
97+
smoke:
98+
needs: build
99+
runs-on: ubuntu-latest
100+
steps:
101+
- name: Pull just-pushed image (amd64)
102+
run: |
103+
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.version }}
104+
105+
- name: Run + probe /healthz + /api/stats
106+
run: |
107+
set -e
108+
docker volume create tam-ci-data
109+
docker run -d --name tam-ci \
110+
-p 37737:37737 \
111+
-v tam-ci-data:/data \
112+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.version }}
113+
# Wait up to 60s for /healthz to come up.
114+
for i in $(seq 1 30); do
115+
if curl -fsS http://127.0.0.1:37737/healthz >/dev/null 2>&1; then
116+
echo "✓ /healthz responding after ${i}*2s"
117+
break
118+
fi
119+
if [ "$i" = "30" ]; then
120+
echo "::error::/healthz did not respond within 60s"
121+
docker logs tam-ci
122+
exit 1
123+
fi
124+
sleep 2
125+
done
126+
# Wait up to 60s for /api/stats (depends on DB migration).
127+
for i in $(seq 1 30); do
128+
code=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:37737/api/stats)
129+
if [ "$code" = "200" ]; then
130+
echo "✓ /api/stats → 200 after ${i}*2s"
131+
break
132+
fi
133+
if [ "$i" = "30" ]; then
134+
echo "::error::/api/stats did not return 200 within 60s (last: $code)"
135+
docker logs tam-ci
136+
exit 1
137+
fi
138+
sleep 2
139+
done
140+
# Check Docker's own healthcheck reports healthy.
141+
for i in $(seq 1 20); do
142+
h=$(docker inspect tam-ci --format '{{.State.Health.Status}}')
143+
if [ "$h" = "healthy" ]; then
144+
echo "✓ docker healthcheck = healthy"
145+
break
146+
fi
147+
if [ "$i" = "20" ]; then
148+
echo "::error::docker healthcheck never reached 'healthy' (last: $h)"
149+
docker logs tam-ci
150+
exit 1
151+
fi
152+
sleep 3
153+
done
154+
155+
- name: Cleanup
156+
if: always()
157+
run: |
158+
docker rm -f tam-ci 2>/dev/null || true
159+
docker volume rm tam-ci-data 2>/dev/null || true

.github/workflows/smoke.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: smoke
2+
3+
# Install-matrix smoke tests — every PR + every push to main.
4+
#
5+
# Tests each user-facing install path against a clean environment and
6+
# asserts that the dashboard answers HTTP 200 (the single user-visible
7+
# acceptance criterion).
8+
#
9+
# - docker: fresh volume + docker run + curl /api/stats
10+
# - install.sh (Linux): bash install.sh inside a python:3.12-slim
11+
# container; verifies systemd unit files are written with substituted
12+
# paths (no leftover placeholders).
13+
# - install.sh (macOS): runs on macos-latest runner; checks plist
14+
# substitution + no port-37737 conflict.
15+
16+
on:
17+
push:
18+
branches: [main]
19+
pull_request:
20+
workflow_dispatch:
21+
22+
jobs:
23+
docker-single-image:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v4
27+
- name: docker pull → dashboard answers 200
28+
run: bash tests/smoke/install-docker-pull.sh
29+
30+
install-sh-linux:
31+
runs-on: ubuntu-latest
32+
container:
33+
image: python:3.12-slim
34+
steps:
35+
- uses: actions/checkout@v4
36+
- name: install deps
37+
run: |
38+
apt-get update -qq
39+
apt-get install -y --no-install-recommends bash git curl >/dev/null
40+
- name: bash install.sh inside container → unit files staged
41+
env:
42+
INSTALL_TEST_MODE: skip-heavy
43+
TAM_MEMORY_DIR: /root/.tam
44+
run: |
45+
bash install.sh --ide cursor 2>&1 | tail -20 || true
46+
for u in tam-dashboard.service tam-reflection.service tam-reflection.path; do
47+
f="$HOME/.config/systemd/user/$u"
48+
test -f "$f" || { echo "FAIL: $u not staged"; exit 1; }
49+
grep -q '@INSTALL_DIR@\|@MEMORY_DIR@\|__HOME__' "$f" \
50+
&& { echo "FAIL: $u has leftover placeholders"; cat "$f"; exit 2; }
51+
done
52+
echo "✓ all unit files staged with substituted paths"
53+
54+
install-sh-macos:
55+
runs-on: macos-latest
56+
steps:
57+
- uses: actions/checkout@v4
58+
- uses: actions/setup-python@v5
59+
with:
60+
python-version: '3.12'
61+
- name: install.sh smoke (sandbox HOME)
62+
run: bash tests/smoke/install-sh-macos.sh

CHANGELOG.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,134 @@ All notable changes to total-agent-memory are documented in this file.
44
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
55
and versions use [Semantic Versioning](https://semver.org/).
66

7+
## [12.4.0] — 2026-05-26 — 100% functional through every install path
8+
9+
`npx connect`, `bash install.sh`, `docker run`, `docker compose up` — same
10+
4 services, same MCP HTTP API, same dashboard. No path is missing pieces
11+
anymore.
12+
13+
### Added — MCP Streamable HTTP transport
14+
- `src/server.py` now supports `MCP_TRANSPORT=http` (also accepts
15+
`streamable-http`). Boots a uvicorn ASGI app exposing:
16+
- `POST/GET/DELETE /mcp` — MCP Streamable HTTP (spec 2025-03-26)
17+
- `GET /healthz` — DB-independent liveness probe
18+
- Same `Server` instance powers both stdio and HTTP, so all 60+ MCP tools
19+
are reachable over the network — IDEs, sidecars, k8s pods can all
20+
connect without a stdio bridge.
21+
- ``stateless=False`` keeps in-process session multiplexing; clean
22+
SIGTERM via uvicorn's `timeout_graceful_shutdown=5` matches Docker
23+
stop-grace.
24+
25+
### Added — scheduler service (cron-style)
26+
- `docker/scheduler_daemon.py` — stdlib-only scheduler that runs
27+
`orphan-backfill` 4×/day and `check-updates` weekly inside the same
28+
image. Replaces the `mcuadros/ofelia` external container and its
29+
Docker-socket mount.
30+
- npm wrapper 1.3.0 ships matching scheduler services on macOS
31+
(`StartCalendarInterval`) and Linux (`OnCalendar` systemd timers).
32+
33+
### Added — full single-image stack
34+
- `tam-entrypoint` shim routes `docker run … mcp|dashboard|reflection|scheduler|all`
35+
through the supervisor.
36+
- Default `TAM_SUPERVISOR_SERVICES=mcp,dashboard,reflection,scheduler`
37+
`docker run -p 3737:3737 -p 37737:37737 -v vol:/data ghcr.io/.../total-agent-memory`
38+
brings the whole product up with one command.
39+
- `EXPOSE 3737 37737`; HEALTHCHECK probes both `/healthz` endpoints.
40+
- ML caches pinned to the data volume (`HF_HOME`, `TRANSFORMERS_CACHE`,
41+
`TORCHINDUCTOR_CACHE_DIR`) — prevents torch._dynamo from crashing on
42+
containers with tmpfs `/tmp` < 100 MB.
43+
- Default `OLLAMA_URL=http://host.docker.internal:11434` — single-image
44+
finds host Ollama out of the box on Docker Desktop / Windows / WSL2.
45+
Graceful degradation to FTS+embeddings if Ollama unreachable.
46+
47+
### Added (v12.3 carry-over — never tagged separately)
48+
- `docker/tam_supervisor.py` — stdlib supervisor (prefix logging,
49+
exponential-backoff restarts, signal propagation).
50+
- `/healthz` endpoint on the dashboard.
51+
- Auto-initialise `memory.db` on first boot inside the container.
52+
- `launchagents/com.total-agent-memory.dashboard.plist` (installed by
53+
`install.sh` on macOS).
54+
- `.github/workflows/docker.yml` — multi-arch (amd64+arm64) build+push
55+
to ghcr on every git tag, with `/healthz` + `/api/stats` + docker
56+
healthcheck smoke before publish.
57+
- `.github/workflows/smoke.yml` — install-matrix smoke on every PR.
58+
- `tests/smoke/install-docker-pull.sh`, `tests/smoke/install-sh-macos.sh`.
59+
- Plist placeholders unified to `__INSTALL_DIR__` / `__MEMORY_DIR__` /
60+
`__HOME__`. Old plists hardcoded `__HOME__/claude-memory-server/...`,
61+
breaking clones with any other directory name.
62+
63+
### Changed
64+
- `docker-compose.yml` `mcp` service is back (was removed in v12.3 when
65+
HTTP transport didn't exist). Same image as `dashboard`/`reflection`,
66+
`command: ["mcp"]`.
67+
- `scheduler` compose service now runs in the main image
68+
(`command: ["scheduler"]`) instead of `mcuadros/ofelia` — no more
69+
Docker socket mount in the stack.
70+
- `install.sh` Step 5 (old `dashboard-service.sh install` call) removed
71+
— it created a `com.claude-total-memory.dashboard` plist that fought
72+
the canonical `com.total-agent-memory.dashboard` plist for port 37737.
73+
74+
### Fixed
75+
- `bash install.sh` under any clone name now writes valid plist/unit
76+
paths instead of references to a non-existent `~/claude-memory-server/`.
77+
- `bash install.sh --uninstall` cleans up the new dashboard plist too.
78+
79+
## [12.3.0] — never tagged — folded into 12.4.0
80+
81+
The 1.1.0 npm wrapper, `docker pull`, and certain `git clone` layouts all
82+
left users with a non-functional web dashboard despite the module being
83+
present in the package. v12.3 closes those gaps end-to-end.
84+
85+
### Added
86+
- `docker/tam_supervisor.py` — single-container supervisor (~150 LOC,
87+
stdlib only) that runs dashboard + reflection together with prefix
88+
logging, exponential-backoff restarts, and clean SIGTERM propagation.
89+
Makes `docker run ghcr.io/.../total-agent-memory` give a working
90+
dashboard with one command.
91+
- `tam-entrypoint` shim in the image — routes `docker run … mcp|dashboard|reflection|all`
92+
through the supervisor so compose services and single-image use share
93+
a single CMD shape.
94+
- `/healthz` endpoint on the dashboard — DB-independent liveness probe
95+
suitable for Docker `HEALTHCHECK` and Kubernetes `livenessProbe`. Fresh
96+
volumes no longer report unhealthy until the first save.
97+
- Auto-initialise `memory.db` on first boot in the supervisor.
98+
- `launchagents/com.total-agent-memory.dashboard.plist` — keeps the
99+
dashboard running and restarts on crash. Installed automatically by
100+
`install.sh` on macOS.
101+
- `.github/workflows/docker.yml` — build + push multi-arch
102+
(`amd64`, `arm64`) ghcr images on every git tag, with `/healthz` +
103+
`/api/stats` + Docker healthcheck smoke before publish. Tags produced:
104+
`:X.Y.Z`, `:X.Y`, `:X`, `:latest`.
105+
- `.github/workflows/smoke.yml` — runs install-matrix smoke on every PR.
106+
- `tests/smoke/install-docker-pull.sh` + `tests/smoke/install-sh-macos.sh`
107+
— black-box end-to-end install verifiers.
108+
109+
### Changed
110+
- `install.sh` Step 5 (the old `dashboard-service.sh install` call)
111+
removed — it created a `com.claude-total-memory.dashboard` plist that
112+
fought the canonical `com.total-agent-memory.dashboard` plist for
113+
port 37737. One always ended up in crash-loop.
114+
- Plist placeholders unified to `__INSTALL_DIR__` / `__MEMORY_DIR__` /
115+
`__HOME__`. Old plists hardcoded `__HOME__/claude-memory-server/...`,
116+
breaking any clone whose directory name wasn't `claude-memory-server`
117+
— the common case after the rebrand.
118+
- Dockerfile `CMD` switched to the supervisor; `EXPOSE` narrowed to
119+
`37737` (the MCP HTTP port 3737 was never functional — see Known Issues).
120+
- `docker-compose.yml` `mcp` service removed — `MCP_TRANSPORT=http` was
121+
silently ignored by `src/server.py` (stdio-only). For MCP, use
122+
`docker/run-mcp.sh` (stdio bridge). HTTP transport lands in v12.4.
123+
124+
### Fixed
125+
- `bash install.sh` under any clone name now writes valid plist/unit
126+
paths instead of references to a non-existent `~/claude-memory-server/`.
127+
- `bash install.sh --uninstall` cleans up the new dashboard plist too.
128+
- New regression test `test_launchagents_substitute_install_dir_and_memory_dir`
129+
guards against placeholder leakage.
130+
131+
### Known issues
132+
- MCP server is still stdio-only inside the Docker image — connect via
133+
`docker/run-mcp.sh`. Streamable HTTP transport is tracked for v12.4.
134+
7135
## [12.2.0] — 2026-05-24 — v11 W3 dispatch fix + Codex env alignment
8136

9137
Bugfix release that restores four previously-broken v11 W3 MCP tools and

0 commit comments

Comments
 (0)