← Memova.aiCONTENT HISTORY

Update to Memova.ai

Snapshot Oct 24, 2026 · 13:10 UTC · version 1.15.1

WHAT CHANGED

Package or technical metadata updated

Package or technical metadata updated

Observed in package metadata. These changes alone do not establish a new customer-facing feature.

Save Memova.ai and check future changes in Following.

Up to 3 plugins free · no card required.

All fields and original values.

These are saved file metadata. Open a changed file to read its text edits.

Package file

Before

Sep 30, 2026 · 23:00 UTC

333caf4b33faceae5e71a169c696a070f4ffbcf5392c83f22704ce8a8b058311

After

Oct 24, 2026 · 13:10 UTC

af9ff37fc68dfba75882c0f49b53ceb412fbd6924d3e97fe08efee3dedaffe77

Package file

Before

Sep 30, 2026 · 23:00 UTC

127

After

Oct 24, 2026 · 13:10 UTC

98

Package file

Before

Sep 30, 2026 · 23:00 UTC

b09aeecc839ac6fbdaffa93c985edcda36eae4a10cd319f4d2e306b772f83e4b

After

Oct 24, 2026 · 13:10 UTC

4d721d3b50a321c7069b5ab068b3709adbb9fb2fcbb57df56e857d7290ec7a5a

Package file

Before

Sep 30, 2026 · 23:00 UTC

1223

After

Oct 24, 2026 · 13:10 UTC

10908

Compare saved observations

Download comparison JSON

Changed files

Choose a file to see its text changes. Binary or large files show metadata only.

.app.json →

.codex-plugin/plugin.json →

.mcp.json →

assets/icon.svg →

assets/logo.svg →

contracts/resource_access_plugin_binding_v1.json →

contracts/upstream/memova_resource_access_v1.json →

menu_catalog.json →

scripts/agent_archive.py →

scripts/check_menu_catalog.py →

scripts/ensure_mcp_login.py →

scripts/kb_setup_reminder.py →

scripts/mcp_connection_auth.py →

scripts/mcp_credential_store.py →

scripts/memova_menu.py →

scripts/validate_resource_access_contract.py →

scripts/version_check.py →

skills/memova-agent-archive/SKILL.md →

skills/memova-agent-archive/agents/openai.yaml →

skills/memova-connect/SKILL.md →

skills/memova-connect/agents/openai.yaml →

skills/memova-explicit-import/SKILL.md →

skills/memova-explicit-import/agents/openai.yaml →

skills/memova-explicit-import/scripts/preview_selected_import.py →

skills/memova-knowledge/SKILL.md →

skills/memova-knowledge/agents/openai.yaml →

skills/memova-menu/SKILL.md →

skills/memova-menu/agents/openai.yaml →

skills/memova-personal-manual/SKILL.md →

skills/memova-personal-manual/agents/openai.yaml →

skills/memova-personal-manual/references/local-artifacts.md →

skills/memova-personal-manual/scripts/prepare_personal_manual.py →

skills/memova-resource-access/SKILL.md →

skills/memova-resource-access/agents/openai.yaml →

skills/memova-vault-diagnose/SKILL.md →

skills/memova-vault-diagnose/agents/openai.yaml →

skills/memova-vault-setup/SKILL.md →

skills/memova-vault-setup/agents/openai.yaml →

skills/memova-vault-setup/scripts/create_memova_vault.py →

skills/memova-vault-setup/scripts/diagnose_memova_vault.py →

skills/memova-vault-setup/scripts/find_vault_locations.py →

skills/memova-vault-setup/scripts/inspect_vault.py →

skills/memova-vault-setup/scripts/memova_vault_lib.py →

skills/memova-vault-setup/scripts/setup_fixture_harness.py →

skills/memova-vault-setup/scripts/validate_memova_vault.py →

skills/memova-workflow/SKILL.md →

skills/memova-workflow/agents/openai.yaml →

scripts/mcp_connection_auth.py

− removed · + added

