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
18 changes: 8 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
go-version: '1.25.9'
cache: true

- name: Download modules
Expand All @@ -34,9 +34,9 @@ jobs:
fi

- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest
run: |
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
"$(go env GOPATH)/bin/golangci-lint" run

- name: Run unit tests
run: go test -race ./...
Expand All @@ -61,7 +61,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
go-version: '1.25.9'
cache: true

- name: Download modules
Expand All @@ -72,14 +72,12 @@ jobs:
curl -fsSL https://binaries.cockroachdb.com/cockroach-v26.1.3.linux-amd64.tgz | tar -xz

- name: Start CockroachDB
run: |
./cockroach-v26.1.3.linux-amd64/cockroach start-single-node --insecure --store=type=mem,size=0.25GiB --listen-addr=127.0.0.1:26257 --http-addr=127.0.0.1:8080 --background
run: ./cockroach-v26.1.3.linux-amd64/cockroach start-single-node --insecure --store=type=mem,size=0.75GiB --listen-addr=127.0.0.1:26257 --http-addr=127.0.0.1:8080 --background

- name: Wait for CockroachDB
run: |
./cockroach-v26.1.3.linux-amd64/cockroach sql --insecure --host=127.0.0.1:26257 -e 'select 1'
run: ./cockroach-v26.1.3.linux-amd64/cockroach sql --insecure --host=127.0.0.1:26257 -e 'select 1'

- name: Run integration and migration upgrade tests
env:
COCKROACH_TEST_DATABASE_URL: postgresql://root@127.0.0.1:26257/defaultdb?sslmode=disable
run: go test -count=1 -tags=integration ./...
run: go test -count=1 -p 1 -tags=integration ./...
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
go-version: '1.25.9'
cache: true

- name: Verify go.mod and go.sum are tidy
Expand All @@ -38,7 +38,7 @@ jobs:
run: |
./cockroach-v26.1.3.linux-amd64/cockroach start-single-node \
--insecure \
--store=type=mem,size=0.25GiB \
--store=type=mem,size=0.75GiB \
--listen-addr=127.0.0.1:26257 \
--http-addr=127.0.0.1:8080 \
--background
Expand All @@ -53,7 +53,7 @@ jobs:
- name: Run integration and migration upgrade tests
env:
COCKROACH_TEST_DATABASE_URL: postgresql://root@127.0.0.1:26257/defaultdb?sslmode=disable
run: go test -count=1 -tags=integration ./...
run: go test -count=1 -p 1 -tags=integration ./...

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
Expand Down
10 changes: 0 additions & 10 deletions LICENSE-OPTIONS.md

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,4 +506,4 @@ The MCP layer uses camelCase tool arguments, consistent with your other MCP serv
- Release scope and process notes live in `docs/release.md`.
- Deployment guidance lives in `docs/operations/deploy.md`.
- Backup/export/restore guidance lives in `docs/operations/backup-restore.md`.
- The repository is licensed under `Apache-2.0`. Historical license notes live in `LICENSE-OPTIONS.md`.
- The repository is licensed under `Apache-2.0`.
68 changes: 36 additions & 32 deletions cmd/memoryd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,36 +400,40 @@ func parseOptionalUUIDFlag(value string) (*uuid.UUID, error) {
}

