Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Each directory is a standalone integration. Pick the one that matches your tool.
| **[Windsurf Trajectory Extractor](https://github.com/jijiamoer/windsurf-trajectory-extractor)** | `git clone https://github.com/jijiamoer/windsurf-trajectory-extractor.git` | Offline protobuf extraction for Windsurf Cascade conversation history. |
| **[Cursor Plugin](nowledge-mem-cursor-plugin)** | Link `nowledge-mem-cursor-plugin` into `~/.cursor/plugins/local/nowledge-mem-cursor` | Cursor-native plugin package with session-start context guidance, bundled MCP config, rules, and honest `save-handoff` semantics. |
| **[Codex Plugin](nowledge-mem-codex-plugin)** | `codex plugin marketplace add nowledge-co/community --sparse .agents --sparse nowledge-mem-codex-plugin` then `codex plugin add nowledge-mem@nowledge-community`; enable `plugins` and `hooks`, run setup, then trust the hooks when Codex asks | Native startup context and memory routing, bundled MCP retrieval and writes, plus Stop-hook capture of real Codex sessions. Coexists cleanly with Codex local Memory. |
| **Raft (formerly Slock)** | Configure per-worker environment variables in Raft runtime config | Install the child runtime connector, set `NMEM_AGENT_ID=<agent-slug>` per worker, and use Mem skills/MCP inside Raft. Raft-managed Codex rollouts are execution traces and are excluded from Codex Thread capture. |
| **Raft** | Configure per-worker environment variables in Raft runtime config | Install the child runtime connector, set `NMEM_AGENT_ID=<agent-slug>` per worker, and use Mem skills/MCP inside Raft. Raft-managed Codex rollouts are execution traces and are excluded from Codex Thread capture. |
| **Lody** | Configure the child runtime in Lody Agent Config | Runtime launcher setup: install the child runtime connector first; set `NMEM_AGENT_ID=<agent-slug>` only when that Agent Config represents a stable role. |
| **Multica** | Configure the Multica agent MCP settings and custom environment | Multi-agent orchestrator setup: install the child runtime connector first; for Claude Code agents, save the Mem MCP config on the Multica agent; then set `NMEM_AGENT_ID=<agent-slug>` in custom env. |
| **Cumora** | Configure the child runtime plus each teammate persona | AI teammate workspace setup: connect Mem at the runtime/daemon boundary, then use a per-persona Context Bundle instruction unless Cumora exposes per-agent runtime environment variables. |
Expand Down
11 changes: 6 additions & 5 deletions integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"claude-code", "grok", "codex-cli", "cursor", "gemini-cli", "copilot-cli",
"openclaw", "hermes", "droid", "alma", "bub", "pi", "opencode", "craft-agent", "workbuddy", "codebuddy",
"kimi-code", "kimi-work", "zcode", "mimo-code", "omp", "claude-desktop", "proma",
"slock", "lody", "multica", "cumora", "paseo", "mcp-direct"
"raft", "lody", "multica", "cumora", "paseo", "mcp-direct"
],
"doesNotApplyTo": ["browser-extension", "raycast", "antigravity-extractor", "windsurf-extractor"]
},
Expand Down Expand Up @@ -284,8 +284,9 @@
"slashCommands": []
},
{
"id": "slock",
"name": "Raft (formerly Slock)",
"id": "raft",
"aliases": ["slock"],
"name": "Raft",
"category": "orchestrator",
"type": "orchestrator",
"version": null,
Expand Down Expand Up @@ -318,12 +319,12 @@
},
"install": {
"command": "Connect the AI tool Raft launches, then set NMEM_AGENT_ID=<agent-slug> in each Raft agent runtime config.",
"detectionHint": "Running inside a Raft-managed child agent (legacy originator: slock-daemon)",
"detectionHint": "Running inside a Raft-managed child agent (originator: raft-daemon; legacy alias: slock-daemon)",
"agentGuide": {
"prompt": "Read https://mem.nowledge.co/SKILL.md and follow the Raft path to configure this worker. Connect the AI tool Raft launches first, then set NMEM_AGENT_ID=<agent-slug>; add NMEM_SPACE only if this whole worker should override its default space. Use Mem skills or MCP inside Raft. Do not save the managed Codex rollout as the Raft conversation. Verify with nmem status and the Context Bundle or Working Memory check, then summarize what changed.",
"promptZh": "读取 https://mem.nowledge.co/SKILL.md,并按 Raft 路径配置这个 worker。先连接 Raft 启动的 AI 工具,再设置 NMEM_AGENT_ID=<agent-slug>;只有当整个 worker 需要覆盖默认 Space 时,才添加 NMEM_SPACE。在 Raft 内使用 Mem skills 或 MCP,不要把托管 Codex rollout 当作 Raft 对话保存。用 nmem status 和 Context Bundle 或 Working Memory 检查验证,然后总结你改了什么。"
},
"docsUrl": "/docs/integrations/slock"
"docsUrl": "/docs/integrations/raft"
},
"toolNaming": {
"convention": "mediated-host",
Expand Down
2 changes: 1 addition & 1 deletion nowledge-mem-codex-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ This enables Codex lifecycle hooks, adds the legacy plugin-hook gate only on hos

Restart Codex after setup. Codex treats **enabled** and **trusted** as separate hook states: review and trust the three Nowledge Mem hooks when Codex prompts you. This confirmation is deliberately user-owned; the installer never bypasses Codex's hook security boundary.

Raft (formerly Slock) can run a managed Codex worker on the same computer as your normal Codex sessions. The hook reads Codex's structured `session_meta.originator` for each transcript: normal Codex sessions still capture automatically, while `slock-daemon` and `raft-daemon` rollouts are skipped because they contain Raft inbox/control traffic rather than the human-visible conversation. Memory skills and MCP remain available inside the Raft worker.
Raft can run a managed Codex worker on the same computer as your normal Codex sessions. The hook reads Codex's structured `session_meta.originator` for each transcript: normal Codex sessions still capture automatically, while `raft-daemon` rollouts are skipped because they contain Raft inbox/control traffic rather than the human-visible conversation. The legacy `slock-daemon` originator remains supported. Memory skills and MCP remain available inside the Raft worker.

To backfill older Codex sessions, preview first:

Expand Down
2 changes: 1 addition & 1 deletion nowledge-mem-codex-plugin/hooks/nmem-stop-save.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"unexpected argument '--json'",
)
CODEX_HOOK_SUCCESS_RESPONSE = {"continue": True, "suppressOutput": True}
DELEGATED_CONVERSATION_ORIGINATORS = frozenset({"slock-daemon", "raft-daemon"})
DELEGATED_CONVERSATION_ORIGINATORS = frozenset({"raft-daemon", "slock-daemon"})
MAX_SESSION_META_BYTES = 256 * 1024