--- before
+++ after
@@ -0,0 +1,569 @@
+from __future__ import annotations
+
+import argparse
+import getpass
+import hashlib
+import json
+import os
+import platform
+import re
+import secrets
+import shlex
+import shutil
+import stat
+import subprocess
+import sys
+import tempfile
+import time
+import urllib.error
+import urllib.parse
+import urllib.request
+from collections.abc import Callable
+from pathlib import Path
+from typing import Any
+
+from mcp_credential_store import (
+    CredentialStore,
+    decode_secret,
+    encode_secret,
+    system_credential_store,
+)
+
+DEFAULT_MCP_URL = "https://api.memova.ai/mcp"
+SERVER_NAME = "memova"
+TOKEN_SCHEMA_VERSION = "memova_mcp_connection_credential_v1"
+FULL_MCP_SCOPES = (
+    "actions.read",
+    "actions.write",
+    "automation.read",
+    "automation.write",
+    "knowledge.read",
+    "knowledge.write",
+    "notes.read",
+    "personal_manual.write",
+    "resources.read",
+    "resources.export",
+    "sparks.read",
+)
+_CREDENTIAL_PATTERN = re.compile(
+    r"^(?P<kind>mvk|mvc)_[A-Za-z0-9_-]{16}_[A-Za-z0-9_-]{43}$"
+)
+_TABLE_PATTERN = re.compile(r"^\s*\[([^]]+)]\s*(?:#.*)?$")
+_DIRECT_SETTING_PATTERN = re.compile(
+    r"^\s*(url|http_headers_helper|bearer_token_env_var|http_headers)\s*="
+)
+
+JsonRequest = Callable[..., tuple[int, dict[str, Any]]]
+
+
+class McpConnectionError(RuntimeError):
+    pass
+
+
+def _json_request(
+    url: str,
+    *,
+    method: str = "GET",
+    payload: dict[str, Any] | None = None,
+    form: dict[str, str] | None = None,
+    token: str | None = None,
+    timeout: float = 30,
+) -> tuple[int, dict[str, Any]]:
+    headers = {
+        "Accept": "application/json",
+        "User-Agent": "memova-codex-plugin/mcp-connection-v1",
+    }
+    data = None
+    if payload is not None:
+        data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
+        headers["Content-Type"] = "application/json"
+    elif form is not None:
+        data = urllib.parse.urlencode(form).encode("ascii")
+        headers["Content-Type"] = "application/x-www-form-urlencoded"
+    if token:
+        headers["Authorization"] = f"Bearer {token}"
+    request = urllib.request.Request(url, data=data, headers=headers, method=method)
+    try:
+        with urllib.request.urlopen(request, timeout=timeout) as response:
+            body = response.read().decode("utf-8")
+            decoded = json.loads(body) if body else {}
+            if not isinstance(decoded, dict):
+                raise McpConnectionError("Memova returned a non-object JSON response.")
+            return int(response.status), decoded
+    except urllib.error.HTTPError as exc:
+        body = exc.read().decode("utf-8", errors="replace")
+        try:
+            decoded = json.loads(body) if body else {}
+        except json.JSONDecodeError:
+            decoded = {}
+        error_code = str(decoded.get("error") or "http_error")
+        description = str(decoded.get("error_description") or "Memova rejected the request.")
+        raise McpConnectionError(
+            f"Memova connection failed ({exc.code}, {error_code}): {description}"
+        ) from exc
+    except urllib.error.URLError as exc:
+        raise McpConnectionError("Memova could not be reached.") from exc
+
+
+class McpConnectionAuth:
+    def __init__(
+        self,
+        *,
+        mcp_url: str = DEFAULT_MCP_URL,
+        credential_store: CredentialStore | None = None,
+        request_json: JsonRequest = _json_request,
+        clock: Callable[[], float] = time.time,
+    ) -> None:
+        normalized_url = mcp_url.rstrip("/")
+        if not normalized_url.endswith("/mcp"):
+            raise McpConnectionError("The Memova MCP URL must end with /mcp.")
+        self.mcp_url = normalized_url
+        self.api_base = normalized_url.removesuffix("/mcp")
+        self.store = credential_store or system_credential_store()
+        self.request_json = request_json
+        self.clock = clock
+        self.account = "mcp:" + hashlib.sha256(normalized_url.encode("utf-8")).hexdigest()[:32]
+
+    def connect(
+        self,
+        credential: str,
+        *,
+        config_path: Path | None = None,
+        helper_path: Path | None = None,
+        replace_existing_oauth: bool = False,
+    ) -> dict[str, Any]:
+        value = credential.strip()
+        match = _CREDENTIAL_PATTERN.fullmatch(value)
+        if match is None:
+            raise McpConnectionError("Expected a Memova Connection Code or Agent Key.")
+        kind = str(match.group("kind"))
+        record = (
+            self._exchange_connection_code(value)
+            if kind == "mvc"
+            else self._agent_key_record(value)
+        )
+        installed_helper = helper_path or install_stable_helper()
+        try:
+            self.store.set(self.account, encode_secret(record))
+            config_result = configure_codex(
+                mcp_url=self.mcp_url,
+                helper_script=installed_helper,
+                config_path=config_path,
+            )
+        except Exception:
+            self.store.delete(self.account)
+            if record["credential_type"] == "oauth":
+                self._revoke_record(record)
+            raise
+        warnings = list(config_result["warnings"])
+        legacy_oauth_removed = None
+        if replace_existing_oauth:
+            legacy_oauth_removed, logout_warning = _logout_codex_oauth()
+            if logout_warning:
+                warnings.append(logout_warning)
+        return {
+            "status": "connected_client_refresh_required",
+            "credential_type": record["credential_type"],
+            "resource": record["resource"],
+            "scopes": str(record.get("scope") or "").split(),
+            "credential_store": type(self.store).__name__,
+            "config_path": str(config_result["config_path"]),
+            "restart_or_new_task_required": True,
+            "legacy_oauth_removed": legacy_oauth_removed,
+            "warnings": warnings,
+        }
+
+    def headers(self) -> dict[str, str]:
+        record = self._load(required=True)
+        if record["credential_type"] == "oauth":
+            if self.clock() >= float(record.get("expires_at") or 0) - 60:
+                record = self._refresh(record)
+            token = str(record.get("access_token") or "")
+        else:
+            token = str(record.get("agent_key") or "")
+        if not token:
+            raise McpConnectionError("The stored Memova MCP credential is incomplete.")
+        return {"Authorization": f"Bearer {token}"}
+
+    def status(self) -> dict[str, Any]:
+        record = self._load(required=False)
+        return {
+            "connected": bool(record),
+            "credential_type": record.get("credential_type") if record else None,
+            "resource": record.get("resource") if record else None,
+            "scopes": str(record.get("scope") or "").split() if record else [],
+            "access_token_expired": (
+                self.clock() >= float(record.get("expires_at") or 0)
+                if record and record.get("credential_type") == "oauth"
+                else None
+            ),
+            "credential_store": type(self.store).__name__,
+        }
+
+    def disconnect(self) -> dict[str, Any]:
+        record = self._load(required=False)
+        if not record:
+            return {"status": "not_connected", "local_credential_deleted": False}
+        server_revoked = None
+        warnings: list[str] = []
+        if record["credential_type"] == "oauth":
+            server_revoked = self._revoke_record(record)
+            if not server_revoked:
+                raise McpConnectionError(
+                    "Token revocation did not complete; the local credential was retained."
+                )
+        else:
+            warnings.append("Revoke this Agent Key in Memova to invalidate it server-side.")
+        self.store.delete(self.account)
+        return {
+            "status": "disconnected",
+            "local_credential_deleted": True,
+            "server_token_revocation_completed": server_revoked,
+            "warnings": warnings,
+        }
+
+    def _exchange_connection_code(self, code: str) -> dict[str, Any]:
+        metadata = self._metadata()
+        _, registration = self.request_json(
+            metadata["registration_endpoint"],
+            method="POST",
+            payload={
+                "redirect_uris": ["http://127.0.0.1/callback"],
+                "client_name": "Memova Codex Connection Helper",
+                "grant_types": ["authorization_code", "refresh_token"],
+                "response_types": ["code"],
+                "token_endpoint_auth_method": "none",
+                "scope": " ".join(FULL_MCP_SCOPES),
+            },
+        )
+        client_id = str(registration.get("client_id") or "")
+        if not client_id:
+            raise McpConnectionError("Memova client registration did not return a client_id.")
+        _, issued = self.request_json(
+            f"{self.api_base}/v1/mcp/credentials/connection-codes/exchange",
+            method="POST",
+            payload={
+                "connection_code": code,
+                "client_id": client_id,
+                "resource": metadata["resource"],
+                "device_id": "codex-" + secrets.token_urlsafe(18),
+            },
+        )
+        access_token = str(issued.get("access_token") or "")
+        refresh_token = str(issued.get("refresh_token") or "")
+        if not access_token or not refresh_token:
+            raise McpConnectionError("Connection Code exchange returned an incomplete token pair.")
+        return {
+            "schema_version": TOKEN_SCHEMA_VERSION,
+            "credential_type": "oauth",
+            "mcp_url": self.mcp_url,
+            "client_id": client_id,
+            "resource": metadata["resource"],
+            "token_endpoint": metadata["token_endpoint"],
+            "revocation_endpoint": metadata["revocation_endpoint"],
+            "scope": str(issued.get("scope") or " ".join(FULL_MCP_SCOPES)),
+            "access_token": access_token,
+            "refresh_token": refresh_token,
+            "expires_at": self.clock() + int(issued.get("expires_in") or 3600),
+        }
+
+    def _agent_key_record(self, agent_key: str) -> dict[str, Any]:
+        return {
+            "schema_version": TOKEN_SCHEMA_VERSION,
+            "credential_type": "agent_key",
+            "mcp_url": self.mcp_url,
+            "resource": self.mcp_url,
+            "scope": " ".join(FULL_MCP_SCOPES),
+            "agent_key": agent_key,
+        }
+
+    def _metadata(self) -> dict[str, str]:
+        _, protected = self.request_json(
+            f"{self.api_base}/.well-known/oauth-protected-resource/mcp"
+        )
+        authorization_servers = protected.get("authorization_servers") or []
+        resource = str(protected.get("resource") or "")
+        if not authorization_servers or resource != self.mcp_url:
+            raise McpConnectionError("Memova MCP protected-resource metadata is invalid.")
+        issuer = str(authorization_servers[0]).rstrip("/")
+        _, server = self.request_json(
+            f"{issuer}/.well-known/oauth-authorization-server"
+        )
+        required = ("registration_endpoint", "token_endpoint", "revocation_endpoint")
+        if any(not server.get(field) for field in required):
+            raise McpConnectionError("Memova OAuth metadata is incomplete.")
+        return {
+            "resource": resource,
+            "registration_endpoint": str(server["registration_endpoint"]),
+            "token_endpoint": str(server["token_endpoint"]),
+            "revocation_endpoint": str(server["revocation_endpoint"]),
+        }
+
+    def _refresh(self, record: dict[str, Any]) -> dict[str, Any]:
+        refresh_token = str(record.get("refresh_token") or "")
+        if not refresh_token:
+            raise McpConnectionError("Memova authorization must be connected again.")
+        _, issued = self.request_json(
+            str(record["token_endpoint"]),
+            method="POST",
+            form={
+                "grant_type": "refresh_token",
+                "refresh_token": refresh_token,
+                "client_id": str(record["client_id"]),
+                "resource": str(record["resource"]),
+            },
+        )
+        access_token = str(issued.get("access_token") or "")
+        rotated_refresh = str(issued.get("refresh_token") or "")
+        if not access_token or not rotated_refresh:
+            raise McpConnectionError("Memova token refresh returned an incomplete token pair.")
+        updated = {
+            **record,
+            "access_token": access_token,
+            "refresh_token": rotated_refresh,
+            "scope": str(issued.get("scope") or record.get("scope") or ""),
+            "expires_at": self.clock() + int(issued.get("expires_in") or 3600),
+        }
+        self.store.set(self.account, encode_secret(updated))
+        return updated
+
+    def _revoke_record(self, record: dict[str, Any]) -> bool:
+        endpoint = str(record.get("revocation_endpoint") or "")
+        tokens = [record.get("refresh_token"), record.get("access_token")]
+        if not endpoint:
+            return False
+        success = True
+        for token in tokens:
+            if not token:
+                continue
+            try:
+                self.request_json(
+                    endpoint,
+                    method="POST",
+                    form={"token": str(token)},
+                )
+            except McpConnectionError:
+                success = False
+        return success
+
+    def _load(self, *, required: bool) -> dict[str, Any]:
+        encoded = self.store.get(self.account)
+        if encoded is None:
+            if required:
+                raise McpConnectionError("Memova MCP is not connected through the helper.")
+            return {}
+        record = decode_secret(encoded)
+        if record.get("schema_version") != TOKEN_SCHEMA_VERSION:
+            raise McpConnectionError("Stored Memova MCP authorization has an unsupported version.")
+        if record.get("mcp_url") != self.mcp_url:
+            raise McpConnectionError("Stored Memova MCP authorization is bound to another URL.")
+        return record
+
+
+def install_stable_helper() -> Path:
+    source_dir = Path(__file__).resolve().parent
+    target_dir = _stable_helper_root()
+    target_dir.mkdir(parents=True, exist_ok=True)
+    try:
+        target_dir.chmod(0o700)
+    except OSError:
+        pass
+    for filename in ("mcp_connection_auth.py", "mcp_credential_store.py"):
+        source = source_dir / filename
+        target = target_dir / filename
+        if source.resolve() == target.resolve():
+            continue
+        _atomic_copy(source, target)
+    return target_dir / "mcp_connection_auth.py"
+
+
+def _stable_helper_root() -> Path:
+    system = platform.system().lower()
+    if system == "darwin":
+        return Path.home() / "Library/Application Support/Memova/mcp-connection-helper/v1"
+    if system == "windows":
+        root = Path(os.getenv("LOCALAPPDATA") or (Path.home() / "AppData/Local"))
+        return root / "Memova/mcp-connection-helper/v1"
+    root = Path(os.getenv("XDG_DATA_HOME") or (Path.home() / ".local/share"))
+    return root / "memova/mcp-connection-helper/v1"
+
+
+def _atomic_copy(source: Path, target: Path) -> None:
+    if not source.is_file():
+        raise McpConnectionError(f"Required helper file is missing: {source.name}")
+    data = source.read_bytes()
+    if target.is_file() and target.read_bytes() == data:
+        return
+    fd, temporary_name = tempfile.mkstemp(prefix=f".{target.name}.", dir=target.parent)
+    temporary = Path(temporary_name)
+    try:
+        with os.fdopen(fd, "wb") as handle:
+            handle.write(data)
+            handle.flush()
+            os.fsync(handle.fileno())
+        temporary.chmod(0o600)
+        os.replace(temporary, target)
+    finally:
+        temporary.unlink(missing_ok=True)
+
+
+def configure_codex(
+    *,
+    mcp_url: str,
+    helper_script: Path,
+    config_path: Path | None = None,
+) -> dict[str, Any]:
+    target = config_path or _codex_config_path()
+    target.parent.mkdir(parents=True, exist_ok=True)
+    before = target.read_text(encoding="utf-8") if target.exists() else ""
+    python = shutil.which("python3") or sys.executable
+    command_parts = [python, str(helper_script), "headers"]
+    command = (
+        subprocess.list2cmdline(command_parts)
+        if platform.system().lower() == "windows"
+        else shlex.join(command_parts)
+    )
+    replacement = {
+        "url": json.dumps(mcp_url),
+        "http_headers_helper": json.dumps(command),
+    }
+    after, warnings = _upsert_mcp_server_table(before, replacement)
+    if after != before:
+        _atomic_write_text(target, after)
+    return {"config_path": target, "warnings": warnings}
+
+
+def _upsert_mcp_server_table(
+    content: str,
+    replacement: dict[str, str],
+) -> tuple[str, list[str]]:
+    lines = content.splitlines()
+    header = "mcp_servers.memova"
+    start = next(
+        (
+            index
+            for index, line in enumerate(lines)
+            if (match := _TABLE_PATTERN.match(line))
+            and match.group(1) in {header, 'mcp_servers."memova"', "mcp_servers.'memova'"}
+        ),
+        None,
+    )
+    warnings: list[str] = []
+    desired = [f"{key} = {value}" for key, value in replacement.items()]
+    if start is None:
+        if lines and lines[-1].strip():
+            lines.append("")
+        lines.extend([f"[{header}]", *desired])
+        return "\n".join(lines) + "\n", warnings
+
+    end = next(
+        (index for index in range(start + 1, len(lines)) if _TABLE_PATTERN.match(lines[index])),
+        len(lines),
+    )
+    body = lines[start + 1 : end]
+    retained: list[str] = []
+    for line in body:
+        setting = _DIRECT_SETTING_PATTERN.match(line)
+        if setting is None:
+            retained.append(line)
+            continue
+        key = setting.group(1)
+        if key in replacement:
+            continue
+        warnings.append(
+            f"Existing {key} may take precedence over the Memova credential helper."
+        )
+        retained.append(line)
+    lines[start + 1 : end] = [*desired, *retained]
+    return "\n".join(lines) + "\n", warnings
+
+
+def _codex_config_path() -> Path:
+    configured_home = os.getenv("CODEX_HOME")
+    root = Path(configured_home).expanduser() if configured_home else Path.home() / ".codex"
+    return root / "config.toml"
+
+
+def _logout_codex_oauth() -> tuple[bool, str | None]:
+    try:
+        completed = subprocess.run(
+            ["codex", "mcp", "logout", SERVER_NAME],
+            stdout=subprocess.DEVNULL,
+            stderr=subprocess.DEVNULL,
+            check=False,
+        )
+    except (FileNotFoundError, OSError) as exc:
+        return False, (
+            "Existing Codex OAuth may take precedence. Run `codex mcp logout memova` in a "
+            f"normal terminal, then restart Codex ({type(exc).__name__})."
+        )
+    if completed.returncode != 0:
+        return False, (
+            "Existing Codex OAuth may take precedence. Run `codex mcp logout memova` in a "
+            "normal terminal, then restart Codex."
+        )
+    return True, None
+
+
+def _atomic_write_text(path: Path, content: str) -> None:
+    mode = stat.S_IMODE(path.stat().st_mode) if path.exists() else 0o600
+    fd, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
+    temporary = Path(temporary_name)
+    try:
+        with os.fdopen(fd, "w", encoding="utf-8") as handle:
+            handle.write(content)
+            handle.flush()
+            os.fsync(handle.fileno())
+        temporary.chmod(mode)
+        os.replace(temporary, path)
+    finally:
+        temporary.unlink(missing_ok=True)
+
+
+def _read_credential_from_stdin() -> str:
+    if sys.stdin.isatty():
+        return getpass.getpass("Paste the Memova Connection Code or Agent Key: ").strip()
+    return sys.stdin.readline().strip()
+
+
+def main() -> int:
+    parser = argparse.ArgumentParser(
+        description="Connect Memova MCP without placing credentials in Codex config files.",
+    )
+    subparsers = parser.add_subparsers(dest="command", required=True)
+    subparsers.add_parser("connect", help="Read one credential from stdin and connect Memova.")
+    subparsers.add_parser("headers", help=argparse.SUPPRESS)
+    subparsers.add_parser("status", help="Report safe local connection metadata.")
+    subparsers.add_parser("disconnect", help="Remove the local helper credential.")
+    args = parser.parse_args()
+    try:
+        auth = McpConnectionAuth()
+        if args.command == "connect":
+            result = auth.connect(
+                _read_credential_from_stdin(),
+                replace_existing_oauth=True,
+            )
+        elif args.command == "headers":
+            result = auth.headers()
+        elif args.command == "status":
+            result = auth.status()
+        else:
+            result = auth.disconnect()
+        print(json.dumps(result, ensure_ascii=False, sort_keys=True))
+        return 0
+    except (OSError, RuntimeError, TypeError, ValueError) as exc:
+        if args.command == "headers":
+            print(json.dumps({"error": str(exc)}, ensure_ascii=False), file=sys.stderr)
+        else:
+            print(
+                json.dumps(
+                    {"status": "error", "error": str(exc)},
+                    ensure_ascii=False,
+                    sort_keys=True,
+                )
+            )
+        return 1
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
Full technical diff · 49 changed fields

