Extension Center
Find extensions by tag or choose Official releases. Review the source and install an approved version.
Explore extensions
8 extensions
spel
Officialblockether · v0.1.21
Spel browser automation with verified installation, reserved sessions, CDP, JavaScript, snapshots and screenshots.
vis-decisions
Officialblockether · v0.6.1
Train, resume, validate and publish Laya, GLiNER, Decision 2.0 and D3 decision models in Vis.
vis-graphify
blockether · v0.15.3
Code knowledge graphs for Vis with graphify: search, change impact, exact references, safe renames and moves, domain flows, and a knowledge base server with ontology, documents and undoable edits.
vis-lang-clojure
Officialblockether · v1.17.3
Clojure tools for Vis: zprint and cljfmt formatting, clj-kondo lint, reader syntax checks, test runs and an nREPL.
vis-lang-interface
Officialblockether · v2.11.3
The shared contract for Vis language extensions.
vis-lang-python
Officialblockether · v1.14.0
Python tools for Vis: ruff formatting and lint, pytest runs and a managed project REPL.
vis-optmem
Officialblockether · v0.6.0
Permanent memory for Vis agents, based on OptMem by Victor Taelin.
vis-podcaster
blockether · v0.1.0
Two-host podcasts with Vis speech engines, ASR-verified takes and quality gates.
blockether/vis-optmem
OfficialPermanent memory for Vis agents, based on OptMem by Victor Taelin.
Install
Only approved releases are listed. Each version keeps its reviewed commit. You are viewing a different version from the catalog default.
Approved releases (8)
- 0.6.0Oct 23, 2026e7f1232f4001Release notes
- 0.5.3Oct 16, 20262d3ef2b5655bRelease notes
- 0.5.2Oct 14, 20263b6dc66c76d0Release notes
- 0.5.1Oct 14, 20269e05a8918750Release notes
- 0.4.0Oct 9, 2026984b5932575eRelease notes
- 0.3.0Oct 8, 202639e3f8b39245Release notes
- 0.1.1Oct 8, 20268bb89de7d4f4Release notes
- 0.1.0Oct 4, 2026998ec0c40f80Release notes
This command selects version 0.5.1 at its approved commit. It never follows a moving tag or branch.
Project: run from your project directory to install in .vis/extensions. This command does not add an extensions entry to vis.yml. Add --save to also record the installation. Save an installation in configuration to share the setup.
Review the source first. --trust allows extension code and build backends to run with your permissions.
vis-agent extension install \
'blockether/vis-optmem' \
--version '0.5.1' \
--project \
--trustThen start Vis or use /reload. Dependencies are prepared automatically.
Already installed? Check, update or roll back
vis-agent extension versions 'blockether/vis-optmem'
vis-agent extension update 'blockether/vis-optmem' --trust
vis-agent extension rollback 'blockether/vis-optmem' --trustUse the identifier shown above, not the Python package name. Add --project for a project installation. Update selects the latest approved stable version; rollback restores the previous pinned source. Choose a specific version and read the lifecycle contract.
Project details
- Version
- 0.5.1
- Extension
- blockether/vis-optmem
- Project folder
- Repository root
- License
- Apache-2.0
- GitHub stars
- 0
- Downloads
- 32
- Updated
- Oct 14, 2026
- GitHub owner
- Blockether
- Reviewed commit
- 9e05a8918750
- Metadata checked
- Oct 14, 2026
Dependencies
Python >=3.11
- vis-agent>=0.2.26
README
vis-optmem
Permanent memory for Vis agents. Every session on your computer reads and writes the same personal memory. So an agent keeps your decisions, the results of its work and what you teach it from one session to the next. A second memory can keep the facts that your whole team shares.
vis-optmem brings OptMem by Victor Taelin to Vis as typed Python functions. It uses the same design and the same files. See Credits.
Install
vis-agent extension install 'blockether/vis-optmem' --global --trust
The first tool call creates the memory folder, so you do not need memo.init(). After that, each
session reads its memory before other work and saves new facts as it works.
Tools
memo.init() # create the memory folder
memo.wake() # read the memory: new memories in full, old ones as summaries
memo.note("The user prefers uv.") # save one memory: one line of at most 280 bytes
memo.nap("0-1", "<one line>") # save a summary that a result asks for
memo.recall(r"uv|poetry") # search every memory with a regular expression
memo.recall(about="Python packaging") # find related memories by meaning
memo.recall(r"uv", scope="all") # search both memories in one call
memo.zoom("0-15") # open a summary into its two halves
memo.forget("0-15") # drop a wrong summary; memo asks for it again
memo.config({"WAKE_LINES": 128}) # show or change the sizes
memo.import_memories("~/old.txt") # add dated lines, each "YYYY-MM-DD text"
Each tool takes scope="personal", the default, or scope="team". memo.recall() also
takes scope="all", which searches both memories. See
Personal and team memory.
Each result is a typed record with a text field. The agent prints the text and does what it says.
A subagent does not use the memory. Its leader reads and writes it.
Turn tools off
To stop agents from using some tools, list them in MEMORY_DISABLED_TOOLS, separated by commas.
A name can have the memo. prefix that the agent instructions show, for example memo.init.
For example, a company memory that an administrator creates and fills needs no init and no
import_memories:
environment:
MEMORY_DISABLED_TOOLS: {literal: "init,import_memories"}
A tool that is turned off refuses each call, and the agent instructions leave it out. The other
tools work as before. A name that is not a memo tool stops every tool, so a typo cannot leave a
tool on. In Python, Memo(disabled=["init"]) replaces the setting.
How the memory works
- Each memory is one dated line. A memory never changes.
- Each two neighboring memories get a one-line summary. Then each two neighboring summaries get a summary, and so on. The agent writes a summary when a result asks for it.
memo.wake()shows the whole memory in a fixed number of lines, 96 by default. New memories show in full. Older periods show as summaries, and the older the period, the larger its summary.memo.zoom()opens a summary when the agent needs its detail.memo.recall()searches the original memories.memo.note()refuses a fact that the memory already holds, because a saved memory never changes andmemo.forget()drops only summaries. The store'sduplicate(text)decides what a repeat is. By default, a note is a repeat when it has the same numbers as one of the closest memories fromsearchand shares at least 60% of its words. The result shows that memory. To save a different fact that looks similar, giveforce=True.memo.note(),memo.nap()andmemo.import_memories()refuse a line that seems to hold a secret or has profanity, in every memory and every store. The secret rules find private keys, JSON Web Tokens, AWS, GitHub, GitLab, Slack, Google, OpenAI, Anthropic, Stripe, npm and Hugging Face tokens,BearerandBasicvalues, tokens and passwords in URLs, and random values after words likepassword=ortoken:. The error names the kind of problem and does not repeat the value.force=Truedoes not skip this check.- The agent prompt asks for memories and searches in English and in professional language, with the task key, the environment, the repository and the short commit SHA when they apply.
Where the memory is
The memory folder is $MEMORY_DIR, else ~/.optmem/memory. To use another folder, set
MEMORY_DIR before you start Vis.
A relative MEMORY_DIR is a folder in the session workspace. To keep the memory in the
project, put it in the shared project vis.yml:
environment:
MEMORY_DIR: {literal: "memory"}
Each person then gets <workspace>/memory. An absolute path or a ~ path does not change.
The files are the same as those of the OptMem memo command, so both tools can use one memory.
A lock file keeps the writes of all sessions and processes in order. Do not edit the files by hand.
Personal and team memory
vis-optmem has two memories:
- personal: your own memory. The tools use it when you give no
scope. - team: one memory that every person on your team shares. It keeps general knowledge that stays useful in other projects: best practices, reusable solutions, pitfalls with their fixes and team conventions.
The two memories never mix. Each has its own memories and its own summaries, so a summary of the team memory never contains a personal note. The agent saves to the team memory only facts that every person may read, and keeps project details and facts about you personal. It writes each team memory so that it stands alone and a search finds it.
The agent reads only the personal memory at the start of a session. It does not read the team
memory: before it plans a task, it searches both memories with memo.recall(scope="all"). So a
large team memory costs no tokens at the start. For the same reason, a team note does not ask
the agent for a summary. Summaries matter only for memo.wake() and memo.zoom(). If you want
them for the team memory, let a scheduled job write them with memo.nap(scope="team").
The team memory has no default place. To enable it, set one of these before you start Vis:
MEMORY_TEAM_DIR: a folder that every person reaches. A relative path is in the session workspace. It must not be the personal memory folder.MEMORY_TEAM_STOREandMEMORY_TEAM_STORE_CONFIG: a custom store, as in Keep the memory in another place.
Then create it once:
memo.init(scope="team")
memo.note("The team uses uv, not Poetry.", scope="team")
In a session, session["memo"]["team"] shows the count of team memories, or
{"store": "unset"} when no team memory is set. In Python, use
Memo(team_directory=...) or Memo(team_store=...).
Keep the memory in another place
To share one memory between computers or people, keep it in a database or another service instead of a folder. Write a store for that place, then select it with two settings.
Subclass
vis_optmem.MemoryStoreand implement its methods. The memory, its summaries and its sizes all go through them:Method What it does locationName the place for people, for example a table exists(),create()Check for the memory, or create it count(),entries(lo, hi)Read the memories append(items)Add memories and give their ids level_count(size),summary(lo, hi)Read the summaries put_summary(lo, hi, text)Save the next summary of a level drop_summaries(lo, hi)Remove a summary and the summaries after it overrides(),write_overrides(overrides)Read and write the sizes search(query, limit), optionalFind related memories for memo.recall(about=)duplicate(text), optionalFind the memory that a new note repeats matches(pattern, newest), optionalFind memories for memo.recall(pattern)Make each write atomic, because many sessions write at the same time. The docstrings of
MemoryStoregive the exact contract.Set
MEMORY_STOREto the store, andMEMORY_STORE_CONFIGto a JSON object. vis-optmem calls the store with the keys of that object as keyword arguments.
examples/sqlite_store.py is a complete store for one SQL
database. For a database on Amazon RDS, keep its tables and transactions and change the
connection. To use the example as it is, put it in your project and add this to vis.yml:
environment:
MEMORY_STORE: {literal: "tools/sqlite_store.py:SqliteStore"}
MEMORY_STORE_CONFIG: {literal: '{"path": "~/team-memory.db"}'}
MEMORY_STORE takes one of these forms:
path/to/store.py:Name: a Python file. A relative path is in the session workspace.module:Name: a module that Python can import.name: a store that a package installs in thevis_optmem.storesentry point group.
Name is a MemoryStore subclass or a function that returns a store. vis-optmem runs this
code in your Vis session, so select only code that you trust. If the store cannot load or
connect, the session shows the error in session["memo"], and the memo tools explain it.
Keep the memory in a service
vis_optmem.remote.RemoteStore keeps a memory in an HTTP service, for example an AWS Lambda
function. Each store call is one JSON request, so the service does the search and the repeat
check where the memories are. This team memory uses such a service:
environment:
MEMORY_TEAM_STORE: {literal: "vis_optmem.remote:RemoteStore"}
MEMORY_TEAM_STORE_CONFIG: {literal: '{"url": "https://memory.example.com/", "memory": "team"}'}
MEMORY_STORE_TOKEN: {env: MEMORY_STORE_TOKEN}
To build the service, implement a MemoryStore in it and serve it with
vis_optmem.remote.lambda_response(event, stores, token). The module docstring of
vis_optmem.remote gives the protocol. tests/test_remote.py runs every memo tool through it.
Keep the memory in Cloudflare
cloudflare/ is a complete memory service for Cloudflare Workers. It serves
the protocol of vis_optmem.remote, so the client side is RemoteStore with no other code.
- Each memory is a Durable Object with its own SQLite database. A Durable Object runs one write at a time, so each write is atomic without retries.
- Workers AI embeds each memory with
@cf/baai/bge-m3, a multilingual model. Somemo.recall(about=...)finds memories by meaning, also in other languages. memo.note()refuses a repeat when two conditions are true: the embeddings are close (cosine of at least 0.85), and both lines have the same names. Names are numbers, paths, flags, CamelCase and ALL-CAPS words, environment words such asstagingandproduction, and capitalized names. So "port 5432" and "port 5433" stay two memories.- A password protects the service. The Worker keeps it in the secret
MEMORY_STORE_TOKEN. Each person keeps the same value in the environment variableMEMORY_STORE_TOKEN, never invis.yml.
To deploy it, you need a Cloudflare account and Node.js. Run these commands in cloudflare/:
npx wrangler login
export MEMORY_STORE_TOKEN="$(openssl rand -base64 32)" # keep it in your password manager
printf %s "$MEMORY_STORE_TOKEN" | npx wrangler secret put MEMORY_STORE_TOKEN
npx wrangler deploy
Wrangler shows the URL of the service, for example https://vis-optmem.example.workers.dev.
Give the URL and the password to your team. Each person sets MEMORY_STORE_TOKEN in the
shell profile before Vis starts, and the project vis.yml selects the service:
environment:
MEMORY_TEAM_STORE: {literal: "vis_optmem.remote:RemoteStore"}
MEMORY_TEAM_STORE_CONFIG: {literal: '{"url": "https://vis-optmem.example.workers.dev/", "memory": "team"}'}
MEMORY_STORE_TOKEN: {env: MEMORY_STORE_TOKEN}
Then one person creates the memory with memo.init(scope="team").
To change the password, put a new secret with npx wrangler secret put MEMORY_STORE_TOKEN
and give the new value to your team. The old value stops at once.
Change these settings in vars of cloudflare/wrangler.jsonc:
| Name | Default | Meaning |
|---|---|---|
MEMORIES |
team |
Memory names that the service keeps, or * for all |
EMBEDDING_MODEL |
@cf/baai/bge-m3 |
Workers AI model; a change embeds every memory again |
SEARCH_MIN |
0.5 |
Lowest cosine that memo.recall(about=...) shows |
DUPLICATE_MIN |
0.85 |
Lowest cosine of a repeat that memo.note() refuses |
The thresholds fit bge-m3. If you change the model, check them again with your own pairs of
repeats and near misses.
Costs and limits:
- Each note, search and repeat check makes one Workers AI call. Workers AI has a free daily allocation, and Cloudflare bills the use above it.
- A Durable Object keeps the vectors of its memory in memory: 4 KB for each memory. So one memory holds about 20,000 memories.
To test a change, run npm test in cloudflare/. To run every memo tool against the Worker,
start npx wrangler dev and follow the docstring of tests/test_cloudflare.py.
The store runs in the Python environment of vis-optmem. That environment has the standard
library and the packages that vis-optmem declares, not the shared Vis packages. So write the
store with the standard library: sqlite3 for a local file, or urllib.request for an HTTP
service, for example an API in front of your database.
In Python, give the store directly: Memo(store=MyStore(...)).
Search by meaning
memo.recall() searches in two ways, alone or together in one call:
pattern: a regular expression that matches the words of a memory exactly.about: a query in plain words. The result lists up to 10 related memories, best first.
memo.recall(r"postgres|migration", about="change a database schema safely", scope="all")
With scope="all", each line starts with its memory, for example [team] #3 2025-01-07 ...,
because both memories number their memories from 0. The related memories take the best of each
memory in turn. If no team memory is set, the search uses only the personal memory and
says so.
The store decides what "related" means. The default search ranks memories by the words that
they share with the query, and rare words count more. Words match when their first 6 letters are
the same, so "migrate" finds "migrations". It does not know synonyms or other languages.
For a real search by meaning, override search(query, limit) in your store. For example, an
HTTP service on AWS can keep an embedding of each memory and return the nearest ones. The agent
calls the same memo.recall(about=...) in both cases.
The default repeat check of memo.note() compares words, so it misses the same fact in other
words. To catch those repeats, also override duplicate(text). Return the memory that already
says the fact, or None to save the note:
class ServiceStore(MemoryStore):
def duplicate(self, text: str) -> Entry | None:
nearest = self.client.nearest(text, limit=1) # your embedding service
if nearest and nearest[0].score >= 0.92:
return nearest[0].entry
return None
Sizes
Change a size with memo.config({"NAME": value}). Use None as the value to go back to the
default.
| Name | Default | Meaning |
|---|---|---|
WAKE_LINES |
96 | Lines that memo.wake() shows |
ENTRY_CHARS |
280 | Longest memory or summary, in bytes (at most 280) |
PART_CHARS |
20000 | Largest part of one result, in bytes |
PART_LINES |
500 | Largest part of one result, in lines |
In Vis, one part holds at most 8000 bytes, so that it fits into one print.
Development
vis-agent python -m pytest tests -q
Credits
The design and the file format come from OptMem by Victor Taelin: the dated memory log, the tree of summaries and the fixed-size view. vis-optmem is an independent Python implementation for Vis. It contains no OptMem code. See NOTICE.
License
Apache License 2.0. See LICENSE.
Community feedback
Votes and display names are not verified identities. One vote per network address; shared networks share a vote. Select your current choice again to clear it.
Loading feedback…