func printUsage(w *os.File) {
fmt.Fprintln(w, "agent-memory")
fmt.Fprintln(w, "")
fmt.Fprintln(w, "Usage:")
fmt.Fprintln(w, " memoryd serve")
fmt.Fprintln(w, " memoryd worker [--once]")
fmt.Fprintln(w, " memoryd import --format=<name> --file=<path> --tenant-id=<uuid> --agent-id=<uuid> [--thread-id=<uuid>] [--source-id=<value>]")
fmt.Fprintln(w, " memoryd doctor")
fmt.Fprintln(w, " memoryd migrate")
fmt.Fprintln(w, " memoryd version")
fmt.Fprintln(w, "")
fmt.Fprintln(w, "Environment:")
fmt.Fprintln(w, " MEMORY_DATABASE_URL CockroachDB connection string")
fmt.Fprintln(w, " MEMORY_HTTP_ADDR HTTP listen address (default :8080)")
fmt.Fprintln(w, " MEMORY_HTTP_AUTH_MODE disabled|api_key (default disabled)")
fmt.Fprintln(w, " MEMORY_HTTP_AUTH_HEADER auth header name (default Authorization)")
fmt.Fprintln(w, " MEMORY_HTTP_AUTH_SCHEME auth scheme prefix (default Bearer)")
fmt.Fprintln(w, " MEMORY_HTTP_AUTH_PRINCIPALS_JSON JSON array of principals when api_key auth is enabled")
fmt.Fprintln(w, " MEMORY_HTTP_RATE_LIMIT_RPS requests per second per principal, 0 disables rate limiting")
fmt.Fprintln(w, " MEMORY_HTTP_RATE_LIMIT_BURST token bucket burst size when rate limiting is enabled")
fmt.Fprintln(w, " MEMORY_LOG_LEVEL debug|info|warn|error (default info)")
fmt.Fprintln(w, " MEMORY_LOG_FORMAT text|json|logfmt (default text)")
fmt.Fprintln(w, " MEMORY_LOG_REPORT_CALLER true|false (default false)")
fmt.Fprintln(w, " MEMORY_LOG_TIMESTAMPS true|false (default true)")
fmt.Fprintln(w, " MEMORY_SHUTDOWN_TIMEOUT graceful shutdown timeout (default 10s)")
fmt.Fprintln(w, " MEMORY_HEALTH_TIMEOUT readiness timeout (default 3s)")
fmt.Fprintln(w, " MEMORY_WORKER_POLL_INTERVAL worker poll interval (default 10s)")
fmt.Fprintln(w, " MEMORY_EMBEDDER_PROVIDER deterministic|openai (default deterministic)")
fmt.Fprintln(w, " MEMORY_EMBEDDER_MODEL required when provider=openai")
fmt.Fprintln(w, " MEMORY_EMBEDDER_DIMENSIONS embedding vector dimensions (default 1536)")
fmt.Fprintln(w, " MEMORY_EMBED_MAX_BATCH max texts per provider embedding call (default 32)")
fmt.Fprintln(w, " MEMORY_OPENAI_API_KEY required when provider=openai")
fmt.Fprintln(w, " MEMORY_OPENAI_BASE_URL optional OpenAI-compatible base URL")
writeUsageLine(w, "agent-memory")
writeUsageLine(w, "")
writeUsageLine(w, "Usage:")
writeUsageLine(w, " memoryd serve")
writeUsageLine(w, " memoryd worker [--once]")
writeUsageLine(w, " memoryd import --format=<name> --file=<path> --tenant-id=<uuid> --agent-id=<uuid> [--thread-id=<uuid>] [--source-id=<value>]")
writeUsageLine(w, " memoryd doctor")
writeUsageLine(w, " memoryd migrate")
writeUsageLine(w, " memoryd version")
writeUsageLine(w, "")
writeUsageLine(w, "Environment:")
writeUsageLine(w, " MEMORY_DATABASE_URL CockroachDB connection string")
writeUsageLine(w, " MEMORY_HTTP_ADDR HTTP listen address (default :8080)")
writeUsageLine(w, " MEMORY_HTTP_AUTH_MODE disabled|api_key (default disabled)")
writeUsageLine(w, " MEMORY_HTTP_AUTH_HEADER auth header name (default Authorization)")
writeUsageLine(w, " MEMORY_HTTP_AUTH_SCHEME auth scheme prefix (default Bearer)")
writeUsageLine(w, " MEMORY_HTTP_AUTH_PRINCIPALS_JSON JSON array of principals when api_key auth is enabled")
writeUsageLine(w, " MEMORY_HTTP_RATE_LIMIT_RPS requests per second per principal, 0 disables rate limiting")
writeUsageLine(w, " MEMORY_HTTP_RATE_LIMIT_BURST token bucket burst size when rate limiting is enabled")
writeUsageLine(w, " MEMORY_LOG_LEVEL debug|info|warn|error (default info)")
writeUsageLine(w, " MEMORY_LOG_FORMAT text|json|logfmt (default text)")
writeUsageLine(w, " MEMORY_LOG_REPORT_CALLER true|false (default false)")
writeUsageLine(w, " MEMORY_LOG_TIMESTAMPS true|false (default true)")
writeUsageLine(w, " MEMORY_SHUTDOWN_TIMEOUT graceful shutdown timeout (default 10s)")
writeUsageLine(w, " MEMORY_HEALTH_TIMEOUT readiness timeout (default 3s)")
writeUsageLine(w, " MEMORY_WORKER_POLL_INTERVAL worker poll interval (default 10s)")
writeUsageLine(w, " MEMORY_EMBEDDER_PROVIDER deterministic|openai (default deterministic)")
writeUsageLine(w, " MEMORY_EMBEDDER_MODEL required when provider=openai")
writeUsageLine(w, " MEMORY_EMBEDDER_DIMENSIONS embedding vector dimensions (default 1536)")
writeUsageLine(w, " MEMORY_EMBED_MAX_BATCH max texts per provider embedding call (default 32)")
writeUsageLine(w, " MEMORY_OPENAI_API_KEY required when provider=openai")
writeUsageLine(w, " MEMORY_OPENAI_BASE_URL optional OpenAI-compatible base URL")
}