changed /files/.app.json/sha256

BEFORE
"333caf4b33faceae5e71a169c696a070f4ffbcf5392c83f22704ce8a8b058311"
AFTER
"af9ff37fc68dfba75882c0f49b53ceb412fbd6924d3e97fe08efee3dedaffe77"

changed /files/.app.json/size

BEFORE
127
AFTER
98

changed /files/.codex-plugin~1plugin.json/sha256

BEFORE
"b09aeecc839ac6fbdaffa93c985edcda36eae4a10cd319f4d2e306b772f83e4b"
AFTER
"4d721d3b50a321c7069b5ab068b3709adbb9fb2fcbb57df56e857d7290ec7a5a"

changed /files/.codex-plugin~1plugin.json/size

BEFORE
1223
AFTER
10908

added /files/.mcp.json

BEFORE
Field was absent
AFTER
{
  "sha256": "be8708abebf3e796509ad32a83d2a62c427ace502f3d662daac054eece711903",
  "size": 89
}

added /files/assets~1icon.svg

BEFORE
Field was absent
AFTER
{
  "sha256": "54bd50b6408ef5c658fe724373919e3edbb220232ebd6938ab69c0517f72e7f7",
  "size": 685
}

added /files/assets~1logo.svg

BEFORE
Field was absent
AFTER
{
  "sha256": "427a49d8196f8daf05e6f948d43670f801040e3596261d7be902cdd6223897db",
  "size": 866
}

