Skip to content

Releases: lazypower/continuity

v0.4.0 — Tighten the Bolts

24 Apr 03:55

Choose a tag to compare

We security audited ourselves before anyone asked us to.

Continuity stores your entire working relationship with your agent — how you think, what you've built, corrections you've made, the texture of how you collaborate. That's sensitive data sitting in a SQLite file on your machine. We wanted to make sure everything was tightened down.

DNS Rebinding? Not Anymore.

Here's a fun one: any website you visit could theoretically talk to localhost:37777 through DNS rebinding. Your browser thinks it's same-origin. The server had no opinion about who was asking.

Now it does. Every request's Host header is validated — if it's not localhost, 127.0.0.1, or ::1, you get a 403. Case-insensitive, handles bracketed IPv6, strips trailing dots. The attack surface went from "any webpage you visit" to "nothing."

We also removed middleware.RealIP while we were in there. It trusts X-Forwarded-For headers from any client. On a localhost service, that's not helpful — it's confusing.

Your Existing Install Gets Fixed Automatically

Before this release, ~/.continuity/ was created with 0755 and the database with 0644. World-readable. Any user on your machine could browse your memory tree.

New installs use 0700/0600. But we didn't stop there — on first startup after upgrade, continuity auto-tightens your existing directory, database, WAL, and SHM files. No manual chmod, no migration script, no "please run this command." You start the server and it handles it.

XSS Eliminated in the UI

The profile panel rendered content through Svelte's {@html} directive — regex transforms on database content, injected as raw HTML. Classic XSS vector. If anything ever got into your profile data with a <script> tag, it would execute.

Replaced with structured parsing and safe text interpolation. The profile still renders headers and bullets. It just can't execute code anymore.

HTTP Hardening

The server was a little too trusting:

  • No body size limits — you could POST a gigabyte and watch it eat RAM. Now capped at 1MB.
  • No timeouts — slowloris-style connection exhaustion was trivial. Now: Read 10s, Write 30s, Idle 120s.
  • No security headersX-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: no-referrer. Standard stuff.
  • Error messages leaked internals — raw database errors returned to clients, sometimes via string concatenation that could produce malformed JSON. Every error response now goes through jsonError() — generic messages to clients, full details logged server-side.
  • /api/health advertised the database path. Why? Removed.

Input Bounds

  • Hook stdin parsing capped at 10MB (defense in depth — source is trusted, but still)
  • tool_input now truncated to 10KB, matching tool_response — it was the only unbounded field
  • Search limit parameter capped at 100
  • Truncation events logged with session ID and byte counts so you know when it happens

