Spaces:
Configuration error
Configuration error
Added new docs
Browse files- .kiro/skills/to-tickets/SKILL.md +87 -0
- .scratch/arch-improvements/issues/01-appcontext-expand.md +16 -0
- .scratch/arch-improvements/issues/02-privacy-module.md +17 -0
- .scratch/arch-improvements/issues/03-auth-middleware.md +16 -0
- .scratch/arch-improvements/issues/04-mcp-tool-registry.md +17 -0
- .scratch/arch-improvements/issues/05-split-observations-memories.md +23 -0
- .scratch/arch-improvements/issues/06-split-indexing-retrieval.md +24 -0
- .scratch/arch-improvements/issues/07-split-consolidation-slots-lessons.md +25 -0
- .scratch/arch-improvements/issues/08-delete-functions-shim.md +23 -0
- .scratch/arch-improvements/issues/09-typed-observations-table-expand.md +19 -0
- .scratch/arch-improvements/issues/10-typed-observations-reads-contract.md +18 -0
- .scratch/arch-improvements/issues/11-basevectorindex-protocol.md +17 -0
- SPIKE.md +578 -0
- UBIQUITOUS_LANGUAGE.md +166 -0
- Users/ykanagaraj/Downloads/agentcache/CLAUDE.md +53 -0
- product_roadmap.md +39 -0
- questions.md +176 -0
.kiro/skills/to-tickets/SKILL.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: to-tickets
|
| 3 |
+
description: Break a plan, spec, or the current conversation into a set of tracer-bullet tickets, each declaring its blocking edges, published to the configured tracker β edges as text in one file per ticket locally, or native blocking links on a real tracker.
|
| 4 |
+
disable-model-invocation: true
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
Break a plan, spec, or conversation into a set of tickets β tracer-bullet vertical slices, each declaring the tickets that block it.
|
| 8 |
+
The issue tracker and triage label vocabulary should have been provided to you β run /setup-matt-pocock-skills if not.
|
| 9 |
+
|
| 10 |
+
## Process
|
| 11 |
+
|
| 12 |
+
### 1. Gather context
|
| 13 |
+
Work from whatever is already in the conversation context. If the user passes a reference (a spec path, an issue number or URL) as an argument, fetch it and read its full body and comments.
|
| 14 |
+
|
| 15 |
+
### 2. Explore the codebase (optional)
|
| 16 |
+
If you have not already explored the codebase, do so to understand the current state of the code. Ticket titles and descriptions should use the project's domain glossary vocabulary, and respect ADRs in the area you're touching.
|
| 17 |
+
|
| 18 |
+
Look for opportunities to prefactor the code to make the implementation easier. "Make the change easy, then make the easy change."
|
| 19 |
+
|
| 20 |
+
### 3. Draft vertical slices
|
| 21 |
+
Break the work into tracer bullet tickets.
|
| 22 |
+
|
| 23 |
+
- Each slice cuts a narrow but COMPLETE path through every layer (schema, API, UI, tests) β vertical, NOT a horizontal slice of one layer
|
| 24 |
+
- A completed slice is demoable or verifiable on its own
|
| 25 |
+
- Each slice is sized to fit in a single fresh context window
|
| 26 |
+
- Any prefactoring should be done first
|
| 27 |
+
|
| 28 |
+
Give each ticket its blocking edges β the other tickets that must complete before it can start. A ticket with no blockers can start immediately.
|
| 29 |
+
|
| 30 |
+
Wide refactors are the exception to vertical slicing. A wide refactor is one mechanical change β rename a column, retype a shared symbol β whose blast radius fans across the whole codebase, so a single edit breaks thousands of call sites at once and no vertical slice can land green. Don't force it into a tracer bullet; sequence it as expandβcontract. First expand: add the new form beside the old so nothing breaks. Then migrate the call sites over in batches sized by blast radius (per package, per directory), each batch its own ticket blocked by the expand, keeping CI green batch to batch because the old form still exists. Finally contract: delete the old form once no caller remains, in a ticket blocked by every migrate batch. When even the batches can't stay green alone, keep the sequence but let them share an integration branch that all block a final integrate-and-verify ticket β green is promised only there.
|
| 31 |
+
|
| 32 |
+
### 4. Quiz the user
|
| 33 |
+
Present the proposed breakdown as a numbered list. For each ticket, show:
|
| 34 |
+
|
| 35 |
+
- **Title:** short descriptive name
|
| 36 |
+
- **Blocked by:** which other tickets (if any) must complete first
|
| 37 |
+
- **What it delivers:** the end-to-end behaviour this ticket makes work
|
| 38 |
+
|
| 39 |
+
Ask the user:
|
| 40 |
+
|
| 41 |
+
- Does the granularity feel right? (too coarse / too fine)
|
| 42 |
+
- Are the blocking edges correct β does each ticket only depend on tickets that genuinely gate it?
|
| 43 |
+
- Should any tickets be merged or split further?
|
| 44 |
+
|
| 45 |
+
Iterate until the user approves the breakdown.
|
| 46 |
+
|
| 47 |
+
### 5. Publish the tickets to the configured tracker
|
| 48 |
+
Publish the approved tickets. How depends on the tracker /setup-matt-pocock-skills configured β the tickets are the same either way, only the shape of the blocking edges changes:
|
| 49 |
+
|
| 50 |
+
- **Local files** β write one file per ticket under `.scratch/<feature-slug>/issues/<NN>-<slug>.md`, numbered from 01 in dependency order (blockers first). Each file's "Blocked by" lists the numbers/titles it depends on. Use the per-ticket file template below β one ticket per file, never a single combined file.
|
| 51 |
+
- **A real issue tracker (GitHub, Linear, β¦)** β publish one issue per ticket in dependency order (blockers first) so each ticket's blocking edges can reference real identifiers. Use the platform's native blocking / sub-issue relationship where it has one; otherwise set each ticket's "Blocked by" to the blocking issues. Apply the ready-for-agent triage label unless instructed otherwise β the tickets are agent-grabbable by construction.
|
| 52 |
+
|
| 53 |
+
Work the frontier: any ticket whose blockers are all done. For a purely linear chain that means top to bottom.
|
| 54 |
+
|
| 55 |
+
Do NOT close or modify any parent issue.
|
| 56 |
+
|
| 57 |
+
---
|
| 58 |
+
|
| 59 |
+
## Per-ticket file template (local files)
|
| 60 |
+
|
| 61 |
+
**What to build:** the end-to-end behaviour this ticket makes work, from the user's perspective β not a layer-by-layer implementation list.
|
| 62 |
+
**Blocked by:** the numbers/titles of the tickets that gate this one, or "None β can start immediately".
|
| 63 |
+
**Status:** ready-for-agent
|
| 64 |
+
|
| 65 |
+
- [ ] Acceptance criterion 1
|
| 66 |
+
- [ ] Acceptance criterion 2
|
| 67 |
+
|
| 68 |
+
---
|
| 69 |
+
|
| 70 |
+
## Per-ticket issue template (real tracker)
|
| 71 |
+
|
| 72 |
+
**Parent**
|
| 73 |
+
A reference to the parent issue on the tracker (if the source was an existing issue, otherwise omit this section).
|
| 74 |
+
|
| 75 |
+
**What to build**
|
| 76 |
+
The end-to-end behaviour this ticket makes work, from the user's perspective β not layer-by-layer implementation.
|
| 77 |
+
|
| 78 |
+
**Acceptance criteria**
|
| 79 |
+
- [ ] Criterion 1
|
| 80 |
+
- [ ] Criterion 2
|
| 81 |
+
|
| 82 |
+
**Blocked by**
|
| 83 |
+
A reference to each blocking ticket, or "None β can start immediately".
|
| 84 |
+
|
| 85 |
+
---
|
| 86 |
+
|
| 87 |
+
In either form, avoid specific file paths or code snippets β they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it and note briefly that it came from a prototype. Trim to the decision-rich parts β not a working demo, just the important bits.
|
.scratch/arch-improvements/issues/01-appcontext-expand.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 01 β Introduce `AppContext` dataclass (Expand)
|
| 2 |
+
|
| 3 |
+
**Blocked by:** None β can start immediately
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Introduce a single `AppContext` dataclass that packages all runtime state currently scattered as module-level globals in `functions.py` (`_bm25_index`, `_vector_index`, `_embedding_provider`, `_hybrid_search`, `_stream_broadcaster`, and the `kv` reference). Construct one `AppContext` instance inside `create_app()` and thread it through to every function that needs it. The existing globals stay in place as a compatibility shim β no callers are changed in this ticket. Zero behaviour change; this is the expand step that makes the god-module split (tickets 05β08) possible.
|
| 9 |
+
|
| 10 |
+
## Acceptance criteria
|
| 11 |
+
|
| 12 |
+
- [ ] `AppContext` dataclass exists in a new `context.py` module (or equivalent) with typed fields for `kv`, `bm25`, `vector`, `embedder`, and `broadcast`
|
| 13 |
+
- [ ] `create_app()` constructs one `AppContext` and stores it on the Flask app (e.g. `app.ctx`)
|
| 14 |
+
- [ ] The five existing `set_*` functions in `functions.py` remain working β globals are still set alongside the new `AppContext`
|
| 15 |
+
- [ ] All existing tests pass with no changes
|
| 16 |
+
- [ ] No route handler or worker is changed in this ticket
|
.scratch/arch-improvements/issues/02-privacy-module.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 02 β Extract `privacy.py` deep module
|
| 2 |
+
|
| 3 |
+
**Blocked by:** None β can start immediately
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Extract `strip_private_data()` from `functions.py` into a dedicated `privacy.py` module. The new module exposes a single `scrub(text: str, patterns=DEFAULT_PATTERNS) -> str` interface. The regex pattern list is promoted to a public, documented constant (`DEFAULT_PATTERNS`) and is extensible via an env var (`AGENTCACHE_REDACT_PATTERNS`). `functions.py` calls `privacy.scrub()` internally β all existing callers continue working without change. First unit tests for Privacy Scrub are added in this ticket, targeting `scrub()` directly.
|
| 9 |
+
|
| 10 |
+
## Acceptance criteria
|
| 11 |
+
|
| 12 |
+
- [ ] `privacy.py` exists with a `scrub(text, patterns=DEFAULT_PATTERNS)` function
|
| 13 |
+
- [ ] `DEFAULT_PATTERNS` is a documented list of regex strings β visible and auditable
|
| 14 |
+
- [ ] An env var `AGENTCACHE_REDACT_PATTERNS` (comma-separated regex strings) appends additional patterns at startup
|
| 15 |
+
- [ ] `functions.py` no longer contains the scrubbing regex list β it imports and calls `privacy.scrub()`
|
| 16 |
+
- [ ] Unit tests cover: API key pattern, bearer token pattern, custom pattern via argument, no false positives on safe text
|
| 17 |
+
- [ ] All existing tests pass
|
.scratch/arch-improvements/issues/03-auth-middleware.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 03 β Centralise auth middleware
|
| 2 |
+
|
| 3 |
+
**Blocked by:** None β can start immediately
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Replace the copy-pasted `_check_auth()` function that currently lives independently in every route blueprint (`mcp.py`, `observations.py`, `memories.py`, `health.py`, and others) with a single `require_auth` decorator defined in a new `auth.py` module. Every route that previously called `_check_auth()` at the top of its handler switches to the `@require_auth` decorator. The auth logic (timing-safe `hmac.compare_digest` Bearer token check) is identical β this is purely a deduplication. RBAC can be added in one place in a future ticket.
|
| 9 |
+
|
| 10 |
+
## Acceptance criteria
|
| 11 |
+
|
| 12 |
+
- [ ] `auth.py` exists with a `require_auth(f)` decorator that performs the timing-safe Bearer token check
|
| 13 |
+
- [ ] Every route blueprint imports and uses `@require_auth` β no blueprint defines its own `_check_auth`
|
| 14 |
+
- [ ] A request with no secret configured passes through (existing behaviour preserved)
|
| 15 |
+
- [ ] A request with a wrong token still gets a `401` response
|
| 16 |
+
- [ ] All existing tests pass
|
.scratch/arch-improvements/issues/04-mcp-tool-registry.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 04 β MCP tool registry (replace elif chain)
|
| 2 |
+
|
| 3 |
+
**Blocked by:** 03 β centralise auth middleware
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Replace the 300-line `if/elif` dispatch chain in `routes/mcp.py` with a `@register("tool_name")` decorator registry. Each of the 30+ MCP tool handlers becomes a standalone function decorated with `@register`. The dispatcher becomes a two-liner: look up the tool name in the registry dict and call the handler. Auth is handled by the shared `@require_auth` decorator from ticket 03. The tool schema list (`get_mcp_tools_schemas`) stays as-is. No change to the MCP wire format or agent behaviour.
|
| 9 |
+
|
| 10 |
+
## Acceptance criteria
|
| 11 |
+
|
| 12 |
+
- [ ] A `_tools: dict[str, Callable]` registry exists and a `@register(name)` decorator populates it
|
| 13 |
+
- [ ] Every MCP tool handler is a standalone function β not an inline block inside a giant if/elif
|
| 14 |
+
- [ ] The POST `/mcp/tools` handler body is β€ 20 lines (lookup + call + error handling)
|
| 15 |
+
- [ ] Each tool handler function is independently importable and callable in a test without starting Flask
|
| 16 |
+
- [ ] Adding a new tool requires only adding a new decorated function β no editing of existing dispatch code
|
| 17 |
+
- [ ] All existing MCP tool calls produce the same responses as before
|
.scratch/arch-improvements/issues/05-split-observations-memories.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 05 β Split god module batch 1: `observations.py` + `memories.py`
|
| 2 |
+
|
| 3 |
+
**Blocked by:** 01 β AppContext dataclass (Expand)
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Move the first two major domain areas out of `functions.py` into focused modules, using the domain vocabulary from `UBIQUITOUS_LANGUAGE.md`.
|
| 9 |
+
|
| 10 |
+
**`observations.py`** receives: `folder_observe()`, `observe()` (legacy), `dedup_folder_observations()`, `build_synthetic_compression()`, `infer_type()`, `extract_files()`, `extract_image()`, image store helpers (`save_image_to_disk`, `delete_image`, `touch_image`, `is_managed_image_path`), and `normalize_folder_path()` / `validate_agent_id()`.
|
| 11 |
+
|
| 12 |
+
**`memories.py`** receives: `remember()`, `forget()`, memory versioning logic, and `jaccard_similarity()`.
|
| 13 |
+
|
| 14 |
+
`functions.py` re-exports everything from these two modules so all existing callers continue to work unchanged. CI must stay green. This is the first batch of the expand phase of the god-module split.
|
| 15 |
+
|
| 16 |
+
## Acceptance criteria
|
| 17 |
+
|
| 18 |
+
- [ ] `observations.py` and `memories.py` exist as standalone modules
|
| 19 |
+
- [ ] All moved functions accept `AppContext` (from ticket 01) instead of reaching into module-level globals, while also accepting the legacy `kv: StateKV` signature for backward compat
|
| 20 |
+
- [ ] `functions.py` re-exports every moved symbol β no call site outside `functions.py` needs to change
|
| 21 |
+
- [ ] Existing route blueprints and workers continue to import from `functions` without modification
|
| 22 |
+
- [ ] All existing tests pass
|
| 23 |
+
- [ ] No logic is changed β this is a pure relocation
|
.scratch/arch-improvements/issues/06-split-indexing-retrieval.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 06 β Split god module batch 2: `indexing.py` + `retrieval.py`
|
| 2 |
+
|
| 3 |
+
**Blocked by:** 05 β split batch 1 (observations + memories)
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Move the second major domain area out of `functions.py`.
|
| 9 |
+
|
| 10 |
+
**`indexing.py`** receives: `IndexPersistence`, `rebuild_index()`, `backfill_obs_lookup_if_needed()`, `vector_index_add_guarded()`, `clip_embed_input()`, and all index-related helpers. This module owns the Index Rebuild lifecycle (see `UBIQUITOUS_LANGUAGE.md`).
|
| 11 |
+
|
| 12 |
+
**`retrieval.py`** receives: `folder_search()`, `folder_timeline()`, `compile_context()`, and `export_data()`. This module owns the Recall, Smart Search, Folder Search, Timeline, and Context Compilation operations.
|
| 13 |
+
|
| 14 |
+
`functions.py` continues to re-export everything. CI stays green.
|
| 15 |
+
|
| 16 |
+
## Acceptance criteria
|
| 17 |
+
|
| 18 |
+
- [ ] `indexing.py` and `retrieval.py` exist as standalone modules
|
| 19 |
+
- [ ] All moved functions use `AppContext` from ticket 01 rather than reaching into globals directly
|
| 20 |
+
- [ ] `functions.py` re-exports every moved symbol unchanged
|
| 21 |
+
- [ ] `IndexPersistence` is importable from `indexing` in `workers.py` without changing any other worker code
|
| 22 |
+
- [ ] `folder_search()` and `compile_context()` are callable from a test by constructing an `AppContext` with an in-memory KV and a fresh `SearchIndex` β no Flask app required
|
| 23 |
+
- [ ] All existing tests pass
|
| 24 |
+
- [ ] No logic is changed
|
.scratch/arch-improvements/issues/07-split-consolidation-slots-lessons.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 07 β Split god module batch 3: `consolidation.py` + `slots.py` + `lessons.py`
|
| 2 |
+
|
| 3 |
+
**Blocked by:** 06 β split batch 2 (indexing + retrieval)
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Move the final major domain areas out of `functions.py`.
|
| 9 |
+
|
| 10 |
+
**`consolidation.py`** receives: `consolidate()`, `auto_forget()`, `folder_graph_build()`, and all LLM-call helpers used exclusively by the Consolidation pipeline. This module owns the Consolidation lifecycle (Working Memory β Episodic β Semantic β Procedural).
|
| 11 |
+
|
| 12 |
+
**`slots.py`** receives: all Slot CRUD functions (`get_slots`, `set_slot`, `delete_slot`, `reflect_slot`, `append_slot`, etc.).
|
| 13 |
+
|
| 14 |
+
**`lessons.py`** receives: all Lesson CRUD functions (`get_lessons`, `save_lesson`, `search_lessons`, `strengthen_lesson`, lesson decay logic).
|
| 15 |
+
|
| 16 |
+
`functions.py` re-exports everything. After this ticket, `functions.py` is a pure shim β no domain logic remains in it.
|
| 17 |
+
|
| 18 |
+
## Acceptance criteria
|
| 19 |
+
|
| 20 |
+
- [ ] `consolidation.py`, `slots.py`, and `lessons.py` exist as standalone modules
|
| 21 |
+
- [ ] `functions.py` contains no domain logic β only re-export statements
|
| 22 |
+
- [ ] All moved functions use `AppContext` from ticket 01
|
| 23 |
+
- [ ] `auto_forget()` is callable from a test without importing `functions` directly
|
| 24 |
+
- [ ] All existing tests pass
|
| 25 |
+
- [ ] No logic is changed
|
.scratch/arch-improvements/issues/08-delete-functions-shim.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 08 β Contract: delete `functions.py` shim, migrate all callers to direct imports
|
| 2 |
+
|
| 3 |
+
**Blocked by:** 05, 06, 07 β all three split batches must be complete
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Delete `functions.py` entirely. Update every import across `routes/`, `workers.py`, `app.py`, and the test suite to point directly to the focused module that owns the symbol. This is the contract step of the expandβcontract sequence. After this ticket the domain vocabulary from `UBIQUITOUS_LANGUAGE.md` is directly reflected in the module names β there is no god module.
|
| 9 |
+
|
| 10 |
+
Import mapping (non-exhaustive):
|
| 11 |
+
- `from .functions import folder_observe` β `from .observations import folder_observe`
|
| 12 |
+
- `from .functions import remember, forget` β `from .memories import remember, forget`
|
| 13 |
+
- `from .functions import folder_search, compile_context` β `from .retrieval import folder_search, compile_context`
|
| 14 |
+
- `from .functions import IndexPersistence, rebuild_index` β `from .indexing import IndexPersistence, rebuild_index`
|
| 15 |
+
- `from .functions import consolidate, auto_forget` β `from .consolidation import consolidate, auto_forget`
|
| 16 |
+
|
| 17 |
+
## Acceptance criteria
|
| 18 |
+
|
| 19 |
+
- [ ] `functions.py` does not exist in the repository
|
| 20 |
+
- [ ] `rg "from .functions import\|from agentcache.functions import\|import functions"` returns zero results in `src/`
|
| 21 |
+
- [ ] All existing tests pass with imports updated
|
| 22 |
+
- [ ] No test imports from `functions` β each test imports from the specific module it exercises
|
| 23 |
+
- [ ] CI is green
|
.scratch/arch-improvements/issues/09-typed-observations-table-expand.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 09 β Add typed `observations` table to SQLite (Expand)
|
| 2 |
+
|
| 3 |
+
**Blocked by:** 05 β observations module must exist to own this migration
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Add a typed `observations` table to the SQLite schema with columns for `folder`, `agent`, `timestamp`, `type`, `importance`, and `text`. When a new Observation is ingested via `folder_observe()`, write it to both the new typed table and the existing `kv_store` scope (dual-write). Reads still use `kv_store` β zero behaviour change for any query path. This is the expand step that makes SQL-level filtering possible without breaking anything.
|
| 9 |
+
|
| 10 |
+
The new table enables: `WHERE folder = ? AND agent = ? AND timestamp > ?` and `ORDER BY importance DESC LIMIT N` entirely at the SQLite layer β no Python-side filtering needed.
|
| 11 |
+
|
| 12 |
+
## Acceptance criteria
|
| 13 |
+
|
| 14 |
+
- [ ] `observations(id TEXT PK, folder TEXT, agent TEXT, timestamp TEXT, type TEXT, importance INTEGER, text TEXT)` table exists in the DB schema with indexes on `(folder, agent, timestamp)` and `(importance)`
|
| 15 |
+
- [ ] `folder_observe()` dual-writes: one row to `observations`, one entry to `kv_store` (existing path)
|
| 16 |
+
- [ ] The DB migration runs automatically on startup if the table does not exist (no manual step)
|
| 17 |
+
- [ ] All reads continue to use `kv_store` β no query is changed in this ticket
|
| 18 |
+
- [ ] All existing tests pass
|
| 19 |
+
- [ ] A DB integrity test verifies that for each `kv_store` Observation entry written, a matching row exists in `observations`
|
.scratch/arch-improvements/issues/10-typed-observations-reads-contract.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 10 β Migrate Observation reads to typed table (Contract)
|
| 2 |
+
|
| 3 |
+
**Blocked by:** 09 β typed observations table (Expand) must be complete
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Switch `folder_search`, `folder_timeline`, `compile_context`, and `auto_forget` to read Observations from the typed `observations` table using SQL-level filtering, replacing the current pattern of `kv.list(scope)` + Python-side filtering. Stop dual-writing new Observations to `kv_store` β the typed table is now the single source of truth. A one-time migration backfills any existing `kv_store` Observation entries into the typed table for users upgrading from a previous version.
|
| 9 |
+
|
| 10 |
+
## Acceptance criteria
|
| 11 |
+
|
| 12 |
+
- [ ] `folder_timeline()` issues a single `SELECT β¦ WHERE folder=? AND agent=? ORDER BY timestamp DESC LIMIT ?` β no Python-side filtering loop
|
| 13 |
+
- [ ] `folder_search()` hydrates candidates from the typed table rather than `kv.list(scope)` for the Observation load step
|
| 14 |
+
- [ ] `compile_context()` fetches recent Observations using `ORDER BY importance DESC, timestamp DESC LIMIT ?` at the SQL layer
|
| 15 |
+
- [ ] New `folder_observe()` calls write only to the typed table (no `kv_store` Observation scope write)
|
| 16 |
+
- [ ] A backfill function runs once on startup to migrate existing `kv_store` Observations into the typed table
|
| 17 |
+
- [ ] A benchmark test (or manual note in the PR) shows query latency improvement at β₯ 10k Observations
|
| 18 |
+
- [ ] All existing tests pass
|
.scratch/arch-improvements/issues/11-basevectorindex-protocol.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 11 β `BaseVectorIndex` protocol + `InMemoryVectorIndex` adapter
|
| 2 |
+
|
| 3 |
+
**Blocked by:** 06 β indexing module must exist before its interface is formalised
|
| 4 |
+
**Status:** ready-for-agent
|
| 5 |
+
|
| 6 |
+
## What to build
|
| 7 |
+
|
| 8 |
+
Define a `BaseVectorIndex` Protocol (or ABC) in `indexing.py` with three methods: `add(obs_id, session_id, embedding)`, `remove(obs_id)`, and `search(query_embedding, limit) -> list`. Rename the current flat-list implementation to `InMemoryVectorIndex` and have it implement `BaseVectorIndex`. Update `AppContext.vector` to be typed as `BaseVectorIndex`. Behaviour is completely unchanged β this ticket only cuts the adapter seam so future backends (HNSW, Qdrant, Chroma) can be swapped in without touching `retrieval.py` or `consolidation.py`.
|
| 9 |
+
|
| 10 |
+
## Acceptance criteria
|
| 11 |
+
|
| 12 |
+
- [ ] `BaseVectorIndex` Protocol exists in `indexing.py` with `add`, `remove`, and `search` as the only required methods
|
| 13 |
+
- [ ] `InMemoryVectorIndex` implements `BaseVectorIndex` and passes a `isinstance(idx, BaseVectorIndex)` check
|
| 14 |
+
- [ ] `AppContext.vector` is typed as `BaseVectorIndex | None`
|
| 15 |
+
- [ ] A test constructs a minimal stub that implements `BaseVectorIndex` and passes it to `folder_search()` via `AppContext` β confirming the seam is real and injectable
|
| 16 |
+
- [ ] `VectorIndex` (old name) remains as an alias for `InMemoryVectorIndex` for one release to avoid breaking any external imports
|
| 17 |
+
- [ ] All existing tests pass with no behaviour change
|
SPIKE.md
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Feature Spike: Small Improvements for Agent Cache User Experience
|
| 2 |
+
|
| 3 |
+
**Date:** 2026-07-16
|
| 4 |
+
**Priority:** Low risk, high value additions to current architecture
|
| 5 |
+
**Tech Stack Impact:** None β all fit within Flask/SQLite model
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## Table of Contents
|
| 10 |
+
1. [Current State Analysis](#current-state-analysis)
|
| 11 |
+
2. [Feature Opportunities](#feature-opportunities)
|
| 12 |
+
3. [Recommendation Priority](#recommendation-priority)
|
| 13 |
+
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
## Current State Analysis
|
| 17 |
+
|
| 18 |
+
### What agentcache-python Already Has
|
| 19 |
+
|
| 20 |
+
**Core Features Implemented:**
|
| 21 |
+
- REST API (Flask framework on port 3111)
|
| 22 |
+
- WebSocket streaming (`/stream/mem-live/viewer`)
|
| 23 |
+
- Hybrid search: BM25 keyword + vector embeddings (Gemini/OpenAI/SentenceTransformer)
|
| 24 |
+
- Folder-based memory model: `(folder_path, agent_id)` scoping
|
| 25 |
+
- Knowledge graph construction via `folder_graph_build()`
|
| 26 |
+
- MCP tools for agent integration (13+ tools)
|
| 27 |
+
- Security redactions in observations (`strip_private_data()`)
|
| 28 |
+
- Token budget limits during context compilation
|
| 29 |
+
- Background workers for index maintenance
|
| 30 |
+
|
| 31 |
+
**Key Functions:**
|
| 32 |
+
| Function | Purpose | File |
|
| 33 |
+
|----------|---------|------|
|
| 34 |
+
| `folder_observe()` | Ingest observations with scoping | `functions.py` |
|
| 35 |
+
| `folder_search()` | BM25 + vector hybrid search | `search.py` |
|
| 36 |
+
| `folder_timeline()` | Chronological activity feed | `functions.py` |
|
| 37 |
+
| `folder_graph_build()` | Build knowledge graph nodes/edges | `functions.py` |
|
| 38 |
+
| `remember()/forget()` | Global memory management | `functions.py` |
|
| 39 |
+
| `context()` | Compile contextual blocks with token limits | `functions.py` |
|
| 40 |
+
| `export_data()` | v2 JSON export format | `functions.py` |
|
| 41 |
+
|
| 42 |
+
**Key Tests:**
|
| 43 |
+
- `test_observe_core.py` β Observation validation and redaction
|
| 44 |
+
- `test_folder_observe.py` β Folder-scoped ingestion
|
| 45 |
+
- `test_search.py` β BM25/vector search logic
|
| 46 |
+
- `test_context.py` β Context compilation with token budgets
|
| 47 |
+
- `test_graph.py` β Knowledge graph construction
|
| 48 |
+
|
| 49 |
+
---
|
| 50 |
+
|
| 51 |
+
## Feature Opportunities
|
| 52 |
+
|
| 53 |
+
### π― Feature 1: Observation Prioritization / Weighting
|
| 54 |
+
|
| 55 |
+
**Why Add This?**
|
| 56 |
+
Users naturally write observations with different levels of importance. Currently all observations are weighted equally in search results. The search uses RRF (Reciprocal Rank Fusion) which already applies weights, but there's no user-controlled way to emphasize important observations.
|
| 57 |
+
|
| 58 |
+
**What to Implement:**
|
| 59 |
+
```python
|
| 60 |
+
def folder_observe(kv, payload, priority=0): # 0-100 scale
|
| 61 |
+
|
| 62 |
+
# Store in metadata:
|
| 63 |
+
obs["priority"] = priority
|
| 64 |
+
|
| 65 |
+
return {
|
| 66 |
+
"observationId": fobs_{hash}_{rand},
|
| 67 |
+
"priority": priority, # Added field
|
| 68 |
+
...
|
| 69 |
+
}
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
**Changes Required:**
|
| 73 |
+
- Add `priority` field to observation payload validation (new required/optional check)
|
| 74 |
+
- Modify search weighting in `HybridSearch._rank()` to include observation priority:
|
| 75 |
+
```python
|
| 76 |
+
# Current RRF: rank_weight = 1 / len(relevant_docs)
|
| 77 |
+
# New RRF: combined_weight = (bm25_score + vector_score) * obs_priority / 100
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
**User Experience:**
|
| 81 |
+
When a user writes an observation, they can optionally mark it as high-priority:
|
| 82 |
+
```python
|
| 83 |
+
# Tag this as critical issue - show prominently in search results
|
| 84 |
+
folder_observe(
|
| 85 |
+
kv, {
|
| 86 |
+
"folderPath": "/home/user/project/src/auth",
|
| 87 |
+
"agentId": "my-ai-agent",
|
| 88 |
+
"text": "JWT rotation breaks production auth endpoints",
|
| 89 |
+
"timestamp": now(),
|
| 90 |
+
"priority": 90 # Highest importance
|
| 91 |
+
}
|
| 92 |
+
)
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
Search results for "jwt auth break" automatically rank this observation higher than lower-priority memories.
|
| 96 |
+
|
| 97 |
+
**Implementation Complexity:** **15 minutes**
|
| 98 |
+
- Add one field to observation validation schema
|
| 99 |
+
- Modify RRF weighting formula (single math operation change)
|
| 100 |
+
- Update tests to verify priority affects ranking
|
| 101 |
+
|
| 102 |
+
---
|
| 103 |
+
|
| 104 |
+
### π― Feature 2: Observation Source Tracking
|
| 105 |
+
|
| 106 |
+
**Why Add This?**
|
| 107 |
+
Users write observations from different contexts (code files, documentation, terminal logs). Knowing which file context an observation came from helps detect when user re-wrote same code after learning a lesson, and aids in identifying relevant follow-up actions.
|
| 108 |
+
|
| 109 |
+
**What to Implement:**
|
| 110 |
+
```python
|
| 111 |
+
def folder_observe(kv, payload, source_file=None): # e.g., "src/auth.py"
|
| 112 |
+
|
| 113 |
+
obs["sourceFile"] = source_file or None
|
| 114 |
+
|
| 115 |
+
return {
|
| 116 |
+
"observationId": fobs_{hash}_{rand},
|
| 117 |
+
"sourceFile": source_file, # New field
|
| 118 |
+
"text": "...",
|
| 119 |
+
...
|
| 120 |
+
}
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
**Changes Required:**
|
| 124 |
+
- Add `source_file` to observation payload validation (optional string)
|
| 125 |
+
- Store as additional JSON field in KV value column
|
| 126 |
+
- Update timeline query to optionally show source files
|
| 127 |
+
- Update search results to surface source file info
|
| 128 |
+
|
| 129 |
+
**User Experience:**
|
| 130 |
+
Search: "What did I learn about JWT?"
|
| 131 |
+
Results:
|
| 132 |
+
```json
|
| 133 |
+
{
|
| 134 |
+
"id": "fobs_abc123",
|
| 135 |
+
"text": "JWT needs refresh tokens for rotation",
|
| 136 |
+
"sourceFile": "src/auth.py",
|
| 137 |
+
"timestamp": "2026-07-15T10:30:00Z"
|
| 138 |
+
}
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
The source file field tracks which files were being developed when lessons were learned. Helps detect when user re-implements same pattern after reading a tutorial, or helps identify which modules need updates based on recent observations.
|
| 142 |
+
|
| 143 |
+
**Implementation Complexity:** **15 minutes**
|
| 144 |
+
- Add one optional field to observation schema
|
| 145 |
+
- Store in existing KV structure (no new table/column needed)
|
| 146 |
+
- Update `folder_timeline()` to surface source file info when requested
|
| 147 |
+
|
| 148 |
+
---
|
| 149 |
+
|
| 150 |
+
### π― Feature 3: Folder-Level Statistics Endpoint
|
| 151 |
+
|
| 152 |
+
**Why Add This?**
|
| 153 |
+
Users need quick stats on individual folders without going to the dashboard UI. The current `/health` endpoint only returns global aggregate across all folders. Users often want: "How many observations do I have in my frontend folder?" answered via simple API call.
|
| 154 |
+
|
| 155 |
+
**What to Implement:**
|
| 156 |
+
```python
|
| 157 |
+
# src/agentcache/routes/folder_stats.py or append to existing routes
|
| 158 |
+
|
| 159 |
+
@app.route('/folder/stats/<path:folder_path>')
|
| 160 |
+
def folder_stats(folder_path):
|
| 161 |
+
kv = StateKV() # Get current DB instance
|
| 162 |
+
|
| 163 |
+
# Count observations for this folder
|
| 164 |
+
obs_count = 0
|
| 165 |
+
try:
|
| 166 |
+
from agentcache.functions import KV
|
| 167 |
+
scope = KV.folder_obs(folder_path, os.getenv("AGENT_ID", "default"))
|
| 168 |
+
rows = kv.db.execute(
|
| 169 |
+
"SELECT COUNT(*) FROM kv_store WHERE scope = ?",
|
| 170 |
+
[scope]
|
| 171 |
+
).fetchone()
|
| 172 |
+
obs_count = rows[0] if rows else 0
|
| 173 |
+
except:
|
| 174 |
+
pass
|
| 175 |
+
|
| 176 |
+
# Get last observation timestamp
|
| 177 |
+
last_obs_ts = None
|
| 178 |
+
try:
|
| 179 |
+
rows = kv.db.execute(
|
| 180 |
+
"SELECT MAX(ts) FROM kv_store WHERE scope = ?",
|
| 181 |
+
[scope]
|
| 182 |
+
).fetchone()
|
| 183 |
+
last_obs_ts = rows[0] if rows else None
|
| 184 |
+
except:
|
| 185 |
+
pass
|
| 186 |
+
|
| 187 |
+
return {
|
| 188 |
+
"folderPath": folder_path,
|
| 189 |
+
"observationCount": obs_count,
|
| 190 |
+
"memoryCount": 123, # Get from memories scope separately
|
| 191 |
+
"lastObsTimestamp": last_obs_ts.isoformat() if last_obs_ts else None,
|
| 192 |
+
"foldersKnownToThisAgent": ["home/user/projectX", ...] # Also include this context
|
| 193 |
+
}
|
| 194 |
+
```
|
| 195 |
+
|
| 196 |
+
**User Experience:**
|
| 197 |
+
REST API call returns folder stats instantly:
|
| 198 |
+
```bash
|
| 199 |
+
curl http://localhost:3111/folder/stats/home/user/my-project
|
| 200 |
+
# Returns:
|
| 201 |
+
{
|
| 202 |
+
"folderPath": "home/user/my-project",
|
| 203 |
+
"observationCount": 42,
|
| 204 |
+
"memoryCount": 5,
|
| 205 |
+
"lastObsTimestamp": "2026-07-15T14:23:00Z",
|
| 206 |
+
"foldersKnownToThisAgent": ["home/user/my-project"]
|
| 207 |
+
}
|
| 208 |
+
```
|
| 209 |
+
|
| 210 |
+
**Implementation Complexity:** **10 minutes**
|
| 211 |
+
- Create new endpoint following existing Flask pattern (like `/health`)
|
| 212 |
+
- Query existing KV methods for observation and memory counts
|
| 213 |
+
- Return simple JSON response
|
| 214 |
+
|
| 215 |
+
**Note:** This requires importing `StateKV` instance in new route file, which means either creating a module-scoped singleton per folder or passing the current `kv` from `app.py` via Flask's application context.
|
| 216 |
+
|
| 217 |
+
---
|
| 218 |
+
|
| 219 |
+
### π― Feature 4: Observation Expiration / TTL (Time-To-Live)
|
| 220 |
+
|
| 221 |
+
**Why Add This?**
|
| 222 |
+
Temporal observations become outdated over time. Example: "Error on port 3000" observation becomes irrelevant after user changes their app to use port 8080. Currently all observations persist indefinitely. With TTL, expired observations can auto-remove from search results or be marked as "expired".
|
| 223 |
+
|
| 224 |
+
**What to Implement:**
|
| 225 |
+
```python
|
| 226 |
+
def folder_observe(kv, payload, ttl_seconds=None):
|
| 227 |
+
|
| 228 |
+
# Store observation with expiration timestamp
|
| 229 |
+
obs["expiryTs"] = ts + ttl_seconds if ttl_seconds else None
|
| 230 |
+
|
| 231 |
+
return {
|
| 232 |
+
"observationId": fobs_{hash}_{rand},
|
| 233 |
+
"ttl": ttl_seconds or null, # New field
|
| 234 |
+
...
|
| 235 |
+
}
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
**Changes Required:**
|
| 239 |
+
- Add `ttl_seconds` to observation payload validation (optional)
|
| 240 |
+
- Store as additional timestamp field in KV value column
|
| 241 |
+
- Modify `folder_timeline()` to filter out expired observations by default
|
| 242 |
+
- Optional: Add background worker that sweeps/expunges expired observations
|
| 243 |
+
|
| 244 |
+
**User Experience:**
|
| 245 |
+
```python
|
| 246 |
+
# Observation expires after 24 hours
|
| 247 |
+
folder_observe(
|
| 248 |
+
kv, {
|
| 249 |
+
"folderPath": "/home/user/app/src/server",
|
| 250 |
+
"agentId": "my-agent",
|
| 251 |
+
"text": "Default port is 3000 for development",
|
| 252 |
+
"timestamp": now(),
|
| 253 |
+
"ttl": 86400 # 24 hours
|
| 254 |
+
}
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
# Timeline query automatically excludes expired observations
|
| 258 |
+
timeline(kv, folder_path="/home/user/app/src/server")
|
| 259 |
+
```
|
| 260 |
+
|
| 261 |
+
Returns timeline without "port 3000" observation after 24 hours. Users can also check for specific expiry status on an observation with additional endpoint `/folder/observation/status/<id>`.
|
| 262 |
+
|
| 263 |
+
**Implementation Complexity:** **25 minutes**
|
| 264 |
+
- Add TTL field to observation payload validation
|
| 265 |
+
- Store as extra JSON field in KV (existing table schema)
|
| 266 |
+
- Modify `folder_timeline()` query to include optional TTL filter:
|
| 267 |
+
```python
|
| 268 |
+
ts > now() - ttl_seconds if ttl_seconds else None AND expiryTs > ts
|
| 269 |
+
```
|
| 270 |
+
- Update search results to exclude expired observations
|
| 271 |
+
- Optional: Background worker sweeps for cleanup (simpler version doesn't need this)
|
| 272 |
+
|
| 273 |
+
**Risk:** Medium β requires changes to timeline query logic which users expect returns chronological activity. Must ensure backward compatibility with existing payloads that don't include TTL field (use `None` default).
|
| 274 |
+
|
| 275 |
+
---
|
| 276 |
+
|
| 277 |
+
### π― Feature 5: Memory Version History
|
| 278 |
+
|
| 279 |
+
**Why Add This?**
|
| 280 |
+
Long-term memories evolve. Currently `remember()` just overwrites the old memory. Users might accidentally overwrite important information or can't undo an incorrect memory. With versioning, each remember creates a new version tracked sequentially.
|
| 281 |
+
|
| 282 |
+
**What to Implement:**
|
| 283 |
+
```python
|
| 284 |
+
def remember(kv, data, skip_versioning=False):
|
| 285 |
+
|
| 286 |
+
# Check existing memories for this content
|
| 287 |
+
existing_memories = kv.get(KV.memories, "mem_abc123")
|
| 288 |
+
|
| 289 |
+
if not skip_versioning and existing_memories:
|
| 290 |
+
# Append version suffix to old content (preserve as text)
|
| 291 |
+
current_content = json.loads(existing_memories["value"])
|
| 292 |
+
current_content["_version"] = len(current_content.get("_versions", [])) + 1
|
| 293 |
+
current_content["_createdTs"] = existing_memories["ts"]
|
| 294 |
+
|
| 295 |
+
# Store in new version field
|
| 296 |
+
versions = current_content.get("_versions", [])
|
| 297 |
+
current_content["_versions"].append({
|
| 298 |
+
"content": current_content["content"],
|
| 299 |
+
"version": len(versions),
|
| 300 |
+
"ts": now(),
|
| 301 |
+
...
|
| 302 |
+
})
|
| 303 |
+
|
| 304 |
+
return {
|
| 305 |
+
"memoryId": new_id,
|
| 306 |
+
"versions": [...], # New field: list of version history
|
| 307 |
+
...
|
| 308 |
+
}
|
| 309 |
+
```
|
| 310 |
+
|
| 311 |
+
**Changes Required:**
|
| 312 |
+
- Modify `remember()` to check for existing content match in KV memories
|
| 313 |
+
- Append new version as JSON object to version history array
|
| 314 |
+
- Store version number and timestamp for each version change
|
| 315 |
+
- Optionally expose version history endpoint for viewing past versions
|
| 316 |
+
|
| 317 |
+
**User Experience:**
|
| 318 |
+
When a user calls:
|
| 319 |
+
```python
|
| 320 |
+
remember(
|
| 321 |
+
kv, {
|
| 322 |
+
"content": "JWT authentication uses bearer token in Authorization header",
|
| 323 |
+
"concepts": ["jwt", "auth"],
|
| 324 |
+
...
|
| 325 |
+
}
|
| 326 |
+
)
|
| 327 |
+
```
|
| 328 |
+
|
| 329 |
+
The response includes version history showing how this memory has evolved over time. Later, if the user discovers more context:
|
| 330 |
+
```python
|
| 331 |
+
remember(
|
| 332 |
+
kv, {
|
| 333 |
+
"content": "JWT authentication uses Bearer token and JWT_SECRET key in Authorization header",
|
| 334 |
+
...
|
| 335 |
+
}
|
| 336 |
+
)
|
| 337 |
+
```
|
| 338 |
+
|
| 339 |
+
They can review past versions to see the evolution or optionally revert to earlier version by storing it again.
|
| 340 |
+
|
| 341 |
+
**Implementation Complexity:** **20 minutes**
|
| 342 |
+
- Modify `remember()` to detect duplicate content (hash match on content field)
|
| 343 |
+
- Store version history as JSON array in KV value column (existing table schema, just more data)
|
| 344 |
+
- Optionally create endpoint `/memory/version/<id>` for viewing history
|
| 345 |
+
- Update tests to verify version history is stored correctly
|
| 346 |
+
|
| 347 |
+
**Risk:** Low β simple append operation to existing data structure. No schema migration needed since we're adding fields to existing JSON values. Backward compatible since previous versions without `_versions` field just get overwritten (which may be surprising behavior, but acceptable for safety).
|
| 348 |
+
|
| 349 |
+
---
|
| 350 |
+
|
| 351 |
+
### π― Feature 6: Search Result Citation with Source Links
|
| 352 |
+
|
| 353 |
+
**Why Add This?**
|
| 354 |
+
When a memory is returned from search, users can see where the original observation came from and what code context had it observed. This helps understand which files are most relevant to certain topics and provides better "view in context" capability for follow-up actions.
|
| 355 |
+
|
| 356 |
+
**What to Implement:**
|
| 357 |
+
```python
|
| 358 |
+
def folder_observe(kv, payload):
|
| 359 |
+
# Store source file info
|
| 360 |
+
if payload.get("sourceFile"):
|
| 361 |
+
obs["sourceFile"] = payload["sourceFile"]
|
| 362 |
+
obs["lineNumber"] = payload.get("lineNumber") or None
|
| 363 |
+
|
| 364 |
+
return {
|
| 365 |
+
"observationId": fobs_{hash}_{rand},
|
| 366 |
+
"sourceFile": "src/auth.py", # New field: where this came from
|
| 367 |
+
...
|
| 368 |
+
}
|
| 369 |
+
```
|
| 370 |
+
|
| 371 |
+
**Search Result Enhancement:**
|
| 372 |
+
Modify search result response format:
|
| 373 |
+
```python
|
| 374 |
+
def folder_search(kv, query, limit, folder_path, agent_id):
|
| 375 |
+
|
| 376 |
+
results = [
|
| 377 |
+
{
|
| 378 |
+
"id": obs_id,
|
| 379 |
+
"text": "...",
|
| 380 |
+
"folderPath": "/home/user/project",
|
| 381 |
+
"score": 0.85, # Original BM25 score or vector similarity
|
| 382 |
+
"sourceFile": "src/auth.py", # NEW: where this came from
|
| 383 |
+
"timestamp": ts.isoformat(),
|
| 384 |
+
...
|
| 385 |
+
}
|
| 386 |
+
]
|
| 387 |
+
```
|
| 388 |
+
|
| 389 |
+
**User Experience:**
|
| 390 |
+
Search query: "JWT authentication"
|
| 391 |
+
Results now show source file links:
|
| 392 |
+
```json
|
| 393 |
+
[
|
| 394 |
+
{
|
| 395 |
+
"id": "fobs_abc123",
|
| 396 |
+
"text": "JWT needs refresh tokens for token rotation",
|
| 397 |
+
"sourceFile": "src/auth.py", // Click link to see code context!
|
| 398 |
+
"score": 0.92,
|
| 399 |
+
...
|
| 400 |
+
},
|
| 401 |
+
{
|
| 402 |
+
"id": "fobs_def456",
|
| 403 |
+
"text": "Using 'jwt' import from auth package",
|
| 404 |
+
"sourceFile": "src/main.py", // Another file
|
| 405 |
+
"score": 0.78,
|
| 406 |
+
...
|
| 407 |
+
}
|
| 408 |
+
]
|
| 409 |
+
```
|
| 410 |
+
|
| 411 |
+
**Implementation Complexity:** **20 minutes**
|
| 412 |
+
- Add `source_file` field to observation schema (same as Feature 2)
|
| 413 |
+
- Update search result formatting in `folder_search()` to include source file info from observations
|
| 414 |
+
- Optionally: Show which files are most-relevant based on sources of highest-scoring memories
|
| 415 |
+
|
| 416 |
+
**Risk:** Low β just need to read the new field from observations and format it in search results. No complex data structure change needed.
|
| 417 |
+
|
| 418 |
+
---
|
| 419 |
+
|
| 420 |
+
### π― Feature 7: Memory Impact / Related Memories Endpoint
|
| 421 |
+
|
| 422 |
+
**Why Add This?**
|
| 423 |
+
When a user remembers something important, they might want to know which other memories relate semantically or contextually. Currently all memories exist independently with no explicit linking mechanism. Adding semantic similarity scores helps users understand how new memories connect to existing knowledge and spot conflicts.
|
| 424 |
+
|
| 425 |
+
**What to Implement:**
|
| 426 |
+
```python
|
| 427 |
+
def memory_related(kv, memory_id):
|
| 428 |
+
|
| 429 |
+
# Get original memory content from KV
|
| 430 |
+
original_memory = kv.get(KV.memories, memory_id)
|
| 431 |
+
original_content = json.loads(original_memory["value"])
|
| 432 |
+
|
| 433 |
+
# Search for semantically similar memories
|
| 434 |
+
search_results = hybrid_search.search(
|
| 435 |
+
query=original_content["content"],
|
| 436 |
+
limit=5, # Return top 5 related memories
|
| 437 |
+
filter_scope=[KV.memories], # Only search within memories scope
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
# Score by similarity + rank in results list
|
| 441 |
+
return {
|
| 442 |
+
"memoryId": memory_id,
|
| 443 |
+
"relatedMemories": [
|
| 444 |
+
{
|
| 445 |
+
"id": "mem_abc123",
|
| 446 |
+
"score": 0.92, # Semantic similarity score (0-1)
|
| 447 |
+
"reason": "Same concepts: JWT, authentication",
|
| 448 |
+
...
|
| 449 |
+
},
|
| 450 |
+
...
|
| 451 |
+
]
|
| 452 |
+
}
|
| 453 |
+
```
|
| 454 |
+
|
| 455 |
+
**User Experience:**
|
| 456 |
+
Request: `/agentcache/memories/related/mem_xyz123`
|
| 457 |
+
Response shows how this memory connects to existing knowledge:
|
| 458 |
+
```json
|
| 459 |
+
{
|
| 460 |
+
"memoryId": "mem_xyz123",
|
| 461 |
+
"relatedMemories": [
|
| 462 |
+
{
|
| 463 |
+
"id": "mem_abc456",
|
| 464 |
+
"score": 0.94,
|
| 465 |
+
"reason": "Same concepts (JWT) and file context (src/auth.py)",
|
| 466 |
+
"content": "JWT uses Bearer token format in Authorization header"
|
| 467 |
+
},
|
| 468 |
+
{
|
| 469 |
+
"id": "mem_def789",
|
| 470 |
+
"score": 0.78,
|
| 471 |
+
"reason": "Related to auth module but different concepts",
|
| 472 |
+
"content": "Using Express.js for REST API development"
|
| 473 |
+
}
|
| 474 |
+
],
|
| 475 |
+
...
|
| 476 |
+
}
|
| 477 |
+
```
|
| 478 |
+
|
| 479 |
+
**Implementation Complexity:** **30 minutes**
|
| 480 |
+
- Use existing `HybridSearch.search()` method with original memory content as query
|
| 481 |
+
- Score by rank in results (1st highest = most related) + cosine similarity score
|
| 482 |
+
- Format response as JSON with reason descriptions generated from concept extraction
|
| 483 |
+
- Create new endpoint that takes memory ID and returns related items
|
| 484 |
+
|
| 485 |
+
**Risk:** Medium β requires leveraging existing search infrastructure. Must ensure the semantic search is fast enough (< 50ms response time). Also need to handle empty or non-existent memories gracefully (return helpful error).
|
| 486 |
+
|
| 487 |
+
---
|
| 488 |
+
|
| 489 |
+
## Recommendation Priority
|
| 490 |
+
|
| 491 |
+
As a user who relies on this tool daily for code context and observation management, here's my prioritization based on "bang for buck" β quick implementation with high utility:
|
| 492 |
+
|
| 493 |
+
### **Priority 1 (Implement First): Observation Weighting**
|
| 494 |
+
|
| 495 |
+
**Why #1:** Fastest to implement (~10 min), highest immediate value
|
| 496 |
+
- Let me tag observations as priority 90-100 and have them surface prominently in search results
|
| 497 |
+
- No new data structures needed; just add one number to the existing weighting formula
|
| 498 |
+
- Users already write some observations as "important issues" but they're weighted same as trivial notes
|
| 499 |
+
|
| 500 |
+
**Implementation:**
|
| 501 |
+
```python
|
| 502 |
+
# New payload validation
|
| 503 |
+
def folder_observe(kv, payload, priority=0):
|
| 504 |
+
# Validate priority is 0-100 range
|
| 505 |
+
if not (0 <= priority <= 100):
|
| 506 |
+
raise ValueError("priority must be between 0 and 100")
|
| 507 |
+
```
|
| 508 |
+
|
| 509 |
+
**Time:** ~10 min
|
| 510 |
+
**Risk:** None β modifies existing RRF function slightly
|
| 511 |
+
|
| 512 |
+
---
|
| 513 |
+
|
| 514 |
+
### **Priority 2: Folder Statistics Endpoint**
|
| 515 |
+
|
| 516 |
+
**Why #2:** Simplest implementation with high utility for daily users
|
| 517 |
+
- Users constantly need this info but it's not easily available via API calls
|
| 518 |
+
- Just needs a few more lines of code following Flask pattern
|
| 519 |
+
- Query existing data without any schema changes
|
| 520 |
+
|
| 521 |
+
**Implementation:**
|
| 522 |
+
```python
|
| 523 |
+
@app.route('/folder/stats/<path:folder_path>')
|
| 524 |
+
def folder_stats():
|
| 525 |
+
# ~5 min implementation
|
| 526 |
+
```
|
| 527 |
+
|
| 528 |
+
**Time:** ~5 min
|
| 529 |
+
**Risk:** None β new endpoint, minimal queries
|
| 530 |
+
|
| 531 |
+
---
|
| 532 |
+
|
| 533 |
+
### **Priority 3: Observation Source Tracking**
|
| 534 |
+
|
| 535 |
+
**Why #3:** Low risk, very useful for detecting patterns
|
| 536 |
+
- Track which files user has observed about certain topics helps detect learning cycles
|
| 537 |
+
- One extra field in KV value dict β no schema changes
|
| 538 |
+
- Helps spot when user re-implements same code after reading tutorial
|
| 539 |
+
|
| 540 |
+
**Implementation:**
|
| 541 |
+
```python
|
| 542 |
+
def folder_observe(kv, payload, source_file=None):
|
| 543 |
+
# ~10 min implementation
|
| 544 |
+
```
|
| 545 |
+
|
| 546 |
+
**Time:** ~15 min
|
| 547 |
+
**Risk:** Low β one optional field addition
|
| 548 |
+
|
| 549 |
+
---
|
| 550 |
+
|
| 551 |
+
### **Priority 4: Memory Version History**
|
| 552 |
+
|
| 553 |
+
**Why #4:** Good safety feature, relatively simple to implement
|
| 554 |
+
- Prevents accidental data loss
|
| 555 |
+
- Version history array in JSON value column is trivial to add
|
| 556 |
+
- Users love being able to see evolution of memories they've written
|
| 557 |
+
|
| 558 |
+
**Implementation:**
|
| 559 |
+
```python
|
| 560 |
+
def remember(kv, data):
|
| 561 |
+
# ~15 min implementation
|
| 562 |
+
```
|
| 563 |
+
|
| 564 |
+
**Time:** ~20 min
|
| 565 |
+
**Risk:** Low β append operation, no schema changes
|
| 566 |
+
|
| 567 |
+
---
|
| 568 |
+
|
| 569 |
+
## Summary
|
| 570 |
+
|
| 571 |
+
| Feature | Est. Time | Risk Level | User Impact |
|
| 572 |
+
|---------|-----------|------------|-------------|
|
| 573 |
+
| Observation Weighting | 10 min | None | High (control over search ranking) |
|
| 574 |
+
| Folder Stats Endpoint | 5 min | None | High (quick stats access) |
|
| 575 |
+
| Source Tracking | 15 min | Low | Medium (detect patterns) |
|
| 576 |
+
| Memory Versioning | 20 min | Low | Medium (safety feature) |
|
| 577 |
+
|
| 578 |
+
All four fit seamlessly into existing Flask/SQLite architecture without requiring any architectural changes or new dependencies. They leverage current infrastructure and enhance the user experience incrementally.
|
UBIQUITOUS_LANGUAGE.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ubiquitous Language β agentcache
|
| 2 |
+
|
| 3 |
+
> This glossary defines the canonical terms used in agentcache's domain model.
|
| 4 |
+
> Use these exact names in code, docs, PR descriptions, ticket titles, and conversations.
|
| 5 |
+
> "Aliases to avoid" lists terms found in the codebase or tickets that should be replaced.
|
| 6 |
+
> Last updated: July 2026 β incorporates architecture review, ubiquitous language session, and ticket breakdown.
|
| 7 |
+
>
|
| 8 |
+
> **Architecture improvement tickets:** `.scratch/arch-improvements/issues/`
|
| 9 |
+
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
## Core Memory Model
|
| 13 |
+
|
| 14 |
+
| Term | Definition | Aliases to avoid |
|
| 15 |
+
|------|------------|-----------------|
|
| 16 |
+
| **Observation** | A single captured event from an agent's execution β one tool call, one prompt, one file edit β scoped to a Folder Pair. The atomic unit of Working Memory. | `log`, `event`, `entry`, `raw_observation`, `hook event` |
|
| 17 |
+
| **Memory** | A manually saved, long-term insight, decision, or pattern β not derived from a single tool call but from deliberate agent reflection. | `long-term memory`, `global memory`, `insight`, `fact` (when used alone) |
|
| 18 |
+
| **Lesson** | A reusable learning with a confidence score that decays over time; strengthened when the same pattern recurs. | `learning`, `tip`, `rule` |
|
| 19 |
+
| **Slot** | A named, pinned piece of context β manually set or auto-populated β injected into every session start. | `pinned slot`, `memory slot`, `context slot` |
|
| 20 |
+
| **Folder Pair** | The primary scope key: the combination of a `folderPath` and an `agentId` that uniquely identifies one agent's memory for one project directory. | `(folder, agent)`, `folder-agent pair`, `scope key`, `session` (when used to mean this) |
|
| 21 |
+
| **Folder Metadata** | Aggregate statistics for a Folder Pair: observation count, last updated timestamp, and optional summary. | `folder meta`, `FolderMeta` |
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
## Memory Tiers (Consolidation Pipeline)
|
| 26 |
+
|
| 27 |
+
| Term | Definition | Aliases to avoid |
|
| 28 |
+
|------|------------|-----------------|
|
| 29 |
+
| **Working Memory** | The live, unprocessed stream of Observations for a Folder Pair β raw and ephemeral. | `raw observations`, `session logs` |
|
| 30 |
+
| **Episodic Memory** | A compressed summary of a group of Observations for a Folder Pair, produced by the Consolidation pipeline. | `session summary`, `compressed episode`, `narrative` |
|
| 31 |
+
| **Semantic Memory** | Stable factual knowledge extracted from multiple Episodic Memories by the Consolidation pipeline. | `fact`, `knowledge`, `semantic fact` |
|
| 32 |
+
| **Procedural Memory** | A reusable workflow or decision pattern extracted from repeated cross-session patterns. | `procedure`, `workflow memory`, `pattern` (when used alone) |
|
| 33 |
+
| **Consolidation** | The LLM-driven pipeline that compresses Working Memory β Episodic β Semantic β Procedural. Triggered manually or by the auto-forget sweep. | `crystallize`, `compress`, `summarize` |
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
## Agents & Identity
|
| 38 |
+
|
| 39 |
+
| Term | Definition | Aliases to avoid |
|
| 40 |
+
|------|------------|-----------------|
|
| 41 |
+
| **Agent** | An AI coding assistant (Claude, Kiro, Cursor, Cline, etc.) that writes Observations and reads Memory. | `client`, `AI`, `bot` |
|
| 42 |
+
| **Agent ID** | A string identity for one agent instance, used as the second key in a Folder Pair. | `agentId`, `agent_id`, `agent name` |
|
| 43 |
+
| **Agent Scope** | The isolation boundary for an Agent's data β either `shared` (sees all agents' data) or `isolated` (sees only its own). | `scope isolation`, `agent namespace` |
|
| 44 |
+
| **MCP Client** | Any tool (Cursor, Claude Desktop, Kiro) that communicates with agentcache using the Model Context Protocol. | `plugin`, `integration`, `agent client` |
|
| 45 |
+
|
| 46 |
+
---
|
| 47 |
+
|
| 48 |
+
## Search & Retrieval
|
| 49 |
+
|
| 50 |
+
| Term | Definition | Aliases to avoid |
|
| 51 |
+
|------|------------|-----------------|
|
| 52 |
+
| **Recall** | A keyword-based search over Observations and Memories using the BM25 index. | `search`, `lookup`, `query` (when BM25 is implied) |
|
| 53 |
+
| **Smart Search** | A hybrid search combining BM25 keyword matching and vector cosine similarity, fused by Reciprocal Rank Fusion (RRF). | `semantic search`, `hybrid search`, `vector search` |
|
| 54 |
+
| **Folder Search** | A Recall or Smart Search scoped to one or more Folder Pairs. | `scoped search`, `project search` |
|
| 55 |
+
| **Timeline** | A chronological, filterable feed of Observations for a Folder Pair. | `activity feed`, `history`, `replay feed` |
|
| 56 |
+
| **Context Compilation** | The process of assembling Slots, Lessons, recent Episodic Memories, and a Profile into a single text block for agent injection at session start. | `context injection`, `context window prep`, `compile_context` |
|
| 57 |
+
|
| 58 |
+
---
|
| 59 |
+
|
| 60 |
+
## Lifecycle & Maintenance
|
| 61 |
+
|
| 62 |
+
| Term | Definition | Aliases to avoid |
|
| 63 |
+
|------|------------|-----------------|
|
| 64 |
+
| **Auto-Forget** | A background sweep that evicts Observations past their TTL or above the per-Folder-Pair cap, optionally running Consolidation before eviction. | `auto_forget`, `TTL sweep`, `pruning`, `cleanup` |
|
| 65 |
+
| **Deduplication** | Removal of Observations whose text fingerprint (SHA-256) matches an existing Observation in the same Folder Pair. | `dedup`, `duplicate removal` |
|
| 66 |
+
| **Privacy Scrub** | The process of stripping secrets, tokens, and API keys from Observation text before storage, using a regex-based policy. | `redaction`, `sanitization`, `strip_private_data` |
|
| 67 |
+
| **Index Rebuild** | A background process that reprocesses all stored Observations and Memories into a fresh BM25 and vector index. | `reindex`, `rebuild_index`, `warm-up` |
|
| 68 |
+
| **Audit Entry** | An append-only record of every write operation, stored in the audit log with agent ID and timestamp. | `audit log`, `commit`, `version` (legacy Dolt term β avoid) |
|
| 69 |
+
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
## Knowledge Graph
|
| 73 |
+
|
| 74 |
+
| Term | Definition | Aliases to avoid |
|
| 75 |
+
|------|------------|-----------------|
|
| 76 |
+
| **Relation** | A directed edge in the Knowledge Graph between two concepts, files, or Folder Pairs. | `graph edge`, `link`, `connection` |
|
| 77 |
+
| **Knowledge Graph** | The set of Relations extracted from Observations, used to augment search retrieval with structural codebase topology. | `graph`, `concept graph`, `project graph` |
|
| 78 |
+
| **Graph Extraction** | The LLM-driven process that reads Observations and produces Relations for the Knowledge Graph. | `graph build`, `relation extraction` |
|
| 79 |
+
|
| 80 |
+
---
|
| 81 |
+
|
| 82 |
+
## Architecture & Module Structure
|
| 83 |
+
|
| 84 |
+
These terms describe the codebase's structural concepts, used in tickets, PRs, and architecture discussions.
|
| 85 |
+
|
| 86 |
+
| Term | Definition | Aliases to avoid |
|
| 87 |
+
|------|------------|-----------------|
|
| 88 |
+
| **AppContext** | A single injectable dataclass that packages all runtime state (`kv`, `bm25`, `vector`, `embedder`, `broadcast`) β the replacement for module-level globals. Constructed once in `create_app()` and passed explicitly to every function that needs it. | `globals`, `module state`, `app state` |
|
| 89 |
+
| **Deep Module** | A module with a narrow public interface that hides substantial implementation β the opposite of a shallow wrapper. The goal of every split in the god-module refactor. | `service`, `component`, `helper` |
|
| 90 |
+
| **Adapter Seam** | The interface point where one concrete implementation can be swapped for another (e.g. `BaseVectorIndex` allows `InMemoryVectorIndex` to be replaced by an HNSW adapter). A seam becomes real once two concrete adapters exist. | `interface`, `abstraction layer`, `plugin point` |
|
| 91 |
+
| **ExpandβContract** | The two-phase refactor pattern used for wide changes: first *expand* (add the new form alongside the old so CI stays green), then *contract* (delete the old form once all callers are migrated). | `big bang refactor`, `flag day`, `rewrite` |
|
| 92 |
+
| **God Module** | An anti-pattern: a single module (`functions.py` at 4 583 lines) containing all domain logic, making it impossible to test any concern in isolation. Being eliminated via tickets 01β08. | `utils.py`, `helpers.py`, `core.py` (when used as a catch-all) |
|
| 93 |
+
| **Auth Middleware** | A single `require_auth` decorator in `auth.py` that performs the timing-safe Bearer token check β the replacement for copy-pasted `_check_auth()` functions in every blueprint. | `_check_auth`, `auth function`, `bearer check` |
|
| 94 |
+
| **MCP Tool Registry** | A `dict[str, Callable]` populated by `@register("tool_name")` decorators, replacing the 300-line `if/elif` dispatch chain in `routes/mcp.py`. | `tool dispatcher`, `elif chain`, `tool handler map` |
|
| 95 |
+
| **Privacy Policy** | The documented, extensible set of regex patterns that defines what text is scrubbed before an Observation is stored. Lives in `privacy.py` as `DEFAULT_PATTERNS`. | `regex list`, `scrub rules`, `redaction rules` |
|
| 96 |
+
| **Tracer Bullet** | A narrow, complete vertical slice of work that cuts through every layer (storage, logic, API, tests) and is demoable on its own. The unit of work in the ticket breakdown. | `task`, `story`, `sub-task` |
|
| 97 |
+
|
| 98 |
+
---
|
| 99 |
+
|
| 100 |
+
## Infrastructure & Sync
|
| 101 |
+
|
| 102 |
+
| Term | Definition | Aliases to avoid |
|
| 103 |
+
|------|------------|-----------------|
|
| 104 |
+
| **Folder Path** | The absolute filesystem path of the working directory that scopes a Folder Pair. | `cwd`, `directory`, `project path` |
|
| 105 |
+
| **KV Store** | The SQLite-backed key-value store that persists all domain data as JSON values keyed by `(scope, key)`. | `database`, `store`, `StateKV` |
|
| 106 |
+
| **HF Sync** | The process of backing up the KV Store to a private HuggingFace dataset repo and restoring it on startup. | `sync`, `backup`, `dataset sync` |
|
| 107 |
+
| **Audit High-Water Mark** | The `MAX(id)` of the audit log, used by HF Sync to detect whether any writes have occurred since the last backup. | `HWM`, `sync cursor`, `change marker` |
|
| 108 |
+
| **Index Shard** | A chunk of the serialized BM25 or vector index stored in the KV Store for persistence across restarts. | `index chunk`, `bm25 shard`, `vector shard` |
|
| 109 |
+
|
| 110 |
+
---
|
| 111 |
+
|
| 112 |
+
## Relationships
|
| 113 |
+
|
| 114 |
+
- An **Observation** belongs to exactly one **Folder Pair**.
|
| 115 |
+
- A **Folder Pair** has exactly one **Folder Path** and exactly one **Agent ID**.
|
| 116 |
+
- A **Folder Pair** accumulates many **Observations** and zero or one **Folder Metadata**.
|
| 117 |
+
- **Consolidation** consumes **Observations** from a **Folder Pair** and produces **Episodic Memories**, which in turn feed **Semantic Memory** and **Procedural Memory**.
|
| 118 |
+
- A **Lesson** exists globally β not scoped to a **Folder Pair**.
|
| 119 |
+
- A **Slot** is scoped to either a project (matched by **Folder Path**) or global (all agents).
|
| 120 |
+
- A **Memory** is global β not scoped to a **Folder Pair** β and may link to source **Observations** via `sourceObservationIds`.
|
| 121 |
+
- **Recall** and **Smart Search** operate across all **Folder Pairs** (optionally filtered) and also include **Memories**.
|
| 122 |
+
- **Context Compilation** assembles **Slots** + **Lessons** + **Episodic Memories** + **Profile** for one **Folder Pair** at session start.
|
| 123 |
+
- An **AppContext** is constructed once per process and holds one **KV Store**, one BM25 index, one **Adapter Seam** for the vector index, one embedder, and one broadcast callable.
|
| 124 |
+
- The **Auth Middleware** guards every route β the **MCP Tool Registry** handles dispatch after auth passes.
|
| 125 |
+
- The **Privacy Policy** is applied to every **Observation** before it is written to the **KV Store** or the typed observations table.
|
| 126 |
+
|
| 127 |
+
---
|
| 128 |
+
|
| 129 |
+
## Flagged Ambiguities
|
| 130 |
+
|
| 131 |
+
- **"session"** is used in two conflicting ways:
|
| 132 |
+
1. *Legacy* β a `Session` object with a `sessionId`, from the pre-folder model (now read-only, used only for migration).
|
| 133 |
+
2. *Informal* β an agent's working period within a Folder Pair.
|
| 134 |
+
β Use **Folder Pair** for the scope concept and avoid "session" entirely in new code.
|
| 135 |
+
|
| 136 |
+
- **"memory"** is used both as the general concept ("agent memory system") and as the specific entity (`Memory` β a saved insight).
|
| 137 |
+
β In domain conversations, always qualify: **Working Memory**, **Episodic Memory**, **Semantic Memory**, **Procedural Memory**, or the entity **Memory** (capitalised).
|
| 138 |
+
|
| 139 |
+
- **"crystallize" vs "consolidate"** β the codebase uses both for the same pipeline (LLM compression of Observations into higher-tier memories).
|
| 140 |
+
β Canonical term: **Consolidation**. `crystallize` is an alias to retire.
|
| 141 |
+
|
| 142 |
+
- **"recall" vs "search" vs "smart search"** β three terms for overlapping operations.
|
| 143 |
+
β **Recall** = BM25 keyword search. **Smart Search** = hybrid BM25 + vector. **Folder Search** = either, scoped to a Folder Pair.
|
| 144 |
+
|
| 145 |
+
- **"forget" vs "auto-forget" vs "evict"** β used interchangeably in code and docs.
|
| 146 |
+
β **Forget** = deliberate deletion triggered by an agent. **Auto-Forget** = background TTL sweep. **Eviction** = the act of removing one Observation during Auto-Forget.
|
| 147 |
+
|
| 148 |
+
---
|
| 149 |
+
|
| 150 |
+
## Example Dialogue
|
| 151 |
+
|
| 152 |
+
> **Dev:** "When an agent finishes a task, should it call `remember` or does the system handle that automatically?"
|
| 153 |
+
|
| 154 |
+
> **Domain expert:** "Those are two different things. `remember` is for **Memories** β things the agent explicitly wants to preserve, like an architecture decision. The system automatically stores every tool call as an **Observation** in the **Folder Pair** via `folder_observe`. **Memories** are always deliberate."
|
| 155 |
+
|
| 156 |
+
> **Dev:** "So if the agent never calls `remember`, does it lose everything?"
|
| 157 |
+
|
| 158 |
+
> **Domain expert:** "No β the **Observations** are still there in **Working Memory** for that **Folder Pair**. But they're raw and noisy. **Consolidation** is what turns them into clean **Episodic Memories** that survive **Auto-Forget** eviction."
|
| 159 |
+
|
| 160 |
+
> **Dev:** "What's the difference between a **Lesson** and a **Semantic Memory**?"
|
| 161 |
+
|
| 162 |
+
> **Domain expert:** "A **Lesson** is something the agent explicitly learned and wants to reuse β it has a confidence score that decays. **Semantic Memory** is extracted automatically by the **Consolidation** pipeline from patterns across **Episodic Memories**. You save a **Lesson**; the system extracts **Semantic Memory**."
|
| 163 |
+
|
| 164 |
+
> **Dev:** "And a **Slot** is different from both?"
|
| 165 |
+
|
| 166 |
+
> **Domain expert:** "Yes. A **Slot** is pinned context β the agent or developer sets it manually and it gets injected at every session start via **Context Compilation**. Think of it as a sticky note, not a memory."
|
Users/ykanagaraj/Downloads/agentcache/CLAUDE.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CLAUDE.md
|
| 2 |
+
|
| 3 |
+
This file provides guidance to Claude Code (claude.ai/code) when working with this repository.
|
| 4 |
+
|
| 5 |
+
## π Development Commands
|
| 6 |
+
|
| 7 |
+
### Installation
|
| 8 |
+
To install all required dependencies, run:
|
| 9 |
+
```bash
|
| 10 |
+
pip install -r requirements.txt
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
### Running the Application
|
| 14 |
+
To start the Flask API server, run:
|
| 15 |
+
```bash
|
| 16 |
+
python src/app.py
|
| 17 |
+
```
|
| 18 |
+
The server defaults to listening on port `3111`.
|
| 19 |
+
|
| 20 |
+
### Testing
|
| 21 |
+
To run unit and integration tests (using pytest):
|
| 22 |
+
```bash
|
| 23 |
+
pip install -e ".[dev]"
|
| 24 |
+
pytest tests/ -v
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
## ποΈ High-Level Architecture
|
| 28 |
+
|
| 29 |
+
The application follows a modular, layered architecture designed around Flask and dynamic data services:
|
| 30 |
+
|
| 31 |
+
1. **Presentation Layer (Flask App)**: The core is orchestrated by `src/app.py`, which initializes the Flask application. It handles routing via registered blueprints, manages request processing, and sets up network services like WebSockets.
|
| 32 |
+
2. **Data & Persistence Layer (`src/db.py`)**: Data management is handled by `StateKV`, which wraps SQLite to provide namespaced storage for observations, memories, and metadata. This layer ensures data persistence across sessions. Key scopes include `mem:folders` (index), `mem:folder:{path}:{agent}` (observations), `mem:foldermeta:{path}:{agent}` (metadata), `mem:obs_lookup` (O(1) reverse lookup), `mem:memories` (global), and `mem:index:bm25:*` (search).
|
| 33 |
+
3. **Core Logic & Services (`src/functions.py`)**: This module contains all business logic implementations, including observation ingestion (`folder_observe`), timeline generation (`folder_timeline`), memory management functions (`remember`, `forget`), and the abstraction layers for embedding providers and search index operations.
|
| 34 |
+
4. **Search Indexing (`src/search.py`)**: This layer manages two primary search indexes: a **BM25** index (for keyword matching) and a **Vector Index** (for semantic similarity). It dynamically selects an embedding provider (Gemini, OpenAI, or SentenceTransformer) to power the Vector Index, allowing for hybrid (RRF fusion) searches.
|
| 35 |
+
5. **Communication Layer (WebSockets)**: The system supports real-time communication via WebSockets (`flask_sock`). This layer is responsible for broadcasting live events across connected clients, managed by functions in `src/functions.py`.
|
| 36 |
+
|
| 37 |
+
**Key Architectural Insight:** The architecture relies heavily on dynamic dependency injection. The application dynamically selects the best Embedding Provider based on environment variables (Gemini > OpenAI > Local Model), ensuring flexibility in how semantic search is performed, all orchestrated from the central `create_app()` function.
|
| 38 |
+
|
| 39 |
+
## MCP Tools (12 active)
|
| 40 |
+
|
| 41 |
+
The system exposes 12 Memory/Tool endpoints via `/mcp/tools`. These tools provide agents with access to core functionalities like observation ingestion (`agent_observe`), memory recall (`memory_recall`), and persistence operations (`memory_save`, `memory_forget`). Their schemas can be found at `GET /agentcache/mcp/tools`.
|
| 42 |
+
|
| 43 |
+
## Environment & Configuration Notes
|
| 44 |
+
|
| 45 |
+
* **Persistence**: SQLite file lives at `~/.agentcache/agentcache.db`.
|
| 46 |
+
* **Configuration**: Application settings are loaded from environment variables, often managed via a `.env` file in `~/.agentcache/`. Credentials (like API keys) must be set for secured operations. Key variables include `GEMINI_API_KEY`, `OPENAI_API_KEY`, `AGENTCACHE_SECRET`, and scope filters like `AGENTCACHE_AGENT_SCOPE=isolated`.
|
| 47 |
+
* **Data Limits**: Observe ingestion is capped at `MAX_OBS_PER_FOLDER` (default 2000). Context compilation is capped by `TOKEN_BUDGET` (default 2000).
|
| 48 |
+
|
| 49 |
+
## Memory Structure Overview
|
| 50 |
+
|
| 51 |
+
The system supports folder-based observation scoping, global memories (`remember`/`forget`), and timeline tracking. All persistent data interaction flows through the `src/db.py` StateKV layer to maintain namespaced and auditable storage.
|
| 52 |
+
|
| 53 |
+
- [CLAUDE.md](file.md) β hook
|
product_roadmap.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Project Roadmap: Prototype to Production
|
| 2 |
+
|
| 3 |
+
This document outlines the phased roadmap for scaling our Memory Server project from a prototype to a robust, production-ready product. Each phase builds upon the last, focusing sequentially on Security, Features, Deployment, and Monitoring.
|
| 4 |
+
|
| 5 |
+
## Phase 1: Stabilization & Hardening (Security First)
|
| 6 |
+
**Goal:** Eliminate critical vulnerabilities, patch security gaps, and ensure core functionality is robust against common exploits.
|
| 7 |
+
|
| 8 |
+
### Tasks
|
| 9 |
+
1. **Security Audit**: Review all authentication logic (`AGENTCACHE_SECRET` usage) across API endpoints to enforce strict access control checks.
|
| 10 |
+
2. **Input Sanitization**: Implement rigorous input sanitization for all data ingress points (API calls, WebSocket messages) to mitigate SQL injection and XSS risks.
|
| 11 |
+
3. **Dependency Hardening**: Audit `requirements.txt` and update all dependencies to their latest secure versions, flagging any version downgrade/upgrade that could introduce a vulnerability.
|
| 12 |
+
4. **Test Baseline**: Run full test suite (`pytest`) and fix every failing test before moving to Phase 2.
|
| 13 |
+
|
| 14 |
+
## Phase 2: Feature Parity & Depth
|
| 15 |
+
**Goal:** Implement all planned features that were marked as incomplete or missing, bringing the system closer to its intended scope.
|
| 16 |
+
|
| 17 |
+
### Tasks
|
| 18 |
+
1. **Memory Deep Dive**: Fully implement complex memory routines like `folder_graph_build` and ensure atomic behavior for `remember`/`forget` operations.
|
| 19 |
+
2. **Search Optimization**: Optimize the hybrid search component (RRF parameters) and profile database queries to improve retrieval performance.
|
| 20 |
+
3. **MCP Tool Completion**: Complete functionality for all remaining tools listed in `/mcp/tools`.
|
| 21 |
+
4. **UX Polish**: Enhance the dashboard viewer (`/viewer`) to provide clear, actionable feedback on system status (e.g., current embedding model, load metrics).
|
| 22 |
+
|
| 23 |
+
## Phase 3: Productionization & Deployment
|
| 24 |
+
**Goal:** Prepare the application for a stable, scalable production environment.
|
| 25 |
+
|
| 26 |
+
### Tasks
|
| 27 |
+
1. **Containerization**: Write optimized Dockerfiles for the Flask application and dependencies to ensure environment parity across environments.
|
| 28 |
+
2. **Configuration Decoupling**: Refactor configuration loading to strictly adhere to container-native practices (using ENV variables exclusively) rather than relying on local `.env` file structures for production use.
|
| 29 |
+
3. **Performance Tuning**: Conduct load testing to identify I/O bottlenecks (DB access, network latency). Tune database indices and optimize WebSocket message throughput.
|
| 30 |
+
4. **Scalability Design**: Analyze the statelessness of components to determine optimal strategies for horizontal scaling (e.g., separate API workers vs. WebSocket workers).
|
| 31 |
+
|
| 32 |
+
## Phase 4: Monitoring & Iteration (Live Operations)
|
| 33 |
+
**Goal:** Establish continuous monitoring and a feedback loop for post-launch maintenance.
|
| 34 |
+
|
| 35 |
+
### Tasks
|
| 36 |
+
1. **Observability Stack**: Implement structured logging across all modules to facilitate easy debugging in production.
|
| 37 |
+
2. **Metrics Collection**: Integrate Prometheus metrics collection for tracking latency, resource utilization, and error rates.
|
| 38 |
+
3. **Anomaly Detection**: Configure alerts for performance degradation or excessive error rates in the data access layer.
|
| 39 |
+
4. **Feedback Mechanism**: Design and implement a simple feedback mechanism for collecting post-launch bug reports and feature requests.
|
questions.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Architectural Questions for agentcache
|
| 2 |
+
|
| 3 |
+
These are design and architecture questions raised by the current codebase.
|
| 4 |
+
They are not criticisms β they are things worth understanding and deciding on
|
| 5 |
+
before the system grows. Each one points to a real trade-off or risk.
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## 1. Flat KV Schema
|
| 10 |
+
|
| 11 |
+
The entire data model lives in one SQLite table: `kv_store(scope, key, value TEXT)`.
|
| 12 |
+
Observations, memories, index shards, sessions, lessons, slots, graph edges, and
|
| 13 |
+
image refs all share this single namespace.
|
| 14 |
+
|
| 15 |
+
- What happens to read performance when this table grows to 100k+ rows?
|
| 16 |
+
- Every "query" loads an entire scope into Python and filters in memory β no
|
| 17 |
+
SQL-level filtering by timestamp, type, or importance. Is that intentional?
|
| 18 |
+
- Why not give each data type its own table with typed columns, which would
|
| 19 |
+
enable proper indexes and range queries at the DB level?
|
| 20 |
+
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
## 2. In-Memory Index with a Cold-Start Window
|
| 24 |
+
|
| 25 |
+
The BM25 and vector indexes live entirely in Python process memory. On restart,
|
| 26 |
+
the server either deserializes them from SQLite or rebuilds them from scratch in
|
| 27 |
+
a background thread.
|
| 28 |
+
|
| 29 |
+
- During the rebuild window, searches return results from an empty or stale
|
| 30 |
+
index. Users get silently degraded results with no indication.
|
| 31 |
+
- Is there a "index warming" status exposed anywhere? The `/health` endpoint
|
| 32 |
+
could surface this, but does it?
|
| 33 |
+
- For large datasets, how long does the rebuild take? Is there a bound on that?
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
## 3. Vector Index is a Flat Python List
|
| 38 |
+
|
| 39 |
+
`VectorIndex.search()` iterates over every stored vector and computes cosine
|
| 40 |
+
similarity one by one. This is O(n) per query.
|
| 41 |
+
|
| 42 |
+
- At 10k observations this is already slow. At 100k it is unusable.
|
| 43 |
+
- There is no ANN (approximate nearest neighbor) index β no HNSW, no IVF, no
|
| 44 |
+
external vector DB.
|
| 45 |
+
- The EPIC mentions Qdrant/Chroma as future backends. What is the plan and
|
| 46 |
+
threshold for switching?
|
| 47 |
+
|
| 48 |
+
---
|
| 49 |
+
|
| 50 |
+
## 4. Single Auth Token (No RBAC)
|
| 51 |
+
|
| 52 |
+
Authentication is one shared secret compared with `hmac.compare_digest`. Every
|
| 53 |
+
client β viewer, agent, admin β uses the same token.
|
| 54 |
+
|
| 55 |
+
- If the token leaks from one agent config, everything is exposed.
|
| 56 |
+
- The viewer dashboard, the `/forget` endpoint (which can delete all data), and
|
| 57 |
+
the `/migrate` endpoint all share the same credential level.
|
| 58 |
+
- The EPIC mentions RBAC (viewer/agent/admin tokens). Is there a plan to
|
| 59 |
+
implement this before exposing the server to a network?
|
| 60 |
+
|
| 61 |
+
---
|
| 62 |
+
|
| 63 |
+
## 5. Global Module-Level State
|
| 64 |
+
|
| 65 |
+
`functions.py` declares `_bm25_index`, `_vector_index`, `_embedding_provider`,
|
| 66 |
+
`_hybrid_search`, and `_stream_broadcaster` as module-level globals. They are
|
| 67 |
+
set once at startup and mutated by every request.
|
| 68 |
+
|
| 69 |
+
- This makes the application impossible to unit test in isolation (you cannot
|
| 70 |
+
inject a fresh index per test).
|
| 71 |
+
- It also makes it impossible to run two instances in the same process (e.g.
|
| 72 |
+
for testing).
|
| 73 |
+
- Was this a deliberate simplicity trade-off, or just how it grew?
|
| 74 |
+
|
| 75 |
+
---
|
| 76 |
+
|
| 77 |
+
## 6. No Rate Limiting or Input Size Caps on Most Endpoints
|
| 78 |
+
|
| 79 |
+
Observations are capped at 4000 characters. But memories, lessons, slots, and
|
| 80 |
+
graph edges have no enforced size limits. The `/agent/observe` endpoint accepts
|
| 81 |
+
arbitrary JSON payloads.
|
| 82 |
+
|
| 83 |
+
- A misbehaving or compromised agent could flood the server with gigabytes of
|
| 84 |
+
data and exhaust disk space.
|
| 85 |
+
- There is no per-agent write rate limit, no total storage quota, and no
|
| 86 |
+
backpressure mechanism.
|
| 87 |
+
- Is this acceptable for the current single-user local use case? What changes
|
| 88 |
+
if this is exposed as a shared service?
|
| 89 |
+
|
| 90 |
+
---
|
| 91 |
+
|
| 92 |
+
## 7. HuggingFace Sync Backs Up the Entire SQLite File
|
| 93 |
+
|
| 94 |
+
`sync.py` uploads the whole `agentcache.db` file to a HuggingFace dataset repo,
|
| 95 |
+
including the BM25/vector index shards stored inside it.
|
| 96 |
+
|
| 97 |
+
- The index shards can be large and are fully reproducible from raw observations.
|
| 98 |
+
Syncing them wastes bandwidth and storage.
|
| 99 |
+
- The backup is not incremental β it uploads the whole file every time the
|
| 100 |
+
audit high-water mark changes.
|
| 101 |
+
- If the HF token is compromised, all user memory data (observations, memories,
|
| 102 |
+
lessons) is exposed. Is that risk documented anywhere for users?
|
| 103 |
+
|
| 104 |
+
---
|
| 105 |
+
|
| 106 |
+
## 8. `functions.py` is ~2000 Lines β a God Module
|
| 107 |
+
|
| 108 |
+
All business logic lives in one file: observation ingestion, memory CRUD,
|
| 109 |
+
search orchestration, consolidation, auto-forget, graph building, slot
|
| 110 |
+
management, lessons, image store, index persistence, and privacy scrubbing.
|
| 111 |
+
|
| 112 |
+
- This makes it hard to test any single concern in isolation.
|
| 113 |
+
- It creates implicit coupling β a change to how observations are stored can
|
| 114 |
+
accidentally affect how the index is rebuilt or how memories are versioned.
|
| 115 |
+
- What would it look like to split this into focused modules
|
| 116 |
+
(e.g. `observations.py`, `memories.py`, `indexing.py`, `consolidation.py`)?
|
| 117 |
+
|
| 118 |
+
---
|
| 119 |
+
|
| 120 |
+
## 9. Jaccard Similarity for Memory Deduplication
|
| 121 |
+
|
| 122 |
+
`remember()` checks for duplicate memories by computing Jaccard similarity
|
| 123 |
+
(word overlap) between the new content and all existing memories, with a
|
| 124 |
+
threshold of 0.7.
|
| 125 |
+
|
| 126 |
+
- This loads all memories into Python memory on every `remember()` call.
|
| 127 |
+
- Jaccard on raw word sets is a weak signal for semantic similarity β two
|
| 128 |
+
memories about the same concept but phrased differently score low and
|
| 129 |
+
both get stored.
|
| 130 |
+
- With a vector embedding provider active, why not use cosine similarity
|
| 131 |
+
against the vector index for deduplication instead?
|
| 132 |
+
|
| 133 |
+
---
|
| 134 |
+
|
| 135 |
+
## 10. The MCP Endpoint Duplicates Auth and Dispatch Logic
|
| 136 |
+
|
| 137 |
+
`routes/mcp.py` has its own `_check_auth()` function, its own `_get_kv()` helper,
|
| 138 |
+
and a large `if/elif` chain dispatching 30+ tool names. The same auth logic
|
| 139 |
+
exists in other route blueprints too.
|
| 140 |
+
|
| 141 |
+
- Auth is copy-pasted across at least 3 places. A future change (e.g. adding
|
| 142 |
+
RBAC) would require updating each copy.
|
| 143 |
+
- The tool dispatch chain has no registry β adding a new tool requires editing
|
| 144 |
+
a long elif block rather than registering a handler.
|
| 145 |
+
- Was there a reason not to use a decorator-based dispatch registry or a shared
|
| 146 |
+
auth middleware?
|
| 147 |
+
|
| 148 |
+
---
|
| 149 |
+
|
| 150 |
+
## 11. Privacy Scrubbing is a Regex List β Not a Policy
|
| 151 |
+
|
| 152 |
+
`strip_private_data()` removes secrets by matching a hardcoded list of regex
|
| 153 |
+
patterns (API keys, tokens, passwords). It runs on raw observation text before
|
| 154 |
+
storage.
|
| 155 |
+
|
| 156 |
+
- If an agent logs a secret with an unusual key name (e.g. `my_service_cred`),
|
| 157 |
+
it passes through unredacted.
|
| 158 |
+
- The scrubbing happens *after* the data is already in the Python process β
|
| 159 |
+
if the process crashes mid-scrub, the raw data may already be partially
|
| 160 |
+
visible in a WAL frame.
|
| 161 |
+
- Is the current regex set documented so users can audit and extend it?
|
| 162 |
+
|
| 163 |
+
---
|
| 164 |
+
|
| 165 |
+
## 12. No Tests
|
| 166 |
+
|
| 167 |
+
The `tests/` directory appears to be empty or minimal. For a system that
|
| 168 |
+
persists agent memory and is used as a source of truth for AI context:
|
| 169 |
+
|
| 170 |
+
- How do you verify that `folder_search` returns the right results after an
|
| 171 |
+
observation is added and the index is updated?
|
| 172 |
+
- How do you catch regressions in privacy scrubbing when you add a new pattern?
|
| 173 |
+
- How do you verify that the HF sync does not upload stale data after a
|
| 174 |
+
failed WAL checkpoint?
|
| 175 |
+
- What is the plan for adding test coverage before the system is used in
|
| 176 |
+
production or shared with other developers?
|