_SCRIPT_DIR = Path(__file__).resolve().parent
Expand Down
4 changes: 2 additions & 2 deletions nowledge-mem-codex-plugin/tests/test_codex_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ def _write_session_meta(self, originator: str) -> Path:
return transcript

def test_delegated_conversation_originator_is_session_scoped(self):
for originator in ("slock-daemon", "raft-daemon"):
for originator in ("raft-daemon", "slock-daemon"):
transcript = self._write_session_meta(originator)
self.assertEqual(
self.module._delegated_conversation_originator(
Expand All @@ -352,7 +352,7 @@ def test_delegated_conversation_originator_is_session_scoped(self):
)

def test_main_skips_only_delegated_thread_capture_but_reports_skills(self):
transcript = self._write_session_meta("slock-daemon")
transcript = self._write_session_meta("raft-daemon")
hook_payload = json.dumps(
{
"session_id": "session-1",
Expand Down
5 changes: 3 additions & 2 deletions shared/behavioral-guidance.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ NMEM_AGENT_ID="reviewer"
`NMEM_AGENT_ID` selects a Nowledge AI Identity directly. Add `NMEM_SPACE` only
when the whole child process should override that identity's default space.
`NMEM_HOST_AGENT_ID` is an advanced external-alias field for integration authors
who need to map an immutable external worker id such as `slock:<uuid>` onto a
Mem AI Identity. Do not ask normal users to set both identity variables; if
who need to map an immutable external worker ID such as `raft:<uuid>` onto a
Mem AI Identity. Existing integrations may still provide the legacy
`slock:<uuid>` alias. Do not ask normal users to set both identity variables; if
every child gets the same value, all agents will collapse into one profile.

**When to use Context Bundle:**
Expand Down
13 changes: 8 additions & 5 deletions tests/plugin_e2e/test_key_plugins_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,7 @@ def log_message(self, format: str, *args: Any) -> None:
env["NMEM_API_URL"] = f"http://127.0.0.1:{server.server_address[1]}"
env["NMEM_SPACE"] = "pi-history-space"
env["NMEM_AGENT_ID"] = "PiHistoryAgent"
env["NMEM_HOST_AGENT_ID"] = "slock:PiHistoryAgent"
env["NMEM_HOST_AGENT_ID"] = "raft:PiHistoryAgent"
result = _run(
["node", str(script), "--session-dir", str(session_dir), "--json", "--apply"],
env=env,
Expand All @@ -1068,7 +1068,7 @@ def log_message(self, format: str, *args: Any) -> None:
assert create_body["metadata"]["analysis"] == "searchable-now-distill-on-demand"
assert create_body["metadata"]["sync_reason"] == "history_sync"
assert create_body["metadata"]["agent_id"] == "PiHistoryAgent"
assert create_body["metadata"]["host_agent_id"] == "slock:PiHistoryAgent"
assert create_body["metadata"]["host_agent_id"] == "raft:PiHistoryAgent"
assert create_body["project"] == "/tmp/pi-history-project"
assert create_body["workspace"] == "/tmp/pi-history-project"
assert [message["content"] for message in create_body["messages"]] == [
Expand Down Expand Up @@ -1160,6 +1160,9 @@ def test_registry_connect_contract_points_agent_prompts_to_universal_skill():
assert "/docs/integrations/" not in guide["promptZh"], entry["id"]

by_id = {entry["id"]: entry for entry in integrations}
assert "raft" in integration_ids
assert "slock" not in integration_ids
assert "slock" in by_id["raft"]["aliases"]
assert by_id["copilot-cli"]["version"] == "0.1.4"
assert by_id["gemini-cli"]["version"] == "0.1.9"
assert by_id["cursor"]["version"] == "0.1.6"
Expand Down Expand Up @@ -1378,7 +1381,7 @@ def test_pi_live_package_install_and_extension_smoke(tmp_path: Path):
process.env.NMEM_API_URL = `http://127.0.0.1:${port}`;
process.env.NMEM_SPACE = "pi-smoke-space";
process.env.NMEM_AGENT_ID = "PiSmokeAgent";
process.env.NMEM_HOST_AGENT_ID = "slock:PiSmokeAgent";
process.env.NMEM_HOST_AGENT_ID = "raft:PiSmokeAgent";

const handlers = new Map();
nowledgeMemPi({ on(event, handler) { handlers.set(event, handler); } });
Expand Down Expand Up @@ -1471,7 +1474,7 @@ def test_pi_live_package_install_and_extension_smoke(tmp_path: Path):
throw new Error("space not propagated");
}
if (create.body.metadata.agent_id !== "PiSmokeAgent") throw new Error("agent_id missing");
if (create.body.metadata.host_agent_id !== "slock:PiSmokeAgent") {
if (create.body.metadata.host_agent_id !== "raft:PiSmokeAgent") {
throw new Error("host_agent_id missing");
}
if (create.body.messages.length !== 2) {
Expand Down Expand Up @@ -1507,7 +1510,7 @@ def test_pi_live_package_install_and_extension_smoke(tmp_path: Path):
assert "--agent-id" in context_call, f"context call missing --agent-id: {context_call}"
assert "PiSmokeAgent" in context_call, f"context call missing agent id value: {context_call}"
assert "--host-agent-id" in context_call, f"context call missing --host-agent-id: {context_call}"
assert "slock:PiSmokeAgent" in context_call, f"context call missing host agent id value: {context_call}"
assert "raft:PiSmokeAgent" in context_call, f"context call missing host agent id value: {context_call}"


@pytest.mark.skipif(_skip_live_host("claude"), reason="Claude live E2E not requested")
Expand Down
Loading