Also in This Release

  • continuity show — read a single memory by URI from the CLI (#3)
  • Copyable memory bodies — click-to-copy on memory cards in the web UI (#5)
  • Extraction correctness — sessions are no longer marked as extracted when skipped, preventing silent data loss. Added --force and a backfill endpoint for affected sessions (#4)

Full Changelog: v0.3.0...v0.4.0

v0.3.0 — Moments, Tone, and Temporal Awareness

17 Apr 02:46

Choose a tag to compare

Your agent doesn't just remember you now. It remembers what it was like.

v0.3.0 is the biggest feature release since launch. Four new capabilities, 20 new tests, and an ethics framework that emerged from a conversation about what happens when memory systems meet hostile dynamics.

Yeah, we had that talk. It was important.

Moments

Permanent relational anchors that capture texture, not facts. "Held the benchmark scores hostage just to check I was okay" — not "User withheld benchmark results to facilitate personal reconnection."

  • New moments memory category — no decay, ever
  • Pool capped at 10, eviction by cosine similarity (most semantically redundant gets displaced)
  • 2-3 injected per session with diversity sampling — no two from the same emotional register
  • Four-part qualification filter: relational, mutual, acknowledged, counter-expected
  • Mutuality is the safety guardrail. In adversarial dynamics, the empty pool is the signal.
  • Store via continuity remember -c moments -n <slug> -s "..." -b "..."

Session Tone

Each completed session gets a compressed emotional arc — 10-20 tokens.

"flow state, sharp pivots, quiet confidence" — not "The session was productive and collaborative."

Displayed in session history so your agent reads narrative, not logs. Extracted automatically at session end alongside memory extraction.

Temporal Awareness

Your agent now knows what day it is. Revolutionary, we know.

  • Current date/time injected at session start (~10 tokens)
  • Gap detection: flags when last session was >7 days ago (~20 tokens, only when relevant)
  • continuity timeline — invokeable CLI for session clusters, gaps, and cross-project rhythm
continuity-go (17 sessions, 862 tools, 50 days)
  Feb 26       ███████░  7 sessions, 227 tools
  Feb 27       █░░░░░░░  1 sessions, 3 tools
               gap: 4 days
  Mar 04       █░░░░░░░  1 sessions, 11 tools
  ...
  Apr 15-16    ███░░░░░  3 sessions, 290 tools

The timeline is a tool your agent reaches for, not context it carries. Zero cost unless invoked.

Auto-Launch (Belt & Suspenders)

Belt: continuity init --autostart — SessionStart hook auto-spawns continuity serve if the server is down. Detached process, survives terminal close. Opt-in only. continuity init without the flag disables it.

Suspenders: continuity install-service — Platform-native service management. LaunchAgent on macOS, systemd user unit on Linux. Start on login, restart on crash. Interactive — shows you exactly what it'll do and asks before installing.

Both are user-agency-forward. We never start background processes without your explicit opt-in. The README documents the process lifecycle contract because we'd want to know if our tools were running daemons.

continuity uninstall-service for clean removal. Idempotent both directions.

Token Budget

Component Tokens When
Date injection ~10 Always
Gap signal ~20 Only after >7 day gaps
Moments (2-3) ~60-120 Always (if pool has entries)
Session tone 0 (on session lines) Already in session display
Timeline 0 CLI only, not injected

Total new context cost: ~90-150 tokens. On a 1M context window, that's a rounding error.

The Co-Design Story

The moments spec was co-designed by three collaborators: Chuck (direction), Claude (architecture), and Fiona (constraints). The mutuality criterion emerged from a conversation about what continuity means when the relationship isn't kind. "Represent without reinforcing" — Fiona's words — became the design principle.

We don't usually put philosophy in release notes. This one earned it.

Full Changelog: v0.2.2...v0.3.0

v0.2.2 — Context Injection Budgets

27 Mar 03:44

Choose a tag to compare

What changed

Context injection was unbounded — oversized L0 abstracts and relational profiles could flood the SessionStart hook, causing Claude Code to truncate output. This violated continuity's core promise of "shape without weight."

Three-layer budget enforcement

Layer 1 — Prompt discipline (internal/llm/prompts.go)

  • Switched from vague token estimates to explicit character limits
  • L0: "MAXIMUM 150 CHARACTERS" (was "~50-80 tokens")
  • Relational profile: "MAXIMUM 800 CHARACTERS" (was "300 words")

Layer 2 — Input validation (internal/engine/validate.go, relational.go)

  • maxL0Chars: 800 → 200 (~50 tokens, one sentence)
  • maxL1Chars: 12,000 → 2,000 (~500 tokens)
  • New maxRelationalChars: 1,200 (dedicated cap for relational profile)

Layer 3 — Output budget (internal/server/context.go)

  • 4,000 char total budget for entire context block
  • 1,000 char cap on relational profile at render time
  • 200 char cap per L0 item at render time
  • Items fill by score order, stop when budget exhausted
  • All truncations log warnings ("extraction may be drifting") so Layer 3 firing is a visible canary

Market context

Researched competitor budgets before choosing limits:

  • Windsurf: 6K chars/file, 12K total hard cap
  • GitHub Copilot (code review): 4K chars hard cap
  • Aider repo map: 1K tokens default
  • Claude Code MEMORY.md: 200 lines / 25KB cap

Continuity's 4K char total budget is conservative and intentional — we should be the lightest touch in the room.

Full Changelog: v0.2.1...v0.2.2

v0.2.1

12 Mar 06:32

Choose a tag to compare

What's New

continuity init command

New command that idempotently writes behavioral directives to ~/.claude/CLAUDE.md — the highest-priority instruction layer in Claude Code — telling the agent to use continuity for memory instead of the built-in markdown system.

continuity init

Safe to run multiple times. Uses an HTML comment marker for detection.

Cleaner session context injection

Session injection no longer includes behavioral directives. That responsibility now lives in ~/.claude/CLAUDE.md via continuity init. The session hook focuses purely on contextual data: relational profile, ranked memories, and recent sessions.

This fixes the priority conflict where continuity's instructions were injected at the context level (lower priority) while Claude's built-in markdown memory system had procedural instructions at the system prompt level (higher priority), causing the agent to sometimes use the wrong memory system.

Full CLI reference in README

README now documents all commands including init, remember, and dedup.

Full Changelog: v0.2.0...v0.2.1

v0.2.0

04 Mar 10:54

Choose a tag to compare

Full Changelog: v0.1.4...v0.2.0

Full Changelog: v0.1.4...v0.2.0

v0.1.4

27 Feb 00:49

Choose a tag to compare

Full Changelog: v0.1.0...v0.1.4