{
  "$defs": {
    "BankTemplateConfig": {
      "description": "Bank configuration fields within a template manifest.\n\nOnly includes configurable (per-bank) fields. Credential fields\n(API keys, base URLs) are intentionally excluded for security.",
      "properties": {
        "reflect_mission": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Mission/context for Reflect operations",
          "title": "Reflect Mission"
        },
        "retain_mission": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Steers what gets extracted during retain",
          "title": "Retain Mission"
        },
        "retain_extraction_mode": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Fact extraction mode: 'concise' (default), 'verbose', or 'custom'",
          "title": "Retain Extraction Mode"
        },
        "retain_custom_instructions": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Custom extraction prompt (when mode='custom')",
          "title": "Retain Custom Instructions"
        },
        "retain_chunk_size": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max token size for each content chunk",
          "title": "Retain Chunk Size"
        },
        "enable_observations": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Toggle observation consolidation",
          "title": "Enable Observations"
        },
        "observations_mission": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Controls what gets synthesised",
          "title": "Observations Mission"
        },
        "disposition_skepticism": {
          "anyOf": [
            {
              "maximum": 5,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Skepticism trait (1-5)",
          "title": "Disposition Skepticism"
        },
        "disposition_literalism": {
          "anyOf": [
            {
              "maximum": 5,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Literalism trait (1-5)",
          "title": "Disposition Literalism"
        },
        "disposition_empathy": {
          "anyOf": [
            {
              "maximum": 5,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Empathy trait (1-5)",
          "title": "Disposition Empathy"
        },
        "entity_labels": {
          "anyOf": [
            {
              "items": {
                "additionalProperties": true,
                "type": "object"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Controlled vocabulary for entity labels",
          "title": "Entity Labels"
        },
        "entities_allow_free_form": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Allow entities outside the label vocabulary",
          "title": "Entities Allow Free Form"
        },
        "retain_default_strategy": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Name of the default retain strategy (key into retain_strategies map)",
          "title": "Retain Default Strategy"
        },
        "retain_strategies": {
          "anyOf": [
            {
              "additionalProperties": true,
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Map of retain strategy name to per-strategy config dict",
          "title": "Retain Strategies"
        },
        "retain_chunk_batch_size": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max chunks per streaming batch (0 disables batching)",
          "title": "Retain Chunk Batch Size"
        },
        "mcp_enabled_tools": {
          "anyOf": [
            {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "MCP tool allowlist for this bank (None = all tools)",
          "title": "Mcp Enabled Tools"
        },
        "consolidation_llm_batch_size": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "LLM batch size for observation consolidation",
          "title": "Consolidation Llm Batch Size"
        },
        "consolidation_source_facts_max_tokens": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max tokens of source facts per consolidation batch",
          "title": "Consolidation Source Facts Max Tokens"
        },
        "consolidation_source_facts_max_tokens_per_observation": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max tokens of source facts per observation",
          "title": "Consolidation Source Facts Max Tokens Per Observation"
        },
        "max_observations_per_scope": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max observations to retain per consolidation scope",
          "title": "Max Observations Per Scope"
        },
        "reflect_source_facts_max_tokens": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max tokens of source facts per reflect call",
          "title": "Reflect Source Facts Max Tokens"
        },
        "llm_gemini_safety_settings": {
          "anyOf": [
            {
              "items": {},
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Per-bank Gemini/VertexAI safety filter settings",
          "title": "Llm Gemini Safety Settings"
        },
        "recall_budget_function": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Recall budget mapping function: 'fixed' or 'adaptive'",
          "title": "Recall Budget Function"
        },
        "recall_budget_fixed_low": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Fixed thinking_budget for budget=low (function='fixed')",
          "title": "Recall Budget Fixed Low"
        },
        "recall_budget_fixed_mid": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Fixed thinking_budget for budget=mid (function='fixed')",
          "title": "Recall Budget Fixed Mid"
        },
        "recall_budget_fixed_high": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Fixed thinking_budget for budget=high (function='fixed')",
          "title": "Recall Budget Fixed High"
        },
        "recall_budget_adaptive_low": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Ratio of max_tokens for budget=low (function='adaptive')",
          "title": "Recall Budget Adaptive Low"
        },
        "recall_budget_adaptive_mid": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Ratio of max_tokens for budget=mid (function='adaptive')",
          "title": "Recall Budget Adaptive Mid"
        },
        "recall_budget_adaptive_high": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Ratio of max_tokens for budget=high (function='adaptive')",
          "title": "Recall Budget Adaptive High"
        },
        "recall_budget_min": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Floor for the adaptive function (after clamping)",
          "title": "Recall Budget Min"
        },
        "recall_budget_max": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Ceiling for the adaptive function (after clamping)",
          "title": "Recall Budget Max"
        }
      },
      "title": "BankTemplateConfig",
      "type": "object"
    },
    "BankTemplateDirective": {
      "description": "A directive definition within a bank template manifest.\n\nDirectives are matched by name on re-import: existing directives\nwith the same name are updated, new ones are created.",
      "properties": {
        "name": {
          "description": "Human-readable name for the directive (used as match key on re-import)",
          "title": "Name",
          "type": "string"
        },
        "content": {
          "description": "The directive text to inject into prompts",
          "title": "Content",
          "type": "string"
        },
        "priority": {
          "default": 0,
          "description": "Higher priority directives are injected first",
          "title": "Priority",
          "type": "integer"
        },
        "is_active": {
          "default": true,
          "description": "Whether this directive is active",
          "title": "Is Active",
          "type": "boolean"
        },
        "tags": {
          "default": [],
          "description": "Tags for filtering",
          "items": {
            "type": "string"
          },
          "title": "Tags",
          "type": "array"
        }
      },
      "required": [
        "name",
        "content"
      ],
      "title": "BankTemplateDirective",
      "type": "object"
    },
    "BankTemplateMentalModel": {
      "description": "A mental model definition within a bank template manifest.",
      "properties": {
        "id": {
          "description": "Unique ID for the mental model (alphanumeric lowercase with hyphens)",
          "title": "Id",
          "type": "string"
        },
        "name": {
          "description": "Human-readable name for the mental model",
          "title": "Name",
          "type": "string"
        },
        "source_query": {
          "description": "The query to run to generate content",
          "title": "Source Query",
          "type": "string"
        },
        "tags": {
          "default": [],
          "description": "Tags for scoped visibility",
          "items": {
            "type": "string"
          },
          "title": "Tags",
          "type": "array"
        },
        "max_tokens": {
          "default": 2048,
          "description": "Maximum tokens for generated content",
          "maximum": 8192,
          "minimum": 256,
          "title": "Max Tokens",
          "type": "integer"
        },
        "trigger": {
          "$ref": "#/$defs/MentalModelTrigger",
          "default": {},
          "description": "Trigger settings"
        }
      },
      "required": [
        "id",
        "name",
        "source_query"
      ],
      "title": "BankTemplateMentalModel",
      "type": "object"
    },
    "MentalModelTrigger": {
      "description": "Trigger settings for a mental model.",
      "properties": {
        "mode": {
          "default": "full",
          "description": "Refresh mode. 'full' (default) regenerates the mental model content from scratch on each refresh. 'delta' performs surgical edits against the existing content: unchanged sections are preserved byte-for-byte, stale content is removed, new content is added. If the mental model has no existing content, or if the source_query has changed since the last refresh, delta mode falls back to a full regeneration automatically.",
          "enum": [
            "full",
            "delta"
          ],
          "title": "Mode",
          "type": "string"
        },
        "refresh_after_consolidation": {
          "default": false,
          "description": "If true, refresh this mental model after observations consolidation (real-time mode)",
          "title": "Refresh After Consolidation",
          "type": "boolean"
        },
        "fact_types": {
          "anyOf": [
            {
              "items": {
                "enum": [
                  "world",
                  "experience",
                  "observation"
                ],
                "type": "string"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Filter which fact types are retrieved during reflect. None means all types (world, experience, observation).",
          "title": "Fact Types"
        },
        "exclude_mental_models": {
          "default": false,
          "description": "If true, exclude all mental models from the reflect loop (skip search_mental_models tool).",
          "title": "Exclude Mental Models",
          "type": "boolean"
        },
        "exclude_mental_model_ids": {
          "anyOf": [
            {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Exclude specific mental models by ID from the reflect loop.",
          "title": "Exclude Mental Model Ids"
        },
        "tags_match": {
          "anyOf": [
            {
              "enum": [
                "any",
                "all",
                "any_strict",
                "all_strict"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Override how the model's tags filter memories during refresh. If not set, defaults to 'all_strict' when the model has tags (security isolation) or 'any' when the model has no tags. Set to 'any' to include untagged memories alongside tagged ones during refresh.",
          "title": "Tags Match"
        },
        "tag_groups": {
          "anyOf": [
            {
              "items": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/TagGroupLeaf"
                  },
                  {
                    "$ref": "#/$defs/TagGroupAnd"
                  },
                  {
                    "$ref": "#/$defs/TagGroupOr"
                  },
                  {
                    "$ref": "#/$defs/TagGroupNot"
                  }
                ]
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Compound boolean tag expressions to use during refresh instead of the model's own tags. When set, these tag groups are passed to reflect and the model's flat tags are NOT used for filtering. Supports nested and/or/not expressions for complex tag-based scoping.",
          "title": "Tag Groups"
        },
        "include_chunks": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Override whether the internal recall used during refresh returns raw chunk text. None means use the bank/global config default (recall_include_chunks).",
          "title": "Include Chunks"
        },
        "recall_max_tokens": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Override the token budget for facts returned by the internal recall during refresh. None means use the bank/global config default (recall_max_tokens).",
          "title": "Recall Max Tokens"
        },
        "recall_chunks_max_tokens": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Override the token budget for raw chunks returned by the internal recall during refresh. None means use the bank/global config default (recall_chunks_max_tokens).",
          "title": "Recall Chunks Max Tokens"
        }
      },
      "title": "MentalModelTrigger",
      "type": "object"
    },
    "TagGroupAnd": {
      "description": "Compound AND group: all child filters must match.",
      "properties": {
        "and": {
          "items": {
            "anyOf": [
              {
                "$ref": "#/$defs/TagGroupLeaf"
              },
              {
                "$ref": "#/$defs/TagGroupAnd"
              },
              {
                "$ref": "#/$defs/TagGroupOr"
              },
              {
                "$ref": "#/$defs/TagGroupNot"
              }
            ]
          },
          "title": "And",
          "type": "array"
        }
      },
      "required": [
        "and"
      ],
      "title": "TagGroupAnd",
      "type": "object"
    },
    "TagGroupLeaf": {
      "description": "A leaf tag filter: matches memories by tag list and match mode.",
      "properties": {
        "tags": {
          "items": {
            "type": "string"
          },
          "title": "Tags",
          "type": "array"
        },
        "match": {
          "default": "any_strict",
          "enum": [
            "any",
            "all",
            "any_strict",
            "all_strict"
          ],
          "title": "Match",
          "type": "string"
        }
      },
      "required": [
        "tags"
      ],
      "title": "TagGroupLeaf",
      "type": "object"
    },
    "TagGroupNot": {
      "description": "Compound NOT group: child filter must NOT match.",
      "properties": {
        "not": {
          "anyOf": [
            {
              "$ref": "#/$defs/TagGroupLeaf"
            },
            {
              "$ref": "#/$defs/TagGroupAnd"
            },
            {
              "$ref": "#/$defs/TagGroupOr"
            },
            {
              "$ref": "#/$defs/TagGroupNot"
            }
          ],
          "title": "Not"
        }
      },
      "required": [
        "not"
      ],
      "title": "TagGroupNot",
      "type": "object"
    },
    "TagGroupOr": {
      "description": "Compound OR group: at least one child filter must match.",
      "properties": {
        "or": {
          "items": {
            "anyOf": [
              {
                "$ref": "#/$defs/TagGroupLeaf"
              },
              {
                "$ref": "#/$defs/TagGroupAnd"
              },
              {
                "$ref": "#/$defs/TagGroupOr"
              },
              {
                "$ref": "#/$defs/TagGroupNot"
              }
            ]
          },
          "title": "Or",
          "type": "array"
        }
      },
      "required": [
        "or"
      ],
      "title": "TagGroupOr",
      "type": "object"
    }
  },
  "description": "A bank template manifest for import/export.\n\nVersion field enables forward-compatible schema evolution: the API\nauto-upgrades older manifest versions to the current schema on import.",
  "example": {
    "bank": {
      "disposition_empathy": 5,
      "enable_observations": true,
      "reflect_mission": "You are helping a support agent remember customer interactions.",
      "retain_mission": "Extract customer issues, resolutions, and sentiment."
    },
    "directives": [
      {
        "content": "Always respond with empathy and understanding.",
        "name": "Always be empathetic",
        "priority": 10
      }
    ],
    "mental_models": [
      {
        "id": "sentiment-overview",
        "name": "Customer Sentiment Overview",
        "source_query": "What is the overall sentiment trend?",
        "trigger": {
          "refresh_after_consolidation": true
        }
      }
    ],
    "version": "1"
  },
  "properties": {
    "version": {
      "description": "Manifest schema version (currently '1')",
      "title": "Version",
      "type": "string"
    },
    "bank": {
      "anyOf": [
        {
          "$ref": "#/$defs/BankTemplateConfig"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Bank configuration to apply. Omit to leave config unchanged."
    },
    "mental_models": {
      "anyOf": [
        {
          "items": {
            "$ref": "#/$defs/BankTemplateMentalModel"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Mental models to create or update (matched by id). Omit to leave unchanged.",
      "title": "Mental Models"
    },
    "directives": {
      "anyOf": [
        {
          "items": {
            "$ref": "#/$defs/BankTemplateDirective"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Directives to create or update (matched by name). Omit to leave unchanged.",
      "title": "Directives"
    }
  },
  "required": [
    "version"
  ],
  "title": "BankTemplateManifest",
  "type": "object"
}