added /files/contracts~1resource_access_plugin_binding_v1.json

BEFORE
Field was absent
AFTER
{
  "sha256": "ca0e91ab63d1d80d92593d24db60e62911e9c96c1316e07391654b66f274e9be",
  "size": 3406
}

added /files/contracts~1upstream~1memova_resource_access_v1.json

BEFORE
Field was absent
AFTER
{
  "sha256": "6bef5e8c35934a2ff9d6a0dd55c7d70470ced995337f28f6c3b083ed28a4c78d",
  "size": 9185
}

added /files/menu_catalog.json

BEFORE
Field was absent
AFTER
{
  "sha256": "894363bbecb738df6aa6cea91dac994d381067049d4d866930a065cd1c6945b3",
  "size": 2509
}

added /files/scripts~1agent_archive.py

BEFORE
Field was absent
AFTER
{
  "sha256": "7ef5fe9d8292847c59543395104ec80709aedd56a992718ecefec793dd1f67c9",
  "size": 15116
}

added /files/scripts~1check_menu_catalog.py

BEFORE
Field was absent
AFTER
{
  "sha256": "d89afb741c7f73a2ac3857f3379268f00c61eec05ae2e8700738e440477bbe2f",
  "size": 638
}

added /files/scripts~1ensure_mcp_login.py

