{
  "$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', 'custom', 'verbatim', or 'chunks'",
          "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": "Target max characters for each content chunk",
          "title": "Retain Chunk Size"
        },
        "retain_structured_chunk_size": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max characters for a single JSONL line or conversation turn to keep whole; defaults to retain_chunk_size when unset",
          "title": "Retain Structured 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"
        },
        "enable_text_search": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Toggle the keyword (BM25) arm during recall, leaving pure vector search",
          "title": "Enable Text Search"
        },
        "enable_temporal_retrieval": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Toggle the temporal arm (and its date-aware query analysis) during recall",
          "title": "Enable Temporal Retrieval"
        },
        "enable_graph_retrieval": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Toggle the entity/link graph arm during recall",
          "title": "Enable Graph Retrieval"
        },
        "enable_reranking": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Toggle cross-encoder reranking during recall",
          "title": "Enable Reranking"
        },
        "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": {
                "$ref": "#/$defs/LabelGroup"
              },
              "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"
        },
        "retain_max_attachments_per_chunk": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Hard cap on inline images in a single extraction chunk",
          "title": "Retain Max Attachments Per Chunk"
        },
        "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"
        },
        "observation_scope_limits": {
          "anyOf": [
            {
              "items": {
                "additionalProperties": true,
                "type": "object"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "DEPRECATED \u2014 use consolidation_strategies, which carries the mission too. Still honoured, but consulted only after consolidation_strategies. Per-scope overrides of max_observations_per_scope: [{\"scope\": [\"run_*\", \"shared\"], \"limit\": 1}]. Each scope is a list of fnmatch tag-globs; a consolidation scope matches under exact cover (every tag matched by a glob and every glob matched by a tag). The first matching rule wins; unmatched scopes fall back to max_observations_per_scope.",
          "title": "Observation Scope Limits"
        },
        "consolidation_strategies": {
          "anyOf": [
            {
              "items": {
                "$ref": "#/$defs/ConsolidationStrategySpec"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Per-scope consolidation settings: [{\"scopes\": [{\"tags\": [\"company:*\"]}], \"observations_mission\": \"Record only generalized trends.\", \"max_observations_per_scope\": 20}]. Each strategy lists the rules it claims scopes with \u2014 a rule's tags are fnmatch globs that must all be on the scope, and its \"tags_match\" decides whether the scope may carry others (\"all\", the default) or not (\"exact\"). The rules are alternatives: any one matching claims the scope. A strategy may set any of observations_mission, max_observations_per_scope, consolidation_source_facts_max_tokens and consolidation_source_facts_max_tokens_per_observation; each is optional. Exactly one strategy applies to a scope: the first in the list that claims it. Whatever that strategy leaves unset \u2014 and every scope no strategy claims \u2014 uses the bank-wide value; a later strategy never fills the gaps. Supersedes observation_scope_limits. Lets one bank be federated across user/team/company tag scopes, each consolidating under its own brief.",
          "title": "Consolidation Strategies"
        },
        "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"
        },
        "knowledge_page_default_trigger": {
          "anyOf": [
            {
              "additionalProperties": true,
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Trigger fields merged over the built-in knowledge-page default when a page is created (e.g. {\"refresh_cron\": \"0 * * * *\"}). A trigger sent with the create request still wins.",
          "title": "Knowledge Page Default Trigger"
        },
        "reflect_default_options": {
          "anyOf": [
            {
              "additionalProperties": true,
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Default reflect options for this bank (e.g. {\"reflect_search_observations_max_tokens\": 3000, \"reflect_search_observations_include_entities\": false}). Applied to every reflect in the bank -- API, MCP and mental-model refresh -- whenever the request (or the model's trigger) leaves the option unset.",
          "title": "Reflect Default Options"
        },
        "mental_model_min_refresh_interval_seconds": {
          "anyOf": [
            {
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Minimum seconds between two automatic refreshes of the same mental model in this bank. 0 (the default) means no floor. Overridable per model via the trigger's min_refresh_interval_seconds.",
          "title": "Mental Model Min Refresh Interval Seconds"
        },
        "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"
        },
        "audit_log_enabled": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Enable audit logging for this bank (overrides the server default)",
          "title": "Audit Log Enabled"
        },
        "store_document_text": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Persist raw source text (documents.original_text / chunks.chunk_text). Set false to keep only derived facts.",
          "title": "Store Document Text"
        },
        "enable_auto_consolidation": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Automatically consolidate observations after retain",
          "title": "Enable Auto Consolidation"
        },
        "consolidation_max_memories_per_round": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max memory units fed into a single consolidation round",
          "title": "Consolidation Max Memories Per Round"
        },
        "consolidation_llm_parallelism": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Number of consolidation LLM batches processed concurrently",
          "title": "Consolidation Llm Parallelism"
        },
        "recall_include_chunks": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Include raw chunks in recall results",
          "title": "Recall Include Chunks"
        },
        "recall_max_tokens": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max tokens of results returned by recall",
          "title": "Recall Max Tokens"
        },
        "recall_chunks_max_tokens": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max tokens of raw chunks returned by recall (when recall_include_chunks is set)",
          "title": "Recall Chunks Max Tokens"
        },
        "memory_defense": {
          "anyOf": [
            {
              "additionalProperties": true,
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Memory Defense policy for this bank (validated against the DefensePolicy schema on write)",
          "title": "Memory Defense"
        }
      },
      "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"
    },
    "Budget": {
      "description": "Budget levels for recall/reflect operations.",
      "enum": [
        "low",
        "mid",
        "high"
      ],
      "title": "Budget",
      "type": "string"
    },
    "ConsolidationScopePattern": {
      "description": "One rule of a consolidation strategy: tags, and how they must match.\n\n``tags`` may be empty \u2014 that is a rule still being filled in, which the editor\nsaves as typed and consolidation ignores. The type pins the *shape*, not\ncompleteness: a string where the tag list belongs is rejected at the door\ninstead of being stored and silently ignored for the life of the bank.\n\nUnknown keys are rejected too, but by :class:`StrictConsolidationStrategySpec`\non the write path rather than by ``extra=\"forbid\"`` here: that would put\n``additionalProperties: false`` in the schema, which openapi-generator cannot\nprocess (\"Codegen Property not yet supported in getPydanticType\").",
      "properties": {
        "tags": {
          "description": "fnmatch tag patterns, e.g. company:*",
          "items": {
            "type": "string"
          },
          "title": "Tags",
          "type": "array"
        },
        "tags_match": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "\"all\" (the default when omitted): the scope has every tag in the rule, other tags allowed. \"exact\": exactly these tags and no others.",
          "title": "Tags Match"
        }
      },
      "title": "ConsolidationScopePattern",
      "type": "object"
    },
    "ConsolidationStrategySpec": {
      "description": "One `consolidation_strategies` entry: the rules it claims scopes with, and\nthe observation settings those scopes use. Every setting is optional; unset\nones come from the bank-wide values.",
      "properties": {
        "scopes": {
          "description": "Alternatives: the strategy claims a scope when any rule matches it",
          "items": {
            "$ref": "#/$defs/ConsolidationScopePattern"
          },
          "title": "Scopes",
          "type": "array"
        },
        "observations_mission": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Observations Mission"
        },
        "max_observations_per_scope": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Max Observations Per Scope"
        },
        "consolidation_source_facts_max_tokens": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Consolidation Source Facts Max Tokens"
        },
        "consolidation_source_facts_max_tokens_per_observation": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Consolidation Source Facts Max Tokens Per Observation"
        }
      },
      "title": "ConsolidationStrategySpec",
      "type": "object"
    },
    "LabelGroup": {
      "description": "A label group (dimension) with its type and allowed values.",
      "properties": {
        "key": {
          "title": "Key",
          "type": "string"
        },
        "description": {
          "default": "",
          "title": "Description",
          "type": "string"
        },
        "type": {
          "default": "value",
          "enum": [
            "value",
            "multi-values",
            "text",
            "multi-text",
            "map"
          ],
          "title": "Type",
          "type": "string"
        },
        "optional": {
          "default": true,
          "title": "Optional",
          "type": "boolean"
        },
        "tag": {
          "default": false,
          "title": "Tag",
          "type": "boolean"
        },
        "values": {
          "default": [],
          "items": {
            "$ref": "#/$defs/LabelValue"
          },
          "title": "Values",
          "type": "array"
        },
        "fields": {
          "additionalProperties": {
            "$ref": "#/$defs/MapField"
          },
          "default": {},
          "title": "Fields",
          "type": "object"
        }
      },
      "required": [
        "key"
      ],
      "title": "LabelGroup",
      "type": "object"
    },
    "LabelValue": {
      "description": "A single allowed value for a label group.",
      "properties": {
        "value": {
          "title": "Value",
          "type": "string"
        },
        "description": {
          "default": "",
          "title": "Description",
          "type": "string"
        }
      },
      "required": [
        "value"
      ],
      "title": "LabelValue",
      "type": "object"
    },
    "MapField": {
      "description": "A field within a map-type entity label group. Supports recursion via type='map'.",
      "properties": {
        "type": {
          "default": "text",
          "enum": [
            "text",
            "multi-text",
            "value",
            "multi-values",
            "map"
          ],
          "title": "Type",
          "type": "string"
        },
        "description": {
          "default": "",
          "title": "Description",
          "type": "string"
        },
        "values": {
          "default": [],
          "items": {
            "$ref": "#/$defs/LabelValue"
          },
          "title": "Values",
          "type": "array"
        },
        "fields": {
          "additionalProperties": {
            "$ref": "#/$defs/MapField"
          },
          "default": {},
          "title": "Fields",
          "type": "object"
        }
      },
      "title": "MapField",
      "type": "object"
    },
    "MentalModelTrigger": {
      "description": "Trigger settings for a mental model.\n\nA refresh is not an ad-hoc reflect with different arguments: it synthesizes a\nwhole document, so it wants its own retrieval and iteration settings. This\ntrigger is therefore the only source for them \u2014 a bank's\n``reflect_default_options`` deliberately does not reach a refresh. The\nper-bank default for these fields is ``knowledge_page_default_trigger``,\nwhich is merged over this same shape when a page is created.",
      "properties": {
        "budget": {
          "anyOf": [
            {
              "$ref": "#/$defs/Budget"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "How many agent iterations a refresh may spend, as a multiple of reflect_max_iterations: 'low' halves it, 'mid' keeps it, 'high' doubles it. A refresh is the heaviest reflect there is \u2014 it writes a whole document, and with exclude_mental_models it must read raw facts first \u2014 so null means 'mid', not the 'low' an ad-hoc reflect defaults to."
        },
        "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"
        },
        "refresh_cron": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Cron expression (UTC, standard 5-field syntax, e.g. '0 3 * * *' for daily at 03:00 UTC) for refreshing this mental model on a fixed schedule. Mutually exclusive with refresh_after_consolidation \u2014 a model refreshes either after consolidation or on a cron schedule, not both. A scheduled refresh only runs when the model is stale (new memories in its scope since the last refresh); if nothing changed, the tick is skipped to avoid a wasted LLM call. null = no schedule.",
          "title": "Refresh Cron"
        },
        "min_refresh_interval_seconds": {
          "anyOf": [
            {
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Minimum seconds between two automatic refreshes of this mental model. A triggered refresh that arrives sooner is not dropped: it is queued and parked until the window expires, and every further trigger in the meantime folds into that one queued refresh, so a burst of retains costs one refresh instead of one per retain. Applies to both refresh_after_consolidation and refresh_cron. Explicit refreshes (API, MCP, control plane) ignore it and run immediately. 0 disables the floor for this model; null falls back to the bank/global mental_model_min_refresh_interval_seconds setting (itself 0 by default, i.e. no floor).",
          "title": "Min Refresh Interval Seconds"
        },
        "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",
                "exact"
              ],
              "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. Under 'all_strict' a memory must carry EVERY one of the model's tags and untagged memories are excluded, which is why a model tagged with labels its memories do not carry refreshes to empty content. Set to 'all' to keep requiring the tags while including untagged memories, or to 'any' to include untagged memories alongside any single tag match. Staleness ignores that widening: an untagged write never marks a tagged model stale, in any mode \u2014 only a write that matches the model's tags does.",
          "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"
        },
        "reflect_search_observations_max_tokens": {
          "anyOf": [
            {
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Override the token budget for the refresh's search_observations calls. Observation evidence is often the largest contributor to the reflect context; lowering it trades the lowest-ranked observations for a smaller LLM context. null means the shipped 5000.",
          "title": "Reflect Search Observations Max Tokens"
        },
        "reflect_search_observations_include_entities": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Override whether search_observations attaches resolved entity names to each observation. Entities can be more than half the serialized tool payload; turning them off keeps the same observations and ranking with a much smaller context. null means enabled.",
          "title": "Reflect Search Observations Include Entities"
        },
        "response_schema": {
          "anyOf": [
            {
              "additionalProperties": true,
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional JSON Schema for structured output. When set, each refresh runs the same structured-output extraction as reflect's response_schema and stores the parsed result under reflect_response.structured_output alongside the markdown content.",
          "title": "Response Schema"
        },
        "keep_trace": {
          "default": false,
          "description": "If true, every refresh of this mental model records how it reached its result under reflect_response.trace: the mode it ran in and why, the resolved scope and time window, how many facts retrieval returned versus how many the agent used, the tool and LLM calls, and any delta operations. Only the latest refresh's trace is kept. This is the only way to diagnose a cron- or consolidation-driven refresh after the fact, since no human sees those run. Tool outputs are reduced to result counts to keep the stored trace bounded; use LLM request tracing for raw prompts and responses.",
          "title": "Keep Trace",
          "type": "boolean"
        }
      },
      "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",
            "exact"
          ],
          "title": "Match",
          "type": "string"
        },
        "resolve": {
          "default": "exact",
          "enum": [
            "exact",
            "fuzzy"
          ],
          "title": "Resolve",
          "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"
}
