mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-09-22 00:32:49 +00:00
docs(protocol): add typed edge link protocol documentation Add comprehensive documentation for the link protocol supporting typed edges in body text. This includes specification for three legal inline forms (bare wikilink, line-level Dataview, inline-bracketed Dataview), predicate syntax rules, and the machine-managed Relations section convention for organizing discovered edges. fix(memory): update path reference from vault_root to working_dir Change the memory_create operation's path anchoring from vault_root to working_dir to maintain consistency with the current working directory configuration. refactor(components): remove edge_extractor module and simplify parsing Remove the edge_extractor component module entirely and inline edge extraction logic directly into LinkedFileParser using parse_wikilinks utility. This simplifies the architecture by eliminating the separate edge extraction component and delegating edge discovery to the maintainer's enrichment operations. feat(parser): update parse method signature and simplify edge extraction Modify LinkedFileParser to return (FileNode, list[FileChunk]) tuple instead of ParsedFile, remove dependency on BaseEdgeExtractor, and implement direct wikilink parsing from body text only. ```
379 lines
13 KiB
YAML
379 lines
13 KiB
YAML
app_name: reme-expert
|
|
enable_logo: false
|
|
log_to_console: true
|
|
log_to_file: false
|
|
|
|
# Expert-exposure profile: every memory_* read + write primitive plus
|
|
# `sync` (event log) are surfaced as MCP tools. The agent picks whatever
|
|
# it needs — finest granularity, no opinion enforced beyond the
|
|
# wikilink-uniqueness gate.
|
|
#
|
|
# **No `ingest` tool.** Cold-path R-M-W in expert mode is owned by the
|
|
# host agent (Claude Code, with the `reme-expert` SKILL loaded), not by
|
|
# reme2's internal ReActAgent. The reme-expert plugin spawns a
|
|
# `reme-distiller` subagent at SessionEnd to handle distillation in its
|
|
# own context window, using the same memory_* primitives.
|
|
#
|
|
# Use when you want the agent to manage memory directly with full
|
|
# control. For an opinionated minimal surface (where reme2's internal
|
|
# Ingestor owns R-M-W), see ./service.yaml.
|
|
|
|
service:
|
|
backend: mcp
|
|
transport: stdio
|
|
sidecar_http: true
|
|
sidecar_http_host: "127.0.0.1"
|
|
sidecar_http_port: 8765
|
|
sidecar_info_path: "./vault/.reme/sidecar.json"
|
|
|
|
jobs:
|
|
# -- Write entry points ------------------------------------------------
|
|
|
|
- backend: base
|
|
name: sync
|
|
description: |
|
|
Hot-path event sync: idempotent upsert of an event FOLDER under
|
|
`events/{date}/{name}/`. The folder contains the index `{name}.md`
|
|
(Event schema, frontmatter + narrative + Materials footer) plus
|
|
any raw materials you pass — conversation snippets, tool outputs,
|
|
data dumps. The watcher indexes everything inside.
|
|
|
|
CONTINUITY MODEL: pick a stable `name` per logical thread and
|
|
call `sync` repeatedly through the task. Each call extends the
|
|
same folder:
|
|
* new `content` → appended under a `## Update — {iso}` section
|
|
* new `materials` → siblings (auto-suffix on filename collision)
|
|
* `topics` + `tags` merged (union) into frontmatter
|
|
* Materials footer regenerated to list every artifact in the folder
|
|
|
|
First call (folder doesn't exist) → CREATE; subsequent calls with
|
|
the same `name` while the event is `status: active` → APPEND.
|
|
If the event is `status: distilled` / `archived`, `sync` REFUSES
|
|
and returns a `suggested_name` so you start a fresh thread
|
|
instead of mutating prior cognition.
|
|
|
|
Zero LLM cost. Call CONTINUOUSLY through a task as facts land,
|
|
and especially at PreCompact to dump verbose raw text into
|
|
`materials` before context truncation loses it. The folder is
|
|
the unit `ingest` later reads from.
|
|
parameters:
|
|
type: object
|
|
required: [name]
|
|
properties:
|
|
name:
|
|
type: string
|
|
description: "kebab-case event identifier (folder + index stem). Reuse the same name across calls in one thread to keep extending the same folder."
|
|
description: { type: string, description: "one-line summary for index frontmatter (set on initial create only)" }
|
|
content: { type: string, description: "markdown body. Initial create: the body. Subsequent calls: appended as a `## Update — {iso}` section." }
|
|
topics:
|
|
type: array
|
|
items: { type: string }
|
|
description: "related topic wikilinks ('[[X]]' or '[[topics/X/X]]'). Unioned into frontmatter on append."
|
|
tags: { type: array, items: { type: string }, description: "free-form tags; unioned on append." }
|
|
materials:
|
|
type: array
|
|
description: "Raw artifacts written as siblings of the index inside the event folder. Filenames must be safe (letters/digits/dot/underscore/dash). On filename collision with an existing artifact, auto-suffixes (foo.txt → foo-2.txt)."
|
|
items:
|
|
type: object
|
|
required: [filename, content]
|
|
properties:
|
|
filename: { type: string, description: "e.g. 'raw-prompt.md', 'tool-output.txt', 'snapshot.json'" }
|
|
content: { type: string }
|
|
on_date: { type: string, description: "ISO date for events/{date}/ bucket; defaults to today" }
|
|
origin_session_id: { type: string, description: "set on initial create only" }
|
|
steps:
|
|
- backend: sync
|
|
|
|
# -- Read tools --------------------------------------------------------
|
|
|
|
- backend: base
|
|
name: memory_search
|
|
description: "Hybrid (vector + keyword) search over chunks."
|
|
parameters:
|
|
type: object
|
|
required: [query]
|
|
properties:
|
|
query: { type: string }
|
|
max_results: { type: integer, default: 5 }
|
|
min_score: { type: number, default: 0.1 }
|
|
paths: { type: array, items: { type: string } }
|
|
tags: { type: array, items: { type: string } }
|
|
exclude_paths: { type: array, items: { type: string } }
|
|
steps:
|
|
- backend: memory_search
|
|
|
|
- backend: base
|
|
name: memory_graph_search
|
|
description: |
|
|
Three-way fusion search: vector + keyword + graph (BFS over wikilinks).
|
|
Pulls in chunks reachable through linked topics/events that pure
|
|
relevance search would miss.
|
|
parameters:
|
|
type: object
|
|
properties:
|
|
query: { type: string }
|
|
max_results: { type: integer, default: 5 }
|
|
min_score: { type: number, default: 0.0 }
|
|
graph_depth: { type: integer, default: 1 }
|
|
seeds: { type: array, items: { type: string } }
|
|
paths: { type: array, items: { type: string } }
|
|
tags: { type: array, items: { type: string } }
|
|
exclude_paths: { type: array, items: { type: string } }
|
|
steps:
|
|
- backend: memory_graph_search
|
|
|
|
- backend: base
|
|
name: memory_get
|
|
description: "Read frontmatter + body of a single file."
|
|
parameters:
|
|
type: object
|
|
required: [path]
|
|
properties:
|
|
path: { type: string }
|
|
include_chunks: { type: boolean, default: false }
|
|
steps:
|
|
- backend: memory_get
|
|
|
|
- backend: base
|
|
name: memory_list
|
|
description: |
|
|
List indexed files filtered by frontmatter exact-match, tags, or
|
|
path prefix. Returns {items: [{path, metadata}], count}.
|
|
parameters:
|
|
type: object
|
|
properties:
|
|
metadata: { type: object }
|
|
tags: { type: array, items: { type: string } }
|
|
path_prefix: { type: string }
|
|
limit: { type: integer, default: 100 }
|
|
steps:
|
|
- backend: memory_list
|
|
|
|
- backend: base
|
|
name: memory_backlinks
|
|
description: "Files linking TO the given path (with edge predicates)."
|
|
parameters:
|
|
type: object
|
|
required: [path]
|
|
properties:
|
|
path: { type: string }
|
|
steps:
|
|
- backend: memory_backlinks
|
|
|
|
- backend: base
|
|
name: memory_links
|
|
description: "Files the given path links to (resolved, with edge predicates)."
|
|
parameters:
|
|
type: object
|
|
required: [path]
|
|
properties:
|
|
path: { type: string }
|
|
steps:
|
|
- backend: memory_links
|
|
|
|
- backend: base
|
|
name: memory_resolve_wikilink
|
|
description: |
|
|
Resolve a `[[target]]` wikilink to a vault path. Stem-form (`X`)
|
|
consults the file_store's stem index; path-form (`a/b` or `a/b.md`)
|
|
is anchored at the vault root.
|
|
parameters:
|
|
type: object
|
|
required: [wikilink]
|
|
properties:
|
|
wikilink: { type: string }
|
|
steps:
|
|
- backend: memory_resolve_wikilink
|
|
|
|
- backend: base
|
|
name: memory_count_tokens
|
|
description: "Estimate token count for a file body or raw text."
|
|
parameters:
|
|
type: object
|
|
properties:
|
|
path: { type: string }
|
|
text: { type: string }
|
|
steps:
|
|
- backend: memory_count_tokens
|
|
|
|
- backend: base
|
|
name: memory_lint
|
|
description: |
|
|
Read-only projection of the Maintainer's lint findings. Walks the
|
|
indexed files (optionally restricted to a path prefix) and returns
|
|
structural issues — broken wikilinks, schema violations, stem
|
|
collisions. Never mutates. The agent decides what to do with each
|
|
finding using existing memory_* primitives (rename, update,
|
|
property_update, archive, delete).
|
|
parameters:
|
|
type: object
|
|
properties:
|
|
target_prefix:
|
|
type: string
|
|
description: "restrict scan to relpaths starting with this prefix (e.g. 'events/2026-05-09/'). Empty string scans the whole vault."
|
|
steps:
|
|
- backend: memory_lint
|
|
|
|
# -- Write primitives (raw building blocks) ----------------------------
|
|
|
|
- backend: base
|
|
name: memory_create
|
|
description: |
|
|
Create a new file (raw primitive — no LLM reasoning). Prefer
|
|
`ingest` when you want an LLM curator. Two gates run unless
|
|
`force=true`: (1) path template — must be `topics/{folder}/{name}.md`,
|
|
`events/{date}/{name}/...`, or `Archive/...`; (2) wikilink uniqueness
|
|
— `[[stem]]` must resolve to ≤1 path post-create.
|
|
parameters:
|
|
type: object
|
|
required: [path]
|
|
properties:
|
|
path: { type: string }
|
|
metadata: { type: object }
|
|
content: { type: string }
|
|
overwrite: { type: boolean, default: false }
|
|
force:
|
|
type: boolean
|
|
default: false
|
|
description: "bypass BOTH the path-template gate and the wikilink-uniqueness gate. Use only when you intentionally need a non-template path or accept the ambiguity."
|
|
steps:
|
|
- backend: memory_create
|
|
|
|
- backend: base
|
|
name: memory_update
|
|
description: |
|
|
Edit-style content update: replace `old_string` with `new_string`
|
|
in the file body. For frontmatter changes use `memory_property_update`.
|
|
parameters:
|
|
type: object
|
|
required: [path, old_string, new_string]
|
|
properties:
|
|
path: { type: string }
|
|
old_string: { type: string }
|
|
new_string: { type: string }
|
|
replace_all: { type: boolean, default: false }
|
|
steps:
|
|
- backend: memory_update
|
|
|
|
- backend: base
|
|
name: memory_property_update
|
|
description: |
|
|
Update a single YAML frontmatter key. value=null deletes the key.
|
|
When key='status', enforces the active → distilled → archived
|
|
single-direction state machine (skip / reverse refused). Pass
|
|
`force=true` to bypass.
|
|
parameters:
|
|
type: object
|
|
required: [path, key]
|
|
properties:
|
|
path: { type: string }
|
|
key: { type: string }
|
|
value: {}
|
|
force:
|
|
type: boolean
|
|
default: false
|
|
description: "bypass the status state machine (only meaningful when key='status')."
|
|
steps:
|
|
- backend: memory_property_update
|
|
|
|
- backend: base
|
|
name: memory_rename
|
|
description: |
|
|
Rename a file and rewrite all incoming `[[wikilink]]` references
|
|
across the vault. Refuses on destination conflict or stem ambiguity.
|
|
parameters:
|
|
type: object
|
|
required: [old_path, new_path]
|
|
properties:
|
|
old_path: { type: string }
|
|
new_path: { type: string }
|
|
steps:
|
|
- backend: memory_rename
|
|
|
|
- backend: base
|
|
name: memory_delete
|
|
description: "Delete a file."
|
|
parameters:
|
|
type: object
|
|
required: [path]
|
|
properties:
|
|
path: { type: string }
|
|
steps:
|
|
- backend: memory_delete
|
|
|
|
- backend: base
|
|
name: memory_archive
|
|
description: |
|
|
Archive a file: flip `status: archived` then move under
|
|
`<vault>/<archive_dir>/<original_relative_path>`.
|
|
parameters:
|
|
type: object
|
|
required: [path]
|
|
properties:
|
|
path: { type: string }
|
|
archive_dir: { type: string, default: "Archive" }
|
|
steps:
|
|
- backend: memory_archive
|
|
|
|
components:
|
|
# Ingestor LLM (opt-in). Without this the Ingestor degrades to a
|
|
# direct create from explicit `target_path`; edits/renames/deletes
|
|
# require the LLM. Uncomment + provide LLM_API_KEY to enable.
|
|
#
|
|
# as_llm:
|
|
# default:
|
|
# backend: openai
|
|
# model_name: ${LLM_MODEL_NAME:-gpt-4o-mini}
|
|
# api_key: ${LLM_API_KEY}
|
|
# client_kwargs:
|
|
# base_url: ${LLM_BASE_URL:-https://api.openai.com/v1}
|
|
# stream: false
|
|
#
|
|
# as_llm_formatter:
|
|
# default:
|
|
# backend: openai
|
|
|
|
as_token_counter:
|
|
default:
|
|
backend: estimated
|
|
|
|
# Embedding is opt-in: leave embedding_model="" on file_store to run
|
|
# keyword-only; uncomment + flip to "default" to enable hybrid search.
|
|
#
|
|
# embedding_model:
|
|
# default:
|
|
# backend: openai
|
|
# model_name: ${EMBEDDING_MODEL_NAME:-text-embedding-3-small}
|
|
# dimensions: 1536
|
|
# pass_dimensions: false
|
|
# enable_cache: true
|
|
# max_batch_size: 10
|
|
# max_cache_size: 2000
|
|
# max_input_length: 8192
|
|
|
|
file_parser:
|
|
md:
|
|
backend: md
|
|
default:
|
|
backend: text
|
|
|
|
file_store:
|
|
default:
|
|
backend: local
|
|
embedding_model: ""
|
|
store_name: "reme"
|
|
db_path: "./vault/.reme"
|
|
working_dir: "./vault"
|
|
fts_enabled: true
|
|
|
|
file_watcher:
|
|
default:
|
|
backend: full
|
|
file_store: default
|
|
default_parser: md
|
|
recursive: true
|
|
|
|
# Retriever (`hybrid`) is a Step, not a pre-instantiated component —
|
|
# the `memory_search` / `memory_graph_search` shells build it on
|
|
# demand. To tune defaults, pass knobs (`vector_weight`, `graph_*`,
|
|
# …) on the step config under each job below.
|