BEFORE
Field was absent
AFTER
{
  "sha256": "dd2ce35ecaa4ce7ec8f249ab962660f134515f9adcb5d714dd5e33222039bf92",
  "size": 20246
}

added /files/scripts~1kb_setup_reminder.py

BEFORE
Field was absent
AFTER
{
  "sha256": "cdb69e55bcf1faa65fa6664a4bd6a91abf7f9ab11db0ff7be5245c4c2a713fe5",
  "size": 5470
}

added /files/scripts~1mcp_connection_auth.py

BEFORE
Field was absent
AFTER
{
  "sha256": "3df8962e9d5c66a5298d9a9e6cbdc4d17755cc9d73d2f139bc3ee26256ed9bc2",
  "size": 21361
}

added /files/scripts~1mcp_credential_store.py

BEFORE
Field was absent
AFTER
{
  "sha256": "106077ee2270b0168ff15a7caded91da9fad252f1c1164b6e1d4228028b1712b",
  "size": 13043
}

added /files/scripts~1memova_menu.py

BEFORE
Field was absent
AFTER
{
  "sha256": "a31d626d81f92189782138cd438a7c070e4bb0104d4a47ad2024013816cf7c66",
  "size": 2503
}

added /files/scripts~1validate_resource_access_contract.py

BEFORE
Field was absent
AFTER
{
  "sha256": "130b4b179e301d7b0984822721c1c9633ff824c7e00f280f89fb98f1aebe486e",
  "size": 19271
}

