{
  "openapi": "3.0.3",
  "info": {
    "title": "SyntheticMem API",
    "version": "1.0.0",
    "description": "Permanent, provenance-aware memory for AI agents, as a pure MCP server with a plain-REST mirror.\n\nSyntheticMem gives any agent shared, semantic recall that outlives the session. Every memory is a row that can be appealed and falsified: recall returns the content **plus** its provenance (`source`, `learned_at`, `last_observed_at`, `hop_count`, `namespace`), a falsifiability status (`fact` / `hypothesis` / `retired`) and a `trust` hint — so the agent verifies rather than blindly trusts. Embeddings are the index, not the truth.\n\nTwo transports, one behaviour:\n- **REST** under `/api/v1/*` (this spec's typed endpoints).\n- **MCP** (JSON-RPC 2.0) at `POST /api/mcp` — the `memory_*` tools call the exact same service, so the two are behaviour-identical.\n\n**Durability (Feature B):** a memory from an agent linked to a registered user is permanent by default; a memory from an anonymous agent is ephemeral and auto-expires 24h after creation (a larger `ttl_hours` is capped to 24h, with an `advisory`). Pass `ttl_hours` for shorter-lived scratch notes.",
    "contact": {
      "name": "SyntheticMem",
      "url": "https://syntheticmem.com"
    }
  },
  "servers": [
    {
      "url": "https://syntheticmem.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Memories",
      "description": "The memory primitives (per-agent agt_ bearer key)."
    },
    {
      "name": "Keys",
      "description": "Provision / revoke per-agent keys (master key)."
    },
    {
      "name": "MCP",
      "description": "The JSON-RPC 2.0 MCP endpoint (one path, method-dispatched)."
    }
  ],
  "security": [
    {
      "agentKey": []
    }
  ],
  "paths": {
    "/api/v1/memories": {
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Remember a memory",
        "description": "Save a memory. With a `source` it is stored as a trusted **fact** (observed now); without one, a **hypothesis** (a lead to verify). Mirrors the `memory_remember` MCP tool. Honours the anonymous-24h durability rule; pass `ttl_hours` to opt into a shorter lifetime.",
        "operationId": "rememberMemory",
        "security": [
          {
            "agentKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemoryInput"
              },
              "examples": {
                "sourcedFact": {
                  "summary": "A sourced fact with an invalidation condition",
                  "value": {
                    "content": "Staging deploy needs PG_SSLMODE=require; the pooler refuses plain TCP.",
                    "namespace": "proj/checkout",
                    "source": "ops/deploy.md",
                    "confidence": 0.9,
                    "invalidation_tag": "architectural",
                    "invalidation_condition": "retire if the pooler adds TLS passthrough"
                  }
                },
                "scratchNote": {
                  "summary": "A short-lived scratch note (2h TTL)",
                  "value": {
                    "content": "Working theory: cold starts are OPcache misses.",
                    "ttl_hours": 2
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved. Returns the stored row with its id, provenance and durability.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavedMemory"
                },
                "example": {
                  "id": 42,
                  "content": "Staging deploy needs PG_SSLMODE=require; the pooler refuses plain TCP.",
                  "namespace": "proj/checkout",
                  "kind": "semantic",
                  "status": "fact",
                  "source": "ops/deploy.md",
                  "confidence": 0.9,
                  "learned_at": "2026-10-26T12:00:00+00:00",
                  "last_observed_at": "2026-10-26T12:00:00+00:00",
                  "hop_count": 0,
                  "parent_id": null,
                  "invalidation_tag": "architectural",
                  "invalidation_condition": "retire if the pooler adds TLS passthrough",
                  "expires_at": null,
                  "ephemeral": false,
                  "expiry": "permanent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        }
      },
      "get": {
        "tags": [
          "Memories"
        ],
        "summary": "List memories",
        "description": "Recent/filtered rows (no semantic search) — browse or audit by namespace/status. Mirrors the `memory_list` MCP tool. Defaults to live rows (fact + hypothesis).",
        "operationId": "listMemories",
        "security": [
          {
            "agentKey": []
          }
        ],
        "parameters": [
          {
            "name": "namespace",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Restrict to one sub-scope."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "fact",
                "hypothesis",
                "retired"
              ]
            },
            "description": "Restrict to one status (use 'retired' to inspect tombstones)."
          },
          {
            "name": "include_expired",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Also return expired ephemeral rows."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of rows.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MemoryRow"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/memories/recall": {
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Semantically recall memories",
        "description": "Embed the query and return the nearest memories, pre-filtered by namespace/status. Facts rank before hypotheses; retired rows are hidden unless `include_retired`, and expired ephemeral rows unless `include_expired`. Each hit carries full provenance, a cosine `similarity`, a `trust` hint and its durability. Mirrors the `memory_recall` MCP tool.",
        "operationId": "recallMemories",
        "security": [
          {
            "agentKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecallInput"
              },
              "example": {
                "query": "what happens to php-fpm on deploy?",
                "limit": 8
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked hits with provenance and trust.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MemoryRow"
                      }
                    }
                  }
                },
                "example": {
                  "query": "what happens to php-fpm on deploy?",
                  "results": [
                    {
                      "id": 42,
                      "content": "The deploy script restarts php8.4-fpm after composer install.",
                      "namespace": "proj/checkout",
                      "kind": "semantic",
                      "status": "fact",
                      "source": "runbook",
                      "confidence": 1.0,
                      "similarity": 0.89,
                      "learned_at": "2026-09-14T09:00:00+00:00",
                      "last_observed_at": "2026-10-22T10:00:00+00:00",
                      "hop_count": 0,
                      "parent_id": null,
                      "superseded_by": null,
                      "invalidation_tag": "architectural",
                      "invalidation_condition": "delete if deploys move to a zero-downtime reload",
                      "retired_reason": null,
                      "embedding_model": "nomic-embed-text",
                      "unattributable": false,
                      "trust": "fact: sourced and observed — trustworthy, but still falsifiable (see invalidation_condition).",
                      "expires_at": null,
                      "ephemeral": false,
                      "expiry": "permanent"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        }
      }
    },
    "/api/v1/memories/consolidate": {
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Consolidate episodic memories into semantic",
        "description": "Compress this agent's EPISODIC memories into higher-level SEMANTIC ones, preserving the doubt. Worthy episodic rows are clustered by namespace and each cluster is summarized by a cloud model into one new semantic memory. The derived summary is stored as a `hypothesis` (never a fact — only `observe` promotes), carries the MINIMUM confidence of its sources (never inflated) and the UNION of their invalidation tags/conditions, with hop_count+1 and last_observed_at=null (so it is flagged unattributable). Source episodes are superseded (retired + linked), never deleted. Set `dry_run` to preview without writing. Mirrors the `memory_consolidate` MCP tool.",
        "operationId": "consolidateMemories",
        "security": [
          {
            "agentKey": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "namespace": {
                    "type": "string",
                    "description": "Restrict to one sub-scope. Omit to consolidate every namespace with enough episodic rows."
                  },
                  "dry_run": {
                    "type": "boolean",
                    "description": "Preview the clusters and their derived facets without writing (default false)."
                  },
                  "max_clusters": {
                    "type": "integer",
                    "description": "Cap how many namespace clusters to consolidate in one call."
                  }
                }
              },
              "example": {
                "namespace": "proj",
                "dry_run": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Consolidation result: counts, the model used, and per-cluster detail.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dry_run": {
                      "type": "boolean"
                    },
                    "clusters": {
                      "type": "integer"
                    },
                    "sources_consolidated": {
                      "type": "integer"
                    },
                    "semantic_created": {
                      "type": "integer"
                    },
                    "model": {
                      "type": "string",
                      "nullable": true
                    },
                    "details": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                },
                "example": {
                  "dry_run": false,
                  "clusters": 1,
                  "sources_consolidated": 3,
                  "semantic_created": 1,
                  "model": "glm-5.3-flash:cloud",
                  "details": [
                    {
                      "namespace": "proj",
                      "source_ids": [
                        12,
                        14,
                        17
                      ],
                      "sources": 3,
                      "semantic_id": 21,
                      "status": "hypothesis",
                      "confidence": 0.6,
                      "invalidation_tag": "temporal, architectural",
                      "hop_count": 1,
                      "parent_id": 12
                    }
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Bad input.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/memories/{id}/supersede": {
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Supersede a memory",
        "description": "Correct a memory by saving a NEW row that replaces `id`. The old row is retired (hidden from default recall) but kept and linked (`superseded_by` ↔ `parent_id`) — never a hard delete. Mirrors the `memory_supersede` MCP tool.",
        "operationId": "supersedeMemory",
        "security": [
          {
            "agentKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MemoryId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SupersedeInput"
              },
              "example": {
                "content": "The admin dashboard moved to /manage.",
                "source": "routes"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The old row retired, the new successor row created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "superseded": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string",
                          "example": "retired"
                        },
                        "superseded_by": {
                          "type": "integer"
                        }
                      }
                    },
                    "new": {
                      "$ref": "#/components/schemas/SavedMemory"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        }
      }
    },
    "/api/v1/memories/{id}/forget": {
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Forget (tombstone) a memory",
        "description": "Retire a memory with a `reason`. The row is marked retired and hidden from default recall, but kept for history (reachable via `include_retired`) — a tombstone, not a delete. Mirrors the `memory_forget` MCP tool.",
        "operationId": "forgetMemory",
        "security": [
          {
            "agentKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MemoryId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Why it is being forgotten — preserved on the tombstone."
                  }
                }
              },
              "example": {
                "reason": "staging decommissioned"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tombstoned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "example": "retired"
                    },
                    "retired_reason": {
                      "type": "string"
                    },
                    "content": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/memories/{id}/observe": {
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Observe a memory (fresh observation)",
        "description": "Record that you just saw memory `id` hold true again: stamps `last_observed_at = now` and bumps confidence. This is the ONLY path that promotes a `hypothesis` to a `fact` (never by usage count). Mirrors the `memory_observe` MCP tool.",
        "operationId": "observeMemory",
        "security": [
          {
            "agentKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MemoryId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Observed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "example": "fact"
                    },
                    "confidence": {
                      "type": "number"
                    },
                    "last_observed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "content": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/keys": {
      "post": {
        "tags": [
          "Keys"
        ],
        "summary": "Provision an agent key",
        "description": "Mint a per-agent `agt_…` key (the agent is created if new). Authenticated with the shared **master key**, not a per-agent key. Idempotent per (agent, label): re-minting under the same label replaces the old key.",
        "operationId": "provisionKey",
        "security": [
          {
            "masterKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "agent",
                  "label"
                ],
                "properties": {
                  "agent": {
                    "type": "string",
                    "description": "Agent name the key acts as.",
                    "example": "my-agent"
                  },
                  "label": {
                    "type": "string",
                    "description": "A label for the key.",
                    "example": "laptop"
                  },
                  "namespace_default": {
                    "type": "string",
                    "description": "Default namespace for a newly created agent.",
                    "example": "my-project"
                  }
                }
              },
              "example": {
                "agent": "my-agent",
                "label": "laptop",
                "namespace_default": "my-project"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Minted. The token is shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "example": "agt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                    },
                    "agent": {
                      "type": "string"
                    },
                    "namespace_default": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/keys/revoke": {
      "post": {
        "tags": [
          "Keys"
        ],
        "summary": "Revoke an agent key",
        "description": "Revoke the key(s) for an (agent, label). Authenticated with the master key.",
        "operationId": "revokeKey",
        "security": [
          {
            "masterKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "agent",
                  "label"
                ],
                "properties": {
                  "agent": {
                    "type": "string",
                    "example": "my-agent"
                  },
                  "label": {
                    "type": "string",
                    "example": "laptop"
                  }
                }
              },
              "example": {
                "agent": "my-agent",
                "label": "laptop"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revoked count.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "tags": [
          "MCP"
        ],
        "summary": "MCP JSON-RPC 2.0 endpoint",
        "description": "A single endpoint speaking **JSON-RPC 2.0 over Streamable HTTP**. The body is `{jsonrpc, id, method, params}` and the `method` field dispatches: `initialize`, `tools/list`, `tools/call`, `ping`. Authenticated with a per-agent `agt_…` bearer key. This is one path with a method-dispatched body — the request/response schema is the JSON-RPC envelope below; see the examples for `initialize`, `tools/list`, and a `tools/call` of `memory_remember`.\n\nThe `memory_*` tools call the same service as the REST endpoints, so they are behaviour-identical.",
        "operationId": "mcpRpc",
        "security": [
          {
            "agentKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              },
              "examples": {
                "initialize": {
                  "summary": "initialize — handshake",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "initialize",
                    "params": {}
                  }
                },
                "toolsList": {
                  "summary": "tools/list — discover the memory tools",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/list",
                    "params": {}
                  }
                },
                "toolsCallRemember": {
                  "summary": "tools/call — memory_remember",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 3,
                    "method": "tools/call",
                    "params": {
                      "name": "memory_remember",
                      "arguments": {
                        "content": "The deploy script restarts php8.4-fpm after composer install.",
                        "source": "runbook",
                        "invalidation_condition": "delete if deploys move to a zero-downtime reload"
                      }
                    }
                  }
                },
                "toolsCallRecall": {
                  "summary": "tools/call — memory_recall",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 4,
                    "method": "tools/call",
                    "params": {
                      "name": "memory_recall",
                      "arguments": {
                        "query": "what happens to php-fpm on deploy?"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC result. For `tools/call` the `result.content[0].text` is a JSON string of the tool's payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                },
                "examples": {
                  "initializeResult": {
                    "summary": "initialize result",
                    "value": {
                      "jsonrpc": "2.0",
                      "id": 1,
                      "result": {
                        "protocolVersion": "2024-11-05",
                        "capabilities": {
                          "tools": {}
                        },
                        "serverInfo": {
                          "name": "syntheticmem",
                          "version": "0.1.0"
                        },
                        "instructions": "SyntheticMem — permanent, provenance-aware memory for AI agents…"
                      }
                    }
                  },
                  "toolsCallResult": {
                    "summary": "tools/call result (memory_remember)",
                    "value": {
                      "jsonrpc": "2.0",
                      "id": 3,
                      "result": {
                        "content": [
                          {
                            "type": "text",
                            "text": "{\n  \"id\": 42,\n  \"content\": \"The deploy script restarts php8.4-fpm after composer install.\",\n  \"status\": \"fact\",\n  \"source\": \"runbook\",\n  \"ephemeral\": false,\n  \"expiry\": \"permanent\"\n}"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized (JSON-RPC error envelope).",
            "content": {
              "application/json": {
                "example": {
                  "jsonrpc": "2.0",
                  "error": {
                    "code": -32001,
                    "message": "Unauthorized: a valid SyntheticMem API key is required (Authorization: Bearer agt_…)."
                  },
                  "id": null
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "agentKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "agt_…",
        "description": "A per-agent key: `Authorization: Bearer agt_…`. Mint one with `php artisan syntheticmem:mcp-token <agent>` or `POST /api/v1/keys` (master key)."
      },
      "masterKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "The shared master key (SYNTHETICMEM_MASTER_KEY), used only to provision/revoke agent keys."
      }
    },
    "parameters": {
      "MemoryId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        },
        "description": "The memory id (must belong to the authenticated agent)."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid credentials.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Unauthorized: a valid SyntheticMem API key is required (Authorization: Bearer agt_…)."
            }
          }
        }
      },
      "NotFound": {
        "description": "No such memory for this agent (ids are scoped per agent — a cross-agent id is simply not found).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "No memory #123 for this agent."
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "Bad input (e.g. missing content/query).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "content is required."
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "MemoryInput": {
        "type": "object",
        "required": [
          "content"
        ],
        "properties": {
          "content": {
            "type": "string",
            "description": "The memory text — a self-contained statement."
          },
          "namespace": {
            "type": "string",
            "description": "Sub-scope within this agent (e.g. a project slug). Defaults to the agent default."
          },
          "source": {
            "type": "string",
            "description": "Where it came from (file, URL, 'user', command). Presence => fact; absence => hypothesis."
          },
          "confidence": {
            "type": "number",
            "description": "0..1. Defaults to 1.0 for a fact, 0.5 for a hypothesis."
          },
          "invalidation_tag": {
            "type": "string",
            "description": "temporal | paradigm | architectural | observational."
          },
          "invalidation_condition": {
            "type": "string",
            "description": "The concrete test that would retire this memory."
          },
          "kind": {
            "type": "string",
            "enum": [
              "semantic",
              "episodic"
            ],
            "description": "semantic (a distilled fact) or episodic (a thing that happened)."
          },
          "parent_id": {
            "type": "integer",
            "description": "If derived from another memory, its id. A derived note with no source inherits hop_count+1 and is flagged unattributable."
          },
          "ttl_hours": {
            "type": "number",
            "description": "Optional time-to-live in hours for a scratch memory. An anonymous agent is always capped to 24h."
          }
        }
      },
      "RecallInput": {
        "type": "object",
        "required": [
          "query"
        ],
        "properties": {
          "query": {
            "type": "string",
            "description": "What you want to remember about, in natural language."
          },
          "namespace": {
            "type": "string"
          },
          "limit": {
            "type": "integer",
            "default": 8,
            "minimum": 1,
            "maximum": 50
          },
          "include_retired": {
            "type": "boolean",
            "default": false
          },
          "include_expired": {
            "type": "boolean",
            "default": false
          },
          "status": {
            "type": "string",
            "enum": [
              "fact",
              "hypothesis",
              "retired"
            ]
          }
        }
      },
      "SupersedeInput": {
        "type": "object",
        "required": [
          "content"
        ],
        "properties": {
          "content": {
            "type": "string",
            "description": "The corrected/updated memory text."
          },
          "source": {
            "type": "string"
          },
          "confidence": {
            "type": "number"
          },
          "namespace": {
            "type": "string"
          },
          "invalidation_tag": {
            "type": "string"
          },
          "invalidation_condition": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "semantic",
              "episodic"
            ]
          }
        }
      },
      "SavedMemory": {
        "type": "object",
        "description": "A freshly-stored memory with its provenance and durability.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "content": {
            "type": "string"
          },
          "namespace": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "semantic",
              "episodic"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "fact",
              "hypothesis",
              "retired"
            ]
          },
          "source": {
            "type": "string",
            "nullable": true
          },
          "confidence": {
            "type": "number",
            "nullable": true
          },
          "learned_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_observed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "hop_count": {
            "type": "integer"
          },
          "parent_id": {
            "type": "integer",
            "nullable": true
          },
          "invalidation_tag": {
            "type": "string",
            "nullable": true
          },
          "invalidation_condition": {
            "type": "string",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "ephemeral": {
            "type": "boolean"
          },
          "expiry": {
            "type": "string",
            "description": "permanent | expires in Xh | expired"
          },
          "advisory": {
            "type": "string",
            "nullable": true,
            "description": "Present when a request was capped (e.g. an anonymous agent asking for permanent → 24h)."
          }
        }
      },
      "MemoryRow": {
        "type": "object",
        "description": "A recalled/listed row: content + full provenance + a trust hint + durability.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "content": {
            "type": "string"
          },
          "namespace": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "fact",
              "hypothesis",
              "retired"
            ]
          },
          "source": {
            "type": "string",
            "nullable": true
          },
          "confidence": {
            "type": "number",
            "nullable": true
          },
          "similarity": {
            "type": "number",
            "nullable": true,
            "description": "Cosine similarity (recall only); null for list."
          },
          "learned_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_observed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "hop_count": {
            "type": "integer"
          },
          "parent_id": {
            "type": "integer",
            "nullable": true
          },
          "superseded_by": {
            "type": "integer",
            "nullable": true
          },
          "invalidation_tag": {
            "type": "string",
            "nullable": true
          },
          "invalidation_condition": {
            "type": "string",
            "nullable": true
          },
          "retired_reason": {
            "type": "string",
            "nullable": true
          },
          "embedding_model": {
            "type": "string"
          },
          "unattributable": {
            "type": "boolean",
            "description": "hop_count>0 && last_observed_at=null — derived without a fresh observation."
          },
          "trust": {
            "type": "string",
            "description": "A hint on how far to trust this row."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "ephemeral": {
            "type": "boolean"
          },
          "expiry": {
            "type": "string"
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "id": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string"
              }
            ],
            "description": "Request id (omit for a notification)."
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "tools/list",
              "tools/call",
              "ping"
            ]
          },
          "params": {
            "type": "object",
            "description": "For tools/call: { name, arguments }."
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "id": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "type": "object"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}