func writeUsageLine(w *os.File, line string) {
_, _ = fmt.Fprintln(w, line)
}
4 changes: 3 additions & 1 deletion internal/importer/cursor_agent_jsonl.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ func (CursorAgentJSONLImporter) ImportFile(ctx context.Context, req FileImportRe
if err != nil {
return nil, fmt.Errorf("open transcript file: %w", err)
}
defer file.Close()
defer func() {
_ = file.Close()
}()

scanner := bufio.NewScanner(file)
scanner.Buffer(make([]byte, 0, 64*1024), 10*1024*1024)
Expand Down
4 changes: 3 additions & 1 deletion internal/server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,9 @@ func hasSupportedV1ContentType(raw string) bool {

func decodeJSONBody(w http.ResponseWriter, r *http.Request, dst any) error {
r.Body = http.MaxBytesReader(w, r.Body, maxJSONBodyBytes)
defer r.Body.Close()
defer func() {
_ = r.Body.Close()
}()

decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields()
Expand Down
15 changes: 0 additions & 15 deletions internal/store/cockroach/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"strings"
"time"

"github.com/google/uuid"
"github.com/jackc/pgx/v5"
Expand Down Expand Up @@ -142,20 +141,6 @@ func scanEpisode(scanner rowScanner) (memory.Episode, error) {
return episode, nil
}

func cursorFromEpisode(episode memory.Episode) memory.CheckpointCursor {
return memory.CheckpointCursor{
CreatedAt: episode.CreatedAt,
ID: episode.ID,
}
}

func optionalTime(value *time.Time) any {
if value == nil {
return nil
}
return *value
}

func scanMemory(scanner rowScanner) (memory.Memory, error) {
var (
record memory.Memory
Expand Down
8 changes: 4 additions & 4 deletions internal/store/cockroach/introspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ func (s *Store) ListProjectionCheckpoints(ctx context.Context, req memory.ListPr
args = append(args, req.TenantID)
if strings.TrimSpace(req.ProjectionName) != "" {
argN++
builder.WriteString(fmt.Sprintf(" AND projection_name = $%d", argN))
fmt.Fprintf(&builder, " AND projection_name = $%d", argN)
args = append(args, strings.TrimSpace(req.ProjectionName))
}
argN++
builder.WriteString(fmt.Sprintf(" ORDER BY projection_name, shard_id LIMIT $%d", argN))
fmt.Fprintf(&builder, " ORDER BY projection_name, shard_id LIMIT $%d", argN)
args = append(args, limit)

rows, err := s.pool.Query(ctx, builder.String(), args...)
Expand Down Expand Up @@ -159,11 +159,11 @@ func (s *Store) ListConsolidationRuns(ctx context.Context, req memory.ListConsol
args = append(args, req.TenantID)
if strings.TrimSpace(req.ProjectionName) != "" {
argN++
builder.WriteString(fmt.Sprintf(" AND projection_name = $%d", argN))
fmt.Fprintf(&builder, " AND projection_name = $%d", argN)
args = append(args, strings.TrimSpace(req.ProjectionName))
}
argN++
builder.WriteString(fmt.Sprintf(" ORDER BY created_at DESC LIMIT $%d", argN))
fmt.Fprintf(&builder, " ORDER BY created_at DESC LIMIT $%d", argN)
args = append(args, limit)

rows, err := s.pool.Query(ctx, builder.String(), args...)
Expand Down
10 changes: 5 additions & 5 deletions internal/store/cockroach/memories.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,27 +352,27 @@ func (s *Store) QueryMemories(ctx context.Context, req memory.QueryMemoriesReque

if req.AgentID != nil {
argN++
builder.WriteString(fmt.Sprintf(" AND agent_id = $%d", argN))
fmt.Fprintf(&builder, " AND agent_id = $%d", argN)
args = append(args, *req.AgentID)
}
if req.ThreadID != nil {
argN++
builder.WriteString(fmt.Sprintf(" AND thread_id = $%d", argN))
fmt.Fprintf(&builder, " AND thread_id = $%d", argN)
args = append(args, *req.ThreadID)
}
if strings.TrimSpace(req.Kind) != "" {
argN++
builder.WriteString(fmt.Sprintf(" AND kind = $%d", argN))
fmt.Fprintf(&builder, " AND kind = $%d", argN)
args = append(args, strings.TrimSpace(req.Kind))
}
if strings.TrimSpace(req.Status) != "" {
argN++
builder.WriteString(fmt.Sprintf(" AND status = $%d", argN))
fmt.Fprintf(&builder, " AND status = $%d", argN)
args = append(args, strings.TrimSpace(req.Status))
}

argN++
builder.WriteString(fmt.Sprintf(" ORDER BY created_at, id LIMIT $%d", argN))
fmt.Fprintf(&builder, " ORDER BY created_at, id LIMIT $%d", argN)
args = append(args, limit)

rows, err := s.pool.Query(ctx, builder.String(), args...)
Expand Down
4 changes: 3 additions & 1 deletion internal/store/cockroach/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ func (s *Store) Migrate(ctx context.Context) error {
}

sqlDB := stdlib.OpenDBFromPool(s.pool)
defer sqlDB.Close()
defer func() {
_ = sqlDB.Close()
}()

goose.SetBaseFS(migrationFS)
goose.SetLogger(discardLogger{})
Expand Down
12 changes: 6 additions & 6 deletions internal/store/cockroach/recall.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (s *Store) SearchMemories(ctx context.Context, req memory.RecallRequest, qu
var (
builder strings.Builder
args []any
argN = 1
argN int
)
builder.WriteString(`
SELECT m.id, m.tenant_id, m.agent_id, m.thread_id, m.kind, m.status, m.content, m.summary, m.attributes,
Expand All @@ -41,27 +41,27 @@ func (s *Store) SearchMemories(ctx context.Context, req memory.RecallRequest, qu

if req.AgentID != nil {
argN++
builder.WriteString(fmt.Sprintf(" AND me.agent_id = $%d", argN))
fmt.Fprintf(&builder, " AND me.agent_id = $%d", argN)
args = append(args, *req.AgentID)
}
if req.ThreadID != nil {
argN++
builder.WriteString(fmt.Sprintf(" AND m.thread_id = $%d", argN))
fmt.Fprintf(&builder, " AND m.thread_id = $%d", argN)
args = append(args, *req.ThreadID)
}
if strings.TrimSpace(req.Kind) != "" {
argN++
builder.WriteString(fmt.Sprintf(" AND m.kind = $%d", argN))
fmt.Fprintf(&builder, " AND m.kind = $%d", argN)
args = append(args, strings.TrimSpace(req.Kind))
}
if strings.TrimSpace(req.Status) != "" {
argN++
builder.WriteString(fmt.Sprintf(" AND m.status = $%d", argN))
fmt.Fprintf(&builder, " AND m.status = $%d", argN)
args = append(args, strings.TrimSpace(req.Status))
}

argN++
builder.WriteString(fmt.Sprintf(" ORDER BY me.embedding <=> $1, m.created_at, m.id LIMIT $%d", argN))
fmt.Fprintf(&builder, " ORDER BY me.embedding <=> $1, m.created_at, m.id LIMIT $%d", argN)
args = append(args, limit)

rows, err := s.pool.Query(ctx, builder.String(), args...)
Expand Down
Loading