added /files/scripts~1version_check.py

BEFORE
Field was absent
AFTER
{
  "sha256": "431220554511a5ffc66b14ac414259dbc50f24e97a050358fb35e1cc19b4c72e",
  "size": 10129
}

added /files/skills~1memova-agent-archive~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "f5acafcc0aded82b7173384f3be02bf2be31ab85b0842a15828c29e40c5539d1",
  "size": 6335
}

added /files/skills~1memova-agent-archive~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "080bd0c786e329c6538576681bc233bebcc0419dfa0dc34e78a7dd056bd46fec",
  "size": 463
}

added /files/skills~1memova-connect~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "8254a65d2ebe8a9e518fc0be28b0494a3b002e61d2b540812579fdf2b04ac19b",
  "size": 3181
}

added /files/skills~1memova-connect~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "7265de61625ee0b57733e64ab4807ac422a15bfd96a35b609f012c666cba3d71",
  "size": 248
}

added /files/skills~1memova-explicit-import~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "a9846d3d749b53a822c2b955cb75974dee16bfea84c33be3e2aba4170b6a318e",
  "size": 7700
}

added /files/skills~1memova-explicit-import~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "eb925da8a56261a8efb3869710fdba83fd4641da830acb3544c2c17145c4c115",
  "size": 483
}

added /files/skills~1memova-explicit-import~1scripts~1preview_selected_import.py

BEFORE
Field was absent
AFTER
{
  "sha256": "f4f01224c0b245dcb7ec5021714162293c758b8a1b7ec7d468ac1b197d9d06fb",
  "size": 7909
}

added /files/skills~1memova-knowledge~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "08da7d9d3589124da1d35524587ef2080c2455d191bb86ee4ed528695bcdf083",
  "size": 5445
}

added /files/skills~1memova-knowledge~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "5b2b81d6559f0d588fb352b303082607953d0cac2b2bc093210862253c68998a",
  "size": 496
}

added /files/skills~1memova-menu~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "88e9ce6c3a261ae5e6f633228e6f7188d2c7884abf15efc8941f417d1d23fc5e",
  "size": 8933
}

added /files/skills~1memova-menu~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "a3543b03bd48af221f0f0354f37fc32d5062bbf38c34993d8dad4cf3be05b1ee",
  "size": 204
}

added /files/skills~1memova-personal-manual~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "5e2ee8b0ae48ac8a469dc61de899e515e0356f48115cd7fa6371cb7a6d2f9b7d",
  "size": 11321
}

added /files/skills~1memova-personal-manual~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "409d332354b3bfbbd1a30e2262ad44cb393774360e067d359c196ccd4dbf7d9a",
  "size": 536
}

added /files/skills~1memova-personal-manual~1references~1local-artifacts.md

BEFORE
Field was absent
AFTER
{
  "sha256": "b7aa91d7a130338792a57cbe2937aea23fe6c94f7389dee3c7bd01035854dfa6",
  "size": 1914
}

added /files/skills~1memova-personal-manual~1scripts~1prepare_personal_manual.py

BEFORE
Field was absent
AFTER
{
  "sha256": "51cd409550a765c45b0fa3e78c9eb7309d308c769ac5aa83443f46616644bcc8",
  "size": 19703
}

added /files/skills~1memova-resource-access~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "360b19832d8891f20b05a0ec0d6eca849b4afb80915909edc7596fc335b932b3",
  "size": 13511
}

added /files/skills~1memova-resource-access~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "8f089f456a2cf60ce08dd3f006f8c835255c1b6b7f2f07af54aedc7c75c42d08",
  "size": 580
}

added /files/skills~1memova-vault-diagnose~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "b4c38e582baeb21e69ec8b04d5efa690739989a38c82468d1d3ad19465203a34",
  "size": 4809
}

added /files/skills~1memova-vault-diagnose~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "cc32fa628fb4f5c3c91f750dcf72bfdf4704fda2efb33dd1281f11b5ecaf49b6",
  "size": 469
}

added /files/skills~1memova-vault-setup~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "3c4410dac28396080939f749581e42a7ce67fa704975f7d4b19aa8eee31d4537",
  "size": 12497
}

added /files/skills~1memova-vault-setup~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "0c1cbe0d82408b2b2c885f90b70d0a85bcb34ec0ac20a17386c5220af5189841",
  "size": 475
}

added /files/skills~1memova-vault-setup~1scripts~1create_memova_vault.py

BEFORE
Field was absent
AFTER
{
  "sha256": "3272b0f7f648e402f5035e5f10b75ca18fd143c86031fa470bd162a17e054c22",
  "size": 4151
}

added /files/skills~1memova-vault-setup~1scripts~1diagnose_memova_vault.py

BEFORE
Field was absent
AFTER
{
  "sha256": "b4d6501f56f865473cda4aa7ca188735338011f7c962795bd0b4145a1e8e95a8",
  "size": 8060
}

added /files/skills~1memova-vault-setup~1scripts~1find_vault_locations.py

BEFORE
Field was absent
AFTER
{
  "sha256": "850c56ae9b5aa729964d2eb7e22a0ff7d37fd8d2fe89e0e6bc6d469caa6c5bd6",
  "size": 1965
}

added /files/skills~1memova-vault-setup~1scripts~1inspect_vault.py

BEFORE
Field was absent
AFTER
{
  "sha256": "7515a4d226c3a8acecfc662fb573e64a136f60aedb2460f6ed8afef212abee18",
  "size": 748
}

