Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b683f8a
chore(memos-local-plugin): rename web/ to viewer/, drop site/ scaffol…
hijzy May 9, 2026
7f38988
chore: update logo name
hijzy May 9, 2026
c9f8aa9
chore: update website
hijzy May 9, 2026
26e7e3d
chore(memos-local-plugin): rename web/ to viewer/, drop unused site/ …
hijzy May 9, 2026
ff01087
fix(memos-local-plugin): infer embedding dimensions for rebuilds
hijzy May 12, 2026
9e7b542
fix(memos-local-plugin): infer embedding dimensions for rebuilds (#1682)
hijzy May 12, 2026
35f6c74
feat(memos-local-plugin): improve Windows installer with auto-detecti…
May 12, 2026
08513dc
fix(memos-local-plugin): fix hermes install path, encoding and log re…
May 12, 2026
d240453
feat: enhance memos-local-plugin Windows installation experience (#1684)
Hun-ger May 12, 2026
d4efb81
fix(memos-local-plugin): preserve OpenClaw memory context
hijzy May 12, 2026
fe76479
fix(memos-local-plugin): harden multi-agent memory handling
MatthewZhuang May 12, 2026
15fbf33
chore(memos-local-plugin): bump beta package version
MatthewZhuang May 12, 2026
2467d92
chore: remove temporary test file
MatthewZhuang May 12, 2026
4e706d2
chore(memos-local-plugin): gitignore telemetry.credentials.json
May 12, 2026
0fc8bea
fix(memos-local-plugin): close v2 ARMS telemetry coverage gaps
May 12, 2026
d3021a4
fix(memos-local-plugin): close v2 ARMS telemetry coverage gaps (#1690)
hijzy May 12, 2026
fda7450
fix(memos-local-plugin): improve Windows bridge launch
hijzy May 12, 2026
dc18cec
fix(memos-local-plugin): improve Windows bridge launch (#1706)
hijzy May 12, 2026
3eed583
fix: ghost overview memory
May 12, 2026
d720695
Merge remote-tracking branch 'origin/mem-agent-0509' into mem-agent-0…
May 12, 2026
0454d2f
fix: ghost overview memory (#1707)
whipser030 May 12, 2026
a3fd86c
fix: fix namespace
May 12, 2026
492fd61
fix: fix namespace (#1709)
whipser030 May 12, 2026
dc94163
fix: The reward score is always negative.
May 12, 2026
6e26081
Merge remote-tracking branch 'origin/mem-agent-0509' into mem-agent-0…
May 12, 2026
20e8532
fix: The reward score is always negative. (#1710)
whipser030 May 12, 2026
e68577e
fix(memos-local-plugin): preserve OpenClaw memory context (#1686)
hijzy May 12, 2026
1e0bc71
fix(memos-local-plugin): hide pager when filter empties tasks list
May 12, 2026
72c8582
merge: mem-agent-0509 into mem-agent-0512 (#1712)
syzsunshine219 May 12, 2026
47ea425
Merge pull request #1713 from MemTensor/mem-agent-0509-magent
May 12, 2026
7b80026
Merge pull request #1714 from MemTensor/bug/tasks-pager-empty-state-m…
May 12, 2026
164b209
chore(memos-local-plugin): bump version to 2.0.0-beta.13
May 12, 2026
acaa7d8
chore(memos-local-plugin): release 2.0.1
May 13, 2026
507ef0b
chore: update website
hijzy May 13, 2026
59cb5ef
chore: update website (#1720)
hijzy May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions apps/memos-local-plugin/.gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
node_modules/
dist/
web/dist/
site/dist/
viewer/dist/
coverage/

# Local builds & artifacts
Expand All @@ -18,3 +17,11 @@ coverage/
TODO.local.md
AGENTS_*.md
.test_*

# ARMS telemetry credentials — generated by CI from secrets before
# `npm publish` (see scripts/generate-telemetry-credentials.cjs and
# .github/workflows/hermes-plugin-publish.yml). Never commit a real
# endpoint/pid: any developer with a local copy must keep it out of
# git. Counters the repo-root `.gitignore`'s `!apps/**/*.json`
# allow-rule.
telemetry.credentials.json
3 changes: 0 additions & 3 deletions apps/memos-local-plugin/.npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ tests/
.notes/
TODO.local.md

# Site is local-only; never publish dist
site/dist/

# Editor / OS
.DS_Store
.vscode/
Expand Down
29 changes: 9 additions & 20 deletions apps/memos-local-plugin/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This document is the living blueprint for `@memtensor/memos-local-plugin`. It
covers the layering, the agent-agnostic core, the contract layer, the per-agent
adapters, the runtime services (server + bridge), the viewer, and the supporting
docs/site/test infrastructure.
docs/test infrastructure.

> If a module disagrees with this document, fix the document **or** the module.
> Don't let them drift.
Expand Down Expand Up @@ -74,12 +74,13 @@ docs/site/test infrastructure.
┌──────────────────┐ ┌──────────────────┐
│ server/ (HTTP) │ │ bridge.cts │
│ /api · /events │ │ JSON-RPC daemon │
│ serves web/dist│ │ used by Hermes │
│ serves viewer/ │ │ used by Hermes │
│ dist │ │ │
└────────┬─────────┘ └──────────────────┘
┌──────────────────────────┐
web/ (viewer)
viewer/
│ Overview · Traces · … │
│ Logs · Settings · … │
└──────────────────────────┘
Expand Down Expand Up @@ -151,7 +152,6 @@ GET /api/skills list + lifecycle
POST /api/feedback explicit user feedback
GET /api/retrieval/preview run a tier1+2+3 retrieval against an arbitrary query
GET /api/hub/* team-sharing surface
GET /api/changelog lists site/content/releases/*.md (read-only)
GET /api/logs/tail channelled, paginated, with `?level=&channel=&limit=`
GET /events SSE: every CoreEvent + every log line (after redact)
```
Expand Down Expand Up @@ -185,7 +185,7 @@ Python package. Implements Hermes' `MemoryProvider` interface and proxies to
- `memos_provider/log_forwarder.py` — forward Python-side logs back over the
bridge so everything ends up in the same `logs/` directory.

### 3.7 `web/`
### 3.7 `viewer/`

Vite app, served at runtime by `server/static.ts`. Ten views map 1:1 to the
algorithm's observable surface:
Expand All @@ -203,26 +203,17 @@ algorithm's observable surface:
| Logs | Channelled, level-filtered, real-time + tail |
| Settings | Config editor (writes back to `config.yaml`) |

### 3.8 `site/`

Local-only static site (Vite, separate config). Hosts:

- The product landing page.
- User-facing docs (`site/content/docs/*.md`).
- All published release notes (`site/content/releases/<version>.md`), indexed
by `site/scripts/build-index.ts`, gated by `release:check` in CI.

### 3.9 `templates/`
### 3.8 `templates/`

Plain files copied — never edited at runtime — by `install.sh`:

- `config.openclaw.yaml`
- `config.hermes.yaml`
- `README.user.md`

### 3.10 `docs/`
### 3.9 `docs/`

Developer-facing docs that are too detailed for the marketing site:
Developer-facing docs:

- `ALGORITHM.md` — the V7 spec, restated and indexed against the code.
- `DATA-MODEL.md` — every table, every column, every index.
Expand Down Expand Up @@ -396,9 +387,7 @@ Common helpers:
## 7. Release & versioning

- SemVer.
- Every published version requires a `site/content/releases/<version>.md`
(enforced by `npm run release:check`, run in CI).
- `CHANGELOG.md` at the project root is regenerated from those files.
- `CHANGELOG.md` at the project root is hand-maintained per release.
- `core/update-check/` lets the running plugin notify users when a newer npm
version is available.

Expand Down
15 changes: 7 additions & 8 deletions apps/memos-local-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# Changelog

All notable changes to `@memtensor/memos-local-plugin` are documented per
release in [`site/content/releases/`](./site/content/releases/). This file is
regenerated from those release notes by `npm run release:index`.

> Do **not** edit this file by hand. Edit the per-version markdown in
> `site/content/releases/<version>.md` instead.
Notable changes to `@memtensor/memos-local-plugin`. Maintained by hand;
for the full per-commit history use `git log` or the GitHub releases page.

## Index

- [`2.0.0-beta.1`](./site/content/releases/2.0.0-beta.1.md) — Complete end-to-end implementation: L1/L2/L3/Skill layers, three-tier retrieval, decision repair, crystallization, dual adapters, HTTP/SSE server, Vite viewer & product site.
- [`2.0.0-alpha.1`](./site/content/releases/2.0.0-alpha.1.md) — Project skeleton, agent-contract layer, install.sh entrypoint, viewer/site directory layout.
- `2.0.0-beta.1` — Complete end-to-end implementation: L1/L2/L3/Skill layers,
three-tier retrieval, decision repair, crystallization, dual adapters,
HTTP/SSE server, Vite viewer.
- `2.0.0-alpha.1` — Project skeleton, agent-contract layer, install.sh
entrypoint, viewer directory layout.
3 changes: 1 addition & 2 deletions apps/memos-local-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ apps/memos-local-plugin/
├── adapters/openclaw/ # In-process TS adapter for OpenClaw
├── adapters/hermes/ # Python adapter that talks to bridge.cts
├── templates/ # config.yaml templates copied to the user's home on install
├── web/ # Runtime viewer (Vite, served by server/)
├── site/ # Local-only marketing site + docs + release notes
├── viewer/ # Runtime viewer (Vite, served by server/)
├── docs/ # Developer-facing docs (algorithm, data model, prompts, …)
├── scripts/ # Build / packaging / release helpers
└── tests/ # unit / integration / e2e (vitest)
Expand Down
7 changes: 3 additions & 4 deletions apps/memos-local-plugin/adapters/hermes/install.hermes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
# extras:
#
# 1. Install node_modules inside $PREFIX (idempotent).
# 2. Build the viewer + site bundles so the HTTP server has static
# assets available.
# 2. Build the viewer bundle so the HTTP server has static assets
# available.
# 3. Symlink the Python memos_provider package into the Hermes
# plugins directory so `from memos_provider import MemTensorProvider`
# resolves from Hermes without extra path munging.
Expand Down Expand Up @@ -41,7 +41,7 @@ fi

# ── 2. viewer bundle ──────────────────────────────────────────────────────────
if [[ -x "./node_modules/.bin/vite" ]]; then
log "Building viewer bundle → web/dist/"
log "Building viewer bundle → viewer/dist/"
./node_modules/.bin/vite build --config vite.config.ts >/dev/null
else
warn "vite not found in node_modules; skipping bundle build"
Expand All @@ -64,4 +64,3 @@ log "Hermes adapter install complete."
log " Plugin code: $PREFIX"
log " Runtime data: $HOME_DIR"
log " Viewer: http://127.0.0.1:18910/"
log " Site: http://127.0.0.1:18910/site/"
109 changes: 92 additions & 17 deletions apps/memos-local-plugin/adapters/hermes/memos_provider/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,24 @@ def _runtime_namespace(self) -> dict[str, Any]:
"profileLabel": profile_id,
}

def _record_namespace(self) -> dict[str, Any]:
"""Namespace used for write-path records.

Hermes delegation hooks can be global and occasionally arrive through
a provider instance whose `profileId` fell back to `default` while
`agent_identity` still carries the real profile label (for example
coder10). For writes, prefer the concrete non-default label so
subagent outcome traces inherit the parent profile instead of leaking
into hermes/default.
"""
ns = dict(self._runtime_namespace())
label = (self._agent_identity or ns.get("profileLabel") or "").strip()
profile_id = str(ns.get("profileId") or "").strip()
if profile_id in ("", "default", "hermes") and label and label not in ("default", "hermes"):
ns["profileId"] = label
ns["profileLabel"] = label
return ns

def _register_tool_call_hook(self) -> None:
if self._hook_registered:
return
Expand Down Expand Up @@ -818,20 +836,25 @@ def sync_turn(
len(thinking),
)
ts_ms = int(time.time() * 1000)
is_feedback_turn = _is_verifier_feedback_prompt(user)
feedback_submitted = False
try:
if user and not self._episode_id:
self._turn_start(user, session_id=session_id or self._session_id)
self._turn_end(
current_trace_id = self._turn_end(
user,
assistant,
tool_calls,
ts_ms,
agent_thinking=thinking,
)
if _is_verifier_feedback_prompt(user):
self._submit_verifier_feedback(user, assistant, ts_ms)
feedback_submitted = True
if is_feedback_turn:
feedback_submitted = self._try_submit_verifier_feedback(
user,
assistant,
ts_ms,
trace_id=current_trace_id,
)
except Exception as err:
if not self._is_transport_closed(err):
logger.warning("MemOS: sync_turn turn.end failed — %s", err)
Expand All @@ -845,21 +868,36 @@ def sync_turn(
self._reconnect_bridge(session_id or self._session_id, timeout=75.0)
if user:
self._turn_start(user, session_id=session_id or self._session_id)
self._turn_end(
current_trace_id = self._turn_end(
user,
assistant,
tool_calls,
ts_ms,
agent_thinking=thinking,
)
if _is_verifier_feedback_prompt(user) and not feedback_submitted:
self._submit_verifier_feedback(user, assistant, ts_ms)
feedback_submitted = True
if is_feedback_turn and not feedback_submitted:
feedback_submitted = self._try_submit_verifier_feedback(
user,
assistant,
ts_ms,
trace_id=current_trace_id,
)
except Exception:
logger.exception(
"MemOS: sync_turn failed after bridge reconnect; "
"memory turn was not persisted"
)
if is_feedback_turn and not feedback_submitted:
# turn.end may time out while the bridge continues lite capture in
# the background. Preserve the user's explicit signal at episode
# scope instead of dropping Decision Repair entirely.
self._try_submit_verifier_feedback(
user,
assistant,
ts_ms,
trace_id="",
fallback=True,
)
if user_content:
self._last_user_text = user_content

Expand All @@ -883,12 +921,16 @@ def on_delegation(
try:
if not self._episode_id and self._last_user_text:
self._turn_start(self._last_user_text, session_id=self._session_id)
namespace = self._record_namespace()
hook_meta = {
"hookKwargs": kwargs,
"namespace": namespace,
}
self._bridge.request(
"subagent.record",
{
"agent": "hermes",
"namespace": namespace,
"sessionId": self._session_id,
"episodeId": self._episode_id or None,
"childSessionId": child_session_id or None,
Expand All @@ -897,6 +939,11 @@ def on_delegation(
"toolCalls": self._extract_child_tool_calls(child_session_id),
"ts": int(time.time() * 1000),
"meta": hook_meta,
"contextHints": {
"agentIdentity": self._agent_identity,
"namespace": namespace,
**self._host_runtime_context(),
},
},
)
except Exception as err:
Expand Down Expand Up @@ -1684,9 +1731,9 @@ def _turn_end(
ts_ms: int,
*,
agent_thinking: str = "",
) -> None:
) -> str:
if not self._bridge:
return
return ""
# Strip private book-keeping fields before sending.
clean_tool_calls = [
{k: v for k, v in tc.items() if k not in {"_id", "_ids"}} for tc in tool_calls
Expand All @@ -1713,16 +1760,44 @@ def _turn_end(
if result and isinstance(result, dict):
trace_ids = result.get("traceIds", [])
if trace_ids and len(trace_ids) > 0:
self._last_trace_id = trace_ids[-1] # Last trace is the current turn
trace_id = trace_ids[-1] # Last trace is the current turn
self._last_trace_id = trace_id
return trace_id
return ""

def _try_submit_verifier_feedback(
self,
user_content: str,
assistant_content: str,
ts_ms: int,
*,
trace_id: str = "",
fallback: bool = False,
) -> bool:
try:
submitted = self._submit_verifier_feedback(
user_content,
assistant_content,
ts_ms,
trace_id=trace_id,
)
if submitted and fallback:
logger.info("MemOS: submitted verifier feedback without trace binding")
return submitted
except Exception as err:
logger.warning("MemOS: verifier feedback submit failed — %s", err)
return False

def _submit_verifier_feedback(
self,
user_content: str,
assistant_content: str,
ts_ms: int,
) -> None:
*,
trace_id: str = "",
) -> bool:
if not self._bridge or not self._episode_id:
return
return False
polarity = _feedback_polarity(user_content)
magnitude = _feedback_magnitude(user_content, polarity)
raw = {
Expand All @@ -1740,10 +1815,10 @@ def _submit_verifier_feedback(
"raw": raw,
"ts": ts_ms,
}
# Include the last trace ID if available
if self._last_trace_id:
payload["traceId"] = self._last_trace_id
self._bridge.request("feedback.submit", payload)
if trace_id:
payload["traceId"] = trace_id
self._bridge.request("feedback.submit", payload, timeout=75.0)
return True


# ─── Discovery entry points ───────────────────────────────────────────────
Expand Down
Loading
Loading