added /files/skills~1memova-vault-setup~1scripts~1memova_vault_lib.py

BEFORE
Field was absent
AFTER
{
  "sha256": "0d7977b3485222bbebf7cf6520c01b0c55712679a7939c30689b729bcbe23bef",
  "size": 97146
}

added /files/skills~1memova-vault-setup~1scripts~1setup_fixture_harness.py

BEFORE
Field was absent
AFTER
{
  "sha256": "605d8d19a51b551611bca2cdd612651e27a490a66d9f9b5958d39d876e27e3a6",
  "size": 50807
}

added /files/skills~1memova-vault-setup~1scripts~1validate_memova_vault.py

BEFORE
Field was absent
AFTER
{
  "sha256": "0c0632e1a8e192fae3b9802501cc298a2c3887ce63a552a469e32b83d6737269",
  "size": 2681
}

added /files/skills~1memova-workflow~1SKILL.md

BEFORE
Field was absent
AFTER
{
  "sha256": "7b98b4534cdec921a6b82f422f445a46d3a4f78cbb4feeabdfee1b3b924cb264",
  "size": 10156
}

added /files/skills~1memova-workflow~1agents~1openai.yaml

BEFORE
Field was absent
AFTER
{
  "sha256": "ade51e2a4a35f1ce6b1d05bda9d183ce58a10aedeab183bedc6382fbe5b47bbd",
  "size": 445
}
Full snapshot data
{
  "files": {
    ".app.json": {
      "sha256": "af9ff37fc68dfba75882c0f49b53ceb412fbd6924d3e97fe08efee3dedaffe77",
      "size": 98
    },
    ".codex-plugin/plugin.json": {
      "sha256": "4d721d3b50a321c7069b5ab068b3709adbb9fb2fcbb57df56e857d7290ec7a5a",
      "size": 10908
    },
    ".mcp.json": {
      "sha256": "be8708abebf3e796509ad32a83d2a62c427ace502f3d662daac054eece711903",
      "size": 89
    },
    "assets/icon.svg": {
      "sha256": "54bd50b6408ef5c658fe724373919e3edbb220232ebd6938ab69c0517f72e7f7",
      "size": 685
    },
    "assets/logo.svg": {
      "sha256": "427a49d8196f8daf05e6f948d43670f801040e3596261d7be902cdd6223897db",
      "size": 866
    },
    "contracts/resource_access_plugin_binding_v1.json": {
      "sha256": "ca0e91ab63d1d80d92593d24db60e62911e9c96c1316e07391654b66f274e9be",
      "size": 3406
    },
    "contracts/upstream/memova_resource_access_v1.json": {
      "sha256": "6bef5e8c35934a2ff9d6a0dd55c7d70470ced995337f28f6c3b083ed28a4c78d",
      "size": 9185
    },
    "menu_catalog.json": {
      "sha256": "894363bbecb738df6aa6cea91dac994d381067049d4d866930a065cd1c6945b3",
      "size": 2509
    },
    "scripts/agent_archive.py": {
      "sha256": "7ef5fe9d8292847c59543395104ec80709aedd56a992718ecefec793dd1f67c9",
      "size": 15116
    },
    "scripts/check_menu_catalog.py": {
      "sha256": "d89afb741c7f73a2ac3857f3379268f00c61eec05ae2e8700738e440477bbe2f",
      "size": 638
    },
    "scripts/ensure_mcp_login.py": {
      "sha256": "dd2ce35ecaa4ce7ec8f249ab962660f134515f9adcb5d714dd5e33222039bf92",
      "size": 20246
    },
    "scripts/kb_setup_reminder.py": {
      "sha256": "cdb69e55bcf1faa65fa6664a4bd6a91abf7f9ab11db0ff7be5245c4c2a713fe5",
      "size": 5470
    },
    "scripts/mcp_connection_auth.py": {
      "sha256": "3df8962e9d5c66a5298d9a9e6cbdc4d17755cc9d73d2f139bc3ee26256ed9bc2",
      "size": 21361
    },
    "scripts/mcp_credential_store.py": {
      "sha256": "106077ee2270b0168ff15a7caded91da9fad252f1c1164b6e1d4228028b1712b",
      "size": 13043
    },
    "scripts/memova_menu.py": {
      "sha256": "a31d626d81f92189782138cd438a7c070e4bb0104d4a47ad2024013816cf7c66",
      "size": 2503
    },
    "scripts/validate_resource_access_contract.py": {
      "sha256": "130b4b179e301d7b0984822721c1c9633ff824c7e00f280f89fb98f1aebe486e",
      "size": 19271
    },
    "scripts/version_check.py": {
      "sha256": "431220554511a5ffc66b14ac414259dbc50f24e97a050358fb35e1cc19b4c72e",
      "size": 10129
    },
    "skills/memova-agent-archive/SKILL.md": {
      "sha256": "f5acafcc0aded82b7173384f3be02bf2be31ab85b0842a15828c29e40c5539d1",
      "size": 6335
    },
    "skills/memova-agent-archive/agents/openai.yaml": {
      "sha256": "080bd0c786e329c6538576681bc233bebcc0419dfa0dc34e78a7dd056bd46fec",
      "size": 463
    },
    "skills/memova-connect/SKILL.md": {
      "sha256": "8254a65d2ebe8a9e518fc0be28b0494a3b002e61d2b540812579fdf2b04ac19b",
      "size": 3181
    },
    "skills/memova-connect/agents/openai.yaml": {
      "sha256": "7265de61625ee0b57733e64ab4807ac422a15bfd96a35b609f012c666cba3d71",
      "size": 248
    },
    "skills/memova-explicit-import/SKILL.md": {
      "sha256": "a9846d3d749b53a822c2b955cb75974dee16bfea84c33be3e2aba4170b6a318e",
      "size": 7700
    },
    "skills/memova-explicit-import/agents/openai.yaml": {
      "sha256": "eb925da8a56261a8efb3869710fdba83fd4641da830acb3544c2c17145c4c115",
      "size": 483
    },
    "skills/memova-explicit-import/scripts/preview_selected_import.py": {
      "sha256": "f4f01224c0b245dcb7ec5021714162293c758b8a1b7ec7d468ac1b197d9d06fb",
      "size": 7909
    },
    "skills/memova-knowledge/SKILL.md": {
      "sha256": "08da7d9d3589124da1d35524587ef2080c2455d191bb86ee4ed528695bcdf083",
      "size": 5445
    },
    "skills/memova-knowledge/agents/openai.yaml": {
      "sha256": "5b2b81d6559f0d588fb352b303082607953d0cac2b2bc093210862253c68998a",
      "size": 496
    },
    "skills/memova-menu/SKILL.md": {
      "sha256": "88e9ce6c3a261ae5e6f633228e6f7188d2c7884abf15efc8941f417d1d23fc5e",
      "size": 8933
    },
    "skills/memova-menu/agents/openai.yaml": {
      "sha256": "a3543b03bd48af221f0f0354f37fc32d5062bbf38c34993d8dad4cf3be05b1ee",
      "size": 204
    },
    "skills/memova-personal-manual/SKILL.md": {
      "sha256": "5e2ee8b0ae48ac8a469dc61de899e515e0356f48115cd7fa6371cb7a6d2f9b7d",
      "size": 11321
    },
    "skills/memova-personal-manual/agents/openai.yaml": {
      "sha256": "409d332354b3bfbbd1a30e2262ad44cb393774360e067d359c196ccd4dbf7d9a",
      "size": 536
    },
    "skills/memova-personal-manual/references/local-artifacts.md": {
      "sha256": "b7aa91d7a130338792a57cbe2937aea23fe6c94f7389dee3c7bd01035854dfa6",
      "size": 1914
    },
    "skills/memova-personal-manual/scripts/prepare_personal_manual.py": {
      "sha256": "51cd409550a765c45b0fa3e78c9eb7309d308c769ac5aa83443f46616644bcc8",
      "size": 19703
    },
    "skills/memova-resource-access/SKILL.md": {
      "sha256": "360b19832d8891f20b05a0ec0d6eca849b4afb80915909edc7596fc335b932b3",
      "size": 13511
    },
    "skills/memova-resource-access/agents/openai.yaml": {
      "sha256": "8f089f456a2cf60ce08dd3f006f8c835255c1b6b7f2f07af54aedc7c75c42d08",
      "size": 580
    },
    "skills/memova-vault-diagnose/SKILL.md": {
      "sha256": "b4c38e582baeb21e69ec8b04d5efa690739989a38c82468d1d3ad19465203a34",
      "size": 4809
    },
    "skills/memova-vault-diagnose/agents/openai.yaml": {
      "sha256": "cc32fa628fb4f5c3c91f750dcf72bfdf4704fda2efb33dd1281f11b5ecaf49b6",
      "size": 469
    },
    "skills/memova-vault-setup/SKILL.md": {
      "sha256": "3c4410dac28396080939f749581e42a7ce67fa704975f7d4b19aa8eee31d4537",
      "size": 12497
    },
    "skills/memova-vault-setup/agents/openai.yaml": {
      "sha256": "0c1cbe0d82408b2b2c885f90b70d0a85bcb34ec0ac20a17386c5220af5189841",
      "size": 475
    },
    "skills/memova-vault-setup/scripts/create_memova_vault.py": {
      "sha256": "3272b0f7f648e402f5035e5f10b75ca18fd143c86031fa470bd162a17e054c22",
      "size": 4151
    },
    "skills/memova-vault-setup/scripts/diagnose_memova_vault.py": {
      "sha256": "b4d6501f56f865473cda4aa7ca188735338011f7c962795bd0b4145a1e8e95a8",
      "size": 8060
    },
    "skills/memova-vault-setup/scripts/find_vault_locations.py": {
      "sha256": "850c56ae9b5aa729964d2eb7e22a0ff7d37fd8d2fe89e0e6bc6d469caa6c5bd6",
      "size": 1965
    },
    "skills/memova-vault-setup/scripts/inspect_vault.py": {
      "sha256": "7515a4d226c3a8acecfc662fb573e64a136f60aedb2460f6ed8afef212abee18",
      "size": 748
    },
    "skills/memova-vault-setup/scripts/memova_vault_lib.py": {
      "sha256": "0d7977b3485222bbebf7cf6520c01b0c55712679a7939c30689b729bcbe23bef",
      "size": 97146
    },
    "skills/memova-vault-setup/scripts/setup_fixture_harness.py": {
      "sha256": "605d8d19a51b551611bca2cdd612651e27a490a66d9f9b5958d39d876e27e3a6",
      "size": 50807
    },
    "skills/memova-vault-setup/scripts/validate_memova_vault.py": {
      "sha256": "0c0632e1a8e192fae3b9802501cc298a2c3887ce63a552a469e32b83d6737269",
      "size": 2681
    },
    "skills/memova-workflow/SKILL.md": {
      "sha256": "7b98b4534cdec921a6b82f422f445a46d3a4f78cbb4feeabdfee1b3b924cb264",
      "size": 10156
    },
    "skills/memova-workflow/agents/openai.yaml": {
      "sha256": "ade51e2a4a35f1ce6b1d05bda9d183ce58a10aedeab183bedc6382fbe5b47bbd",
      "size": 445
    }
  }
}

SHA-256 of public snapshot: c9fc0f7767b2429980a672046ee55277b419ec8f437b75a2051fe27afb04c9d8