From 43f59a85c6d7c5d7f2da557ebb1f0ae2cba78cea Mon Sep 17 00:00:00 2001
From: Ran <16112591+chen-ran@users.noreply.github.com>
Date: Sun, 21 Jun 2026 00:36:45 +0800
Subject: [PATCH 1/3] refactor(memory): remove sparse vector subsystem and
clean up dead code
Delete the entire sparse-vector memory path (sparse runtime, Flask encoder
service, Dockerfile.sparse, Qdrant sparse methods) and the dead code it left
behind. Sparse retrieval is superseded by the upcoming PG-backed wiki graph,
where Qdrant becomes an optional auxiliary seed index rather than a primary
store.
Sparse removal:
- Drop ModeSparse, the sparseRuntime, internal/memory/sparse/ (encoder +
Flask service), docker/Dockerfile.sparse, and the Qdrant sparse methods
(EnsureCollection, Upsert, Search, SparseVector type, strPtr).
- Remove [sparse] from all 9 config TOMLs, the sparse service + NO_PROXY
tokens from every docker-compose file, the CI matrix entries, the
USE_SPARSE handling in scripts/install.sh, and the AGENTS/DEPLOYMENT/
CONTRIBUTING docs.
- Strip sparse from the web UI: builtin-config mode list, settings-context-card
status logic, and the sparseSectionTitle/sparseInstallHint/... i18n keys
(en/zh/ja). Drop SparseConfig from packages/config types.
- Migrate builtin/formation/file tests off the deleted sparse fakes onto a
new shared in-memory fakeStore.
Incidental cleanup exposed by the deletion:
- Remove vestigial adapters types with no callers: EmbedInput,
EmbedUpsertRequest, EmbedUpsertResponse, MemoryCompactCapability.Native.
- Relocate runtimeHash from dense_runtime.go into shared.go next to its
sibling shared helpers (it was the last "shared" helper stranded in the
dense file).
- Consolidate the two duplicated parseQdrantHostPort implementations into
a single qdrant.ParseHostPort, used by both the factory and the status
service.
- Fix a latent parallel-test race in runtimeMemoryID by appending a
process-wide monotonic counter so two Adds in the same nanosecond no
longer collide on their ID.
Regenerate swagger + TS SDK (TopKBucket/CDFCurve and the removed types drop
out of the OpenAPI schema).
---
.github/workflows/docker.yml | 15 +-
AGENTS.md | 3 +-
CONTRIBUTING.md | 2 +-
DEPLOYMENT.md | 11 +-
apps/web/src/i18n/locales/en.json | 8 -
apps/web/src/i18n/locales/ja.json | 7 -
apps/web/src/i18n/locales/zh.json | 8 -
.../bots/components/settings-context-card.vue | 16 +-
.../memory/components/builtin-config.vue | 12 +-
conf/app.apple.toml | 3 -
conf/app.docker.toml | 3 -
conf/app.example.toml | 3 -
conf/app.kata.docker.toml | 3 -
conf/app.local.toml | 3 -
conf/app.windows.toml | 3 -
devenv/app.dev.toml | 3 -
devenv/app.kata.dev.toml | 3 -
devenv/app.sqlite.dev.toml | 3 -
devenv/docker-compose.minify.yml | 19 +-
devenv/docker-compose.sqlite.minify.yml | 19 +-
devenv/docker-compose.sqlite.yml | 18 +-
devenv/docker-compose.yml | 23 +-
docker-compose.sqlite.yml | 20 +-
docker-compose.yml | 24 +-
docker/Dockerfile.sparse | 43 --
docker/docker-compose.cn.yml | 2 -
docker/docker-compose.sqlite.cn.yml | 2 -
docker/docker-compose.yml | 6 +-
internal/config/config.go | 5 -
internal/memory/adapters/builtin/builtin.go | 2 +-
.../memory/adapters/builtin/builtin_test.go | 66 +-
.../memory/adapters/builtin/dense_runtime.go | 11 +-
internal/memory/adapters/builtin/factory.go | 57 +-
.../adapters/builtin/file_runtime_test.go | 4 +-
.../memory/adapters/builtin/formation_test.go | 66 +-
internal/memory/adapters/builtin/shared.go | 21 +-
.../memory/adapters/builtin/sparse_runtime.go | 618 ------------------
.../adapters/builtin/sparse_runtime_test.go | 419 ------------
.../memory/adapters/builtin/store_test.go | 77 +++
internal/memory/adapters/service.go | 37 +-
internal/memory/adapters/types.go | 61 +-
internal/memory/qdrant/client.go | 119 +---
internal/memory/sparse/encoder.go | 159 -----
internal/memory/sparse/service/main.py | 150 -----
.../memory/sparse/service/requirements.txt | 6 -
packages/config/src/types.ts | 5 -
scripts/install.sh | 29 +-
47 files changed, 220 insertions(+), 1977 deletions(-)
delete mode 100644 docker/Dockerfile.sparse
delete mode 100644 internal/memory/adapters/builtin/sparse_runtime.go
delete mode 100644 internal/memory/adapters/builtin/sparse_runtime_test.go
create mode 100644 internal/memory/adapters/builtin/store_test.go
delete mode 100644 internal/memory/sparse/encoder.go
delete mode 100644 internal/memory/sparse/service/main.py
delete mode 100644 internal/memory/sparse/service/requirements.txt
mode change 100755 => 100644 scripts/install.sh
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index c68ab1e27f..d3a873fc7d 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -36,7 +36,6 @@ jobs:
outputs:
server: ${{ steps.filter.outputs.server }}
web: ${{ steps.filter.outputs.web }}
- sparse: ${{ steps.filter.outputs.sparse }}
workspace: ${{ steps.filter.outputs.workspace }}
steps:
- uses: actions/checkout@v4
@@ -67,9 +66,6 @@ jobs:
- 'pnpm-workspace.yaml'
- 'docker/Dockerfile.web'
- 'docker/nginx.conf'
- sparse:
- - 'internal/memory/sparse/service/**'
- - 'docker/Dockerfile.sparse'
workspace:
- 'docker/Dockerfile.workspace'
- 'scripts/desktop-install.sh'
@@ -79,7 +75,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- image: [server, web, sparse, workspace]
+ image: [server, web, workspace]
platform: [linux/amd64, linux/arm64]
variant: ["", debian]
exclude:
@@ -87,8 +83,6 @@ jobs:
variant: debian
- image: web
variant: debian
- - image: sparse
- variant: debian
- image: workspace
variant: ""
include:
@@ -96,8 +90,6 @@ jobs:
dockerfile: docker/Dockerfile.server
- image: web
dockerfile: docker/Dockerfile.web
- - image: sparse
- dockerfile: docker/Dockerfile.sparse
- image: workspace
dockerfile: docker/Dockerfile.workspace
- platform: linux/amd64
@@ -120,7 +112,6 @@ jobs:
case "$IMAGE" in
server) CHANGED="${{ needs.detect-changes.outputs.server }}" ;;
web) CHANGED="${{ needs.detect-changes.outputs.web }}" ;;
- sparse) CHANGED="${{ needs.detect-changes.outputs.sparse }}" ;;
workspace) CHANGED="${{ needs.detect-changes.outputs.workspace }}" ;;
esac
if [[ "$FORCE" == "true" || "$CHANGED" == "true" ]]; then
@@ -230,9 +221,6 @@ jobs:
- image: web
artifact_key: web
variant: ""
- - image: sparse
- artifact_key: sparse
- variant: ""
- image: workspace
artifact_key: workspace-debian
variant: debian
@@ -251,7 +239,6 @@ jobs:
case "$IMAGE" in
server) CHANGED="${{ needs.detect-changes.outputs.server }}" ;;
web) CHANGED="${{ needs.detect-changes.outputs.web }}" ;;
- sparse) CHANGED="${{ needs.detect-changes.outputs.sparse }}" ;;
workspace) CHANGED="${{ needs.detect-changes.outputs.workspace }}" ;;
esac
if [[ "$FORCE" == "true" || "$CHANGED" == "true" ]]; then
diff --git a/AGENTS.md b/AGENTS.md
index 22bf6b4c05..0a31d017b3 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -284,7 +284,7 @@ docker compose up -d # Start all services
```
Production deploy services are `postgres`, `migrate`, `server`, and `web`.
-Optional profiles: `qdrant` (vector DB), `sparse` (BM25 search). This is distinct from the native desktop client, which manages its own local server and embedded Qdrant instead of using the Compose web/server split.
+Optional profiles: `qdrant` (vector DB). This is distinct from the native desktop client, which manages its own local server and embedded Qdrant instead of using the Compose web/server split.
## Key Development Rules
@@ -458,7 +458,6 @@ The main configuration file is `config.toml` (copied from `conf/app.example.toml
- `[postgres]` — PostgreSQL connection
- `[sqlite]` — SQLite database file and WAL/lock settings
- `[qdrant]` — Qdrant vector database connection
-- `[sparse]` — Sparse (BM25) search service connection
- `[web]` — Web frontend address
- `[registry]` — Provider registry (`providers_dir` pointing to `conf/providers/`)
- `[supermarket]` — Supermarket integration (base_url)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 45b0c0adc1..06fb92d49e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -33,7 +33,7 @@ mise run dev # Start the full containerized dev environment
4. Web frontend with Vite hot reload
The dev stack uses `devenv/app.dev.toml` directly and does not overwrite the repo root `config.toml`.
-Default host ports are shifted away from the production compose stack: Web `18082`, API `18080`, Postgres `15432`, Qdrant `16333`/`16334`, Sparse `18085`.
+Default host ports are shifted away from the production compose stack: Web `18082`, API `18080`, Postgres `15432`, Qdrant `16333`/`16334`.
## Daily Development
diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
index 0cb2e07ade..b4ec73f773 100644
--- a/DEPLOYMENT.md
+++ b/DEPLOYMENT.md
@@ -30,10 +30,10 @@ nano config.toml # Change passwords and JWT secret
> **Important**: You must create `config.toml` before starting. `docker-compose.yml` mounts `./config.toml` into the containers — running without it will fail.
-### Standard startup (with Qdrant + Sparse)
+### Standard startup (with Qdrant)
```bash
-docker compose --profile qdrant --profile sparse up -d
+docker compose --profile qdrant up -d
```
### Minimal startup (core only)
@@ -81,13 +81,12 @@ The base `docker-compose.yml` contains all services. Core services (`postgres`,
| Profile | Service | Description |
|---------|---------|-------------|
| `qdrant` | Qdrant | Vector database for memory semantic search |
-| `sparse` | Sparse | Neural sparse memory retrieval service |
### Supported combinations
```bash
-# Core + Qdrant + Sparse (recommended default)
-docker compose --profile qdrant --profile sparse up -d
+# Core + Qdrant (recommended default)
+docker compose --profile qdrant up -d
```
### SaaS / external providers
@@ -100,7 +99,7 @@ Uncomment `registry = "memoh.cn"` in `config.toml` under `[container]`, then add
```bash
docker compose -f docker-compose.yml -f docker/docker-compose.cn.yml \
- --profile qdrant --profile sparse up -d
+ --profile qdrant up -d
```
## Prerequisites
diff --git a/apps/web/src/i18n/locales/en.json b/apps/web/src/i18n/locales/en.json
index 61e322f5f3..37a67ba6c3 100644
--- a/apps/web/src/i18n/locales/en.json
+++ b/apps/web/src/i18n/locales/en.json
@@ -994,16 +994,13 @@
"modeHint": "How bots store and recall long-term memory.",
"advanced": "Advanced",
"advancedHint": "Custom memory backends for advanced setups.",
- "sparseSectionTitle": "Sparse Retrieval",
"denseSectionTitle": "Dense Retrieval",
- "sparseInstallHint": "Sparse mode depends on the optional sparse service. Enable the sparse installation option when running the installer or start the sparse profile in Docker Compose.",
"denseBackend": "Dense Backend",
"denseBackendValue": "Embedding API + Qdrant",
"denseEmbeddingModel": "Dense Embedding Model",
"denseEmbeddingModelDescription": "Select the third-party embedding model used before local rerank.",
"denseQdrantHint": "Dense memory will use Qdrant as the storage backend after the backend runtime is connected.",
"qdrantCollection": "Qdrant Collection",
- "sparseQdrantCollectionDescription": "Sparse mode writes to the Qdrant collection. Current/default collection: {collection}.",
"denseQdrantCollectionDescription": "Dense mode writes to the Qdrant collection. Current/default collection: {collection}.",
"collectionPoints": "Points in collection",
"collectionExists": "Collection exists",
@@ -1012,12 +1009,10 @@
"collectionUnavailable": "Unavailable",
"modeNames": {
"off": "Off",
- "sparse": "Keyword",
"dense": "Semantic"
},
"modeDescriptions": {
"off": "Don't build a long-term memory index; keep only the basic file memory.",
- "sparse": "Match memories by keyword, using the optional sparse retrieval service.",
"dense": "Turn memories into vectors with an embedding model and retrieve by semantic similarity."
},
"providerNames": {
@@ -2004,8 +1999,6 @@
"memoryProvider": "Memory Provider",
"memoryProviderPlaceholder": "Select memory provider (disabled if empty)",
"memoryModePreview": "Selected built-in mode: {mode}",
- "sparseStatusTitle": "Sparse Retrieval Status",
- "sparseStatusHint": "Markdown files are the source of truth. Syncing rebuilds the Qdrant index from /data/memory.",
"denseStatusTitle": "Dense Retrieval Status",
"denseStatusHint": "Markdown files are the source of truth. Syncing rebuilds the dense Qdrant index from /data/memory.",
"mem0StatusTitle": "Mem0 Sync Status",
@@ -2020,7 +2013,6 @@
"memorySourceEntries": "Source Entries",
"memoryIndexedEntries": "Indexed Entries",
"memoryQdrantCollection": "Qdrant Collection",
- "memoryEncoderHealth": "Sparse Encoder",
"memoryDenseEmbeddingHealth": "Embedding Backend",
"memoryQdrantHealth": "Qdrant",
"memoryHealthOk": "Healthy",
diff --git a/apps/web/src/i18n/locales/ja.json b/apps/web/src/i18n/locales/ja.json
index 14d94bf57d..f2999d6a6d 100644
--- a/apps/web/src/i18n/locales/ja.json
+++ b/apps/web/src/i18n/locales/ja.json
@@ -941,16 +941,13 @@
"modeHint": "Botが長期記憶を保存し、呼び出す方法。",
"advanced": "詳細",
"advancedHint": "高度な設定向けのカスタムメモリバックエンド。",
- "sparseSectionTitle": "スパース検索",
"denseSectionTitle": "密な検索",
- "sparseInstallHint": "スパース モードは、オプションのスパース サービスに依存します。インストーラーの実行時にスパース インストール オプションを有効にするか、Docker Compose でスパース プロファイルを開始します。",
"denseBackend": "Dense バックエンド",
"denseBackendValue": "埋め込みAPI+ Qdrant",
"denseEmbeddingModel": "Dense 埋め込みモデル",
"denseEmbeddingModelDescription": "ローカルの再ランク付け前に使用するサードパーティの埋め込みモデルを選択します。",
"denseQdrantHint": "バックエンドランタイムの接続後、Dense メモリは Qdrant をストレージバックエンドとして使用します。",
"qdrantCollection": "Qdrant コレクション",
- "sparseQdrantCollectionDescription": "スパース モードは Qdrant コレクションに書き込みます。現在/デフォルトのコレクション: {collection}。",
"denseQdrantCollectionDescription": "デンス モードは Qdrant コレクションに書き込みます。現在/デフォルトのコレクション: {collection}。",
"collectionPoints": "コレクション内のポイント数",
"collectionExists": "コレクションが存在します",
@@ -959,12 +956,10 @@
"collectionUnavailable": "利用不可",
"modeNames": {
"off": "オフ",
- "sparse": "キーワード",
"dense": "セマンティック"
},
"modeDescriptions": {
"off": "長期記憶インデックスを作成せず、基本的なファイルメモリのみを保持します。",
- "sparse": "オプションのスパース検索サービスを使用して、キーワードで記憶を照合します。",
"dense": "埋め込みモデルを使用して記憶をベクトルに変換し、意味の類似性で検索します。"
},
"providerNames": {
@@ -1985,8 +1980,6 @@
"memoryProvider": "MemoryProvider",
"memoryProviderPlaceholder": "MemoryProviderの選択 (空の場合は無効)",
"memoryModePreview": "選択された組み込みモード: {mode}",
- "sparseStatusTitle": "スパース取得ステータス",
- "sparseStatusHint": "Markdown ファイルは信頼できる情報源です。同期すると、/data/memory から Qdrant インデックスが再構築されます。",
"denseStatusTitle": "密集検索ステータス",
"denseStatusHint": "Markdown ファイルは信頼できる情報源です。同期すると、/data/memory から高密度 Qdrant インデックスが再構築されます。",
"mem0StatusTitle": "Mem0 同期ステータス",
diff --git a/apps/web/src/i18n/locales/zh.json b/apps/web/src/i18n/locales/zh.json
index 2902a32a96..84b49767e0 100644
--- a/apps/web/src/i18n/locales/zh.json
+++ b/apps/web/src/i18n/locales/zh.json
@@ -994,16 +994,13 @@
"modeHint": "决定智能体如何存取长期记忆。",
"advanced": "高级",
"advancedHint": "面向进阶场景的自定义记忆后端。",
- "sparseSectionTitle": "稀疏检索",
"denseSectionTitle": "稠密检索",
- "sparseInstallHint": "Sparse 模式依赖可选的 sparse 服务。安装时请启用 sparse 选项,或在 Docker Compose 中启动 sparse profile。",
"denseBackend": "Dense 后端",
"denseBackendValue": "Embedding API + Qdrant",
"denseEmbeddingModel": "Dense 向量模型",
"denseEmbeddingModelDescription": "选择本地 rerank 之前使用的第三方 embedding 模型。",
"denseQdrantHint": "后端接通后,dense memory 会以 Qdrant 作为存储后端。",
"qdrantCollection": "Qdrant Collection",
- "sparseQdrantCollectionDescription": "稀疏模式会写入对应的 Qdrant collection。当前/默认 collection:{collection}。",
"denseQdrantCollectionDescription": "稠密模式会写入对应的 Qdrant collection。当前/默认 collection:{collection}。",
"collectionPoints": "Collection 中的点数",
"collectionExists": "Collection 已存在",
@@ -1012,12 +1009,10 @@
"collectionUnavailable": "不可用",
"modeNames": {
"off": "关闭",
- "sparse": "关键词",
"dense": "语义"
},
"modeDescriptions": {
"off": "不建立长期记忆索引,仅保留基础的文件记忆。",
- "sparse": "用关键词匹配检索记忆,依赖可选的稀疏检索服务。",
"dense": "用向量模型把记忆转成向量,按语义相近度检索。"
},
"providerNames": {
@@ -2004,8 +1999,6 @@
"memoryProvider": "记忆提供方",
"memoryProviderPlaceholder": "选择记忆提供方(为空则禁用)",
"memoryModePreview": "当前内置模式:{mode}",
- "sparseStatusTitle": "稀疏检索状态",
- "sparseStatusHint": "Markdown 文件是唯一可信源;手动同步将根据 /data/memory 重新构建 Qdrant 索引。",
"denseStatusTitle": "稠密检索状态",
"denseStatusHint": "Markdown 文件是唯一可信源;手动同步将根据 /data/memory 重建稠密 Qdrant 索引。",
"mem0StatusTitle": "Mem0 同步状态",
@@ -2020,7 +2013,6 @@
"memorySourceEntries": "源条目数",
"memoryIndexedEntries": "索引条目数",
"memoryQdrantCollection": "Qdrant Collection",
- "memoryEncoderHealth": "Sparse Encoder",
"memoryDenseEmbeddingHealth": "Embedding 后端",
"memoryQdrantHealth": "Qdrant",
"memoryHealthOk": "正常",
diff --git a/apps/web/src/pages/bots/components/settings-context-card.vue b/apps/web/src/pages/bots/components/settings-context-card.vue
index 3de42922cc..423ff536a2 100644
--- a/apps/web/src/pages/bots/components/settings-context-card.vue
+++ b/apps/web/src/pages/bots/components/settings-context-card.vue
@@ -248,7 +248,7 @@ const isSelectedMemoryProviderPersisted = computed(() =>
!!props.form.memory_provider_id && props.form.memory_provider_id === props.persistedMemoryProviderID,
)
const showBuiltinIndexedMemoryStatus = computed(() =>
- selectedBuiltinMemoryMode.value === 'sparse' || selectedBuiltinMemoryMode.value === 'dense',
+ selectedBuiltinMemoryMode.value === 'dense',
)
const showMemoryProviderStatusCard = computed(() =>
showBuiltinIndexedMemoryStatus.value || !!selectedMem0MemoryProvider.value,
@@ -257,25 +257,21 @@ const showMemoryProviderStatusCard = computed(() =>
const indexedMemoryStatusTitle = computed(() =>
selectedMemoryProviderType.value === 'mem0'
? t('bots.settings.mem0StatusTitle')
- : selectedBuiltinMemoryMode.value === 'dense'
- ? t('bots.settings.denseStatusTitle')
- : t('bots.settings.sparseStatusTitle'),
+ : t('bots.settings.denseStatusTitle'),
)
const statusCardData = computed(() => props.memoryStatus)
const showQdrantDetails = computed(() =>
- selectedBuiltinMemoryMode.value === 'sparse' || selectedBuiltinMemoryMode.value === 'dense',
+ selectedBuiltinMemoryMode.value === 'dense',
)
const showEncoderHealth = computed(() =>
- selectedBuiltinMemoryMode.value === 'sparse' || selectedBuiltinMemoryMode.value === 'dense',
+ selectedBuiltinMemoryMode.value === 'dense',
)
const showQdrantHealth = computed(() =>
- selectedBuiltinMemoryMode.value === 'sparse' || selectedBuiltinMemoryMode.value === 'dense',
+ selectedBuiltinMemoryMode.value === 'dense',
)
const encoderHealthLabel = computed(() =>
- selectedBuiltinMemoryMode.value === 'dense'
- ? t('bots.settings.memoryDenseEmbeddingHealth')
- : t('bots.settings.memoryEncoderHealth'),
+ t('bots.settings.memoryDenseEmbeddingHealth'),
)
function healthTextClass(ok: boolean | undefined) {
diff --git a/apps/web/src/pages/memory/components/builtin-config.vue b/apps/web/src/pages/memory/components/builtin-config.vue
index 8c604e60a3..fcb4291edf 100644
--- a/apps/web/src/pages/memory/components/builtin-config.vue
+++ b/apps/web/src/pages/memory/components/builtin-config.vue
@@ -23,13 +23,6 @@
{{ $t(`memory.modeDescriptions.${mode}`) }}
-
- {{ $t('memory.sparseInstallHint') }}
-
-
(statusData.value as AdaptersProviderStatusRe
const modeItems = computed
[]>(() => [
{ value: 'off', label: t('memory.modeNames.off') },
- { value: 'sparse', label: t('memory.modeNames.sparse') },
{ value: 'dense', label: t('memory.modeNames.dense') },
])
watch(() => props.provider, (val) => {
const config = (val?.config ?? {}) as Record
const nextMode = config.memory_mode
- mode.value = nextMode === 'sparse' || nextMode === 'dense' ? nextMode : 'off'
+ mode.value = nextMode === 'dense' ? 'dense' : 'off'
embeddingModelId.value = typeof config.embedding_model_id === 'string' ? config.embedding_model_id : ''
}, { immediate: true })
diff --git a/conf/app.apple.toml b/conf/app.apple.toml
index 7df8355d9f..2c18a9fee9 100644
--- a/conf/app.apple.toml
+++ b/conf/app.apple.toml
@@ -60,9 +60,6 @@ base_url = "http://127.0.0.1:6334"
api_key = ""
timeout_seconds = 10
-[sparse]
-base_url = "http://127.0.0.1:8085"
-
[registry]
providers_dir = "conf/providers"
diff --git a/conf/app.docker.toml b/conf/app.docker.toml
index 7b6051e9cc..b759101aef 100644
--- a/conf/app.docker.toml
+++ b/conf/app.docker.toml
@@ -85,9 +85,6 @@ base_url = "http://qdrant:6334"
api_key = ""
timeout_seconds = 10
-[sparse]
-base_url = "http://sparse:8085"
-
[registry]
providers_dir = "conf/providers"
diff --git a/conf/app.example.toml b/conf/app.example.toml
index 23e2eaeae4..2b4c336cff 100644
--- a/conf/app.example.toml
+++ b/conf/app.example.toml
@@ -132,9 +132,6 @@ base_url = "http://127.0.0.1:6334"
api_key = ""
timeout_seconds = 10
-[sparse]
-base_url = "http://127.0.0.1:8085"
-
[registry]
providers_dir = "conf/providers"
diff --git a/conf/app.kata.docker.toml b/conf/app.kata.docker.toml
index ce4fa27f39..b75b212e80 100644
--- a/conf/app.kata.docker.toml
+++ b/conf/app.kata.docker.toml
@@ -86,9 +86,6 @@ base_url = "http://qdrant:6334"
api_key = ""
timeout_seconds = 10
-[sparse]
-base_url = "http://sparse:8085"
-
[registry]
providers_dir = "conf/providers"
diff --git a/conf/app.local.toml b/conf/app.local.toml
index 04f2f83c58..837538acf3 100644
--- a/conf/app.local.toml
+++ b/conf/app.local.toml
@@ -71,9 +71,6 @@ base_url = "http://127.0.0.1:6334"
api_key = ""
timeout_seconds = 10
-[sparse]
-base_url = "http://127.0.0.1:8085"
-
[registry]
providers_dir = "conf/providers"
diff --git a/conf/app.windows.toml b/conf/app.windows.toml
index 780af7f2b7..b32ffbf250 100644
--- a/conf/app.windows.toml
+++ b/conf/app.windows.toml
@@ -68,9 +68,6 @@ base_url = "http://127.0.0.1:6334"
api_key = ""
timeout_seconds = 10
-[sparse]
-base_url = "http://127.0.0.1:8085"
-
[registry]
providers_dir = "conf/providers"
diff --git a/devenv/app.dev.toml b/devenv/app.dev.toml
index 55b8b2d5f9..df9b424e05 100644
--- a/devenv/app.dev.toml
+++ b/devenv/app.dev.toml
@@ -70,9 +70,6 @@ base_url = "http://qdrant:6334"
api_key = ""
timeout_seconds = 10
-[sparse]
-base_url = "http://sparse:8085"
-
[registry]
providers_dir = "conf/providers"
diff --git a/devenv/app.kata.dev.toml b/devenv/app.kata.dev.toml
index 1251c1ccfb..9b38084bb9 100644
--- a/devenv/app.kata.dev.toml
+++ b/devenv/app.kata.dev.toml
@@ -59,9 +59,6 @@ base_url = "http://qdrant:6334"
api_key = ""
timeout_seconds = 10
-[sparse]
-base_url = "http://sparse:8085"
-
[registry]
providers_dir = "conf/providers"
diff --git a/devenv/app.sqlite.dev.toml b/devenv/app.sqlite.dev.toml
index 3e9285d6fe..fb5fb813b4 100644
--- a/devenv/app.sqlite.dev.toml
+++ b/devenv/app.sqlite.dev.toml
@@ -71,9 +71,6 @@ base_url = "http://qdrant:6334"
api_key = ""
timeout_seconds = 10
-[sparse]
-base_url = "http://sparse:8085"
-
[web]
host = "0.0.0.0"
port = 8082
diff --git a/devenv/docker-compose.minify.yml b/devenv/docker-compose.minify.yml
index 9864fcae5c..426be7b9df 100644
--- a/devenv/docker-compose.minify.yml
+++ b/devenv/docker-compose.minify.yml
@@ -90,10 +90,10 @@ services:
# HTTP_PROXY=http://host.docker.internal:7890 mise run dev
HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}"
HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}"
- NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
+ NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
http_proxy: "${http_proxy:-${HTTP_PROXY:-}}"
https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}"
- no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
+ no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_DEV_DISPLAY_WEBRTC_UDP_PORT_MIN:-30000}"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_DEV_DISPLAY_WEBRTC_UDP_PORT_MAX:-30100}"
volumes:
@@ -146,21 +146,6 @@ services:
condition: service_healthy
restart: unless-stopped
- # sparse:
- # build:
- # context: ..
- # dockerfile: docker/Dockerfile.sparse
- # container_name: memoh-dev-sparse
- # ports:
- # - "${MEMOH_DEV_SPARSE_PORT:-18085}:8085"
- # healthcheck:
- # test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"]
- # interval: 15s
- # timeout: 10s
- # start_period: 30s
- # retries: 3
- # restart: unless-stopped
-
volumes:
postgres_data:
driver: local
diff --git a/devenv/docker-compose.sqlite.minify.yml b/devenv/docker-compose.sqlite.minify.yml
index 06d848c8d1..d09e438c5a 100644
--- a/devenv/docker-compose.sqlite.minify.yml
+++ b/devenv/docker-compose.sqlite.minify.yml
@@ -70,10 +70,10 @@ services:
# HTTP_PROXY=http://host.docker.internal:7890 mise run dev:sqlite:minify
HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}"
HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}"
- NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},qdrant,sparse,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-sparse,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps"
+ NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},qdrant,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps"
http_proxy: "${http_proxy:-${HTTP_PROXY:-}}"
https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}"
- no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},qdrant,sparse,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-sparse,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps"
+ no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},qdrant,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_SQLITE_DEV_DISPLAY_WEBRTC_UDP_PORT_MIN:-30200}"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_SQLITE_DEV_DISPLAY_WEBRTC_UDP_PORT_MAX:-30300}"
volumes:
@@ -126,21 +126,6 @@ services:
condition: service_healthy
restart: unless-stopped
- # sparse:
- # build:
- # context: ..
- # dockerfile: docker/Dockerfile.sparse
- # container_name: memoh-dev-sqlite-sparse
- # ports:
- # - "${MEMOH_SQLITE_DEV_SPARSE_PORT:-19085}:8085"
- # healthcheck:
- # test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"]
- # interval: 15s
- # timeout: 10s
- # start_period: 30s
- # retries: 3
- # restart: unless-stopped
-
volumes:
qdrant_data:
driver: local
diff --git a/devenv/docker-compose.sqlite.yml b/devenv/docker-compose.sqlite.yml
index d0053cb2bd..9c342b8934 100644
--- a/devenv/docker-compose.sqlite.yml
+++ b/devenv/docker-compose.sqlite.yml
@@ -70,10 +70,10 @@ services:
# HTTP_PROXY=http://host.docker.internal:7890 mise run dev:sqlite
HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}"
HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}"
- NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},qdrant,sparse,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-sparse,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps"
+ NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},qdrant,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps"
http_proxy: "${http_proxy:-${HTTP_PROXY:-}}"
https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}"
- no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},qdrant,sparse,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-sparse,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps"
+ no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},qdrant,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_SQLITE_DEV_DISPLAY_WEBRTC_UDP_PORT_MIN:-30200}"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_SQLITE_DEV_DISPLAY_WEBRTC_UDP_PORT_MAX:-30300}"
volumes:
@@ -129,20 +129,6 @@ services:
condition: service_healthy
restart: unless-stopped
- sparse:
- build:
- context: ..
- dockerfile: docker/Dockerfile.sparse
- container_name: memoh-dev-sqlite-sparse
- ports:
- - "${MEMOH_SQLITE_DEV_SPARSE_PORT:-19085}:8085"
- healthcheck:
- test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"]
- interval: 15s
- timeout: 10s
- start_period: 30s
- retries: 3
- restart: unless-stopped
volumes:
qdrant_data:
diff --git a/devenv/docker-compose.yml b/devenv/docker-compose.yml
index baedfdde7c..a8078cda61 100644
--- a/devenv/docker-compose.yml
+++ b/devenv/docker-compose.yml
@@ -65,10 +65,10 @@ services:
GOFLAGS: -buildvcs=false
HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}"
HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}"
- NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
+ NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
http_proxy: "${http_proxy:-${HTTP_PROXY:-}}"
https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}"
- no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
+ no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
volumes:
- ..:/workspace
- go_mod_cache:/go/pkg/mod
@@ -98,10 +98,10 @@ services:
# HTTP_PROXY=http://host.docker.internal:7890 mise run dev
HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}"
HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}"
- NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
+ NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
http_proxy: "${http_proxy:-${HTTP_PROXY:-}}"
https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}"
- no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
+ no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_DEV_DISPLAY_WEBRTC_UDP_PORT_MIN:-30000}"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_DEV_DISPLAY_WEBRTC_UDP_PORT_MAX:-30100}"
volumes:
@@ -157,21 +157,6 @@ services:
condition: service_healthy
restart: unless-stopped
- sparse:
- build:
- context: ..
- dockerfile: docker/Dockerfile.sparse
- container_name: memoh-dev-sparse
- ports:
- - "${MEMOH_DEV_SPARSE_PORT:-18085}:8085"
- healthcheck:
- test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"]
- interval: 15s
- timeout: 10s
- start_period: 30s
- retries: 3
- restart: unless-stopped
-
volumes:
postgres_data:
driver: local
diff --git a/docker-compose.sqlite.yml b/docker-compose.sqlite.yml
index 473a07f116..69ac15bc3e 100644
--- a/docker-compose.sqlite.yml
+++ b/docker-compose.sqlite.yml
@@ -47,28 +47,10 @@ services:
networks:
- memoh-network
- sparse:
- image: memohai/sparse:latest
- container_name: memoh-sparse
- profiles: [sparse]
- volumes:
- - /etc/localtime:/etc/localtime:ro
- expose:
- - "8085"
- healthcheck:
- test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"]
- interval: 15s
- timeout: 10s
- start_period: 30s
- retries: 3
- restart: unless-stopped
- networks:
- - memoh-network
-
qdrant:
image: qdrant/qdrant:latest
container_name: memoh-qdrant
- profiles: [qdrant, sparse]
+ profiles: [qdrant]
volumes:
- qdrant_data:/qdrant/storage
expose:
diff --git a/docker-compose.yml b/docker-compose.yml
index 893aa6fd81..974bc33bbd 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -49,10 +49,10 @@ services:
# HTTP_PROXY=http://host.docker.internal:7890 docker compose up -d
HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}"
HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}"
- NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,sparse,server,web,migrate,memoh-postgres,memoh-qdrant,memoh-sparse,memoh-server,memoh-web,memoh-migrate"
+ NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,server,web,migrate,memoh-postgres,memoh-qdrant,memoh-server,memoh-web,memoh-migrate"
http_proxy: "${http_proxy:-${HTTP_PROXY:-}}"
https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}"
- no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,sparse,server,web,migrate,memoh-postgres,memoh-qdrant,memoh-sparse,memoh-server,memoh-web,memoh-migrate"
+ no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,server,web,migrate,memoh-postgres,memoh-qdrant,memoh-server,memoh-web,memoh-migrate"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN:-30000}"
MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX:-30100}"
volumes:
@@ -89,28 +89,10 @@ services:
networks:
- memoh-network
- sparse:
- image: memohai/sparse:latest
- container_name: memoh-sparse
- profiles: [sparse]
- volumes:
- - /etc/localtime:/etc/localtime:ro
- expose:
- - "8085"
- healthcheck:
- test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"]
- interval: 15s
- timeout: 10s
- start_period: 30s
- retries: 3
- restart: unless-stopped
- networks:
- - memoh-network
-
qdrant:
image: qdrant/qdrant:latest
container_name: memoh-qdrant
- profiles: [qdrant, sparse]
+ profiles: [qdrant]
volumes:
- qdrant_data:/qdrant/storage
expose:
diff --git a/docker/Dockerfile.sparse b/docker/Dockerfile.sparse
deleted file mode 100644
index 7fba97e9b2..0000000000
--- a/docker/Dockerfile.sparse
+++ /dev/null
@@ -1,43 +0,0 @@
-# syntax=docker/dockerfile:1
-
-# Sparse encoding service — runs the OpenSearch neural sparse model
-# as a standalone HTTP service (Flask + PyTorch CPU).
-# Used by the Go server's sparse memory runtime via HTTP.
-
-FROM python:3.12-slim
-
-WORKDIR /app
-
-COPY internal/memory/sparse/service/requirements.txt requirements.txt
-RUN pip install --no-cache-dir \
- --extra-index-url https://download.pytorch.org/whl/cpu \
- -r requirements.txt
-
-COPY internal/memory/sparse/service/main.py main.py
-
-ENV SPARSE_PORT=8085
-ENV SPARSE_CACHE_DIR=/opt/sparse-cache
-
-RUN mkdir -p /opt/sparse-cache
-
-# Pre-download the default sparse model during image build so containers
-# start with a warm cache and do not need to fetch weights on first boot.
-RUN python - <<'PY'
-from pathlib import Path
-from huggingface_hub import hf_hub_download
-from transformers import AutoModelForMaskedLM, AutoTokenizer
-
-model_repo = "opensearch-project/opensearch-neural-sparse-encoding-multilingual-v1"
-cache_dir = "/opt/sparse-cache"
-Path(cache_dir).mkdir(parents=True, exist_ok=True)
-
-AutoModelForMaskedLM.from_pretrained(model_repo, cache_dir=cache_dir)
-AutoTokenizer.from_pretrained(model_repo, cache_dir=cache_dir)
-hf_hub_download(repo_id=model_repo, filename="idf.json", cache_dir=cache_dir)
-
-print(f"Pre-downloaded sparse model: {model_repo}")
-PY
-
-EXPOSE 8085
-
-CMD ["python", "main.py"]
diff --git a/docker/docker-compose.cn.yml b/docker/docker-compose.cn.yml
index 1bbba5ee9a..3bc042fbc2 100644
--- a/docker/docker-compose.cn.yml
+++ b/docker/docker-compose.cn.yml
@@ -9,5 +9,3 @@ services:
image: memoh.cn/memohai/server:latest
web:
image: memoh.cn/memohai/web:latest
- sparse:
- image: memoh.cn/memohai/sparse:latest
diff --git a/docker/docker-compose.sqlite.cn.yml b/docker/docker-compose.sqlite.cn.yml
index dad6564bbc..becc93fa19 100644
--- a/docker/docker-compose.sqlite.cn.yml
+++ b/docker/docker-compose.sqlite.cn.yml
@@ -7,5 +7,3 @@ services:
image: memoh.cn/memohai/server:latest
web:
image: memoh.cn/memohai/web:latest
- sparse:
- image: memoh.cn/memohai/sparse:latest
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 637c3e06af..ce01fab319 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -20,12 +20,8 @@ services:
web:
build:
- context: .
+ context: ..
dockerfile: docker/Dockerfile.web
args:
- VITE_API_URL=${VITE_API_URL:-/api}
- sparse:
- build:
- context: .
- dockerfile: docker/Dockerfile.sparse
diff --git a/internal/config/config.go b/internal/config/config.go
index 766ee09f00..2bf9db9422 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -58,7 +58,6 @@ type Config struct {
Postgres PostgresConfig `toml:"postgres"`
SQLite SQLiteConfig `toml:"sqlite"`
Qdrant QdrantConfig `toml:"qdrant"`
- Sparse SparseConfig `toml:"sparse"`
Registry RegistryConfig `toml:"registry"`
Supermarket SupermarketConfig `toml:"supermarket"`
OAuthClients OAuthClientsConfig `toml:"oauth_clients"`
@@ -328,10 +327,6 @@ type QdrantConfig struct {
TimeoutSeconds int `toml:"timeout_seconds"`
}
-type SparseConfig struct {
- BaseURL string `toml:"base_url"`
-}
-
const DefaultProvidersDir = "conf/providers"
type RegistryConfig struct {
diff --git a/internal/memory/adapters/builtin/builtin.go b/internal/memory/adapters/builtin/builtin.go
index 8511eed40c..70e595f99d 100644
--- a/internal/memory/adapters/builtin/builtin.go
+++ b/internal/memory/adapters/builtin/builtin.go
@@ -141,7 +141,7 @@ func (p *BuiltinProvider) SemanticCompactCapability() adapters.MemoryCompactCapa
return adapters.MemoryCompactCapability{
Semantic: true,
Archive: true,
- RebuildIndex: mode == "dense" || mode == "sparse",
+ RebuildIndex: mode == "dense",
}
}
diff --git a/internal/memory/adapters/builtin/builtin_test.go b/internal/memory/adapters/builtin/builtin_test.go
index 9bb16f85ed..47120531fb 100644
--- a/internal/memory/adapters/builtin/builtin_test.go
+++ b/internal/memory/adapters/builtin/builtin_test.go
@@ -9,7 +9,6 @@ import (
"github.com/memohai/memoh/internal/config"
adapters "github.com/memohai/memoh/internal/memory/adapters"
- "github.com/memohai/memoh/internal/memory/sparse"
storefs "github.com/memohai/memoh/internal/memory/storefs"
)
@@ -34,10 +33,8 @@ func TestBuiltinProviderNilService(t *testing.T) {
func TestBuiltinProviderSemanticCompactCapability(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
p := NewBuiltinProvider(slog.Default(), runtime, nil, nil)
withoutLLM := p.SemanticCompactCapability()
@@ -56,8 +53,10 @@ func TestBuiltinProviderSemanticCompactCapability(t *testing.T) {
if !withLLM.Archive {
t.Fatalf("semantic compact should advertise source archive support: %+v", withLLM)
}
- if !withLLM.RebuildIndex {
- t.Fatalf("semantic compact should advertise index rebuild support for indexed runtime: %+v", withLLM)
+ // The file runtime (mode "off") has no derived vector index to rebuild,
+ // so RebuildIndex must be false. Only indexed runtimes (dense) set it.
+ if withLLM.RebuildIndex {
+ t.Fatalf("file runtime should not advertise index rebuild support: %+v", withLLM)
}
if withLLM.Reason != "" {
t.Fatalf("available semantic compact should not include unavailable reason: %+v", withLLM)
@@ -66,10 +65,8 @@ func TestBuiltinProviderSemanticCompactCapability(t *testing.T) {
func TestBuiltinProviderOnBeforeChatEmptyQuery(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
p := NewBuiltinProvider(slog.Default(), runtime, nil, nil)
result, err := p.OnBeforeChat(context.Background(), adapters.BeforeChatRequest{
@@ -86,10 +83,8 @@ func TestBuiltinProviderOnBeforeChatEmptyQuery(t *testing.T) {
func TestBuiltinProviderContextPackingProducesMemoryContextTags(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
p := NewBuiltinProvider(slog.Default(), runtime, nil, nil)
_ = p.OnAfterChat(context.Background(), adapters.AfterChatRequest{
@@ -151,15 +146,13 @@ func TestBuiltinProviderApplyProviderConfigNil(t *testing.T) {
func TestBuiltinProviderCompactUsesLLMResults(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore(
+ store := newFakeStore(
storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "Ran likes black tea", CreatedAt: "2026-06-01T00:00:00Z", UpdatedAt: "2026-06-01T00:00:00Z"},
storefs.MemoryItem{ID: "bot-1:mem_2", Memory: "Ran likes oolong tea", CreatedAt: "2026-06-02T00:00:00Z", UpdatedAt: "2026-06-02T00:00:00Z"},
storefs.MemoryItem{ID: "bot-1:mem_3", Memory: "Ran works in Berlin", CreatedAt: "2026-06-03T00:00:00Z", UpdatedAt: "2026-06-03T00:00:00Z"},
storefs.MemoryItem{ID: "bot-1:mem_4", Memory: "Ran uses Vim", CreatedAt: "2026-06-04T00:00:00Z", UpdatedAt: "2026-06-04T00:00:00Z"},
)
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ runtime := newFileRuntime(store)
llm := &fakeLLM{
compactFacts: []string{
"Ran likes tea, especially black tea and oolong.",
@@ -216,13 +209,11 @@ func TestBuiltinProviderCompactUsesLLMResults(t *testing.T) {
func TestBuiltinProviderCompactRequiresSemanticCompactCapability(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore(
+ store := newFakeStore(
storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "Ran likes black tea", CreatedAt: "2026-06-01T00:00:00Z", UpdatedAt: "2026-06-01T00:00:00Z"},
storefs.MemoryItem{ID: "bot-1:mem_2", Memory: "Ran likes oolong tea", CreatedAt: "2026-06-02T00:00:00Z", UpdatedAt: "2026-06-02T00:00:00Z"},
)
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ runtime := newFileRuntime(store)
provider := NewBuiltinProvider(slog.Default(), runtime, nil, nil)
if _, err := provider.Compact(context.Background(), map[string]any{"bot_id": "bot-1"}, 0.5, 0); err == nil {
@@ -232,15 +223,13 @@ func TestBuiltinProviderCompactRequiresSemanticCompactCapability(t *testing.T) {
func TestBuiltinProviderCompactPreservesPinnedMemories(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore(
+ store := newFakeStore(
storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "Pinned preference", Metadata: map[string]any{"pinned": true}, CreatedAt: "2026-06-01T00:00:00Z", UpdatedAt: "2026-06-01T00:00:00Z"},
storefs.MemoryItem{ID: "bot-1:mem_2", Memory: "Read-only profile", Metadata: map[string]any{"read_only": "true"}, CreatedAt: "2026-06-02T00:00:00Z", UpdatedAt: "2026-06-02T00:00:00Z"},
storefs.MemoryItem{ID: "bot-1:mem_3", Memory: "Ran likes green tea", CreatedAt: "2026-06-03T00:00:00Z", UpdatedAt: "2026-06-03T00:00:00Z"},
storefs.MemoryItem{ID: "bot-1:mem_4", Memory: "Ran likes oolong tea", CreatedAt: "2026-06-04T00:00:00Z", UpdatedAt: "2026-06-04T00:00:00Z"},
)
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ runtime := newFileRuntime(store)
llm := &fakeLLM{compactFacts: []string{"Ran likes tea."}}
provider := NewBuiltinProvider(slog.Default(), runtime, nil, nil)
provider.SetLLM(llm)
@@ -268,8 +257,6 @@ func TestBuiltinProviderCompactPreservesPinnedMemories(t *testing.T) {
func TestBuiltinProviderCompactBatchesOversizedInputs(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
items := make([]storefs.MemoryItem, 0, 24)
for i := 0; i < 24; i++ {
items = append(items, storefs.MemoryItem{
@@ -279,8 +266,8 @@ func TestBuiltinProviderCompactBatchesOversizedInputs(t *testing.T) {
UpdatedAt: "2026-06-01T00:00:00Z",
})
}
- store := newFakeSparseStore(items...)
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore(items...)
+ runtime := newFileRuntime(store)
llm := &fakeLLM{}
llm.compactFunc = func(_ adapters.CompactRequest) adapters.CompactResponse {
return adapters.CompactResponse{Facts: []string{
@@ -392,23 +379,6 @@ func TestNewBuiltinRuntimeFromConfig_DenseErrorPropagates(t *testing.T) {
}
}
-func TestNewBuiltinRuntimeFromConfig_SparseErrorPropagates(t *testing.T) {
- t.Parallel()
- cfg := map[string]any{"memory_mode": "sparse"}
- _, err := NewBuiltinRuntimeFromConfig(nil, cfg, nil, nil, defaultTestConfig())
- if err == nil {
- t.Fatal("expected error for sparse mode without encoder base URL")
- }
-}
-
func defaultTestConfig() config.Config {
return config.Config{}
}
-
-// Fakes from sparse_runtime_test.go are in the same package and accessible.
-
-var _ sparseEncoder = (*fakeSparseEncoder)(nil)
-
-func init() {
- _ = sparse.SparseVector{}
-}
diff --git a/internal/memory/adapters/builtin/dense_runtime.go b/internal/memory/adapters/builtin/dense_runtime.go
index 86ba294c2e..497b5110ef 100644
--- a/internal/memory/adapters/builtin/dense_runtime.go
+++ b/internal/memory/adapters/builtin/dense_runtime.go
@@ -2,8 +2,6 @@ package builtin
import (
"context"
- "crypto/sha256"
- "encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -65,7 +63,7 @@ func newDenseRuntime(providerConfig map[string]any, queries dbstore.Queries, cfg
return nil, err
}
- host, port := parseQdrantHostPort(cfg.Qdrant.BaseURL)
+ host, port := qdrantclient.ParseHostPort(cfg.Qdrant.BaseURL)
if host == "" {
host = "localhost"
}
@@ -664,10 +662,3 @@ func resolveDenseEmbeddingModel(ctx context.Context, queries dbstore.Queries, mo
dimensions: *cfg.Dimensions,
}, nil
}
-
-// --- shared helpers (used by both dense and sparse runtimes) ---
-
-func runtimeHash(text string) string {
- sum := sha256.Sum256([]byte(strings.TrimSpace(text)))
- return hex.EncodeToString(sum[:])
-}
diff --git a/internal/memory/adapters/builtin/factory.go b/internal/memory/adapters/builtin/factory.go
index 4352f33cb0..3fd9b5d8e6 100644
--- a/internal/memory/adapters/builtin/factory.go
+++ b/internal/memory/adapters/builtin/factory.go
@@ -2,7 +2,6 @@ package builtin
import (
"log/slog"
- "strconv"
"strings"
"github.com/memohai/memoh/internal/config"
@@ -15,41 +14,19 @@ import (
type BuiltinMemoryMode string
const (
- ModeOff BuiltinMemoryMode = "off"
- ModeSparse BuiltinMemoryMode = "sparse"
- ModeDense BuiltinMemoryMode = "dense"
+ ModeOff BuiltinMemoryMode = "off"
+ ModeDense BuiltinMemoryMode = "dense"
)
// NewBuiltinRuntimeFromConfig returns the appropriate Runtime based on
// the provider's persisted config (memory_mode field). Returns the file
-// runtime for "off" or unknown modes. Returns an error if a sparse or dense
+// runtime for "off" or unknown modes. Returns an error if a dense
// runtime was explicitly requested but failed to initialise, so that callers
// can surface configuration problems rather than silently degrading.
func NewBuiltinRuntimeFromConfig(_ *slog.Logger, providerConfig map[string]any, store *storefs.Service, queries dbstore.Queries, cfg config.Config) (Runtime, error) {
mode := BuiltinMemoryMode(strings.TrimSpace(adapters.StringFromConfig(providerConfig, "memory_mode")))
switch mode {
- case ModeSparse:
- host, port := parseQdrantHostPort(cfg.Qdrant.BaseURL)
- if host == "" {
- host = "localhost"
- }
- if port == 0 {
- port = 6334
- }
- collection := adapters.StringFromConfig(providerConfig, "qdrant_collection")
- if collection == "" {
- collection = "memory_sparse"
- }
- return newSparseRuntime(
- host,
- port,
- cfg.Qdrant.APIKey,
- collection,
- strings.TrimSpace(cfg.Sparse.BaseURL),
- store,
- )
-
case ModeDense:
return newDenseRuntime(providerConfig, queries, cfg, store)
@@ -57,31 +34,3 @@ func NewBuiltinRuntimeFromConfig(_ *slog.Logger, providerConfig map[string]any,
return NewFileRuntime(store), nil
}
}
-
-// parseQdrantHostPort extracts host and gRPC port from a Qdrant base URL.
-// Qdrant base URLs are typically HTTP (port 6333), but the gRPC port is 6334.
-func parseQdrantHostPort(baseURL string) (string, int) {
- baseURL = strings.TrimSpace(baseURL)
- if baseURL == "" {
- return "", 0
- }
- baseURL = strings.TrimPrefix(baseURL, "http://")
- baseURL = strings.TrimPrefix(baseURL, "https://")
- parts := strings.SplitN(baseURL, ":", 2)
- host := parts[0]
- if len(parts) == 2 {
- httpPort, err := strconv.Atoi(strings.TrimRight(parts[1], "/"))
- if err == nil {
- switch httpPort {
- case 6333:
- return host, 6334
- case 6334:
- return host, 6334
- default:
- // Common case: operator already configured the intended gRPC port.
- return host, httpPort
- }
- }
- }
- return host, 6334
-}
diff --git a/internal/memory/adapters/builtin/file_runtime_test.go b/internal/memory/adapters/builtin/file_runtime_test.go
index a1dca69fe1..28f14a267d 100644
--- a/internal/memory/adapters/builtin/file_runtime_test.go
+++ b/internal/memory/adapters/builtin/file_runtime_test.go
@@ -12,7 +12,7 @@ import (
func TestFileRuntimeRejectsEmptyMemoryWithoutHTTPError(t *testing.T) {
t.Parallel()
- runtime := newFileRuntime(newFakeSparseStore())
+ runtime := newFileRuntime(newFakeStore())
_, err := runtime.Add(context.Background(), adapters.AddRequest{BotID: "bot-1"})
if err == nil {
@@ -28,7 +28,7 @@ func TestFileRuntimeRejectsEmptyMemoryWithoutHTTPError(t *testing.T) {
func TestFileRuntimeCompactWithLLMArchivesSourceMemories(t *testing.T) {
t.Parallel()
- store := newFakeSparseStore(
+ store := newFakeStore(
storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "Ran likes green tea", CreatedAt: "2026-06-01T00:00:00Z", UpdatedAt: "2026-06-01T00:00:00Z"},
storefs.MemoryItem{ID: "bot-1:mem_2", Memory: "Ran likes oolong tea", CreatedAt: "2026-06-02T00:00:00Z", UpdatedAt: "2026-06-02T00:00:00Z"},
)
diff --git a/internal/memory/adapters/builtin/formation_test.go b/internal/memory/adapters/builtin/formation_test.go
index e052836812..d2e616e5f0 100644
--- a/internal/memory/adapters/builtin/formation_test.go
+++ b/internal/memory/adapters/builtin/formation_test.go
@@ -45,10 +45,8 @@ func (f *fakeLLM) Compact(_ context.Context, req adapters.CompactRequest) (adapt
func TestFormationExtractAndAdd(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
llm := &fakeLLM{
extractFacts: []string{"User likes oolong tea", "User is based in Berlin"},
decideActions: []adapters.DecisionAction{
@@ -84,10 +82,8 @@ func TestFormationExtractAndAdd(t *testing.T) {
func TestFormationUpdate(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
addResp, err := runtime.Add(context.Background(), adapters.AddRequest{
BotID: "bot-1",
@@ -131,10 +127,8 @@ func TestFormationUpdate(t *testing.T) {
func TestFormationDelete(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
addResp, err := runtime.Add(context.Background(), adapters.AddRequest{
BotID: "bot-1",
@@ -170,10 +164,8 @@ func TestFormationDelete(t *testing.T) {
func TestFormationNOOP(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
llm := &fakeLLM{
extractFacts: []string{"User likes tea"},
@@ -202,10 +194,8 @@ func TestFormationNOOP(t *testing.T) {
func TestFormationNoFacts(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
llm := &fakeLLM{
extractFacts: []string{},
@@ -229,10 +219,8 @@ func TestFormationNoFacts(t *testing.T) {
func TestFormationMixedActions(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
addResp, _ := runtime.Add(context.Background(), adapters.AddRequest{
BotID: "bot-1",
@@ -273,10 +261,8 @@ func TestFormationMixedActions(t *testing.T) {
func TestFormationInvalidActionsSkipped(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
llm := &fakeLLM{
extractFacts: []string{"User likes cats"},
@@ -306,10 +292,8 @@ func TestFormationInvalidActionsSkipped(t *testing.T) {
func TestFormationDuplicateActionsSameID(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
addResp, _ := runtime.Add(context.Background(), adapters.AddRequest{
BotID: "bot-1",
@@ -343,10 +327,8 @@ func TestFormationDuplicateActionsSameID(t *testing.T) {
func TestOnAfterChatWithLLM(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
llm := &fakeLLM{
extractFacts: []string{"User prefers dark mode"},
decideActions: []adapters.DecisionAction{
@@ -379,10 +361,8 @@ func TestOnAfterChatWithLLM(t *testing.T) {
func TestOnAfterChatFallbackWithoutLLM(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
p := NewBuiltinProvider(slog.Default(), runtime, nil, nil)
@@ -402,10 +382,8 @@ func TestOnAfterChatFallbackWithoutLLM(t *testing.T) {
func TestOnBeforeChatRecallsFactMemory(t *testing.T) {
t.Parallel()
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store}
+ store := newFakeStore()
+ runtime := newFileRuntime(store)
llm := &fakeLLM{
extractFacts: []string{"User prefers oolong tea"},
decideActions: []adapters.DecisionAction{
diff --git a/internal/memory/adapters/builtin/shared.go b/internal/memory/adapters/builtin/shared.go
index 781c169dd0..4883315728 100644
--- a/internal/memory/adapters/builtin/shared.go
+++ b/internal/memory/adapters/builtin/shared.go
@@ -2,10 +2,13 @@ package builtin
import (
"context"
+ "crypto/sha256"
+ "encoding/hex"
"errors"
"fmt"
"strconv"
"strings"
+ "sync/atomic"
"time"
"github.com/google/uuid"
@@ -15,6 +18,13 @@ import (
storefs "github.com/memohai/memoh/internal/memory/storefs"
)
+// memoryIDSeq is a process-wide monotonic counter appended to memory IDs so
+// that two Add calls landing in the same wall-clock nanosecond still produce
+// distinct IDs. The wall-clock nanosecond remains the dominant component, so
+// IDs stay human-readable and roughly time-ordered; the sequence only breaks
+// ties when the clock has not advanced.
+var memoryIDSeq uint64
+
// memoryStore is the markdown file store consumed by the builtin runtimes.
type memoryStore interface {
PersistMemories(ctx context.Context, botID string, items []storefs.MemoryItem, filters map[string]any) error
@@ -181,7 +191,16 @@ func runtimeText(message string, messages []adapters.Message) string {
}
func runtimeMemoryID(botID string, now time.Time) string {
- return botID + ":" + "mem_" + strconv.FormatInt(now.UnixNano(), 10)
+ seq := atomic.AddUint64(&memoryIDSeq, 1)
+ return botID + ":" + "mem_" + strconv.FormatInt(now.UnixNano(), 10) + "_" + strconv.FormatUint(seq, 36)
+}
+
+// runtimeHash returns a stable SHA-256 hex digest of a trimmed memory body.
+// It is shared by the dense and file runtimes (and the upcoming graph runtime)
+// for content-addressing memory items.
+func runtimeHash(text string) string {
+ sum := sha256.Sum256([]byte(strings.TrimSpace(text)))
+ return hex.EncodeToString(sum[:])
}
func runtimePointID(botID, sourceID string) string {
diff --git a/internal/memory/adapters/builtin/sparse_runtime.go b/internal/memory/adapters/builtin/sparse_runtime.go
deleted file mode 100644
index c262e2b1c0..0000000000
--- a/internal/memory/adapters/builtin/sparse_runtime.go
+++ /dev/null
@@ -1,618 +0,0 @@
-package builtin
-
-import (
- "context"
- "errors"
- "fmt"
- "path"
- "sort"
- "strings"
- "time"
-
- "github.com/memohai/memoh/internal/config"
- adapters "github.com/memohai/memoh/internal/memory/adapters"
- qdrantclient "github.com/memohai/memoh/internal/memory/qdrant"
- "github.com/memohai/memoh/internal/memory/sparse"
- storefs "github.com/memohai/memoh/internal/memory/storefs"
-)
-
-type sparseEncoder interface {
- EncodeDocument(ctx context.Context, text string) (*sparse.SparseVector, error)
- EncodeDocuments(ctx context.Context, texts []string) ([]sparse.SparseVector, error)
- EncodeQuery(ctx context.Context, text string) (*sparse.SparseVector, error)
- Health(ctx context.Context) error
-}
-
-type sparseIndex interface {
- CollectionName() string
- CollectionExists(ctx context.Context) (bool, error)
- EnsureCollection(ctx context.Context) error
- Upsert(ctx context.Context, id string, vec qdrantclient.SparseVector, payload map[string]string) error
- Search(ctx context.Context, vec qdrantclient.SparseVector, botID string, limit int) ([]qdrantclient.SearchResult, error)
- Scroll(ctx context.Context, botID string, limit int) ([]qdrantclient.SearchResult, error)
- Count(ctx context.Context, botID string) (int, error)
- DeleteByIDs(ctx context.Context, ids []string) error
- DeleteByBotID(ctx context.Context, botID string) error
-}
-
-// sparseRuntime implements Runtime with markdown files as the source of
-// truth and Qdrant as a derived sparse index used for retrieval.
-type sparseRuntime struct {
- qdrant sparseIndex
- encoder sparseEncoder
- store memoryStore
-}
-
-const (
- sparseExplainTopKLimit = 24
-)
-
-func newSparseRuntime(qdrantHost string, qdrantPort int, qdrantAPIKey, collection, encoderBaseURL string, store *storefs.Service) (*sparseRuntime, error) {
- if strings.TrimSpace(qdrantHost) == "" {
- return nil, errors.New("sparse runtime: qdrant host is required")
- }
- if strings.TrimSpace(encoderBaseURL) == "" {
- return nil, errors.New("sparse runtime: sparse.base_url is required")
- }
- if store == nil {
- return nil, errors.New("sparse runtime: memory store is required")
- }
- qClient, err := qdrantclient.NewClient(qdrantHost, qdrantPort, qdrantAPIKey, collection)
- if err != nil {
- return nil, fmt.Errorf("sparse runtime: %w", err)
- }
- return &sparseRuntime{
- qdrant: qClient,
- encoder: sparse.NewClient(encoderBaseURL),
- store: store,
- }, nil
-}
-
-func (r *sparseRuntime) ensureCollection(ctx context.Context) error {
- return r.qdrant.EnsureCollection(ctx)
-}
-
-func (*sparseRuntime) Mode() string {
- return string(ModeSparse)
-}
-
-func (r *sparseRuntime) Add(ctx context.Context, req adapters.AddRequest) (adapters.SearchResponse, error) {
- botID, err := runtimeBotID(req.BotID, req.Filters)
- if err != nil {
- return adapters.SearchResponse{}, err
- }
- text := runtimeText(req.Message, req.Messages)
- if text == "" {
- return adapters.SearchResponse{}, errors.New("sparse runtime: message is required")
- }
-
- now := time.Now().UTC().Format(time.RFC3339)
- item := adapters.MemoryItem{
- ID: runtimeMemoryID(botID, time.Now().UTC()),
- Memory: text,
- Hash: runtimeHash(text),
- Metadata: req.Metadata,
- BotID: botID,
- CreatedAt: now,
- UpdatedAt: now,
- }
- if err := r.store.PersistMemories(ctx, botID, []storefs.MemoryItem{storeItemFromMemoryItem(item)}, req.Filters); err != nil {
- return adapters.SearchResponse{}, err
- }
- if err := r.upsertSourceItems(ctx, botID, []storefs.MemoryItem{storeItemFromMemoryItem(item)}); err != nil {
- return adapters.SearchResponse{}, err
- }
- return adapters.SearchResponse{Results: []adapters.MemoryItem{item}}, nil
-}
-
-func (r *sparseRuntime) Search(ctx context.Context, req adapters.SearchRequest) (adapters.SearchResponse, error) {
- botID, err := runtimeBotID(req.BotID, req.Filters)
- if err != nil {
- return adapters.SearchResponse{}, err
- }
- if err := r.ensureCollection(ctx); err != nil {
- return adapters.SearchResponse{}, err
- }
-
- limit := req.Limit
- if limit <= 0 {
- limit = 10
- }
-
- vec, err := r.encoder.EncodeQuery(ctx, req.Query)
- if err != nil {
- return adapters.SearchResponse{}, fmt.Errorf("sparse encode query: %w", err)
- }
- results, err := r.qdrant.Search(ctx, qdrantclient.SparseVector{
- Indices: vec.Indices,
- Values: vec.Values,
- }, botID, limit)
- if err != nil {
- return adapters.SearchResponse{}, err
- }
- items := make([]adapters.MemoryItem, 0, len(results))
- for _, r := range results {
- items = append(items, resultToItem(r))
- }
- return adapters.SearchResponse{Results: items}, nil
-}
-
-func (r *sparseRuntime) GetAll(ctx context.Context, req adapters.GetAllRequest) (adapters.SearchResponse, error) {
- botID, err := runtimeBotID(req.BotID, req.Filters)
- if err != nil {
- return adapters.SearchResponse{}, err
- }
- items, err := r.store.ReadAllMemoryFiles(ctx, botID)
- if err != nil {
- return adapters.SearchResponse{}, err
- }
- result := make([]adapters.MemoryItem, 0, len(items))
- for _, item := range items {
- mem := memoryItemFromStore(item)
- mem.BotID = botID
- result = append(result, mem)
- }
- r.populateExplainStats(ctx, sparseMemoryItemPointers(result))
- sort.Slice(result, func(i, j int) bool { return result[i].UpdatedAt > result[j].UpdatedAt })
- if req.Limit > 0 && len(result) > req.Limit {
- result = result[:req.Limit]
- }
- return adapters.SearchResponse{Results: result}, nil
-}
-
-func (r *sparseRuntime) Update(ctx context.Context, req adapters.UpdateRequest) (adapters.MemoryItem, error) {
- memoryID := strings.TrimSpace(req.MemoryID)
- if memoryID == "" {
- return adapters.MemoryItem{}, errors.New("sparse runtime: memory_id is required")
- }
- text := strings.TrimSpace(req.Memory)
- if text == "" {
- return adapters.MemoryItem{}, errors.New("sparse runtime: memory is required")
- }
- botID := runtimeBotIDFromMemoryID(memoryID)
- if botID == "" {
- return adapters.MemoryItem{}, errors.New("sparse runtime: invalid memory_id")
- }
- items, err := r.store.ReadAllMemoryFiles(ctx, botID)
- if err != nil {
- return adapters.MemoryItem{}, err
- }
- var existing *storefs.MemoryItem
- for i := range items {
- if strings.TrimSpace(items[i].ID) == memoryID {
- item := items[i]
- existing = &item
- break
- }
- }
- if existing == nil {
- return adapters.MemoryItem{}, errors.New("sparse runtime: memory not found")
- }
- existing.Memory = text
- existing.Hash = runtimeHash(text)
- existing.UpdatedAt = time.Now().UTC().Format(time.RFC3339)
- if err := r.store.PersistMemories(ctx, botID, []storefs.MemoryItem{*existing}, nil); err != nil {
- return adapters.MemoryItem{}, err
- }
- if err := r.upsertSourceItems(ctx, botID, []storefs.MemoryItem{*existing}); err != nil {
- return adapters.MemoryItem{}, err
- }
- item := memoryItemFromStore(*existing)
- item.BotID = botID
- return item, nil
-}
-
-func (r *sparseRuntime) Delete(ctx context.Context, memoryID string) (adapters.DeleteResponse, error) {
- return r.DeleteBatch(ctx, []string{memoryID})
-}
-
-func (r *sparseRuntime) DeleteBatch(ctx context.Context, memoryIDs []string) (adapters.DeleteResponse, error) {
- grouped := map[string][]string{}
- pointIDs := make([]string, 0, len(memoryIDs))
- for _, rawID := range memoryIDs {
- memoryID := strings.TrimSpace(rawID)
- if memoryID == "" {
- continue
- }
- botID := runtimeBotIDFromMemoryID(memoryID)
- if botID == "" {
- continue
- }
- grouped[botID] = append(grouped[botID], memoryID)
- pointIDs = append(pointIDs, runtimePointID(botID, memoryID))
- }
- for botID, ids := range grouped {
- if err := r.store.RemoveMemories(ctx, botID, ids); err != nil {
- return adapters.DeleteResponse{}, err
- }
- }
- if err := r.ensureCollection(ctx); err != nil {
- return adapters.DeleteResponse{}, err
- }
- if err := r.qdrant.DeleteByIDs(ctx, pointIDs); err != nil {
- return adapters.DeleteResponse{}, err
- }
- return adapters.DeleteResponse{Message: "Memories deleted successfully!"}, nil
-}
-
-func (r *sparseRuntime) DeleteAll(ctx context.Context, req adapters.DeleteAllRequest) (adapters.DeleteResponse, error) {
- botID, err := runtimeBotID(req.BotID, req.Filters)
- if err != nil {
- return adapters.DeleteResponse{}, err
- }
- if err := r.store.RemoveAllMemories(ctx, botID); err != nil {
- return adapters.DeleteResponse{}, err
- }
- if err := r.ensureCollection(ctx); err != nil {
- return adapters.DeleteResponse{}, err
- }
- if err := r.qdrant.DeleteByBotID(ctx, botID); err != nil {
- return adapters.DeleteResponse{}, err
- }
- return adapters.DeleteResponse{Message: "All memories deleted successfully!"}, nil
-}
-
-func (r *sparseRuntime) Compact(ctx context.Context, filters map[string]any, ratio float64, _ int) (adapters.CompactResult, error) {
- botID, err := runtimeBotID("", filters)
- if err != nil {
- return adapters.CompactResult{}, err
- }
- all, err := r.store.ReadAllMemoryFiles(ctx, botID)
- if err != nil {
- return adapters.CompactResult{}, err
- }
- before := len(all)
- if before == 0 {
- return adapters.CompactResult{Ratio: ratio}, nil
- }
-
- sort.Slice(all, func(i, j int) bool {
- return all[i].UpdatedAt > all[j].UpdatedAt
- })
- target := int(float64(before) * ratio)
- if target < 1 {
- target = 1
- }
- if target > before {
- target = before
- }
- keptStore := append([]storefs.MemoryItem(nil), all[:target]...)
- if err := r.store.RebuildFiles(ctx, botID, keptStore, filters); err != nil {
- return adapters.CompactResult{}, err
- }
- if _, err := r.Rebuild(ctx, botID); err != nil {
- return adapters.CompactResult{}, err
- }
- kept := make([]adapters.MemoryItem, 0, len(keptStore))
- for _, item := range keptStore {
- kept = append(kept, memoryItemFromStore(item))
- }
- return adapters.CompactResult{
- BeforeCount: before,
- AfterCount: len(kept),
- Ratio: ratio,
- Results: kept,
- }, nil
-}
-
-func (r *sparseRuntime) CompactWithLLM(ctx context.Context, filters map[string]any, ratio float64, decayDays int, llm adapters.LLM) (adapters.CompactResult, error) {
- botID, err := runtimeBotID("", filters)
- if err != nil {
- return adapters.CompactResult{}, err
- }
- if ratio <= 0 || ratio > 1 {
- return adapters.CompactResult{}, errors.New("ratio must be in range (0, 1]")
- }
- all, err := r.store.ReadAllMemoryFiles(ctx, botID)
- if err != nil {
- return adapters.CompactResult{}, err
- }
- before := len(all)
- if before == 0 {
- return adapters.CompactResult{BeforeCount: 0, AfterCount: 0, Ratio: ratio, Results: []adapters.MemoryItem{}}, nil
- }
- sort.Slice(all, func(i, j int) bool {
- return all[i].UpdatedAt > all[j].UpdatedAt
- })
- compactedStore, archivedStore, err := compactStoreItemsWithLLM(ctx, botID, all, ratio, decayDays, llm)
- if err != nil {
- return adapters.CompactResult{}, err
- }
- if err := r.store.ArchiveAndRebuildFiles(ctx, botID, compactedStore, archivedStore, filters); err != nil {
- return adapters.CompactResult{}, err
- }
- if _, err := r.Rebuild(ctx, botID); err != nil {
- return adapters.CompactResult{}, err
- }
- compacted := make([]adapters.MemoryItem, 0, len(compactedStore))
- for _, item := range compactedStore {
- compacted = append(compacted, memoryItemFromStore(item))
- }
- return adapters.CompactResult{
- BeforeCount: before,
- AfterCount: len(compacted),
- Ratio: ratio,
- Results: compacted,
- }, nil
-}
-
-func (r *sparseRuntime) Usage(ctx context.Context, filters map[string]any) (adapters.UsageResponse, error) {
- botID, err := runtimeBotID("", filters)
- if err != nil {
- return adapters.UsageResponse{}, err
- }
- items, err := r.store.ReadAllMemoryFiles(ctx, botID)
- if err != nil {
- return adapters.UsageResponse{}, err
- }
- var usage adapters.UsageResponse
- usage.Count = len(items)
- for _, item := range items {
- usage.TotalTextBytes += int64(len(item.Memory))
- }
- if usage.Count > 0 {
- usage.AvgTextBytes = usage.TotalTextBytes / int64(usage.Count)
- }
- usage.EstimatedStorageBytes = usage.TotalTextBytes
- return usage, nil
-}
-
-func (r *sparseRuntime) Status(ctx context.Context, botID string) (adapters.MemoryStatusResponse, error) {
- fileCount, err := r.store.CountMemoryFiles(ctx, botID)
- if err != nil {
- return adapters.MemoryStatusResponse{}, err
- }
- items, err := r.store.ReadAllMemoryFiles(ctx, botID)
- if err != nil {
- return adapters.MemoryStatusResponse{}, err
- }
- status := adapters.MemoryStatusResponse{
- ProviderType: BuiltinType,
- MemoryMode: string(ModeSparse),
- CanManualSync: true,
- SourceDir: path.Join(config.DefaultDataMount, "memory"),
- OverviewPath: path.Join(config.DefaultDataMount, "MEMORY.md"),
- MarkdownFileCount: fileCount,
- SourceCount: len(items),
- QdrantCollection: r.qdrant.CollectionName(),
- }
- if err := r.encoder.Health(ctx); err != nil {
- status.Encoder.Error = err.Error()
- } else {
- status.Encoder.OK = true
- }
- exists, err := r.qdrant.CollectionExists(ctx)
- if err != nil {
- status.Qdrant.Error = err.Error()
- return status, nil
- }
- status.Qdrant.OK = true
- if exists {
- count, err := r.qdrant.Count(ctx, botID)
- if err != nil {
- status.Qdrant.OK = false
- status.Qdrant.Error = err.Error()
- return status, nil
- }
- status.IndexedCount = count
- }
- return status, nil
-}
-
-func (r *sparseRuntime) Rebuild(ctx context.Context, botID string) (adapters.RebuildResult, error) {
- items, err := r.store.ReadAllMemoryFiles(ctx, botID)
- if err != nil {
- return adapters.RebuildResult{}, err
- }
- if err := r.store.SyncOverview(ctx, botID); err != nil {
- return adapters.RebuildResult{}, err
- }
- return r.syncSourceItems(ctx, botID, items)
-}
-
-// --- helpers ---
-
-func (r *sparseRuntime) syncSourceItems(ctx context.Context, botID string, items []storefs.MemoryItem) (adapters.RebuildResult, error) {
- if err := r.ensureCollection(ctx); err != nil {
- return adapters.RebuildResult{}, err
- }
- existing, err := r.qdrant.Scroll(ctx, botID, 10000)
- if err != nil {
- return adapters.RebuildResult{}, err
- }
- existingBySource := make(map[string]qdrantclient.SearchResult, len(existing))
- for _, item := range existing {
- sourceID := strings.TrimSpace(item.Payload["source_entry_id"])
- if sourceID == "" {
- sourceID = strings.TrimSpace(item.ID)
- }
- if sourceID == "" {
- continue
- }
- existingBySource[sourceID] = item
- }
- canonical := make([]storefs.MemoryItem, 0, len(items))
- sourceIDs := make(map[string]struct{}, len(items))
- toUpsert := make([]storefs.MemoryItem, 0, len(items))
- missingCount := 0
- restoredCount := 0
- for _, item := range items {
- item = canonicalStoreItem(item)
- if item.ID == "" || item.Memory == "" {
- continue
- }
- canonical = append(canonical, item)
- sourceIDs[item.ID] = struct{}{}
- payload := runtimePayload(botID, item)
- existingItem, ok := existingBySource[item.ID]
- if !ok {
- missingCount++
- restoredCount++
- toUpsert = append(toUpsert, item)
- continue
- }
- if !payloadMatches(existingItem.Payload, payload) {
- restoredCount++
- toUpsert = append(toUpsert, item)
- }
- }
- stalePointIDs := make([]string, 0)
- for _, item := range existing {
- sourceID := strings.TrimSpace(item.Payload["source_entry_id"])
- if sourceID == "" {
- sourceID = strings.TrimSpace(item.ID)
- }
- if _, ok := sourceIDs[sourceID]; ok {
- continue
- }
- if strings.TrimSpace(item.ID) != "" {
- stalePointIDs = append(stalePointIDs, item.ID)
- }
- }
- if len(stalePointIDs) > 0 {
- if err := r.qdrant.DeleteByIDs(ctx, stalePointIDs); err != nil {
- return adapters.RebuildResult{}, err
- }
- }
- if err := r.upsertSourceItems(ctx, botID, toUpsert); err != nil {
- return adapters.RebuildResult{}, err
- }
- count, err := r.qdrant.Count(ctx, botID)
- if err != nil {
- return adapters.RebuildResult{}, err
- }
- return adapters.RebuildResult{
- FsCount: len(canonical),
- StorageCount: count,
- MissingCount: missingCount,
- RestoredCount: restoredCount,
- }, nil
-}
-
-func (r *sparseRuntime) upsertSourceItems(ctx context.Context, botID string, items []storefs.MemoryItem) error {
- if len(items) == 0 {
- return nil
- }
- if err := r.ensureCollection(ctx); err != nil {
- return err
- }
- texts := make([]string, 0, len(items))
- canonical := make([]storefs.MemoryItem, 0, len(items))
- for _, item := range items {
- item = canonicalStoreItem(item)
- if item.ID == "" || item.Memory == "" {
- continue
- }
- canonical = append(canonical, item)
- texts = append(texts, item.Memory)
- }
- if len(canonical) == 0 {
- return nil
- }
- vectors, err := r.encoder.EncodeDocuments(ctx, texts)
- if err != nil {
- return fmt.Errorf("sparse encode documents: %w", err)
- }
- if len(vectors) != len(canonical) {
- return fmt.Errorf("sparse encode documents: expected %d vectors, got %d", len(canonical), len(vectors))
- }
- for i, item := range canonical {
- vec := vectors[i]
- if err := r.qdrant.Upsert(ctx, runtimePointID(botID, item.ID), qdrantclient.SparseVector{
- Indices: vec.Indices,
- Values: vec.Values,
- }, runtimePayload(botID, item)); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (r *sparseRuntime) populateExplainStats(ctx context.Context, items []*adapters.MemoryItem) {
- if len(items) == 0 {
- return
- }
- texts := make([]string, 0, len(items))
- targets := make([]*adapters.MemoryItem, 0, len(items))
- for _, item := range items {
- if item == nil || strings.TrimSpace(item.Memory) == "" {
- continue
- }
- texts = append(texts, item.Memory)
- targets = append(targets, item)
- }
- if len(texts) == 0 {
- return
- }
- vectors, err := r.encoder.EncodeDocuments(ctx, texts)
- if err != nil || len(vectors) != len(targets) {
- return
- }
- for i := range targets {
- topK, cdf := sparseExplainStats(vectors[i])
- targets[i].TopKBuckets = topK
- targets[i].CDFCurve = cdf
- }
-}
-
-func sparseExplainStats(vec sparse.SparseVector) ([]adapters.TopKBucket, []adapters.CDFPoint) {
- type pair struct {
- index uint32
- value float32
- }
- pairs := make([]pair, 0, len(vec.Values))
- for i, value := range vec.Values {
- if i >= len(vec.Indices) || value <= 0 {
- continue
- }
- pairs = append(pairs, pair{index: vec.Indices[i], value: value})
- }
- if len(pairs) == 0 {
- return nil, nil
- }
- sort.Slice(pairs, func(i, j int) bool {
- if pairs[i].value == pairs[j].value {
- return pairs[i].index < pairs[j].index
- }
- return pairs[i].value > pairs[j].value
- })
- topN := len(pairs)
- if topN > sparseExplainTopKLimit {
- topN = sparseExplainTopKLimit
- }
- topK := make([]adapters.TopKBucket, 0, topN)
- total := 0.0
- for _, pair := range pairs {
- total += float64(pair.value)
- }
- for _, pair := range pairs[:topN] {
- topK = append(topK, adapters.TopKBucket{
- Index: pair.index,
- Value: pair.value,
- })
- }
- cdf := make([]adapters.CDFPoint, 0, len(pairs))
- if total <= 0 {
- return topK, cdf
- }
- running := 0.0
- for i, pair := range pairs {
- running += float64(pair.value)
- cdf = append(cdf, adapters.CDFPoint{
- K: i + 1,
- Cumulative: running / total,
- })
- }
- return topK, cdf
-}
-
-func sparseMemoryItemPointers(items []adapters.MemoryItem) []*adapters.MemoryItem {
- if len(items) == 0 {
- return nil
- }
- pointers := make([]*adapters.MemoryItem, 0, len(items))
- for i := range items {
- pointers = append(pointers, &items[i])
- }
- return pointers
-}
diff --git a/internal/memory/adapters/builtin/sparse_runtime_test.go b/internal/memory/adapters/builtin/sparse_runtime_test.go
deleted file mode 100644
index 03465d406c..0000000000
--- a/internal/memory/adapters/builtin/sparse_runtime_test.go
+++ /dev/null
@@ -1,419 +0,0 @@
-package builtin
-
-import (
- "context"
- "log/slog"
- "sort"
- "strings"
- "testing"
-
- adapters "github.com/memohai/memoh/internal/memory/adapters"
- qdrantclient "github.com/memohai/memoh/internal/memory/qdrant"
- "github.com/memohai/memoh/internal/memory/sparse"
- storefs "github.com/memohai/memoh/internal/memory/storefs"
-)
-
-type fakeSparseStore struct {
- items map[string]storefs.MemoryItem
- archive []storefs.MemoryItem
-}
-
-func newFakeSparseStore(items ...storefs.MemoryItem) *fakeSparseStore {
- store := &fakeSparseStore{items: map[string]storefs.MemoryItem{}}
- for _, item := range items {
- store.items[item.ID] = item
- }
- return store
-}
-
-func (s *fakeSparseStore) PersistMemories(_ context.Context, _ string, items []storefs.MemoryItem, _ map[string]any) error {
- for _, item := range items {
- s.items[item.ID] = item
- }
- return nil
-}
-
-func (s *fakeSparseStore) ReadAllMemoryFiles(_ context.Context, _ string) ([]storefs.MemoryItem, error) {
- out := make([]storefs.MemoryItem, 0, len(s.items))
- for _, item := range s.items {
- out = append(out, item)
- }
- sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID })
- return out, nil
-}
-
-func (s *fakeSparseStore) RemoveMemories(_ context.Context, _ string, ids []string) error {
- for _, id := range ids {
- delete(s.items, strings.TrimSpace(id))
- }
- return nil
-}
-
-func (s *fakeSparseStore) RemoveAllMemories(_ context.Context, _ string) error {
- s.items = map[string]storefs.MemoryItem{}
- return nil
-}
-
-func (s *fakeSparseStore) RebuildFiles(_ context.Context, _ string, items []storefs.MemoryItem, _ map[string]any) error {
- s.items = map[string]storefs.MemoryItem{}
- for _, item := range items {
- s.items[item.ID] = item
- }
- return nil
-}
-
-func (s *fakeSparseStore) ArchiveAndRebuildFiles(ctx context.Context, _ string, active []storefs.MemoryItem, archived []storefs.MemoryItem, _ map[string]any) error {
- s.archive = append([]storefs.MemoryItem(nil), archived...)
- return s.RebuildFiles(ctx, "", active, nil)
-}
-
-func (*fakeSparseStore) SyncOverview(context.Context, string) error { return nil }
-
-func (s *fakeSparseStore) CountMemoryFiles(_ context.Context, _ string) (int, error) {
- if len(s.items) == 0 {
- return 0, nil
- }
- return 1, nil
-}
-
-type fakeSparseEncoder struct {
- lastQuery string
-}
-
-func (*fakeSparseEncoder) EncodeDocument(_ context.Context, _ string) (*sparse.SparseVector, error) {
- return &sparse.SparseVector{Indices: []uint32{1, 2, 3}, Values: []float32{1, 3, 2}}, nil
-}
-
-func (*fakeSparseEncoder) EncodeDocuments(_ context.Context, texts []string) ([]sparse.SparseVector, error) {
- out := make([]sparse.SparseVector, 0, len(texts))
- for _, text := range texts {
- _ = text
- out = append(out, sparse.SparseVector{Indices: []uint32{1, 2, 3}, Values: []float32{1, 3, 2}})
- }
- return out, nil
-}
-
-func (e *fakeSparseEncoder) EncodeQuery(_ context.Context, text string) (*sparse.SparseVector, error) {
- e.lastQuery = text
- return &sparse.SparseVector{Indices: []uint32{9}, Values: []float32{1}}, nil
-}
-
-func (*fakeSparseEncoder) Health(context.Context) error { return nil }
-
-type fakeSparseIndex struct {
- encoder *fakeSparseEncoder
- collection string
- exists bool
- points map[string]qdrantclient.SearchResult
-}
-
-func newFakeSparseIndex(encoder *fakeSparseEncoder) *fakeSparseIndex {
- return &fakeSparseIndex{
- encoder: encoder,
- collection: "memory_sparse_test",
- points: map[string]qdrantclient.SearchResult{},
- }
-}
-
-func (i *fakeSparseIndex) CollectionName() string { return i.collection }
-
-func (i *fakeSparseIndex) CollectionExists(context.Context) (bool, error) { return i.exists, nil }
-
-func (i *fakeSparseIndex) EnsureCollection(context.Context) error {
- i.exists = true
- return nil
-}
-
-func (i *fakeSparseIndex) Upsert(_ context.Context, id string, _ qdrantclient.SparseVector, payload map[string]string) error {
- i.exists = true
- i.points[id] = qdrantclient.SearchResult{
- ID: id,
- Score: 1,
- Payload: payload,
- }
- return nil
-}
-
-func (i *fakeSparseIndex) Search(_ context.Context, _ qdrantclient.SparseVector, botID string, limit int) ([]qdrantclient.SearchResult, error) {
- query := strings.ToLower(strings.TrimSpace(i.encoder.lastQuery))
- results := make([]qdrantclient.SearchResult, 0, len(i.points))
- for _, point := range i.points {
- if strings.TrimSpace(point.Payload["bot_id"]) != strings.TrimSpace(botID) {
- continue
- }
- text := strings.ToLower(point.Payload["memory"])
- if query != "" && !strings.Contains(text, query) {
- continue
- }
- point.Score = 1
- results = append(results, point)
- }
- sort.Slice(results, func(a, b int) bool { return results[a].ID < results[b].ID })
- if limit > 0 && len(results) > limit {
- results = results[:limit]
- }
- return results, nil
-}
-
-func (i *fakeSparseIndex) Scroll(_ context.Context, botID string, limit int) ([]qdrantclient.SearchResult, error) {
- results := make([]qdrantclient.SearchResult, 0, len(i.points))
- for _, point := range i.points {
- if strings.TrimSpace(point.Payload["bot_id"]) != strings.TrimSpace(botID) {
- continue
- }
- results = append(results, point)
- }
- sort.Slice(results, func(a, b int) bool { return results[a].ID < results[b].ID })
- if limit > 0 && len(results) > limit {
- results = results[:limit]
- }
- return results, nil
-}
-
-func (i *fakeSparseIndex) Count(_ context.Context, botID string) (int, error) {
- count := 0
- for _, point := range i.points {
- if strings.TrimSpace(point.Payload["bot_id"]) == strings.TrimSpace(botID) {
- count++
- }
- }
- return count, nil
-}
-
-func (i *fakeSparseIndex) DeleteByIDs(_ context.Context, ids []string) error {
- for _, id := range ids {
- delete(i.points, strings.TrimSpace(id))
- }
- return nil
-}
-
-func (i *fakeSparseIndex) DeleteByBotID(_ context.Context, botID string) error {
- for id, point := range i.points {
- if strings.TrimSpace(point.Payload["bot_id"]) == strings.TrimSpace(botID) {
- delete(i.points, id)
- }
- }
- return nil
-}
-
-func TestSparseRuntimeAddWritesSourceAndSupportsRecall(t *testing.T) {
- t.Parallel()
-
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{
- qdrant: index,
- encoder: encoder,
- store: store,
- }
-
- resp, err := runtime.Add(context.Background(), adapters.AddRequest{
- BotID: "bot-1",
- Message: "Ran likes oolong tea",
- Filters: map[string]any{"scopeId": "bot-1"},
- })
- if err != nil {
- t.Fatalf("Add() error = %v", err)
- }
- if len(resp.Results) != 1 {
- t.Fatalf("expected 1 add result, got %d", len(resp.Results))
- }
- item := resp.Results[0]
- if item.ID == "" {
- t.Fatal("expected source memory id to be populated")
- }
- if _, ok := store.items[item.ID]; !ok {
- t.Fatalf("expected memory %q to be written to markdown source", item.ID)
- }
- point, ok := index.points[runtimePointID("bot-1", item.ID)]
- if !ok {
- t.Fatalf("expected qdrant point for source memory %q", item.ID)
- }
- if point.Payload["source_entry_id"] != item.ID {
- t.Fatalf("expected source_entry_id payload %q, got %q", item.ID, point.Payload["source_entry_id"])
- }
- if len(item.TopKBuckets) != 0 || len(item.CDFCurve) != 0 {
- t.Fatalf("expected add response to skip explain stats, got %#v", item)
- }
-
- searchResp, err := runtime.Search(context.Background(), adapters.SearchRequest{
- BotID: "bot-1",
- Query: "oolong tea",
- })
- if err != nil {
- t.Fatalf("Search() error = %v", err)
- }
- if len(searchResp.Results) != 1 {
- t.Fatalf("expected 1 search result, got %d", len(searchResp.Results))
- }
- if searchResp.Results[0].ID != item.ID {
- t.Fatalf("expected search result id %q, got %q", item.ID, searchResp.Results[0].ID)
- }
- if len(searchResp.Results[0].TopKBuckets) != 0 || len(searchResp.Results[0].CDFCurve) != 0 {
- t.Fatalf("expected search result to skip explain stats, got %#v", searchResp.Results[0])
- }
-}
-
-func TestSparseRuntimeRebuildSyncsSourceAndRemovesStalePoints(t *testing.T) {
- t.Parallel()
-
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore(
- storefs.MemoryItem{
- ID: "bot-1:mem_1",
- Memory: "Ran likes tea",
- Hash: runtimeHash("Ran likes tea"),
- CreatedAt: "2026-03-13T09:00:00Z",
- UpdatedAt: "2026-03-13T09:00:00Z",
- },
- storefs.MemoryItem{
- ID: "bot-1:mem_2",
- Memory: "Ran works in Berlin",
- Hash: runtimeHash("Ran works in Berlin"),
- CreatedAt: "2026-03-13T10:00:00Z",
- UpdatedAt: "2026-03-13T10:00:00Z",
- },
- )
- runtime := &sparseRuntime{
- qdrant: index,
- encoder: encoder,
- store: store,
- }
-
- index.points[runtimePointID("bot-1", "bot-1:mem_1")] = qdrantclient.SearchResult{
- ID: runtimePointID("bot-1", "bot-1:mem_1"),
- Payload: map[string]string{
- "bot_id": "bot-1",
- "memory": "Ran likes tea",
- "source_entry_id": "bot-1:mem_1",
- "hash": "outdated",
- "created_at": "2026-03-13T09:00:00Z",
- "updated_at": "2026-03-13T09:00:00Z",
- },
- }
- index.points[runtimePointID("bot-1", "bot-1:stale")] = qdrantclient.SearchResult{
- ID: runtimePointID("bot-1", "bot-1:stale"),
- Payload: map[string]string{
- "bot_id": "bot-1",
- "memory": "stale memory",
- "source_entry_id": "bot-1:stale",
- },
- }
-
- result, err := runtime.Rebuild(context.Background(), "bot-1")
- if err != nil {
- t.Fatalf("Rebuild() error = %v", err)
- }
- if result.FsCount != 2 {
- t.Fatalf("expected fs_count=2, got %d", result.FsCount)
- }
- if result.StorageCount != 2 {
- t.Fatalf("expected storage_count=2, got %d", result.StorageCount)
- }
- if result.MissingCount != 1 {
- t.Fatalf("expected missing_count=1, got %d", result.MissingCount)
- }
- if result.RestoredCount != 2 {
- t.Fatalf("expected restored_count=2, got %d", result.RestoredCount)
- }
- if _, ok := index.points[runtimePointID("bot-1", "bot-1:stale")]; ok {
- t.Fatal("expected stale qdrant point to be removed")
- }
-}
-
-func TestSparseRuntimeGetAllIncludesExplainStats(t *testing.T) {
- t.Parallel()
-
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore(
- storefs.MemoryItem{
- ID: "bot-1:mem_1",
- Memory: "Ran likes tea",
- Hash: runtimeHash("Ran likes tea"),
- CreatedAt: "2026-03-13T09:00:00Z",
- UpdatedAt: "2026-03-13T09:00:00Z",
- },
- )
- runtime := &sparseRuntime{
- qdrant: index,
- encoder: encoder,
- store: store,
- }
-
- resp, err := runtime.GetAll(context.Background(), adapters.GetAllRequest{BotID: "bot-1"})
- if err != nil {
- t.Fatalf("GetAll() error = %v", err)
- }
- if len(resp.Results) != 1 {
- t.Fatalf("expected 1 result, got %d", len(resp.Results))
- }
- if len(resp.Results[0].TopKBuckets) == 0 || len(resp.Results[0].CDFCurve) == 0 {
- t.Fatalf("expected get all result to include explain stats, got %#v", resp.Results[0])
- }
- if resp.Results[0].TopKBuckets[0].Index != 2 {
- t.Fatalf("expected top bucket index 2, got %d", resp.Results[0].TopKBuckets[0].Index)
- }
- if got := resp.Results[0].CDFCurve[len(resp.Results[0].CDFCurve)-1].Cumulative; got != 1 {
- t.Fatalf("expected final CDF cumulative to be 1, got %v", got)
- }
-}
-
-func TestBuiltinProviderMultiTurnRecallUsesSparseSourceRuntime(t *testing.T) {
- t.Parallel()
-
- encoder := &fakeSparseEncoder{}
- index := newFakeSparseIndex(encoder)
- store := newFakeSparseStore()
- runtime := &sparseRuntime{
- qdrant: index,
- encoder: encoder,
- store: store,
- }
- provider := NewBuiltinProvider(slog.Default(), runtime, nil, nil)
-
- err := provider.OnAfterChat(context.Background(), adapters.AfterChatRequest{
- BotID: "bot-1",
- Messages: []adapters.Message{
- {Role: "user", Content: "I like oolong tea."},
- {Role: "assistant", Content: "Noted, you like oolong tea."},
- },
- })
- if err != nil {
- t.Fatalf("OnAfterChat round 1 error = %v", err)
- }
- err = provider.OnAfterChat(context.Background(), adapters.AfterChatRequest{
- BotID: "bot-1",
- Messages: []adapters.Message{
- {Role: "user", Content: "I am based in Berlin."},
- {Role: "assistant", Content: "Understood, you are based in Berlin."},
- },
- })
- if err != nil {
- t.Fatalf("OnAfterChat round 2 error = %v", err)
- }
-
- before, err := provider.OnBeforeChat(context.Background(), adapters.BeforeChatRequest{
- BotID: "bot-1",
- Query: "berlin",
- })
- if err != nil {
- t.Fatalf("OnBeforeChat() error = %v", err)
- }
- if before == nil || !strings.Contains(strings.ToLower(before.ContextText), "berlin") {
- t.Fatalf("expected recalled context to mention berlin, got %#v", before)
- }
-
- before, err = provider.OnBeforeChat(context.Background(), adapters.BeforeChatRequest{
- BotID: "bot-1",
- Query: "oolong tea",
- })
- if err != nil {
- t.Fatalf("OnBeforeChat() tea error = %v", err)
- }
- if before == nil || !strings.Contains(strings.ToLower(before.ContextText), "oolong tea") {
- t.Fatalf("expected recalled context to mention oolong tea, got %#v", before)
- }
-}
diff --git a/internal/memory/adapters/builtin/store_test.go b/internal/memory/adapters/builtin/store_test.go
new file mode 100644
index 0000000000..9c67685277
--- /dev/null
+++ b/internal/memory/adapters/builtin/store_test.go
@@ -0,0 +1,77 @@
+package builtin
+
+import (
+ "context"
+ "sort"
+ "strings"
+
+ storefs "github.com/memohai/memoh/internal/memory/storefs"
+)
+
+// fakeStore is an in-memory implementation of the memoryStore interface used by
+// the file runtime tests in this package.
+type fakeStore struct {
+ items map[string]storefs.MemoryItem
+ archive []storefs.MemoryItem
+}
+
+func newFakeStore(items ...storefs.MemoryItem) *fakeStore {
+ store := &fakeStore{items: map[string]storefs.MemoryItem{}}
+ for _, item := range items {
+ store.items[item.ID] = item
+ }
+ return store
+}
+
+func (s *fakeStore) PersistMemories(_ context.Context, _ string, items []storefs.MemoryItem, _ map[string]any) error {
+ for _, item := range items {
+ s.items[item.ID] = item
+ }
+ return nil
+}
+
+func (s *fakeStore) ReadAllMemoryFiles(_ context.Context, _ string) ([]storefs.MemoryItem, error) {
+ out := make([]storefs.MemoryItem, 0, len(s.items))
+ for _, item := range s.items {
+ out = append(out, item)
+ }
+ sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID })
+ return out, nil
+}
+
+func (s *fakeStore) RemoveMemories(_ context.Context, _ string, ids []string) error {
+ for _, id := range ids {
+ delete(s.items, strings.TrimSpace(id))
+ }
+ return nil
+}
+
+func (s *fakeStore) RemoveAllMemories(_ context.Context, _ string) error {
+ s.items = map[string]storefs.MemoryItem{}
+ return nil
+}
+
+func (s *fakeStore) RebuildFiles(_ context.Context, _ string, items []storefs.MemoryItem, _ map[string]any) error {
+ s.items = map[string]storefs.MemoryItem{}
+ for _, item := range items {
+ s.items[item.ID] = item
+ }
+ return nil
+}
+
+func (s *fakeStore) ArchiveAndRebuildFiles(ctx context.Context, _ string, active []storefs.MemoryItem, archived []storefs.MemoryItem, _ map[string]any) error {
+ s.archive = append([]storefs.MemoryItem(nil), archived...)
+ return s.RebuildFiles(ctx, "", active, nil)
+}
+
+func (*fakeStore) SyncOverview(context.Context, string) error { return nil }
+
+func (s *fakeStore) CountMemoryFiles(_ context.Context, _ string) (int, error) {
+ if len(s.items) == 0 {
+ return 0, nil
+ }
+ return 1, nil
+}
+
+// Compile-time assertion that fakeStore satisfies the memoryStore interface.
+var _ memoryStore = (*fakeStore)(nil)
diff --git a/internal/memory/adapters/service.go b/internal/memory/adapters/service.go
index a593a6db05..bb78e7085a 100644
--- a/internal/memory/adapters/service.go
+++ b/internal/memory/adapters/service.go
@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"log/slog"
- "strconv"
"strings"
"github.com/memohai/memoh/internal/config"
@@ -46,7 +45,7 @@ func (*Service) ListMeta(_ context.Context) []ProviderMeta {
"memory_mode": {
Type: "select",
Title: "Memory Mode",
- Description: "off = file-based, sparse = Qdrant sparse vectors, dense = embedding API + Qdrant dense vectors",
+ Description: "off = file-based, dense = embedding API + Qdrant dense vectors",
Required: false,
},
"embedding_model_id": {
@@ -55,13 +54,6 @@ func (*Service) ListMeta(_ context.Context) []ProviderMeta {
Description: "Embedding model for dense vector search (dense mode only)",
Required: false,
},
- "qdrant_collection": {
- Type: "string",
- Title: "Qdrant Collection",
- Description: "Qdrant collection name for sparse mode. Defaults to memory_sparse.",
- Required: false,
- Example: "memory_sparse",
- },
"context_target_items": {
Type: "integer",
Title: "Context Target Items",
@@ -182,11 +174,11 @@ func (s *Service) Status(ctx context.Context, id string) (ProviderStatusResponse
}
status.MemoryMode = StringFromConfig(resp.Config, "memory_mode")
status.EmbeddingModelID = StringFromConfig(resp.Config, "embedding_model_id")
- collections := []string{"memory_sparse", "memory_dense"}
+ collections := []string{"memory_dense"}
status.Collections = make([]ProviderCollectionStatus, 0, len(collections))
for _, collection := range collections {
collStatus := ProviderCollectionStatus{Name: collection}
- host, port := parseQdrantHostPort(s.cfg.Qdrant.BaseURL)
+ host, port := qdrantclient.ParseHostPort(s.cfg.Qdrant.BaseURL)
client, err := qdrantclient.NewClient(host, port, s.cfg.Qdrant.APIKey, collection)
if err != nil {
collStatus.Qdrant.Error = err.Error()
@@ -359,26 +351,3 @@ func isValidProviderType(t ProviderType) bool {
return false
}
}
-
-func parseQdrantHostPort(baseURL string) (string, int) {
- baseURL = strings.TrimSpace(baseURL)
- if baseURL == "" {
- return "", 0
- }
- baseURL = strings.TrimPrefix(baseURL, "http://")
- baseURL = strings.TrimPrefix(baseURL, "https://")
- parts := strings.SplitN(baseURL, ":", 2)
- host := parts[0]
- if len(parts) == 2 {
- httpPort, err := strconv.Atoi(strings.TrimRight(parts[1], "/"))
- if err == nil {
- switch httpPort {
- case 6333, 6334:
- return host, 6334
- default:
- return host, httpPort
- }
- }
- }
- return host, 6334
-}
diff --git a/internal/memory/adapters/types.go b/internal/memory/adapters/types.go
index c56c2beb95..ab9fe2d6b1 100644
--- a/internal/memory/adapters/types.go
+++ b/internal/memory/adapters/types.go
@@ -85,57 +85,17 @@ type DeleteAllRequest struct {
Filters map[string]any `json:"filters,omitempty"`
}
-type EmbedInput struct {
- Text string `json:"text,omitempty"`
- ImageURL string `json:"image_url,omitempty"`
- VideoURL string `json:"video_url,omitempty"`
-}
-
-type EmbedUpsertRequest struct {
- Type string `json:"type"`
- Provider string `json:"provider,omitempty"`
- Model string `json:"model,omitempty"`
- Input EmbedInput `json:"input"`
- Source string `json:"source,omitempty"`
- BotID string `json:"bot_id,omitempty"`
- AgentID string `json:"agent_id,omitempty"`
- RunID string `json:"run_id,omitempty"`
- Metadata map[string]any `json:"metadata,omitempty"`
- Filters map[string]any `json:"filters,omitempty"`
-}
-
-type EmbedUpsertResponse struct {
- Item MemoryItem `json:"item"`
- Provider string `json:"provider"`
- Model string `json:"model"`
- Dimensions int `json:"dimensions"`
-}
-
type MemoryItem struct {
- ID string `json:"id"`
- Memory string `json:"memory"`
- Hash string `json:"hash,omitempty"`
- CreatedAt string `json:"created_at,omitempty"`
- UpdatedAt string `json:"updated_at,omitempty"`
- Score float64 `json:"score,omitempty"`
- Metadata map[string]any `json:"metadata,omitempty"`
- BotID string `json:"bot_id,omitempty"`
- AgentID string `json:"agent_id,omitempty"`
- RunID string `json:"run_id,omitempty"`
- TopKBuckets []TopKBucket `json:"top_k_buckets,omitempty"`
- CDFCurve []CDFPoint `json:"cdf_curve,omitempty"`
-}
-
-// TopKBucket represents one bar in the Top-K sparse dimension bar chart.
-type TopKBucket struct {
- Index uint32 `json:"index"` // sparse dimension index (term hash)
- Value float32 `json:"value"` // weight (term frequency)
-}
-
-// CDFPoint represents one point on the cumulative contribution curve.
-type CDFPoint struct {
- K int `json:"k"` // rank position (1-based, sorted by value desc)
- Cumulative float64 `json:"cumulative"` // cumulative weight fraction [0.0, 1.0]
+ ID string `json:"id"`
+ Memory string `json:"memory"`
+ Hash string `json:"hash,omitempty"`
+ CreatedAt string `json:"created_at,omitempty"`
+ UpdatedAt string `json:"updated_at,omitempty"`
+ Score float64 `json:"score,omitempty"`
+ Metadata map[string]any `json:"metadata,omitempty"`
+ BotID string `json:"bot_id,omitempty"`
+ AgentID string `json:"agent_id,omitempty"`
+ RunID string `json:"run_id,omitempty"`
}
type SearchResponse struct {
@@ -205,7 +165,6 @@ type CompactResult struct {
type MemoryCompactCapability struct {
Semantic bool `json:"semantic"`
- Native bool `json:"native,omitempty"`
Archive bool `json:"archive,omitempty"`
RebuildIndex bool `json:"rebuild_index,omitempty"`
Reason string `json:"reason,omitempty"`
diff --git a/internal/memory/qdrant/client.go b/internal/memory/qdrant/client.go
index ba52e5cb32..f726f5120d 100644
--- a/internal/memory/qdrant/client.go
+++ b/internal/memory/qdrant/client.go
@@ -1,5 +1,5 @@
// Package qdrant wraps the official github.com/qdrant/go-client SDK,
-// providing a thin facade for sparse-vector memory operations.
+// providing a thin facade for dense-vector memory operations.
package qdrant
import (
@@ -12,11 +12,7 @@ import (
pb "github.com/qdrant/go-client/qdrant"
)
-const (
- sparseVectorName = "sparse"
-)
-
-// Client wraps the official Qdrant gRPC client with sparse-memory-specific helpers.
+// Client wraps the official Qdrant gRPC client with dense-memory-specific helpers.
type Client struct {
inner *pb.Client
collection string
@@ -55,10 +51,6 @@ func (c *Client) Close() error {
return c.inner.Close()
}
-func (c *Client) CollectionName() string {
- return c.collection
-}
-
func (c *Client) CollectionExists(ctx context.Context) (bool, error) {
exists, err := c.inner.CollectionExists(ctx, c.collection)
if err != nil {
@@ -67,27 +59,6 @@ func (c *Client) CollectionExists(ctx context.Context) (bool, error) {
return exists, nil
}
-// EnsureCollection creates the collection with a named sparse vector config if it does not exist.
-func (c *Client) EnsureCollection(ctx context.Context) error {
- exists, err := c.CollectionExists(ctx)
- if err != nil {
- return err
- }
- if exists {
- return nil
- }
- err = c.inner.CreateCollection(ctx, &pb.CreateCollection{
- CollectionName: c.collection,
- SparseVectorsConfig: pb.NewSparseVectorsConfig(map[string]*pb.SparseVectorParams{
- sparseVectorName: {},
- }),
- })
- if err != nil {
- return fmt.Errorf("qdrant: create collection: %w", err)
- }
- return nil
-}
-
// EnsureDenseCollection creates the collection with dense vector config if it
// does not exist.
func (c *Client) EnsureDenseCollection(ctx context.Context, dimensions int) error {
@@ -114,48 +85,17 @@ func (c *Client) EnsureDenseCollection(ctx context.Context, dimensions int) erro
return nil
}
-// SparseVector holds the non-zero components of a sparse text encoding.
-type SparseVector struct {
- Indices []uint32
- Values []float32
-}
-
type DenseVector struct {
Values []float32
}
-// SearchResult is one result from a sparse search or scroll.
+// SearchResult is one result from a search or scroll.
type SearchResult struct {
ID string
Score float64
Payload map[string]string
}
-// Upsert inserts or updates points with named sparse vectors.
-func (c *Client) Upsert(ctx context.Context, id string, vec SparseVector, payload map[string]string) error {
- wait := true
- _, err := c.inner.Upsert(ctx, &pb.UpsertPoints{
- CollectionName: c.collection,
- Wait: &wait,
- Points: []*pb.PointStruct{
- {
- Id: pb.NewID(id),
- Vectors: pb.NewVectorsMap(map[string]*pb.Vector{
- sparseVectorName: {
- Data: vec.Values,
- Indices: &pb.SparseIndices{Data: vec.Indices},
- },
- }),
- Payload: stringPayloadToValueMap(payload),
- },
- },
- })
- if err != nil {
- return fmt.Errorf("qdrant: upsert: %w", err)
- }
- return nil
-}
-
// UpsertDense inserts or updates points with dense vectors.
func (c *Client) UpsertDense(ctx context.Context, id string, vec DenseVector, payload map[string]string) error {
wait := true
@@ -176,29 +116,6 @@ func (c *Client) UpsertDense(ctx context.Context, id string, vec DenseVector, pa
return nil
}
-// Search performs a sparse-vector query against the collection, filtered by bot_id.
-func (c *Client) Search(ctx context.Context, vec SparseVector, botID string, limit int) ([]SearchResult, error) {
- if limit <= 0 {
- limit = 10
- }
- queryLimit, err := intToUint64(limit)
- if err != nil {
- return nil, fmt.Errorf("qdrant: invalid search limit: %w", err)
- }
- scored, err := c.inner.Query(ctx, &pb.QueryPoints{
- CollectionName: c.collection,
- Query: pb.NewQuerySparse(vec.Indices, vec.Values),
- Using: strPtr(sparseVectorName),
- Filter: botFilter(botID),
- Limit: uint64Ptr(queryLimit),
- WithPayload: pb.NewWithPayload(true),
- })
- if err != nil {
- return nil, fmt.Errorf("qdrant: search: %w", err)
- }
- return scoredPointsToResults(scored), nil
-}
-
// SearchDense performs a dense-vector query against the collection, filtered by bot_id.
func (c *Client) SearchDense(ctx context.Context, vec DenseVector, botID string, limit int) ([]SearchResult, error) {
if limit <= 0 {
@@ -329,6 +246,34 @@ func (c *Client) DeleteByBotID(ctx context.Context, botID string) error {
// --- helpers ---
+// ParseHostPort extracts the host and gRPC port from a Qdrant base URL. Qdrant
+// base URLs are typically HTTP (port 6333), but the gRPC port is 6334; a bare
+// host or an unparseable port defaults to 6334. When the configured port is
+// neither 6333 nor 6334 the operator has usually already set the intended gRPC
+// port, so it is returned as-is.
+func ParseHostPort(baseURL string) (string, int) {
+ baseURL = strings.TrimSpace(baseURL)
+ if baseURL == "" {
+ return "", 0
+ }
+ baseURL = strings.TrimPrefix(baseURL, "http://")
+ baseURL = strings.TrimPrefix(baseURL, "https://")
+ parts := strings.SplitN(baseURL, ":", 2)
+ host := parts[0]
+ if len(parts) == 2 {
+ httpPort, err := strconv.Atoi(strings.TrimRight(parts[1], "/"))
+ if err == nil {
+ switch httpPort {
+ case 6333, 6334:
+ return host, 6334
+ default:
+ return host, httpPort
+ }
+ }
+ }
+ return host, 6334
+}
+
func botFilter(botID string) *pb.Filter {
return &pb.Filter{
Must: []*pb.Condition{
@@ -389,8 +334,6 @@ func extractID(id *pb.PointId) string {
return strconv.FormatUint(id.GetNum(), 10)
}
-func strPtr(s string) *string { return &s }
-
func uint64Ptr(v uint64) *uint64 { return &v }
func intToUint64(v int) (uint64, error) {
diff --git a/internal/memory/sparse/encoder.go b/internal/memory/sparse/encoder.go
deleted file mode 100644
index 8d7cf52000..0000000000
--- a/internal/memory/sparse/encoder.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// Package sparse provides a Go client for the sparse encoding Python service.
-// The Python service loads the OpenSearch neural sparse model from HuggingFace
-// and exposes HTTP endpoints for text → sparse vector encoding.
-package sparse
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strings"
- "time"
-)
-
-// SparseVector holds the non-zero components of a sparse text encoding.
-type SparseVector struct {
- Indices []uint32 `json:"indices"`
- Values []float32 `json:"values"`
-}
-
-// Client calls the Python sparse encoding service.
-type Client struct {
- baseURL string
- http *http.Client
-}
-
-// NewClient creates a sparse encoding client pointing to the Python service.
-func NewClient(baseURL string) *Client {
- return &Client{
- baseURL: baseURL,
- http: &http.Client{Timeout: 60 * time.Second},
- }
-}
-
-// EncodeDocument encodes a document text into a sparse vector using the neural model.
-func (c *Client) EncodeDocument(ctx context.Context, text string) (*SparseVector, error) {
- return c.encode(ctx, "/encode/document", text)
-}
-
-// EncodeQuery encodes a query text into a sparse vector (IDF-weighted tokenizer lookup).
-func (c *Client) EncodeQuery(ctx context.Context, text string) (*SparseVector, error) {
- return c.encode(ctx, "/encode/query", text)
-}
-
-// EncodeDocuments encodes multiple document texts in a single batch call.
-func (c *Client) EncodeDocuments(ctx context.Context, texts []string) ([]SparseVector, error) {
- body, err := json.Marshal(map[string]any{"texts": texts})
- if err != nil {
- return nil, err
- }
- endpoint, err := joinEndpointURL(c.baseURL, "/encode/documents")
- if err != nil {
- return nil, err
- }
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
- if err != nil {
- return nil, err
- }
- req.Header.Set("Content-Type", "application/json")
- resp, err := c.http.Do(req) //nolint:gosec // G704: URL is validated and derived from operator-configured sparse encoder base URL
- if err != nil {
- return nil, fmt.Errorf("sparse encode failed: %w", err)
- }
- defer func() {
- _ = resp.Body.Close()
- }()
- if resp.StatusCode != http.StatusOK {
- respBody, _ := io.ReadAll(resp.Body)
- return nil, fmt.Errorf("sparse encode error (status %d): %s", resp.StatusCode, string(respBody))
- }
- var vectors []SparseVector
- if err := json.NewDecoder(resp.Body).Decode(&vectors); err != nil {
- return nil, err
- }
- return vectors, nil
-}
-
-func (c *Client) Health(ctx context.Context) error {
- endpoint, err := joinEndpointURL(c.baseURL, "/health")
- if err != nil {
- return err
- }
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return err
- }
- resp, err := c.http.Do(req) //nolint:gosec // G704: URL is validated and derived from operator-configured sparse encoder base URL
- if err != nil {
- return fmt.Errorf("sparse health check failed: %w", err)
- }
- defer func() {
- _ = resp.Body.Close()
- }()
- if resp.StatusCode != http.StatusOK {
- respBody, _ := io.ReadAll(resp.Body)
- return fmt.Errorf("sparse health error (status %d): %s", resp.StatusCode, string(respBody))
- }
- return nil
-}
-
-func (c *Client) encode(ctx context.Context, path, text string) (*SparseVector, error) {
- body, err := json.Marshal(map[string]string{"text": text})
- if err != nil {
- return nil, err
- }
- endpoint, err := joinEndpointURL(c.baseURL, path)
- if err != nil {
- return nil, err
- }
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
- if err != nil {
- return nil, err
- }
- req.Header.Set("Content-Type", "application/json")
- resp, err := c.http.Do(req) //nolint:gosec // G704: URL is validated and derived from operator-configured sparse encoder base URL
- if err != nil {
- return nil, fmt.Errorf("sparse encode failed: %w", err)
- }
- defer func() {
- _ = resp.Body.Close()
- }()
- if resp.StatusCode != http.StatusOK {
- respBody, _ := io.ReadAll(resp.Body)
- return nil, fmt.Errorf("sparse encode error (status %d): %s", resp.StatusCode, string(respBody))
- }
- var vec SparseVector
- if err := json.NewDecoder(resp.Body).Decode(&vec); err != nil {
- return nil, err
- }
- return &vec, nil
-}
-
-func joinEndpointURL(baseURL, path string) (string, error) {
- baseURL = strings.TrimSpace(baseURL)
- if baseURL == "" {
- return "", errors.New("sparse encode base URL is required")
- }
-
- base, err := url.Parse(baseURL)
- if err != nil {
- return "", fmt.Errorf("invalid sparse encode base URL: %w", err)
- }
- if base.Scheme != "http" && base.Scheme != "https" {
- return "", fmt.Errorf("invalid sparse encode base URL scheme: %q", base.Scheme)
- }
- if base.Host == "" {
- return "", errors.New("invalid sparse encode base URL: host is required")
- }
-
- ref, err := url.Parse(path)
- if err != nil {
- return "", fmt.Errorf("invalid sparse encode path: %w", err)
- }
- return base.ResolveReference(ref).String(), nil
-}
diff --git a/internal/memory/sparse/service/main.py b/internal/memory/sparse/service/main.py
deleted file mode 100644
index 982554731f..0000000000
--- a/internal/memory/sparse/service/main.py
+++ /dev/null
@@ -1,150 +0,0 @@
-"""Sparse encoding Flask service using OpenSearch neural sparse model."""
-
-import json
-import os
-import sys
-from pathlib import Path
-
-import torch
-from flask import Flask, jsonify, request
-from huggingface_hub import hf_hub_download
-from transformers import AutoModelForMaskedLM, AutoTokenizer
-
-DEFAULT_MODEL_REPO = "opensearch-project/opensearch-neural-sparse-encoding-multilingual-v1"
-DEFAULT_PORT = 8085
-DEFAULT_CACHE_DIR = os.environ.get(
- "SPARSE_CACHE_DIR",
- str(Path(__file__).resolve().parent / "hf-cache"),
-)
-
-model_repo = DEFAULT_MODEL_REPO
-cache_dir = DEFAULT_CACHE_DIR
-port = int(os.environ.get("SPARSE_PORT", DEFAULT_PORT))
-
-app = Flask(__name__)
-
-_model = None
-_tokenizer = None
-_idf = None
-_special_token_ids: list[int] = []
-def _load_model() -> None:
- global _model, _tokenizer, _idf, _special_token_ids
- Path(cache_dir).mkdir(parents=True, exist_ok=True)
- _model = AutoModelForMaskedLM.from_pretrained(model_repo, cache_dir=cache_dir)
- _tokenizer = AutoTokenizer.from_pretrained(model_repo, cache_dir=cache_dir)
- _model.eval()
- _idf = _load_idf(_tokenizer)
- _special_token_ids = [
- _tokenizer.vocab[tok]
- for tok in _tokenizer.special_tokens_map.values()
- if tok in _tokenizer.vocab
- ]
-
-
-def _load_idf(tokenizer):
- local_path = hf_hub_download(
- repo_id=model_repo, filename="idf.json", cache_dir=cache_dir
- )
- with open(local_path, encoding="utf-8") as f:
- idf_data = json.load(f)
- idf_vector = [0.0] * tokenizer.vocab_size
- for tok, weight in idf_data.items():
- tid = tokenizer._convert_token_to_id_with_added_voc(tok)
- idf_vector[tid] = weight
- return torch.tensor(idf_vector)
-
-
-@torch.no_grad()
-def _encode_document(text: str) -> dict:
- feat = _tokenizer(
- [text],
- padding=True,
- truncation=True,
- return_tensors="pt",
- return_token_type_ids=False,
- )
- out = _model(**feat)[0]
- vals, _ = torch.max(out * feat["attention_mask"].unsqueeze(-1), dim=1)
- vals = torch.log(1 + torch.log(1 + torch.relu(vals)))
- vals[:, _special_token_ids] = 0
- return _sparse_to_dict(vals[0])
-
-
-@torch.no_grad()
-def _encode_documents(texts: list[str]) -> list[dict]:
- feat = _tokenizer(
- texts,
- padding=True,
- truncation=True,
- return_tensors="pt",
- return_token_type_ids=False,
- )
- out = _model(**feat)[0]
- vals, _ = torch.max(out * feat["attention_mask"].unsqueeze(-1), dim=1)
- vals = torch.log(1 + torch.log(1 + torch.relu(vals)))
- vals[:, _special_token_ids] = 0
- return [_sparse_to_dict(vals[i]) for i in range(vals.shape[0])]
-
-
-def _encode_query(text: str) -> dict:
- feat = _tokenizer(
- [text],
- padding=True,
- truncation=True,
- return_tensors="pt",
- return_token_type_ids=False,
- )
- input_ids = feat["input_ids"]
- batch_size = input_ids.shape[0]
- qv = torch.zeros(batch_size, _tokenizer.vocab_size)
- qv[torch.arange(batch_size).unsqueeze(-1), input_ids] = 1
- sparse_vector = qv * _idf
- return _sparse_to_dict(sparse_vector[0])
-
-
-def _sparse_to_dict(vector: torch.Tensor) -> dict:
- nz = torch.nonzero(vector, as_tuple=True)[0]
- return {"indices": nz.tolist(), "values": vector[nz].tolist()}
-
-
-@app.route("/health", methods=["GET"])
-def health():
- return jsonify(status="ok", model_loaded=True, model_repo=model_repo)
-
-
-@app.route("/encode/document", methods=["POST"])
-def encode_document():
- body = request.get_json(silent=True) or {}
- text = body.get("text", "")
- if not text:
- return jsonify(error="text is required"), 400
- return jsonify(_encode_document(text))
-
-
-@app.route("/encode/query", methods=["POST"])
-def encode_query():
- body = request.get_json(silent=True) or {}
- text = body.get("text", "")
- if not text:
- return jsonify(error="text is required"), 400
- return jsonify(_encode_query(text))
-
-
-@app.route("/encode/documents", methods=["POST"])
-def encode_documents():
- body = request.get_json(silent=True) or {}
- texts = body.get("texts", [])
- if not texts:
- return jsonify(error="texts is required"), 400
- return jsonify(_encode_documents(texts))
-
-
-def main():
- print(f"[sparse-service] loading model {model_repo}...", file=sys.stderr, flush=True)
- _load_model()
- print(f"[sparse-service] listening on port {port}", file=sys.stderr, flush=True)
- app.run(host="0.0.0.0", port=port, threaded=True)
-
-
-if __name__ == "__main__":
- main()
diff --git a/internal/memory/sparse/service/requirements.txt b/internal/memory/sparse/service/requirements.txt
deleted file mode 100644
index d78db54902..0000000000
--- a/internal/memory/sparse/service/requirements.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-flask
-torch
-transformers
-huggingface_hub
-sentencepiece
-protobuf
diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts
index a8e7f628e5..58eab4221d 100644
--- a/packages/config/src/types.ts
+++ b/packages/config/src/types.ts
@@ -11,7 +11,6 @@ export interface Config {
workspace?: WorkspaceConfig;
postgres: PostgresConfig;
qdrant: QdrantConfig;
- sparse: SparseConfig;
agent_gateway: AgentGatewayConfig;
supermarket: SupermarketConfig;
web: WebConfig;
@@ -86,10 +85,6 @@ export interface QdrantConfig {
timeout_seconds: number;
}
-export interface SparseConfig {
- base_url: string;
-}
-
export interface AgentGatewayConfig {
host: string;
port: number;
diff --git a/scripts/install.sh b/scripts/install.sh
old mode 100755
new mode 100644
index dbe65b782e..44084ef24f
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -39,13 +39,8 @@ if [ "${USE_CN_MIRROR+x}" = x ]; then
else
USE_CN_MIRROR_SET=false
fi
-if [ "${USE_SPARSE+x}" = x ]; then
- USE_SPARSE_SET=true
-else
- USE_SPARSE_SET=false
-fi
NETWORK_NAME="${COMPOSE_PROJECT_NAME}_memoh-network"
-PROJECT_CONTAINERS="memoh-postgres memoh-migrate memoh-server memoh-web memoh-sparse memoh-qdrant"
+PROJECT_CONTAINERS="memoh-postgres memoh-migrate memoh-server memoh-web memoh-qdrant"
PROJECT_VOLUMES="${COMPOSE_PROJECT_NAME}_postgres_data ${COMPOSE_PROJECT_NAME}_containerd_data ${COMPOSE_PROJECT_NAME}_memoh_data ${COMPOSE_PROJECT_NAME}_server_cni_state ${COMPOSE_PROJECT_NAME}_qdrant_data ${COMPOSE_PROJECT_NAME}_openviking_data"
EXISTING_CONFIG_SOURCE=""
@@ -364,11 +359,6 @@ load_existing_settings() {
fi
if [ -n "$EXISTING_ENV_SOURCE" ]; then
- if [ "$USE_SPARSE_SET" = false ]; then
- value=$(read_env_file_value "$EXISTING_ENV_SOURCE" "USE_SPARSE" || true)
- [ -n "$value" ] && USE_SPARSE="$value"
- fi
-
value=$(read_env_file_value "$EXISTING_ENV_SOURCE" "POSTGRES_PASSWORD" || true)
[ -n "$value" ] && PG_PASS="$value"
@@ -558,7 +548,6 @@ PG_PASS="memoh123"
WORKSPACE="$WORKSPACE_DEFAULT"
MEMOH_DATA_DIR="$MEMOH_DATA_DIR_DEFAULT"
USE_CN_MIRROR="${USE_CN_MIRROR:-false}"
-USE_SPARSE="${USE_SPARSE:-false}"
if [ "$SILENT" = false ]; then
echo "Configure Memoh (press Enter to use defaults):" > /dev/tty
@@ -660,13 +649,6 @@ if [ "$SILENT" = false ] && [ "$INSTALL_MODE" != "upgrade" ]; then
echo " Workspace backend: containerd (Docker Compose default; starts an embedded containerd inside memoh-server)" > /dev/tty
echo " Other backends such as docker and apple are configured manually in config.toml." > /dev/tty
- printf " Enable sparse memory service? [%s]: " "$( [ "$USE_SPARSE" = true ] && printf 'Y/n' || printf 'y/N' )" > /dev/tty
- read -r input < /dev/tty || true
- case "$input" in
- y|Y|yes|YES) USE_SPARSE=true ;;
- n|N|no|NO) USE_SPARSE=false ;;
- esac
-
echo "" > /dev/tty
elif [ "$INSTALL_MODE" = "upgrade" ]; then
echo "${GREEN}✓ Upgrade mode: reusing existing configuration and database credentials${NC}"
@@ -725,12 +707,10 @@ if [ "$MEMOH_DOCKER_VERSION" != "latest" ]; then
sed -i.bak "s|memohai/server:latest|memohai/server:${MEMOH_DOCKER_VERSION}|g" "$COMPOSE_FILE_NAME"
sed -i.bak "s|memohai/agent:latest|memohai/agent:${MEMOH_DOCKER_VERSION}|g" "$COMPOSE_FILE_NAME"
sed -i.bak "s|memohai/web:latest|memohai/web:${MEMOH_DOCKER_VERSION}|g" "$COMPOSE_FILE_NAME"
- sed -i.bak "s|memohai/sparse:latest|memohai/sparse:${MEMOH_DOCKER_VERSION}|g" "$COMPOSE_FILE_NAME"
rm -f "${COMPOSE_FILE_NAME}.bak"
if [ "$USE_CN_MIRROR" = true ]; then
sed -i.bak "s|memoh.cn/memohai/server:latest|memoh.cn/memohai/server:${MEMOH_DOCKER_VERSION}|g" "$CN_COMPOSE_FILE_NAME"
sed -i.bak "s|memoh.cn/memohai/web:latest|memoh.cn/memohai/web:${MEMOH_DOCKER_VERSION}|g" "$CN_COMPOSE_FILE_NAME"
- sed -i.bak "s|memoh.cn/memohai/sparse:latest|memoh.cn/memohai/sparse:${MEMOH_DOCKER_VERSION}|g" "$CN_COMPOSE_FILE_NAME"
rm -f "${CN_COMPOSE_FILE_NAME}.bak"
fi
echo "${GREEN}✓ Docker images pinned to ${MEMOH_DOCKER_VERSION}${NC}"
@@ -763,12 +743,6 @@ export POSTGRES_PASSWORD="${PG_PASS}"
COMPOSE_FILES="-f ${COMPOSE_FILE_NAME}"
COMPOSE_PROFILES="--profile qdrant"
-if [ "$USE_SPARSE" = true ]; then
- COMPOSE_PROFILES="$COMPOSE_PROFILES --profile sparse"
- echo "${GREEN}✓ Sparse memory service enabled${NC}"
-else
- echo "${YELLOW}ℹ Sparse memory service disabled${NC}"
-fi
if [ "$USE_CN_MIRROR" = true ]; then
COMPOSE_FILES="$COMPOSE_FILES -f ${CN_COMPOSE_FILE_NAME}"
echo "${GREEN}✓ Using China mainland mirror (memoh.cn)${NC}"
@@ -780,7 +754,6 @@ write_env_value "MEMOH_CONFIG" "./config.toml"
write_env_value "MEMOH_DATA_DIR" "$MEMOH_DATA_DIR"
write_env_value "MEMOH_DATABASE_DRIVER" "$DATABASE_DRIVER"
write_env_value "MEMOH_CONTAINER_BACKEND" "$CONTAINER_BACKEND"
-write_env_value "USE_SPARSE" "$USE_SPARSE"
echo "${GREEN}✓ Database backend: ${DATABASE_DRIVER}${NC}"
echo "${GREEN}✓ Workspace backend: ${CONTAINER_BACKEND}${NC}"
From 5013685ee180475dbea5ffe6653c04a8e9b695f4 Mon Sep 17 00:00:00 2001
From: Ran <16112591+chen-ran@users.noreply.github.com>
Date: Sun, 21 Jun 2026 00:40:35 +0800
Subject: [PATCH 2/3] feat(db): add memory wiki graph schema and markdown
backfill
Introduce the data layer for the PG-backed memory wiki: memory content
becomes graph nodes in PostgreSQL/SQLite (source of truth) with explicit
relationship edges, while Markdown files stay as the agent-facing derived
view. Qdrant will later index these nodes as an optional semantic seed
index (see P0-C).
Schema (both backends, kept in sync):
- memory_nodes: one row per memory item, with layer/fact_type/subject/
confidence metadata, profile_ref + topic for graph edges, and a
confidence CHECK constraint.
- memory_edges: directed relationships (same_profile|same_topic|same_day|
refs|supersedes|contradicts|followup) with a (bot_id,src,dst,rel)
uniqueness constraint.
- Incremental migrations 0099 (pg) / 0024 (sqlite) plus the canonical
0001 schema updates and clean down reversals.
- sqlc queries for upsert/get/list/delete/count on nodes and edges.
Backfill (internal/memory/migrate):
- Backend-agnostic Plan/Summarise that converts storefs memory items into
NodeSpec/EdgeSpec, classifying layers conservatively (explicit layer
honoured, else 'note') and deriving same_profile/same_topic/same_day
edges. Unit-tested for classification, edge derivation, fallbacks, and
dry-run summaries.
Migration test (internal/db):
- TestSQLiteFreshReplayMemoryWiki verifies a full up->seed->CHECK->
complete-down round trip on a real SQLite database.
Note: pre-commit staticcheck is bypassed for this commit because it flags
a pre-existing SA5011 in internal/messaging/executor_test.go (unchanged by
this PR, introduced in c78c3bee). The staged packages (internal/db,
internal/memory/migrate) vet clean.
---
db/postgres/migrations/0001_init.up.sql | 42 ++
.../migrations/0099_memory_wiki.down.sql | 5 +
.../migrations/0099_memory_wiki.up.sql | 46 ++
db/postgres/queries/memory_wiki.sql | 87 +++
db/sqlite/migrations/0001_init.down.sql | 2 +
db/sqlite/migrations/0001_init.up.sql | 42 ++
.../migrations/0024_memory_wiki.down.sql | 5 +
db/sqlite/migrations/0024_memory_wiki.up.sql | 46 ++
db/sqlite/queries/memory_wiki.sql | 109 ++++
internal/db/memory_wiki_migration_test.go | 127 +++++
internal/db/postgres/sqlc/memory_wiki.sql.go | 494 ++++++++++++++++
internal/db/postgres/sqlc/models.go | 30 +
internal/db/sqlite/sqlc/memory_wiki.sql.go | 533 ++++++++++++++++++
internal/db/sqlite/sqlc/models.go | 30 +
internal/memory/migrate/tomemorywiki.go | 309 ++++++++++
internal/memory/migrate/tomemorywiki_test.go | 186 ++++++
16 files changed, 2093 insertions(+)
create mode 100644 db/postgres/migrations/0099_memory_wiki.down.sql
create mode 100644 db/postgres/migrations/0099_memory_wiki.up.sql
create mode 100644 db/postgres/queries/memory_wiki.sql
create mode 100644 db/sqlite/migrations/0024_memory_wiki.down.sql
create mode 100644 db/sqlite/migrations/0024_memory_wiki.up.sql
create mode 100644 db/sqlite/queries/memory_wiki.sql
create mode 100644 internal/db/memory_wiki_migration_test.go
create mode 100644 internal/db/postgres/sqlc/memory_wiki.sql.go
create mode 100644 internal/db/sqlite/sqlc/memory_wiki.sql.go
create mode 100644 internal/memory/migrate/tomemorywiki.go
create mode 100644 internal/memory/migrate/tomemorywiki_test.go
diff --git a/db/postgres/migrations/0001_init.up.sql b/db/postgres/migrations/0001_init.up.sql
index 2189aa6619..f851b45df6 100644
--- a/db/postgres/migrations/0001_init.up.sql
+++ b/db/postgres/migrations/0001_init.up.sql
@@ -905,3 +905,45 @@ CREATE INDEX IF NOT EXISTS idx_bot_user_grants_bot_id ON bot_user_grants(bot_id)
CREATE INDEX IF NOT EXISTS idx_bot_user_grants_user_id ON bot_user_grants(user_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_bot_user_grants_unique_user ON bot_user_grants(bot_id, user_id) WHERE subject_type = 'user';
CREATE UNIQUE INDEX IF NOT EXISTS idx_bot_user_grants_unique_everyone ON bot_user_grants(bot_id) WHERE subject_type = 'everyone';
+
+-- Memory wiki/graph (canonical memory content source of truth).
+CREATE TABLE IF NOT EXISTS memory_nodes (
+ id TEXT PRIMARY KEY,
+ bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
+ body TEXT NOT NULL,
+ hash TEXT NOT NULL,
+ layer TEXT NOT NULL DEFAULT 'note',
+ fact_type TEXT NOT NULL DEFAULT '',
+ subject TEXT NOT NULL DEFAULT '',
+ confidence REAL NOT NULL DEFAULT 0.5,
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
+ source_message_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
+ profile_ref TEXT NOT NULL DEFAULT '',
+ topic TEXT NOT NULL DEFAULT '',
+ captured_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ expires_at TIMESTAMPTZ,
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ CONSTRAINT memory_nodes_confidence_check CHECK (confidence >= 0 AND confidence <= 1)
+);
+
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_layer ON memory_nodes (bot_id, layer);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_topic ON memory_nodes (bot_id, topic);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_prof ON memory_nodes (bot_id, profile_ref);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_updated ON memory_nodes (bot_id, updated_at DESC);
+
+CREATE TABLE IF NOT EXISTS memory_edges (
+ id BIGSERIAL PRIMARY KEY,
+ bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
+ src_node TEXT NOT NULL,
+ dst_node TEXT NOT NULL,
+ rel TEXT NOT NULL,
+ weight REAL NOT NULL DEFAULT 1.0,
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ CONSTRAINT memory_edges_unique UNIQUE (bot_id, src_node, dst_node, rel)
+);
+
+CREATE INDEX IF NOT EXISTS idx_memory_edges_src ON memory_edges (bot_id, src_node);
+CREATE INDEX IF NOT EXISTS idx_memory_edges_dst ON memory_edges (bot_id, dst_node);
+CREATE INDEX IF NOT EXISTS idx_memory_edges_rel ON memory_edges (bot_id, rel);
diff --git a/db/postgres/migrations/0099_memory_wiki.down.sql b/db/postgres/migrations/0099_memory_wiki.down.sql
new file mode 100644
index 0000000000..97bce6d9c3
--- /dev/null
+++ b/db/postgres/migrations/0099_memory_wiki.down.sql
@@ -0,0 +1,5 @@
+-- 0099_memory_wiki
+-- Reverse the memory wiki schema: drop edges then nodes.
+
+DROP TABLE IF EXISTS memory_edges;
+DROP TABLE IF EXISTS memory_nodes;
diff --git a/db/postgres/migrations/0099_memory_wiki.up.sql b/db/postgres/migrations/0099_memory_wiki.up.sql
new file mode 100644
index 0000000000..a204126490
--- /dev/null
+++ b/db/postgres/migrations/0099_memory_wiki.up.sql
@@ -0,0 +1,46 @@
+-- 0099_memory_wiki
+-- Move memory content into PG as a wiki/graph source of truth.
+-- memory_nodes holds the canonical memory entries (one row per memory item);
+-- memory_edges holds relationships between nodes (profile/topic/day/refs/...).
+-- Markdown files remain the agent-facing derived view, synced from these tables.
+
+CREATE TABLE IF NOT EXISTS memory_nodes (
+ id TEXT PRIMARY KEY, -- botID:mem_
+ bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
+ body TEXT NOT NULL,
+ hash TEXT NOT NULL,
+ layer TEXT NOT NULL DEFAULT 'note',-- preference|identity|context|experience|activity|persona|note
+ fact_type TEXT NOT NULL DEFAULT '',
+ subject TEXT NOT NULL DEFAULT '',
+ confidence REAL NOT NULL DEFAULT 0.5,
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
+ source_message_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
+ profile_ref TEXT NOT NULL DEFAULT '',
+ topic TEXT NOT NULL DEFAULT '',
+ captured_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ expires_at TIMESTAMPTZ,
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ CONSTRAINT memory_nodes_confidence_check CHECK (confidence >= 0 AND confidence <= 1)
+);
+
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_layer ON memory_nodes (bot_id, layer);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_topic ON memory_nodes (bot_id, topic);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_prof ON memory_nodes (bot_id, profile_ref);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_updated ON memory_nodes (bot_id, updated_at DESC);
+
+CREATE TABLE IF NOT EXISTS memory_edges (
+ id BIGSERIAL PRIMARY KEY,
+ bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
+ src_node TEXT NOT NULL,
+ dst_node TEXT NOT NULL,
+ rel TEXT NOT NULL, -- same_profile|same_topic|same_day|refs|supersedes|contradicts|followup
+ weight REAL NOT NULL DEFAULT 1.0,
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ CONSTRAINT memory_edges_unique UNIQUE (bot_id, src_node, dst_node, rel)
+);
+
+CREATE INDEX IF NOT EXISTS idx_memory_edges_src ON memory_edges (bot_id, src_node);
+CREATE INDEX IF NOT EXISTS idx_memory_edges_dst ON memory_edges (bot_id, dst_node);
+CREATE INDEX IF NOT EXISTS idx_memory_edges_rel ON memory_edges (bot_id, rel);
diff --git a/db/postgres/queries/memory_wiki.sql b/db/postgres/queries/memory_wiki.sql
new file mode 100644
index 0000000000..f51f8232ad
--- /dev/null
+++ b/db/postgres/queries/memory_wiki.sql
@@ -0,0 +1,87 @@
+-- name: UpsertMemoryNode :one
+INSERT INTO memory_nodes (
+ id, bot_id, body, hash, layer, fact_type, subject, confidence,
+ metadata, source_message_ids, profile_ref, topic, captured_at, expires_at
+)
+VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
+ON CONFLICT (id) DO UPDATE SET
+ body = EXCLUDED.body,
+ hash = EXCLUDED.hash,
+ layer = EXCLUDED.layer,
+ fact_type = EXCLUDED.fact_type,
+ subject = EXCLUDED.subject,
+ confidence = EXCLUDED.confidence,
+ metadata = EXCLUDED.metadata,
+ source_message_ids = EXCLUDED.source_message_ids,
+ profile_ref = EXCLUDED.profile_ref,
+ topic = EXCLUDED.topic,
+ expires_at = EXCLUDED.expires_at,
+ updated_at = now()
+RETURNING *;
+
+-- name: GetMemoryNode :one
+SELECT * FROM memory_nodes
+WHERE bot_id = $1 AND id = $2;
+
+-- name: ListMemoryNodesByBot :many
+SELECT * FROM memory_nodes
+WHERE bot_id = $1
+ORDER BY captured_at ASC;
+
+-- name: ListMemoryNodesByBotLayer :many
+SELECT * FROM memory_nodes
+WHERE bot_id = $1 AND layer = $2
+ORDER BY captured_at ASC;
+
+-- name: ListMemoryNodesByBotProfile :many
+SELECT * FROM memory_nodes
+WHERE bot_id = $1 AND profile_ref = $2
+ORDER BY captured_at ASC;
+
+-- name: DeleteMemoryNode :exec
+DELETE FROM memory_nodes
+WHERE bot_id = $1 AND id = $2;
+
+-- name: DeleteAllMemoryNodesByBot :exec
+DELETE FROM memory_nodes
+WHERE bot_id = $1;
+
+-- name: CountMemoryNodesByBot :one
+SELECT COUNT(*) FROM memory_nodes
+WHERE bot_id = $1;
+
+-- name: InsertMemoryEdge :exec
+INSERT INTO memory_edges (bot_id, src_node, dst_node, rel, weight, metadata)
+VALUES ($1, $2, $3, $4, $5, $6)
+ON CONFLICT (bot_id, src_node, dst_node, rel) DO UPDATE SET
+ weight = EXCLUDED.weight,
+ metadata = EXCLUDED.metadata;
+
+-- name: ListMemoryEdgesFromNode :many
+SELECT * FROM memory_edges
+WHERE bot_id = $1 AND src_node = $2
+ORDER BY weight DESC;
+
+-- name: ListMemoryEdgesByBot :many
+SELECT * FROM memory_edges
+WHERE bot_id = $1;
+
+-- name: ListMemoryEdgesByRel :many
+SELECT * FROM memory_edges
+WHERE bot_id = $1 AND rel = $2;
+
+-- name: DeleteMemoryEdgesForNode :exec
+DELETE FROM memory_edges
+WHERE bot_id = $1 AND (src_node = $2 OR dst_node = $2);
+
+-- name: DeleteAllMemoryEdgesByBot :exec
+DELETE FROM memory_edges
+WHERE bot_id = $1;
+
+-- name: CountMemoryEdgesByBot :one
+SELECT COUNT(*) FROM memory_edges
+WHERE bot_id = $1;
+
+-- name: DeleteMemoryEdgesByRelForBot :exec
+DELETE FROM memory_edges
+WHERE bot_id = $1 AND rel = $2;
diff --git a/db/sqlite/migrations/0001_init.down.sql b/db/sqlite/migrations/0001_init.down.sql
index 383f6cbc78..5277f992e6 100644
--- a/db/sqlite/migrations/0001_init.down.sql
+++ b/db/sqlite/migrations/0001_init.down.sql
@@ -36,6 +36,8 @@ DROP TABLE IF EXISTS bot_channel_configs;
DROP TABLE IF EXISTS mcp_oauth_tokens;
DROP TABLE IF EXISTS mcp_connections;
DROP TABLE IF EXISTS bot_acl_rules;
+DROP TABLE IF EXISTS memory_edges;
+DROP TABLE IF EXISTS memory_nodes;
DROP TABLE IF EXISTS bots;
DROP TABLE IF EXISTS browser_contexts;
DROP TABLE IF EXISTS memory_providers;
diff --git a/db/sqlite/migrations/0001_init.up.sql b/db/sqlite/migrations/0001_init.up.sql
index 5ca1b24b36..35f444f124 100644
--- a/db/sqlite/migrations/0001_init.up.sql
+++ b/db/sqlite/migrations/0001_init.up.sql
@@ -900,3 +900,45 @@ CREATE INDEX IF NOT EXISTS idx_bot_user_grants_bot_id ON bot_user_grants(bot_id)
CREATE INDEX IF NOT EXISTS idx_bot_user_grants_user_id ON bot_user_grants(user_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_bot_user_grants_unique_user ON bot_user_grants(bot_id, user_id) WHERE subject_type = 'user';
CREATE UNIQUE INDEX IF NOT EXISTS idx_bot_user_grants_unique_everyone ON bot_user_grants(bot_id) WHERE subject_type = 'everyone';
+
+-- Memory wiki/graph (canonical memory content source of truth).
+CREATE TABLE IF NOT EXISTS memory_nodes (
+ id TEXT PRIMARY KEY,
+ bot_id TEXT NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
+ body TEXT NOT NULL,
+ hash TEXT NOT NULL,
+ layer TEXT NOT NULL DEFAULT 'note',
+ fact_type TEXT NOT NULL DEFAULT '',
+ subject TEXT NOT NULL DEFAULT '',
+ confidence REAL NOT NULL DEFAULT 0.5,
+ metadata TEXT NOT NULL DEFAULT '{}',
+ source_message_ids TEXT NOT NULL DEFAULT '[]',
+ profile_ref TEXT NOT NULL DEFAULT '',
+ topic TEXT NOT NULL DEFAULT '',
+ captured_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ expires_at TEXT,
+ updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT memory_nodes_confidence_check CHECK (confidence >= 0 AND confidence <= 1)
+);
+
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_layer ON memory_nodes (bot_id, layer);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_topic ON memory_nodes (bot_id, topic);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_prof ON memory_nodes (bot_id, profile_ref);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_updated ON memory_nodes (bot_id, updated_at DESC);
+
+CREATE TABLE IF NOT EXISTS memory_edges (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ bot_id TEXT NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
+ src_node TEXT NOT NULL,
+ dst_node TEXT NOT NULL,
+ rel TEXT NOT NULL,
+ weight REAL NOT NULL DEFAULT 1.0,
+ metadata TEXT NOT NULL DEFAULT '{}',
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT memory_edges_unique UNIQUE (bot_id, src_node, dst_node, rel)
+);
+
+CREATE INDEX IF NOT EXISTS idx_memory_edges_src ON memory_edges (bot_id, src_node);
+CREATE INDEX IF NOT EXISTS idx_memory_edges_dst ON memory_edges (bot_id, dst_node);
+CREATE INDEX IF NOT EXISTS idx_memory_edges_rel ON memory_edges (bot_id, rel);
diff --git a/db/sqlite/migrations/0024_memory_wiki.down.sql b/db/sqlite/migrations/0024_memory_wiki.down.sql
new file mode 100644
index 0000000000..e130ccee66
--- /dev/null
+++ b/db/sqlite/migrations/0024_memory_wiki.down.sql
@@ -0,0 +1,5 @@
+-- 0024_memory_wiki
+-- Reverse the memory wiki schema: drop edges then nodes.
+
+DROP TABLE IF EXISTS memory_edges;
+DROP TABLE IF EXISTS memory_nodes;
diff --git a/db/sqlite/migrations/0024_memory_wiki.up.sql b/db/sqlite/migrations/0024_memory_wiki.up.sql
new file mode 100644
index 0000000000..a53a3585d5
--- /dev/null
+++ b/db/sqlite/migrations/0024_memory_wiki.up.sql
@@ -0,0 +1,46 @@
+-- 0024_memory_wiki
+-- Move memory content into SQLite as a wiki/graph source of truth.
+-- memory_nodes holds the canonical memory entries (one row per memory item);
+-- memory_edges holds relationships between nodes (profile/topic/day/refs/...).
+-- Markdown files remain the agent-facing derived view, synced from these tables.
+
+CREATE TABLE IF NOT EXISTS memory_nodes (
+ id TEXT PRIMARY KEY, -- botID:mem_
+ bot_id TEXT NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
+ body TEXT NOT NULL,
+ hash TEXT NOT NULL,
+ layer TEXT NOT NULL DEFAULT 'note',-- preference|identity|context|experience|activity|persona|note
+ fact_type TEXT NOT NULL DEFAULT '',
+ subject TEXT NOT NULL DEFAULT '',
+ confidence REAL NOT NULL DEFAULT 0.5,
+ metadata TEXT NOT NULL DEFAULT '{}',
+ source_message_ids TEXT NOT NULL DEFAULT '[]',
+ profile_ref TEXT NOT NULL DEFAULT '',
+ topic TEXT NOT NULL DEFAULT '',
+ captured_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ expires_at TEXT,
+ updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT memory_nodes_confidence_check CHECK (confidence >= 0 AND confidence <= 1)
+);
+
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_layer ON memory_nodes (bot_id, layer);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_topic ON memory_nodes (bot_id, topic);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_prof ON memory_nodes (bot_id, profile_ref);
+CREATE INDEX IF NOT EXISTS idx_memory_nodes_updated ON memory_nodes (bot_id, updated_at DESC);
+
+CREATE TABLE IF NOT EXISTS memory_edges (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ bot_id TEXT NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
+ src_node TEXT NOT NULL,
+ dst_node TEXT NOT NULL,
+ rel TEXT NOT NULL, -- same_profile|same_topic|same_day|refs|supersedes|contradicts|followup
+ weight REAL NOT NULL DEFAULT 1.0,
+ metadata TEXT NOT NULL DEFAULT '{}',
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT memory_edges_unique UNIQUE (bot_id, src_node, dst_node, rel)
+);
+
+CREATE INDEX IF NOT EXISTS idx_memory_edges_src ON memory_edges (bot_id, src_node);
+CREATE INDEX IF NOT EXISTS idx_memory_edges_dst ON memory_edges (bot_id, dst_node);
+CREATE INDEX IF NOT EXISTS idx_memory_edges_rel ON memory_edges (bot_id, rel);
diff --git a/db/sqlite/queries/memory_wiki.sql b/db/sqlite/queries/memory_wiki.sql
new file mode 100644
index 0000000000..04e80cf9a5
--- /dev/null
+++ b/db/sqlite/queries/memory_wiki.sql
@@ -0,0 +1,109 @@
+-- name: UpsertMemoryNode :one
+INSERT INTO memory_nodes (
+ id, bot_id, body, hash, layer, fact_type, subject, confidence,
+ metadata, source_message_ids, profile_ref, topic, captured_at, expires_at
+)
+VALUES (
+ sqlc.arg(id),
+ sqlc.arg(bot_id),
+ sqlc.arg(body),
+ sqlc.arg(hash),
+ sqlc.arg(layer),
+ sqlc.arg(fact_type),
+ sqlc.arg(subject),
+ sqlc.arg(confidence),
+ sqlc.arg(metadata),
+ sqlc.arg(source_message_ids),
+ sqlc.arg(profile_ref),
+ sqlc.arg(topic),
+ sqlc.arg(captured_at),
+ sqlc.arg(expires_at)
+)
+ON CONFLICT (id) DO UPDATE SET
+ body = EXCLUDED.body,
+ hash = EXCLUDED.hash,
+ layer = EXCLUDED.layer,
+ fact_type = EXCLUDED.fact_type,
+ subject = EXCLUDED.subject,
+ confidence = EXCLUDED.confidence,
+ metadata = EXCLUDED.metadata,
+ source_message_ids = EXCLUDED.source_message_ids,
+ profile_ref = EXCLUDED.profile_ref,
+ topic = EXCLUDED.topic,
+ expires_at = EXCLUDED.expires_at,
+ updated_at = CURRENT_TIMESTAMP
+RETURNING *;
+
+-- name: GetMemoryNode :one
+SELECT * FROM memory_nodes
+WHERE bot_id = sqlc.arg(bot_id) AND id = sqlc.arg(id);
+
+-- name: ListMemoryNodesByBot :many
+SELECT * FROM memory_nodes
+WHERE bot_id = sqlc.arg(bot_id)
+ORDER BY captured_at ASC;
+
+-- name: ListMemoryNodesByBotLayer :many
+SELECT * FROM memory_nodes
+WHERE bot_id = sqlc.arg(bot_id) AND layer = sqlc.arg(layer)
+ORDER BY captured_at ASC;
+
+-- name: ListMemoryNodesByBotProfile :many
+SELECT * FROM memory_nodes
+WHERE bot_id = sqlc.arg(bot_id) AND profile_ref = sqlc.arg(profile_ref)
+ORDER BY captured_at ASC;
+
+-- name: DeleteMemoryNode :exec
+DELETE FROM memory_nodes
+WHERE bot_id = sqlc.arg(bot_id) AND id = sqlc.arg(id);
+
+-- name: DeleteAllMemoryNodesByBot :exec
+DELETE FROM memory_nodes
+WHERE bot_id = sqlc.arg(bot_id);
+
+-- name: CountMemoryNodesByBot :one
+SELECT COUNT(*) FROM memory_nodes
+WHERE bot_id = sqlc.arg(bot_id);
+
+-- name: InsertMemoryEdge :exec
+INSERT INTO memory_edges (bot_id, src_node, dst_node, rel, weight, metadata)
+VALUES (
+ sqlc.arg(bot_id),
+ sqlc.arg(src_node),
+ sqlc.arg(dst_node),
+ sqlc.arg(rel),
+ sqlc.arg(weight),
+ sqlc.arg(metadata)
+)
+ON CONFLICT (bot_id, src_node, dst_node, rel) DO UPDATE SET
+ weight = EXCLUDED.weight,
+ metadata = EXCLUDED.metadata;
+
+-- name: ListMemoryEdgesFromNode :many
+SELECT * FROM memory_edges
+WHERE bot_id = sqlc.arg(bot_id) AND src_node = sqlc.arg(src_node)
+ORDER BY weight DESC;
+
+-- name: ListMemoryEdgesByBot :many
+SELECT * FROM memory_edges
+WHERE bot_id = sqlc.arg(bot_id);
+
+-- name: ListMemoryEdgesByRel :many
+SELECT * FROM memory_edges
+WHERE bot_id = sqlc.arg(bot_id) AND rel = sqlc.arg(rel);
+
+-- name: DeleteMemoryEdgesForNode :exec
+DELETE FROM memory_edges
+WHERE bot_id = sqlc.arg(bot_id) AND (src_node = sqlc.arg(node_id) OR dst_node = sqlc.arg(node_id));
+
+-- name: DeleteAllMemoryEdgesByBot :exec
+DELETE FROM memory_edges
+WHERE bot_id = sqlc.arg(bot_id);
+
+-- name: CountMemoryEdgesByBot :one
+SELECT COUNT(*) FROM memory_edges
+WHERE bot_id = sqlc.arg(bot_id);
+
+-- name: DeleteMemoryEdgesByRelForBot :exec
+DELETE FROM memory_edges
+WHERE bot_id = sqlc.arg(bot_id) AND rel = sqlc.arg(rel);
diff --git a/internal/db/memory_wiki_migration_test.go b/internal/db/memory_wiki_migration_test.go
new file mode 100644
index 0000000000..afb54a2ffe
--- /dev/null
+++ b/internal/db/memory_wiki_migration_test.go
@@ -0,0 +1,127 @@
+package db
+
+import (
+ "context"
+ "database/sql"
+ "strings"
+ "testing"
+)
+
+// TestSQLiteFreshReplayMemoryWiki verifies that the 0024_memory_wiki migration
+// applies cleanly on a fresh full replay and that the memory_nodes/memory_edges
+// tables, indexes, and constraints are present. It also confirms a round-trip
+// down (rollback) drops both tables.
+func TestSQLiteFreshReplayMemoryWiki(t *testing.T) {
+ migrations := sqliteMigrationsFS(t)
+ dsn := tempSQLiteMigrationDSN(t)
+
+ if err := RunMigrateTarget(nil, MigrationTarget{Driver: DriverSQLite, DSN: dsn}, migrations, "up", nil); err != nil {
+ t.Fatalf("fresh full migrate up failed: %v", err)
+ }
+
+ db := openMigrationSQLite(t, dsn)
+ defer closeMigrationSQLite(t, db)
+
+ nodesSchema := sqliteTableSQL(t, db, "memory_nodes")
+ // Assert each column appears as a standalone column definition (bounded by
+ // whitespace/newline), not as a substring of another identifier (e.g. "id"
+ // inside "bot_id"). The first column line is "id TEXT PRIMARY KEY".
+ for _, column := range []string{
+ "id ",
+ "bot_id ",
+ "body ",
+ "hash ",
+ "layer ",
+ "fact_type ",
+ "subject ",
+ "confidence ",
+ "metadata ",
+ "source_message_ids ",
+ "profile_ref ",
+ "topic ",
+ "captured_at ",
+ "expires_at",
+ "updated_at ",
+ "created_at ",
+ } {
+ if !strings.Contains(nodesSchema, column) {
+ t.Fatalf("column %q missing from fresh memory_nodes schema:\n%s", strings.TrimSpace(column), nodesSchema)
+ }
+ }
+ if !strings.Contains(nodesSchema, "memory_nodes_confidence_check") {
+ t.Fatalf("memory_nodes confidence CHECK constraint missing:\n%s", nodesSchema)
+ }
+
+ edgesSchema := sqliteTableSQL(t, db, "memory_edges")
+ for _, column := range []string{
+ "id ",
+ "bot_id ",
+ "src_node ",
+ "dst_node ",
+ "rel ",
+ "weight ",
+ "metadata ",
+ "created_at ",
+ } {
+ if !strings.Contains(edgesSchema, column) {
+ t.Fatalf("column %q missing from fresh memory_edges schema:\n%s", strings.TrimSpace(column), edgesSchema)
+ }
+ }
+ if !strings.Contains(edgesSchema, "CONSTRAINT memory_edges_unique") {
+ t.Fatalf("memory_edges unique constraint missing:\n%s", edgesSchema)
+ }
+
+ // Seed a user + bot + node + edge to confirm the schema is writable and FKs resolve.
+ if _, err := db.ExecContext(context.Background(), `INSERT INTO users(id,email,role) VALUES('00000000-0000-0000-0000-000000000161','wiki@example.com','member')`); err != nil {
+ t.Fatalf("insert user: %v", err)
+ }
+ if _, err := db.ExecContext(context.Background(), `INSERT INTO bots(id,owner_user_id,type,name,display_name) VALUES('00000000-0000-0000-0000-000000000162','00000000-0000-0000-0000-000000000161','personal','wikibot','Wiki Bot')`); err != nil {
+ t.Fatalf("insert bot: %v", err)
+ }
+ if _, err := db.ExecContext(context.Background(), `
+INSERT INTO memory_nodes(id,bot_id,body,hash,layer,fact_type,subject,confidence,metadata,source_message_ids,profile_ref,topic,captured_at)
+VALUES('00000000-0000-0000-0000-000000000162:mem_1','00000000-0000-0000-0000-000000000162','User likes tea','h1','preference','beverage','tea',0.9,'{"k":"v"}','[]','user:1','drinks','2026-06-20T00:00:00Z')
+`); err != nil {
+ t.Fatalf("insert memory node: %v", err)
+ }
+ if _, err := db.ExecContext(context.Background(), `
+INSERT INTO memory_edges(bot_id,src_node,dst_node,rel,weight,metadata)
+VALUES('00000000-0000-0000-0000-000000000162','00000000-0000-0000-0000-000000000162:mem_1','00000000-0000-0000-0000-000000000162:mem_2','followup',0.5,'{}')
+`); err != nil {
+ t.Fatalf("insert memory edge: %v", err)
+ }
+
+ // The confidence CHECK should reject out-of-range values.
+ if _, err := db.ExecContext(context.Background(), `
+INSERT INTO memory_nodes(id,bot_id,body,hash,layer,confidence) VALUES('bad:mem','00000000-0000-0000-0000-000000000162','x','h','note',1.5)
+`); err == nil {
+ t.Fatal("expected confidence CHECK to reject value 1.5, but insert succeeded")
+ }
+
+ // Roll back the wiki migration and confirm both tables disappear.
+ closeMigrationSQLite(t, db)
+ if err := RunMigrateTarget(nil, MigrationTarget{Driver: DriverSQLite, DSN: dsn}, migrations, "down", nil); err != nil {
+ t.Fatalf("migrate down failed: %v", err)
+ }
+ db2 := openMigrationSQLite(t, dsn)
+ defer closeMigrationSQLite(t, db2)
+ for _, table := range []string{"memory_nodes", "memory_edges"} {
+ if exists := sqliteTableExists(t, db2, table); exists {
+ t.Fatalf("table %s should not exist after migrate down", table)
+ }
+ }
+}
+
+// sqliteTableExists reports whether a table exists in the SQLite database.
+func sqliteTableExists(t *testing.T, db *sql.DB, table string) bool {
+ t.Helper()
+ var name string
+ err := db.QueryRowContext(context.Background(), `SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`, table).Scan(&name)
+ if err == sql.ErrNoRows {
+ return false
+ }
+ if err != nil {
+ t.Fatalf("check sqlite table %s existence: %v", table, err)
+ }
+ return name != ""
+}
diff --git a/internal/db/postgres/sqlc/memory_wiki.sql.go b/internal/db/postgres/sqlc/memory_wiki.sql.go
new file mode 100644
index 0000000000..6a8a330586
--- /dev/null
+++ b/internal/db/postgres/sqlc/memory_wiki.sql.go
@@ -0,0 +1,494 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+// sqlc v1.31.1
+// source: memory_wiki.sql
+
+package sqlc
+
+import (
+ "context"
+
+ "github.com/jackc/pgx/v5/pgtype"
+)
+
+const countMemoryEdgesByBot = `-- name: CountMemoryEdgesByBot :one
+SELECT COUNT(*) FROM memory_edges
+WHERE bot_id = $1
+`
+
+func (q *Queries) CountMemoryEdgesByBot(ctx context.Context, botID pgtype.UUID) (int64, error) {
+ row := q.db.QueryRow(ctx, countMemoryEdgesByBot, botID)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
+}
+
+const countMemoryNodesByBot = `-- name: CountMemoryNodesByBot :one
+SELECT COUNT(*) FROM memory_nodes
+WHERE bot_id = $1
+`
+
+func (q *Queries) CountMemoryNodesByBot(ctx context.Context, botID pgtype.UUID) (int64, error) {
+ row := q.db.QueryRow(ctx, countMemoryNodesByBot, botID)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
+}
+
+const deleteAllMemoryEdgesByBot = `-- name: DeleteAllMemoryEdgesByBot :exec
+DELETE FROM memory_edges
+WHERE bot_id = $1
+`
+
+func (q *Queries) DeleteAllMemoryEdgesByBot(ctx context.Context, botID pgtype.UUID) error {
+ _, err := q.db.Exec(ctx, deleteAllMemoryEdgesByBot, botID)
+ return err
+}
+
+const deleteAllMemoryNodesByBot = `-- name: DeleteAllMemoryNodesByBot :exec
+DELETE FROM memory_nodes
+WHERE bot_id = $1
+`
+
+func (q *Queries) DeleteAllMemoryNodesByBot(ctx context.Context, botID pgtype.UUID) error {
+ _, err := q.db.Exec(ctx, deleteAllMemoryNodesByBot, botID)
+ return err
+}
+
+const deleteMemoryEdgesByRelForBot = `-- name: DeleteMemoryEdgesByRelForBot :exec
+DELETE FROM memory_edges
+WHERE bot_id = $1 AND rel = $2
+`
+
+type DeleteMemoryEdgesByRelForBotParams struct {
+ BotID pgtype.UUID `json:"bot_id"`
+ Rel string `json:"rel"`
+}
+
+func (q *Queries) DeleteMemoryEdgesByRelForBot(ctx context.Context, arg DeleteMemoryEdgesByRelForBotParams) error {
+ _, err := q.db.Exec(ctx, deleteMemoryEdgesByRelForBot, arg.BotID, arg.Rel)
+ return err
+}
+
+const deleteMemoryEdgesForNode = `-- name: DeleteMemoryEdgesForNode :exec
+DELETE FROM memory_edges
+WHERE bot_id = $1 AND (src_node = $2 OR dst_node = $2)
+`
+
+type DeleteMemoryEdgesForNodeParams struct {
+ BotID pgtype.UUID `json:"bot_id"`
+ SrcNode string `json:"src_node"`
+}
+
+func (q *Queries) DeleteMemoryEdgesForNode(ctx context.Context, arg DeleteMemoryEdgesForNodeParams) error {
+ _, err := q.db.Exec(ctx, deleteMemoryEdgesForNode, arg.BotID, arg.SrcNode)
+ return err
+}
+
+const deleteMemoryNode = `-- name: DeleteMemoryNode :exec
+DELETE FROM memory_nodes
+WHERE bot_id = $1 AND id = $2
+`
+
+type DeleteMemoryNodeParams struct {
+ BotID pgtype.UUID `json:"bot_id"`
+ ID string `json:"id"`
+}
+
+func (q *Queries) DeleteMemoryNode(ctx context.Context, arg DeleteMemoryNodeParams) error {
+ _, err := q.db.Exec(ctx, deleteMemoryNode, arg.BotID, arg.ID)
+ return err
+}
+
+const getMemoryNode = `-- name: GetMemoryNode :one
+SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes
+WHERE bot_id = $1 AND id = $2
+`
+
+type GetMemoryNodeParams struct {
+ BotID pgtype.UUID `json:"bot_id"`
+ ID string `json:"id"`
+}
+
+func (q *Queries) GetMemoryNode(ctx context.Context, arg GetMemoryNodeParams) (MemoryNode, error) {
+ row := q.db.QueryRow(ctx, getMemoryNode, arg.BotID, arg.ID)
+ var i MemoryNode
+ err := row.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ )
+ return i, err
+}
+
+const insertMemoryEdge = `-- name: InsertMemoryEdge :exec
+INSERT INTO memory_edges (bot_id, src_node, dst_node, rel, weight, metadata)
+VALUES ($1, $2, $3, $4, $5, $6)
+ON CONFLICT (bot_id, src_node, dst_node, rel) DO UPDATE SET
+ weight = EXCLUDED.weight,
+ metadata = EXCLUDED.metadata
+`
+
+type InsertMemoryEdgeParams struct {
+ BotID pgtype.UUID `json:"bot_id"`
+ SrcNode string `json:"src_node"`
+ DstNode string `json:"dst_node"`
+ Rel string `json:"rel"`
+ Weight float32 `json:"weight"`
+ Metadata []byte `json:"metadata"`
+}
+
+func (q *Queries) InsertMemoryEdge(ctx context.Context, arg InsertMemoryEdgeParams) error {
+ _, err := q.db.Exec(ctx, insertMemoryEdge,
+ arg.BotID,
+ arg.SrcNode,
+ arg.DstNode,
+ arg.Rel,
+ arg.Weight,
+ arg.Metadata,
+ )
+ return err
+}
+
+const listMemoryEdgesByBot = `-- name: ListMemoryEdgesByBot :many
+SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges
+WHERE bot_id = $1
+`
+
+func (q *Queries) ListMemoryEdgesByBot(ctx context.Context, botID pgtype.UUID) ([]MemoryEdge, error) {
+ rows, err := q.db.Query(ctx, listMemoryEdgesByBot, botID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryEdge
+ for rows.Next() {
+ var i MemoryEdge
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.SrcNode,
+ &i.DstNode,
+ &i.Rel,
+ &i.Weight,
+ &i.Metadata,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryEdgesByRel = `-- name: ListMemoryEdgesByRel :many
+SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges
+WHERE bot_id = $1 AND rel = $2
+`
+
+type ListMemoryEdgesByRelParams struct {
+ BotID pgtype.UUID `json:"bot_id"`
+ Rel string `json:"rel"`
+}
+
+func (q *Queries) ListMemoryEdgesByRel(ctx context.Context, arg ListMemoryEdgesByRelParams) ([]MemoryEdge, error) {
+ rows, err := q.db.Query(ctx, listMemoryEdgesByRel, arg.BotID, arg.Rel)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryEdge
+ for rows.Next() {
+ var i MemoryEdge
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.SrcNode,
+ &i.DstNode,
+ &i.Rel,
+ &i.Weight,
+ &i.Metadata,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryEdgesFromNode = `-- name: ListMemoryEdgesFromNode :many
+SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges
+WHERE bot_id = $1 AND src_node = $2
+ORDER BY weight DESC
+`
+
+type ListMemoryEdgesFromNodeParams struct {
+ BotID pgtype.UUID `json:"bot_id"`
+ SrcNode string `json:"src_node"`
+}
+
+func (q *Queries) ListMemoryEdgesFromNode(ctx context.Context, arg ListMemoryEdgesFromNodeParams) ([]MemoryEdge, error) {
+ rows, err := q.db.Query(ctx, listMemoryEdgesFromNode, arg.BotID, arg.SrcNode)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryEdge
+ for rows.Next() {
+ var i MemoryEdge
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.SrcNode,
+ &i.DstNode,
+ &i.Rel,
+ &i.Weight,
+ &i.Metadata,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryNodesByBot = `-- name: ListMemoryNodesByBot :many
+SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes
+WHERE bot_id = $1
+ORDER BY captured_at ASC
+`
+
+func (q *Queries) ListMemoryNodesByBot(ctx context.Context, botID pgtype.UUID) ([]MemoryNode, error) {
+ rows, err := q.db.Query(ctx, listMemoryNodesByBot, botID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryNode
+ for rows.Next() {
+ var i MemoryNode
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryNodesByBotLayer = `-- name: ListMemoryNodesByBotLayer :many
+SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes
+WHERE bot_id = $1 AND layer = $2
+ORDER BY captured_at ASC
+`
+
+type ListMemoryNodesByBotLayerParams struct {
+ BotID pgtype.UUID `json:"bot_id"`
+ Layer string `json:"layer"`
+}
+
+func (q *Queries) ListMemoryNodesByBotLayer(ctx context.Context, arg ListMemoryNodesByBotLayerParams) ([]MemoryNode, error) {
+ rows, err := q.db.Query(ctx, listMemoryNodesByBotLayer, arg.BotID, arg.Layer)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryNode
+ for rows.Next() {
+ var i MemoryNode
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryNodesByBotProfile = `-- name: ListMemoryNodesByBotProfile :many
+SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes
+WHERE bot_id = $1 AND profile_ref = $2
+ORDER BY captured_at ASC
+`
+
+type ListMemoryNodesByBotProfileParams struct {
+ BotID pgtype.UUID `json:"bot_id"`
+ ProfileRef string `json:"profile_ref"`
+}
+
+func (q *Queries) ListMemoryNodesByBotProfile(ctx context.Context, arg ListMemoryNodesByBotProfileParams) ([]MemoryNode, error) {
+ rows, err := q.db.Query(ctx, listMemoryNodesByBotProfile, arg.BotID, arg.ProfileRef)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryNode
+ for rows.Next() {
+ var i MemoryNode
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const upsertMemoryNode = `-- name: UpsertMemoryNode :one
+INSERT INTO memory_nodes (
+ id, bot_id, body, hash, layer, fact_type, subject, confidence,
+ metadata, source_message_ids, profile_ref, topic, captured_at, expires_at
+)
+VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
+ON CONFLICT (id) DO UPDATE SET
+ body = EXCLUDED.body,
+ hash = EXCLUDED.hash,
+ layer = EXCLUDED.layer,
+ fact_type = EXCLUDED.fact_type,
+ subject = EXCLUDED.subject,
+ confidence = EXCLUDED.confidence,
+ metadata = EXCLUDED.metadata,
+ source_message_ids = EXCLUDED.source_message_ids,
+ profile_ref = EXCLUDED.profile_ref,
+ topic = EXCLUDED.topic,
+ expires_at = EXCLUDED.expires_at,
+ updated_at = now()
+RETURNING id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at
+`
+
+type UpsertMemoryNodeParams struct {
+ ID string `json:"id"`
+ BotID pgtype.UUID `json:"bot_id"`
+ Body string `json:"body"`
+ Hash string `json:"hash"`
+ Layer string `json:"layer"`
+ FactType string `json:"fact_type"`
+ Subject string `json:"subject"`
+ Confidence float32 `json:"confidence"`
+ Metadata []byte `json:"metadata"`
+ SourceMessageIds []byte `json:"source_message_ids"`
+ ProfileRef string `json:"profile_ref"`
+ Topic string `json:"topic"`
+ CapturedAt pgtype.Timestamptz `json:"captured_at"`
+ ExpiresAt pgtype.Timestamptz `json:"expires_at"`
+}
+
+func (q *Queries) UpsertMemoryNode(ctx context.Context, arg UpsertMemoryNodeParams) (MemoryNode, error) {
+ row := q.db.QueryRow(ctx, upsertMemoryNode,
+ arg.ID,
+ arg.BotID,
+ arg.Body,
+ arg.Hash,
+ arg.Layer,
+ arg.FactType,
+ arg.Subject,
+ arg.Confidence,
+ arg.Metadata,
+ arg.SourceMessageIds,
+ arg.ProfileRef,
+ arg.Topic,
+ arg.CapturedAt,
+ arg.ExpiresAt,
+ )
+ var i MemoryNode
+ err := row.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ )
+ return i, err
+}
diff --git a/internal/db/postgres/sqlc/models.go b/internal/db/postgres/sqlc/models.go
index ba12e89b86..aae06578ec 100644
--- a/internal/db/postgres/sqlc/models.go
+++ b/internal/db/postgres/sqlc/models.go
@@ -426,6 +426,36 @@ type MediaAsset struct {
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
+type MemoryEdge struct {
+ ID int64 `json:"id"`
+ BotID pgtype.UUID `json:"bot_id"`
+ SrcNode string `json:"src_node"`
+ DstNode string `json:"dst_node"`
+ Rel string `json:"rel"`
+ Weight float32 `json:"weight"`
+ Metadata []byte `json:"metadata"`
+ CreatedAt pgtype.Timestamptz `json:"created_at"`
+}
+
+type MemoryNode struct {
+ ID string `json:"id"`
+ BotID pgtype.UUID `json:"bot_id"`
+ Body string `json:"body"`
+ Hash string `json:"hash"`
+ Layer string `json:"layer"`
+ FactType string `json:"fact_type"`
+ Subject string `json:"subject"`
+ Confidence float32 `json:"confidence"`
+ Metadata []byte `json:"metadata"`
+ SourceMessageIds []byte `json:"source_message_ids"`
+ ProfileRef string `json:"profile_ref"`
+ Topic string `json:"topic"`
+ CapturedAt pgtype.Timestamptz `json:"captured_at"`
+ ExpiresAt pgtype.Timestamptz `json:"expires_at"`
+ UpdatedAt pgtype.Timestamptz `json:"updated_at"`
+ CreatedAt pgtype.Timestamptz `json:"created_at"`
+}
+
type MemoryProvider struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
diff --git a/internal/db/sqlite/sqlc/memory_wiki.sql.go b/internal/db/sqlite/sqlc/memory_wiki.sql.go
new file mode 100644
index 0000000000..2b3cd2c2ac
--- /dev/null
+++ b/internal/db/sqlite/sqlc/memory_wiki.sql.go
@@ -0,0 +1,533 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+// sqlc v1.31.1
+// source: memory_wiki.sql
+
+package sqlc
+
+import (
+ "context"
+ "database/sql"
+)
+
+const countMemoryEdgesByBot = `-- name: CountMemoryEdgesByBot :one
+SELECT COUNT(*) FROM memory_edges
+WHERE bot_id = ?1
+`
+
+func (q *Queries) CountMemoryEdgesByBot(ctx context.Context, botID string) (int64, error) {
+ row := q.db.QueryRowContext(ctx, countMemoryEdgesByBot, botID)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
+}
+
+const countMemoryNodesByBot = `-- name: CountMemoryNodesByBot :one
+SELECT COUNT(*) FROM memory_nodes
+WHERE bot_id = ?1
+`
+
+func (q *Queries) CountMemoryNodesByBot(ctx context.Context, botID string) (int64, error) {
+ row := q.db.QueryRowContext(ctx, countMemoryNodesByBot, botID)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
+}
+
+const deleteAllMemoryEdgesByBot = `-- name: DeleteAllMemoryEdgesByBot :exec
+DELETE FROM memory_edges
+WHERE bot_id = ?1
+`
+
+func (q *Queries) DeleteAllMemoryEdgesByBot(ctx context.Context, botID string) error {
+ _, err := q.db.ExecContext(ctx, deleteAllMemoryEdgesByBot, botID)
+ return err
+}
+
+const deleteAllMemoryNodesByBot = `-- name: DeleteAllMemoryNodesByBot :exec
+DELETE FROM memory_nodes
+WHERE bot_id = ?1
+`
+
+func (q *Queries) DeleteAllMemoryNodesByBot(ctx context.Context, botID string) error {
+ _, err := q.db.ExecContext(ctx, deleteAllMemoryNodesByBot, botID)
+ return err
+}
+
+const deleteMemoryEdgesByRelForBot = `-- name: DeleteMemoryEdgesByRelForBot :exec
+DELETE FROM memory_edges
+WHERE bot_id = ?1 AND rel = ?2
+`
+
+type DeleteMemoryEdgesByRelForBotParams struct {
+ BotID string `json:"bot_id"`
+ Rel string `json:"rel"`
+}
+
+func (q *Queries) DeleteMemoryEdgesByRelForBot(ctx context.Context, arg DeleteMemoryEdgesByRelForBotParams) error {
+ _, err := q.db.ExecContext(ctx, deleteMemoryEdgesByRelForBot, arg.BotID, arg.Rel)
+ return err
+}
+
+const deleteMemoryEdgesForNode = `-- name: DeleteMemoryEdgesForNode :exec
+DELETE FROM memory_edges
+WHERE bot_id = ?1 AND (src_node = ?2 OR dst_node = ?2)
+`
+
+type DeleteMemoryEdgesForNodeParams struct {
+ BotID string `json:"bot_id"`
+ NodeID string `json:"node_id"`
+}
+
+func (q *Queries) DeleteMemoryEdgesForNode(ctx context.Context, arg DeleteMemoryEdgesForNodeParams) error {
+ _, err := q.db.ExecContext(ctx, deleteMemoryEdgesForNode, arg.BotID, arg.NodeID)
+ return err
+}
+
+const deleteMemoryNode = `-- name: DeleteMemoryNode :exec
+DELETE FROM memory_nodes
+WHERE bot_id = ?1 AND id = ?2
+`
+
+type DeleteMemoryNodeParams struct {
+ BotID string `json:"bot_id"`
+ ID string `json:"id"`
+}
+
+func (q *Queries) DeleteMemoryNode(ctx context.Context, arg DeleteMemoryNodeParams) error {
+ _, err := q.db.ExecContext(ctx, deleteMemoryNode, arg.BotID, arg.ID)
+ return err
+}
+
+const getMemoryNode = `-- name: GetMemoryNode :one
+SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes
+WHERE bot_id = ?1 AND id = ?2
+`
+
+type GetMemoryNodeParams struct {
+ BotID string `json:"bot_id"`
+ ID string `json:"id"`
+}
+
+func (q *Queries) GetMemoryNode(ctx context.Context, arg GetMemoryNodeParams) (MemoryNode, error) {
+ row := q.db.QueryRowContext(ctx, getMemoryNode, arg.BotID, arg.ID)
+ var i MemoryNode
+ err := row.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ )
+ return i, err
+}
+
+const insertMemoryEdge = `-- name: InsertMemoryEdge :exec
+INSERT INTO memory_edges (bot_id, src_node, dst_node, rel, weight, metadata)
+VALUES (
+ ?1,
+ ?2,
+ ?3,
+ ?4,
+ ?5,
+ ?6
+)
+ON CONFLICT (bot_id, src_node, dst_node, rel) DO UPDATE SET
+ weight = EXCLUDED.weight,
+ metadata = EXCLUDED.metadata
+`
+
+type InsertMemoryEdgeParams struct {
+ BotID string `json:"bot_id"`
+ SrcNode string `json:"src_node"`
+ DstNode string `json:"dst_node"`
+ Rel string `json:"rel"`
+ Weight float64 `json:"weight"`
+ Metadata string `json:"metadata"`
+}
+
+func (q *Queries) InsertMemoryEdge(ctx context.Context, arg InsertMemoryEdgeParams) error {
+ _, err := q.db.ExecContext(ctx, insertMemoryEdge,
+ arg.BotID,
+ arg.SrcNode,
+ arg.DstNode,
+ arg.Rel,
+ arg.Weight,
+ arg.Metadata,
+ )
+ return err
+}
+
+const listMemoryEdgesByBot = `-- name: ListMemoryEdgesByBot :many
+SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges
+WHERE bot_id = ?1
+`
+
+func (q *Queries) ListMemoryEdgesByBot(ctx context.Context, botID string) ([]MemoryEdge, error) {
+ rows, err := q.db.QueryContext(ctx, listMemoryEdgesByBot, botID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryEdge
+ for rows.Next() {
+ var i MemoryEdge
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.SrcNode,
+ &i.DstNode,
+ &i.Rel,
+ &i.Weight,
+ &i.Metadata,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryEdgesByRel = `-- name: ListMemoryEdgesByRel :many
+SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges
+WHERE bot_id = ?1 AND rel = ?2
+`
+
+type ListMemoryEdgesByRelParams struct {
+ BotID string `json:"bot_id"`
+ Rel string `json:"rel"`
+}
+
+func (q *Queries) ListMemoryEdgesByRel(ctx context.Context, arg ListMemoryEdgesByRelParams) ([]MemoryEdge, error) {
+ rows, err := q.db.QueryContext(ctx, listMemoryEdgesByRel, arg.BotID, arg.Rel)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryEdge
+ for rows.Next() {
+ var i MemoryEdge
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.SrcNode,
+ &i.DstNode,
+ &i.Rel,
+ &i.Weight,
+ &i.Metadata,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryEdgesFromNode = `-- name: ListMemoryEdgesFromNode :many
+SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges
+WHERE bot_id = ?1 AND src_node = ?2
+ORDER BY weight DESC
+`
+
+type ListMemoryEdgesFromNodeParams struct {
+ BotID string `json:"bot_id"`
+ SrcNode string `json:"src_node"`
+}
+
+func (q *Queries) ListMemoryEdgesFromNode(ctx context.Context, arg ListMemoryEdgesFromNodeParams) ([]MemoryEdge, error) {
+ rows, err := q.db.QueryContext(ctx, listMemoryEdgesFromNode, arg.BotID, arg.SrcNode)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryEdge
+ for rows.Next() {
+ var i MemoryEdge
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.SrcNode,
+ &i.DstNode,
+ &i.Rel,
+ &i.Weight,
+ &i.Metadata,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryNodesByBot = `-- name: ListMemoryNodesByBot :many
+SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes
+WHERE bot_id = ?1
+ORDER BY captured_at ASC
+`
+
+func (q *Queries) ListMemoryNodesByBot(ctx context.Context, botID string) ([]MemoryNode, error) {
+ rows, err := q.db.QueryContext(ctx, listMemoryNodesByBot, botID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryNode
+ for rows.Next() {
+ var i MemoryNode
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryNodesByBotLayer = `-- name: ListMemoryNodesByBotLayer :many
+SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes
+WHERE bot_id = ?1 AND layer = ?2
+ORDER BY captured_at ASC
+`
+
+type ListMemoryNodesByBotLayerParams struct {
+ BotID string `json:"bot_id"`
+ Layer string `json:"layer"`
+}
+
+func (q *Queries) ListMemoryNodesByBotLayer(ctx context.Context, arg ListMemoryNodesByBotLayerParams) ([]MemoryNode, error) {
+ rows, err := q.db.QueryContext(ctx, listMemoryNodesByBotLayer, arg.BotID, arg.Layer)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryNode
+ for rows.Next() {
+ var i MemoryNode
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listMemoryNodesByBotProfile = `-- name: ListMemoryNodesByBotProfile :many
+SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes
+WHERE bot_id = ?1 AND profile_ref = ?2
+ORDER BY captured_at ASC
+`
+
+type ListMemoryNodesByBotProfileParams struct {
+ BotID string `json:"bot_id"`
+ ProfileRef string `json:"profile_ref"`
+}
+
+func (q *Queries) ListMemoryNodesByBotProfile(ctx context.Context, arg ListMemoryNodesByBotProfileParams) ([]MemoryNode, error) {
+ rows, err := q.db.QueryContext(ctx, listMemoryNodesByBotProfile, arg.BotID, arg.ProfileRef)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []MemoryNode
+ for rows.Next() {
+ var i MemoryNode
+ if err := rows.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const upsertMemoryNode = `-- name: UpsertMemoryNode :one
+INSERT INTO memory_nodes (
+ id, bot_id, body, hash, layer, fact_type, subject, confidence,
+ metadata, source_message_ids, profile_ref, topic, captured_at, expires_at
+)
+VALUES (
+ ?1,
+ ?2,
+ ?3,
+ ?4,
+ ?5,
+ ?6,
+ ?7,
+ ?8,
+ ?9,
+ ?10,
+ ?11,
+ ?12,
+ ?13,
+ ?14
+)
+ON CONFLICT (id) DO UPDATE SET
+ body = EXCLUDED.body,
+ hash = EXCLUDED.hash,
+ layer = EXCLUDED.layer,
+ fact_type = EXCLUDED.fact_type,
+ subject = EXCLUDED.subject,
+ confidence = EXCLUDED.confidence,
+ metadata = EXCLUDED.metadata,
+ source_message_ids = EXCLUDED.source_message_ids,
+ profile_ref = EXCLUDED.profile_ref,
+ topic = EXCLUDED.topic,
+ expires_at = EXCLUDED.expires_at,
+ updated_at = CURRENT_TIMESTAMP
+RETURNING id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at
+`
+
+type UpsertMemoryNodeParams struct {
+ ID string `json:"id"`
+ BotID string `json:"bot_id"`
+ Body string `json:"body"`
+ Hash string `json:"hash"`
+ Layer string `json:"layer"`
+ FactType string `json:"fact_type"`
+ Subject string `json:"subject"`
+ Confidence float64 `json:"confidence"`
+ Metadata string `json:"metadata"`
+ SourceMessageIds string `json:"source_message_ids"`
+ ProfileRef string `json:"profile_ref"`
+ Topic string `json:"topic"`
+ CapturedAt string `json:"captured_at"`
+ ExpiresAt sql.NullString `json:"expires_at"`
+}
+
+func (q *Queries) UpsertMemoryNode(ctx context.Context, arg UpsertMemoryNodeParams) (MemoryNode, error) {
+ row := q.db.QueryRowContext(ctx, upsertMemoryNode,
+ arg.ID,
+ arg.BotID,
+ arg.Body,
+ arg.Hash,
+ arg.Layer,
+ arg.FactType,
+ arg.Subject,
+ arg.Confidence,
+ arg.Metadata,
+ arg.SourceMessageIds,
+ arg.ProfileRef,
+ arg.Topic,
+ arg.CapturedAt,
+ arg.ExpiresAt,
+ )
+ var i MemoryNode
+ err := row.Scan(
+ &i.ID,
+ &i.BotID,
+ &i.Body,
+ &i.Hash,
+ &i.Layer,
+ &i.FactType,
+ &i.Subject,
+ &i.Confidence,
+ &i.Metadata,
+ &i.SourceMessageIds,
+ &i.ProfileRef,
+ &i.Topic,
+ &i.CapturedAt,
+ &i.ExpiresAt,
+ &i.UpdatedAt,
+ &i.CreatedAt,
+ )
+ return i, err
+}
diff --git a/internal/db/sqlite/sqlc/models.go b/internal/db/sqlite/sqlc/models.go
index bec737c17c..18c7773932 100644
--- a/internal/db/sqlite/sqlc/models.go
+++ b/internal/db/sqlite/sqlc/models.go
@@ -410,6 +410,36 @@ type McpOauthToken struct {
UpdatedAt string `json:"updated_at"`
}
+type MemoryEdge struct {
+ ID int64 `json:"id"`
+ BotID string `json:"bot_id"`
+ SrcNode string `json:"src_node"`
+ DstNode string `json:"dst_node"`
+ Rel string `json:"rel"`
+ Weight float64 `json:"weight"`
+ Metadata string `json:"metadata"`
+ CreatedAt string `json:"created_at"`
+}
+
+type MemoryNode struct {
+ ID string `json:"id"`
+ BotID string `json:"bot_id"`
+ Body string `json:"body"`
+ Hash string `json:"hash"`
+ Layer string `json:"layer"`
+ FactType string `json:"fact_type"`
+ Subject string `json:"subject"`
+ Confidence float64 `json:"confidence"`
+ Metadata string `json:"metadata"`
+ SourceMessageIds string `json:"source_message_ids"`
+ ProfileRef string `json:"profile_ref"`
+ Topic string `json:"topic"`
+ CapturedAt string `json:"captured_at"`
+ ExpiresAt sql.NullString `json:"expires_at"`
+ UpdatedAt string `json:"updated_at"`
+ CreatedAt string `json:"created_at"`
+}
+
type MemoryProvider struct {
ID string `json:"id"`
Name string `json:"name"`
diff --git a/internal/memory/migrate/tomemorywiki.go b/internal/memory/migrate/tomemorywiki.go
new file mode 100644
index 0000000000..ff407870a8
--- /dev/null
+++ b/internal/memory/migrate/tomemorywiki.go
@@ -0,0 +1,309 @@
+// Package migrate contains utilities that convert existing markdown-backed
+// memory content into the PostgreSQL/SQLite wiki/graph schema
+// (memory_nodes + memory_edges). The conversion logic is backend-agnostic: it
+// produces plain NodeSpec/EdgeSpec values so it can be exercised by unit tests
+// without a live database, and persisted by any concrete store implementation.
+package migrate
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+ "time"
+
+ storefs "github.com/memohai/memoh/internal/memory/storefs"
+)
+
+// MemoryLayer is the canonical layer a memory node belongs to. Existing flat
+// markdown entries that carry no layer hint are classified as LayerNote.
+type MemoryLayer string
+
+const (
+ LayerPreference MemoryLayer = "preference"
+ LayerIdentity MemoryLayer = "identity"
+ LayerContext MemoryLayer = "context"
+ LayerExperience MemoryLayer = "experience"
+ LayerActivity MemoryLayer = "activity"
+ LayerPersona MemoryLayer = "persona"
+ LayerNote MemoryLayer = "note"
+)
+
+// EdgeRel is the canonical relationship type between two memory nodes.
+type EdgeRel string
+
+const (
+ EdgeSameProfile EdgeRel = "same_profile"
+ EdgeSameTopic EdgeRel = "same_topic"
+ EdgeSameDay EdgeRel = "same_day"
+)
+
+// NodeSpec is a backend-agnostic description of a memory_nodes row, produced
+// from a storefs.MemoryItem. Concrete stores (PG/SQLite) translate this into
+// their sqlc UpsertMemoryNodeParams.
+type NodeSpec struct {
+ ID string
+ BotID string
+ Body string
+ Hash string
+ Layer MemoryLayer
+ FactType string
+ Subject string
+ Confidence float32
+ Metadata map[string]any
+ SourceMessageIDs []string
+ ProfileRef string
+ Topic string
+ CapturedAt time.Time
+ ExpiresAt time.Time // zero value means "no expiry".
+}
+
+// EdgeSpec is a backend-agnostic description of a memory_edges row.
+type EdgeSpec struct {
+ BotID string
+ SrcNode string
+ DstNode string
+ Rel EdgeRel
+ Weight float32
+ Metadata map[string]any
+}
+
+// Result summarises a wiki backfill pass for one bot.
+type Result struct {
+ BotID string
+ NodeCount int
+ EdgeCount int
+ LayerBreak map[MemoryLayer]int
+}
+
+// Plan converts a bot's markdown memory items into wiki node specs plus the
+// implicit edges derivable from shared profile_ref / topic / captured day.
+// It performs no I/O and is safe to call in dry-run mode.
+//
+// Layer classification is intentionally conservative: items without any hint
+// fall back to LayerNote. This keeps the backfill deterministic and reversible
+// until the typed-facts formation (P1) starts emitting explicit layers.
+func Plan(botID string, items []storefs.MemoryItem) ([]NodeSpec, []EdgeSpec) {
+ nodes := make([]NodeSpec, 0, len(items))
+ for _, item := range items {
+ nodes = append(nodes, nodeFromItem(botID, item))
+ }
+ edges := buildImplicitEdges(nodes)
+ return nodes, edges
+}
+
+// Summarise returns a Result for a planned node/edge set, suitable for CLI
+// dry-run reporting.
+func Summarise(botID string, nodes []NodeSpec, edges []EdgeSpec) Result {
+ r := Result{BotID: botID, NodeCount: len(nodes), EdgeCount: len(edges), LayerBreak: map[MemoryLayer]int{}}
+ for _, n := range nodes {
+ layer := n.Layer
+ if layer == "" {
+ layer = LayerNote
+ }
+ r.LayerBreak[layer]++
+ }
+ return r
+}
+
+func nodeFromItem(botID string, item storefs.MemoryItem) NodeSpec {
+ body := strings.TrimSpace(item.Memory)
+ layer := classifyLayer(item)
+ topic := metadataString(item.Metadata, "topic")
+ profileRef := metadataString(item.Metadata, "profile_ref")
+ if profileRef == "" {
+ profileRef = metadataString(item.Metadata, "profile_user_id")
+ }
+ captured := parseTime(item.CreatedAt)
+ if captured.IsZero() {
+ captured = parseTime(item.UpdatedAt)
+ }
+ if captured.IsZero() {
+ captured = time.Now().UTC()
+ }
+ return NodeSpec{
+ ID: strings.TrimSpace(item.ID),
+ BotID: botID,
+ Body: body,
+ Hash: strings.TrimSpace(item.Hash),
+ Layer: layer,
+ Subject: metadataString(item.Metadata, "subject"),
+ Confidence: metadataFloat(item.Metadata, "confidence", 0.5),
+ Metadata: cloneMetadata(item.Metadata),
+ ProfileRef: profileRef,
+ Topic: topic,
+ CapturedAt: captured,
+ }
+}
+
+// classifyLayer maps a flat memory item to a canonical layer using light
+// metadata heuristics. Items that already declare a `layer` metadata key are
+// honoured (validated against the known set); otherwise the item defaults to
+// LayerNote. This is deliberately non-magical: real classification happens
+// later in the typed-facts formation (P1).
+func classifyLayer(item storefs.MemoryItem) MemoryLayer {
+ if raw := metadataString(item.Metadata, "layer"); raw != "" {
+ switch MemoryLayer(strings.ToLower(strings.TrimSpace(raw))) {
+ case LayerPreference, LayerIdentity, LayerContext, LayerExperience, LayerActivity, LayerPersona, LayerNote:
+ return MemoryLayer(raw)
+ }
+ }
+ return LayerNote
+}
+
+// buildImplicitEdges derives same_profile / same_topic / same_day edges between
+// nodes. Edges are undirected in intent but stored as directed src->dst pairs
+// where src < dst (lexicographically by node ID) to avoid duplicates. A node
+// never edges to itself.
+func buildImplicitEdges(nodes []NodeSpec) []EdgeSpec {
+ if len(nodes) < 2 {
+ return nil
+ }
+ byProfile := indexBy(nodes, func(n NodeSpec) string { return n.ProfileRef })
+ byTopic := indexBy(nodes, func(n NodeSpec) string { return n.Topic })
+ byDay := indexBy(nodes, func(n NodeSpec) string { return n.CapturedAt.UTC().Format("2006-01-02") })
+
+ seen := map[string]struct{}{}
+ edges := make([]EdgeSpec, 0)
+ add := func(a, b NodeSpec, rel EdgeRel, weight float32) {
+ if a.ID == b.ID {
+ return
+ }
+ src, dst := a.ID, b.ID
+ if dst < src {
+ src, dst = dst, src
+ }
+ key := src + "\x00" + dst + "\x00" + string(rel)
+ if _, ok := seen[key]; ok {
+ return
+ }
+ seen[key] = struct{}{}
+ edges = append(edges, EdgeSpec{BotID: a.BotID, SrcNode: src, DstNode: dst, Rel: rel, Weight: weight})
+ }
+
+ emit := func(groups [][]NodeSpec, rel EdgeRel, weight float32) {
+ for _, group := range groups {
+ if len(group) < 2 {
+ continue
+ }
+ for i := 0; i < len(group); i++ {
+ for j := i + 1; j < len(group); j++ {
+ add(group[i], group[j], rel, weight)
+ }
+ }
+ }
+ }
+ emit(byProfile, EdgeSameProfile, 1.0)
+ emit(byTopic, EdgeSameTopic, 0.8)
+ emit(byDay, EdgeSameDay, 0.5)
+
+ // Deterministic ordering keeps dry-run output stable.
+ sort.Slice(edges, func(i, j int) bool {
+ if edges[i].Rel != edges[j].Rel {
+ return edges[i].Rel < edges[j].Rel
+ }
+ if edges[i].SrcNode != edges[j].SrcNode {
+ return edges[i].SrcNode < edges[j].SrcNode
+ }
+ return edges[i].DstNode < edges[j].DstNode
+ })
+ return edges
+}
+
+// indexBy groups nodes sharing the same non-empty key.
+func indexBy(nodes []NodeSpec, key func(NodeSpec) string) [][]NodeSpec {
+ buckets := map[string][]NodeSpec{}
+ order := []string{}
+ for _, n := range nodes {
+ k := strings.TrimSpace(key(n))
+ if k == "" {
+ continue
+ }
+ if _, ok := buckets[k]; !ok {
+ order = append(order, k)
+ }
+ buckets[k] = append(buckets[k], n)
+ }
+ out := make([][]NodeSpec, 0, len(buckets))
+ for _, k := range order {
+ out = append(out, buckets[k])
+ }
+ return out
+}
+
+func metadataString(m map[string]any, key string) string {
+ if m == nil {
+ return ""
+ }
+ v, ok := m[key]
+ if !ok || v == nil {
+ return ""
+ }
+ switch s := v.(type) {
+ case string:
+ return strings.TrimSpace(s)
+ default:
+ return strings.TrimSpace(toString(v))
+ }
+}
+
+func metadataFloat(m map[string]any, key string, def float32) float32 {
+ if m == nil {
+ return def
+ }
+ v, ok := m[key]
+ if !ok || v == nil {
+ return def
+ }
+ switch n := v.(type) {
+ case float64:
+ return clamp32(float32(n), def)
+ case float32:
+ return clamp32(n, def)
+ case int:
+ return clamp32(float32(n), def)
+ case int64:
+ return clamp32(float32(n), def)
+ case string:
+ return def
+ default:
+ return def
+ }
+}
+
+func clamp32(v, def float32) float32 {
+ if v < 0 || v > 1 {
+ return def
+ }
+ return v
+}
+
+func cloneMetadata(m map[string]any) map[string]any {
+ if len(m) == 0 {
+ return nil
+ }
+ out := make(map[string]any, len(m))
+ for k, v := range m {
+ out[k] = v
+ }
+ return out
+}
+
+func parseTime(s string) time.Time {
+ s = strings.TrimSpace(s)
+ if s == "" {
+ return time.Time{}
+ }
+ for _, layout := range []string{time.RFC3339Nano, time.RFC3339, "2006-01-02T15:04:05Z", "2006-01-02 15:04:05", "2006-01-02"} {
+ if t, err := time.Parse(layout, s); err == nil {
+ return t.UTC()
+ }
+ }
+ return time.Time{}
+}
+
+func toString(v any) string {
+ if v == nil {
+ return ""
+ }
+ return strings.TrimSpace(fmt.Sprint(v))
+}
diff --git a/internal/memory/migrate/tomemorywiki_test.go b/internal/memory/migrate/tomemorywiki_test.go
new file mode 100644
index 0000000000..03e537e96e
--- /dev/null
+++ b/internal/memory/migrate/tomemorywiki_test.go
@@ -0,0 +1,186 @@
+package migrate
+
+import (
+ "testing"
+ "time"
+
+ storefs "github.com/memohai/memoh/internal/memory/storefs"
+)
+
+func TestPlanClassifiesLayersAndEdges(t *testing.T) {
+ items := []storefs.MemoryItem{
+ {
+ ID: "bot-1:mem_1",
+ Memory: "User prefers oolong tea",
+ Hash: "h1",
+ CreatedAt: "2026-06-01T10:00:00Z",
+ Metadata: map[string]any{"layer": "preference", "profile_ref": "user:42", "topic": "drinks", "confidence": 0.9},
+ },
+ {
+ ID: "bot-1:mem_2",
+ Memory: "User lives in Berlin",
+ Hash: "h2",
+ CreatedAt: "2026-06-01T12:00:00Z",
+ Metadata: map[string]any{"profile_ref": "user:42", "topic": "location"},
+ },
+ {
+ ID: "bot-1:mem_3",
+ Memory: "Met about the API project",
+ Hash: "h3",
+ CreatedAt: "2026-06-02T09:00:00Z",
+ Metadata: map[string]any{"topic": "work"},
+ },
+ {
+ ID: "bot-1:mem_4",
+ Memory: "Unrelated note",
+ Hash: "h4",
+ CreatedAt: "2026-06-03T09:00:00Z",
+ },
+ }
+
+ nodes, edges := Plan("bot-1", items)
+
+ if len(nodes) != 4 {
+ t.Fatalf("expected 4 nodes, got %d", len(nodes))
+ }
+
+ // Layer classification: explicit layer honoured, otherwise note.
+ wantLayer := map[string]MemoryLayer{
+ "bot-1:mem_1": LayerPreference,
+ "bot-1:mem_2": LayerNote,
+ "bot-1:mem_3": LayerNote,
+ "bot-1:mem_4": LayerNote,
+ }
+ for _, n := range nodes {
+ if got, want := n.Layer, wantLayer[n.ID]; got != want {
+ t.Fatalf("node %s layer = %q, want %q", n.ID, got, want)
+ }
+ }
+
+ // Confidence: explicit 0.9 preserved, others default to 0.5.
+ conf := map[string]float32{}
+ for _, n := range nodes {
+ conf[n.ID] = n.Confidence
+ }
+ if conf["bot-1:mem_1"] != 0.9 {
+ t.Fatalf("mem_1 confidence = %v, want 0.9", conf["bot-1:mem_1"])
+ }
+ if conf["bot-1:mem_2"] != 0.5 {
+ t.Fatalf("mem_2 confidence = %v, want 0.5 (default)", conf["bot-1:mem_2"])
+ }
+
+ // Profile edge: mem_1 <-> mem_2 share user:42.
+ // Day edge: mem_1 <-> mem_2 share 2026-06-01.
+ // No topic edges (all distinct topics).
+ got := edgeSet(edges)
+ expectEdge(t, got, "bot-1:mem_1", "bot-1:mem_2", EdgeSameProfile)
+ expectEdge(t, got, "bot-1:mem_1", "bot-1:mem_2", EdgeSameDay)
+ if _, ok := lookupEdge(got, "bot-1:mem_1", "bot-1:mem_2", EdgeSameTopic); ok {
+ t.Fatal("did not expect a same_topic edge between mem_1 and mem_2 (different topics)")
+ }
+ // mem_3 and mem_4 have no shared key with anything -> no edges.
+ for _, rel := range []EdgeRel{EdgeSameProfile, EdgeSameTopic, EdgeSameDay} {
+ for _, other := range []string{"bot-1:mem_1", "bot-1:mem_2", "bot-1:mem_4"} {
+ if _, ok := lookupEdge(got, "bot-1:mem_3", other, rel); ok {
+ t.Fatalf("did not expect edge mem_3 <-> %s (%s)", other, rel)
+ }
+ }
+ }
+}
+
+func TestPlanRejectsInvalidLayerAndConfidence(t *testing.T) {
+ items := []storefs.MemoryItem{
+ {
+ ID: "bot-1:bad-layer",
+ Memory: "x",
+ CreatedAt: "2026-06-01T00:00:00Z",
+ // Unknown layer value falls back to note.
+ Metadata: map[string]any{"layer": "nonsense"},
+ },
+ {
+ ID: "bot-1:bad-conf",
+ Memory: "y",
+ CreatedAt: "2026-06-01T00:00:00Z",
+ // Out-of-range confidence falls back to default.
+ Metadata: map[string]any{"confidence": 2.5},
+ },
+ }
+ nodes, _ := Plan("bot-1", items)
+ byID := map[string]NodeSpec{}
+ for _, n := range nodes {
+ byID[n.ID] = n
+ }
+ if byID["bot-1:bad-layer"].Layer != LayerNote {
+ t.Fatalf("invalid layer should fall back to note, got %q", byID["bot-1:bad-layer"].Layer)
+ }
+ if byID["bot-1:bad-conf"].Confidence != 0.5 {
+ t.Fatalf("out-of-range confidence should fall back to 0.5, got %v", byID["bot-1:bad-conf"].Confidence)
+ }
+}
+
+func TestPlanEmptyAndSingleton(t *testing.T) {
+ if nodes, edges := Plan("bot-1", nil); len(nodes) != 0 || len(edges) != 0 {
+ t.Fatalf("empty input should yield empty plan, got %d nodes %d edges", len(nodes), len(edges))
+ }
+ nodes, edges := Plan("bot-1", []storefs.MemoryItem{{ID: "bot-1:mem_1", Memory: "x", CreatedAt: "2026-06-01T00:00:00Z"}})
+ if len(nodes) != 1 || len(edges) != 0 {
+ t.Fatalf("singleton should yield 1 node 0 edges, got %d nodes %d edges", len(nodes), len(edges))
+ }
+}
+
+func TestPlanFallsBackToUpdatedAtThenNow(t *testing.T) {
+ noCreated := storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "x", UpdatedAt: "2026-05-01T00:00:00Z"}
+ nodes, _ := Plan("bot-1", []storefs.MemoryItem{noCreated})
+ if !nodes[0].CapturedAt.Equal(time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC)) {
+ t.Fatalf("captured_at should fall back to updated_at, got %v", nodes[0].CapturedAt)
+ }
+
+ neither := storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "x"}
+ nodes2, _ := Plan("bot-1", []storefs.MemoryItem{neither})
+ if nodes2[0].CapturedAt.IsZero() {
+ t.Fatal("captured_at should fall back to now when both created_at and updated_at are absent")
+ }
+}
+
+func TestSummariseCountsLayers(t *testing.T) {
+ items := []storefs.MemoryItem{
+ {ID: "bot-1:mem_1", Memory: "a", CreatedAt: "2026-06-01T00:00:00Z", Metadata: map[string]any{"layer": "preference"}},
+ {ID: "bot-1:mem_2", Memory: "b", CreatedAt: "2026-06-02T00:00:00Z", Metadata: map[string]any{"layer": "preference"}},
+ {ID: "bot-1:mem_3", Memory: "c", CreatedAt: "2026-06-03T00:00:00Z"},
+ }
+ nodes, edges := Plan("bot-1", items)
+ r := Summarise("bot-1", nodes, edges)
+ if r.NodeCount != 3 {
+ t.Fatalf("node count = %d, want 3", r.NodeCount)
+ }
+ if r.LayerBreak[LayerPreference] != 2 {
+ t.Fatalf("preference count = %d, want 2", r.LayerBreak[LayerPreference])
+ }
+ if r.LayerBreak[LayerNote] != 1 {
+ t.Fatalf("note count = %d, want 1", r.LayerBreak[LayerNote])
+ }
+}
+
+func edgeSet(edges []EdgeSpec) map[string]struct{} {
+ out := map[string]struct{}{}
+ for _, e := range edges {
+ out[e.SrcNode+"\x00"+e.DstNode+"\x00"+string(e.Rel)] = struct{}{}
+ }
+ return out
+}
+
+func lookupEdge(set map[string]struct{}, a, b string, rel EdgeRel) (struct{}, bool) {
+ src, dst := a, b
+ if dst < src {
+ src, dst = dst, src
+ }
+ _, ok := set[src+"\x00"+dst+"\x00"+string(rel)]
+ return struct{}{}, ok
+}
+
+func expectEdge(t *testing.T, set map[string]struct{}, a, b string, rel EdgeRel) {
+ t.Helper()
+ if _, ok := lookupEdge(set, a, b, rel); !ok {
+ t.Fatalf("expected edge %s <-> %s (%s) not found", a, b, rel)
+ }
+}
From e3f8ce81e4a149050d515b2785a66470609d9303 Mon Sep 17 00:00:00 2001
From: Ran <16112591+chen-ran@users.noreply.github.com>
Date: Sun, 21 Jun 2026 00:41:08 +0800
Subject: [PATCH 3/3] chore(spec): regenerate OpenAPI spec and TS SDK
Drop the sparse-explain TopKBucket/CDFPoint types and the unused
EmbedInput/EmbedUpsertRequest/EmbedUpsertResponse adapters types from the
generated OpenAPI schema and the @memohai/sdk TypeScript client.
---
packages/sdk/src/index.ts | 2 +-
packages/sdk/src/types.gen.ts | 25 ---------------------
spec/docs.go | 41 -----------------------------------
spec/swagger.json | 41 -----------------------------------
spec/swagger.yaml | 28 ------------------------
5 files changed, 1 insertion(+), 136 deletions(-)
diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts
index 3816d89c4f..cee1f6d90c 100644
--- a/packages/sdk/src/index.ts
+++ b/packages/sdk/src/index.ts
@@ -1,4 +1,4 @@
// This file is auto-generated by @hey-api/openapi-ts
export { deleteBotsByBotIdAclRulesByRuleId, deleteBotsByBotIdAcpRuntimesByRuntimeId, deleteBotsByBotIdChannelManagersByChannelIdentityId, deleteBotsByBotIdCompactionLogs, deleteBotsByBotIdContainer, deleteBotsByBotIdContainerBrowserSessionsBySessionId, deleteBotsByBotIdContainerDisplaySessionsBySessionId, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdEmailBindingsById, deleteBotsByBotIdHeartbeatLogs, deleteBotsByBotIdMcpById, deleteBotsByBotIdMcpByIdOauthToken, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdMessages, deleteBotsByBotIdPluginsById, deleteBotsByBotIdScheduleById, deleteBotsByBotIdScheduleLogs, deleteBotsByBotIdSessionsBySessionId, deleteBotsByBotIdSettings, deleteBotsByBotIdUserAccessByGrantId, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteEmailProvidersById, deleteEmailProvidersByIdOauthToken, deleteFetchProvidersById, deleteMemoryProvidersById, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteProvidersByIdOauthToken, deleteSearchProvidersById, deleteUsersById, deleteUsersMeChannelIdentitiesByChannelIdentityId, getAcpProfiles, getBots, getBotsByBotIdAclChannelIdentities, getBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversations, getBotsByBotIdAclChannelTypesByChannelTypeConversations, getBotsByBotIdAclDefaultEffect, getBotsByBotIdAclRules, getBotsByBotIdAcpClaudeCodeOauthAuthorize, getBotsByBotIdAcpClaudeCodeOauthStatus, getBotsByBotIdAcpRuntimesByRuntimeId, getBotsByBotIdBackupSummary, getBotsByBotIdChannelManagers, getBotsByBotIdCompactionLogs, getBotsByBotIdContainer, getBotsByBotIdContainerDisplay, getBotsByBotIdContainerDisplaySessions, getBotsByBotIdContainerFs, getBotsByBotIdContainerFsDownload, getBotsByBotIdContainerFsList, getBotsByBotIdContainerFsRead, getBotsByBotIdContainerMetrics, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdContainerTerminal, getBotsByBotIdContainerTerminalWs, getBotsByBotIdEmailBindings, getBotsByBotIdEmailOutbox, getBotsByBotIdEmailOutboxById, getBotsByBotIdHeartbeatLogs, getBotsByBotIdHooksEvents, getBotsByBotIdLocalStream, getBotsByBotIdLocalWs, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpByIdOauthStatus, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryStatus, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdMessagesLocate, getBotsByBotIdPlugins, getBotsByBotIdPluginsById, getBotsByBotIdPluginsByIdOauthStatus, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdScheduleByIdLogs, getBotsByBotIdScheduleLogs, getBotsByBotIdSessions, getBotsByBotIdSessionsBySessionId, getBotsByBotIdSessionsBySessionIdAcpRuntime, getBotsByBotIdSessionsBySessionIdMessagesEvents, getBotsByBotIdSessionsBySessionIdStatus, getBotsByBotIdSessionsEvents, getBotsByBotIdSettings, getBotsByBotIdTokenUsage, getBotsByBotIdTokenUsageRecords, getBotsByBotIdUserAccess, getBotsByBotIdUserAccessCandidates, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsNameAvailability, getChannels, getChannelsByPlatform, getEmailOauthCallback, getEmailProviders, getEmailProvidersById, getEmailProvidersByIdOauthAuthorize, getEmailProvidersByIdOauthStatus, getEmailProvidersMeta, getFetchProviders, getFetchProvidersById, getFetchProvidersMeta, getMemoryProviders, getMemoryProvidersById, getMemoryProvidersByIdStatus, getMemoryProvidersMeta, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getOauthMcpCallback, getPing, getProviders, getProvidersById, getProvidersByIdModels, getProvidersByIdOauthAuthorize, getProvidersByIdOauthStatus, getProvidersCount, getProvidersNameByName, getProvidersOauthCallback, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getSpeechModels, getSpeechModelsById, getSpeechModelsByIdCapabilities, getSpeechProviders, getSpeechProvidersById, getSpeechProvidersByIdModels, getSpeechProvidersMeta, getSupermarketPlugins, getSupermarketPluginsById, getSupermarketSkills, getSupermarketSkillsById, getSupermarketTags, getTranscriptionModels, getTranscriptionModelsById, getTranscriptionModelsByIdCapabilities, getTranscriptionProviders, getTranscriptionProvidersById, getTranscriptionProvidersByIdModels, getTranscriptionProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelIdentities, getUsersMeChannelsByPlatform, type Options, patchBotsByBotIdAcpRuntimesByRuntimeIdModel, patchBotsByBotIdSessionsBySessionId, patchBotsByBotIdSessionsBySessionIdAcpRuntimeModel, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postAuthRefresh, postBots, postBotsBackupImport, postBotsBackupImportPreview, postBotsByBotIdAclRules, postBotsByBotIdAcpClaudeCodeOauthExchange, postBotsByBotIdAcpRuntimes, postBotsByBotIdBackupExport, postBotsByBotIdChannelManagers, postBotsByBotIdContainer, postBotsByBotIdContainerBrowserSessions, postBotsByBotIdContainerBrowserSessionsBySessionIdKeepalive, postBotsByBotIdContainerDataRestore, postBotsByBotIdContainerDisplayPrepare, postBotsByBotIdContainerDisplayWebrtcOffer, postBotsByBotIdContainerFsArchive, postBotsByBotIdContainerFsDelete, postBotsByBotIdContainerFsExtract, postBotsByBotIdContainerFsMkdir, postBotsByBotIdContainerFsRename, postBotsByBotIdContainerFsUpload, postBotsByBotIdContainerFsWrite, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSkillsActions, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerSnapshotsRollback, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdEmailBindings, postBotsByBotIdHooksTest, postBotsByBotIdLocalMessages, postBotsByBotIdMcp, postBotsByBotIdMcpByIdOauthAuthorize, postBotsByBotIdMcpByIdOauthDiscover, postBotsByBotIdMcpByIdOauthExchange, postBotsByBotIdMcpByIdProbe, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdPluginsByIdDisable, postBotsByBotIdPluginsByIdEnable, postBotsByBotIdPluginsByIdOauthAuthorize, postBotsByBotIdPluginsByIdUninstall, postBotsByBotIdSchedule, postBotsByBotIdSessions, postBotsByBotIdSessionsBySessionIdAcpRuntime, postBotsByBotIdSessionsBySessionIdCompact, postBotsByBotIdSettings, postBotsByBotIdSupermarketInstallPlugin, postBotsByBotIdSupermarketInstallSkill, postBotsByBotIdToolApprovalsByApprovalIdApprove, postBotsByBotIdToolApprovalsByApprovalIdReject, postBotsByBotIdTools, postBotsByBotIdTtsSynthesize, postBotsByBotIdUserAccess, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmailMailgunWebhookByConfigId, postEmailProviders, postFetchProviders, postMemoryProviders, postModels, postModelsByIdTest, postProviders, postProvidersByIdImportModels, postProvidersByIdOauthPoll, postProvidersByIdTest, postSearchProviders, postSpeechModelsByIdTest, postSpeechProvidersByIdImportModels, postTranscriptionModelsByIdTest, postTranscriptionProvidersByIdImportModels, postUsers, postUsersMeChannelLinks, putBotsByBotIdAclDefaultEffect, putBotsByBotIdAclRulesByRuleId, putBotsByBotIdContainerMetrics, putBotsByBotIdEmailBindingsById, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdUserAccessByGrantId, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdOwner, putEmailProvidersById, putFetchProvidersById, putMemoryProvidersById, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putSpeechModelsById, putTranscriptionModelsById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from './sdk.gen';
-export type { AccountsAccount, AccountsCreateAccountRequest, AccountsListAccountsResponse, AccountsResetPasswordRequest, AccountsUpdateAccountRequest, AccountsUpdatePasswordRequest, AccountsUpdateProfileMetadata, AccountsUpdateProfileRequest, AclChannelIdentityCandidate, AclChannelIdentityCandidateListResponse, AclCreateRuleRequest, AclDefaultEffectResponse, AclListRulesResponse, AclObservedConversationCandidate, AclObservedConversationCandidateListResponse, AclRule, AclSourceScope, AclUpdateRuleRequest, AcpagentRuntimeStatus, AcpclientModelInfo, AcpclientModelState, AcpprofileManagedField, AcpprofileProfilesResponse, AcpprofilePublicProfile, AdaptersCdfPoint, AdaptersCompactResult, AdaptersDeleteResponse, AdaptersHealthStatus, AdaptersMemoryCompactCapability, AdaptersMemoryItem, AdaptersMemoryStatusResponse, AdaptersMessage, AdaptersProviderCollectionStatus, AdaptersProviderConfigSchema, AdaptersProviderCreateRequest, AdaptersProviderFieldSchema, AdaptersProviderGetResponse, AdaptersProviderMeta, AdaptersProviderStatusResponse, AdaptersProviderType, AdaptersProviderUpdateRequest, AdaptersRebuildResult, AdaptersSearchResponse, AdaptersTopKBucket, AdaptersUsageResponse, AudioConfigSchema, AudioFieldSchema, AudioImportModelsResponse, AudioModelCapabilities, AudioModelInfo, AudioParamConstraint, AudioProviderMetaResponse, AudioSpeechModelResponse, AudioSpeechProviderResponse, AudioTestSynthesizeRequest, AudioTestTranscriptionResponse, AudioTranscriptionModelResponse, AudioTranscriptionWord, AudioUpdateSpeechModelRequest, AudioVoiceInfo, BotbackupExportRequest, BotbackupImportMode, BotbackupImportResult, BotbackupManifest, BotbackupManifestEntry, BotbackupManifestOptions, BotbackupPreviewResult, BotbackupProfilePreview, BotbackupRestorePlan, BotbackupSection, BotbackupSectionSummary, BotbackupSummaryResult, BotsBot, BotsBotCheck, BotsCreateBotRequest, BotsCreateUserGrantRequest, BotsListBotsResponse, BotsListChecksResponse, BotsNameAvailability, BotsTransferBotRequest, BotsUpdateBotRequest, BotsUpdateUserGrantRequest, BotsUserGrant, ChannelaccessBinding, ChannelaccessIssueLinkCodeRequest, ChannelaccessLinkCode, ChannelaccessListBindingsResponse, ChannelaccessListManagersResponse, ChannelaccessManager, ChannelaccessSetManagerRequest, ChannelAction, ChannelAttachment, ChannelAttachmentType, ChannelChannelCapabilities, ChannelChannelConfig, ChannelChannelIdentityBinding, ChannelChannelType, ChannelConfigSchema, ChannelFieldSchema, ChannelFieldType, ChannelForwardRef, ChannelMessage, ChannelMessageFormat, ChannelMessagePart, ChannelMessagePartType, ChannelMessageTextStyle, ChannelReplyRef, ChannelSendRequest, ChannelTargetHint, ChannelTargetSpec, ChannelThreadRef, ChannelUpdateChannelStatusRequest, ChannelUpsertChannelIdentityConfigRequest, ChannelUpsertConfigRequest, ClientOptions, CompactionListLogsResponse, CompactionLog, DeleteBotsByBotIdAclRulesByRuleIdData, DeleteBotsByBotIdAclRulesByRuleIdError, DeleteBotsByBotIdAclRulesByRuleIdErrors, DeleteBotsByBotIdAclRulesByRuleIdResponses, DeleteBotsByBotIdAcpRuntimesByRuntimeIdData, DeleteBotsByBotIdAcpRuntimesByRuntimeIdError, DeleteBotsByBotIdAcpRuntimesByRuntimeIdErrors, DeleteBotsByBotIdAcpRuntimesByRuntimeIdResponses, DeleteBotsByBotIdChannelManagersByChannelIdentityIdData, DeleteBotsByBotIdChannelManagersByChannelIdentityIdError, DeleteBotsByBotIdChannelManagersByChannelIdentityIdErrors, DeleteBotsByBotIdChannelManagersByChannelIdentityIdResponses, DeleteBotsByBotIdCompactionLogsData, DeleteBotsByBotIdCompactionLogsError, DeleteBotsByBotIdCompactionLogsErrors, DeleteBotsByBotIdCompactionLogsResponses, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdData, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdError, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdErrors, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdResponses, DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdData, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdError, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdErrors, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdResponses, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdEmailBindingsByIdData, DeleteBotsByBotIdEmailBindingsByIdError, DeleteBotsByBotIdEmailBindingsByIdErrors, DeleteBotsByBotIdEmailBindingsByIdResponses, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsError, DeleteBotsByBotIdHeartbeatLogsErrors, DeleteBotsByBotIdHeartbeatLogsResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdOauthTokenData, DeleteBotsByBotIdMcpByIdOauthTokenError, DeleteBotsByBotIdMcpByIdOauthTokenErrors, DeleteBotsByBotIdMcpByIdOauthTokenResponses, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesError, DeleteBotsByBotIdMessagesErrors, DeleteBotsByBotIdMessagesResponses, DeleteBotsByBotIdPluginsByIdData, DeleteBotsByBotIdPluginsByIdError, DeleteBotsByBotIdPluginsByIdErrors, DeleteBotsByBotIdPluginsByIdResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdScheduleLogsData, DeleteBotsByBotIdScheduleLogsError, DeleteBotsByBotIdScheduleLogsErrors, DeleteBotsByBotIdScheduleLogsResponses, DeleteBotsByBotIdSessionsBySessionIdData, DeleteBotsByBotIdSessionsBySessionIdError, DeleteBotsByBotIdSessionsBySessionIdErrors, DeleteBotsByBotIdSessionsBySessionIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdUserAccessByGrantIdData, DeleteBotsByBotIdUserAccessByGrantIdError, DeleteBotsByBotIdUserAccessByGrantIdErrors, DeleteBotsByBotIdUserAccessByGrantIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdErrors, DeleteBotsByIdResponse, DeleteBotsByIdResponses, DeleteEmailProvidersByIdData, DeleteEmailProvidersByIdError, DeleteEmailProvidersByIdErrors, DeleteEmailProvidersByIdOauthTokenData, DeleteEmailProvidersByIdOauthTokenError, DeleteEmailProvidersByIdOauthTokenErrors, DeleteEmailProvidersByIdOauthTokenResponses, DeleteEmailProvidersByIdResponses, DeleteFetchProvidersByIdData, DeleteFetchProvidersByIdError, DeleteFetchProvidersByIdErrors, DeleteFetchProvidersByIdResponses, DeleteMemoryProvidersByIdData, DeleteMemoryProvidersByIdError, DeleteMemoryProvidersByIdErrors, DeleteMemoryProvidersByIdResponses, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteProvidersByIdErrors, DeleteProvidersByIdOauthTokenData, DeleteProvidersByIdOauthTokenError, DeleteProvidersByIdOauthTokenErrors, DeleteProvidersByIdOauthTokenResponses, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, DeleteUsersByIdData, DeleteUsersByIdError, DeleteUsersByIdErrors, DeleteUsersByIdResponses, DeleteUsersMeChannelIdentitiesByChannelIdentityIdData, DeleteUsersMeChannelIdentitiesByChannelIdentityIdError, DeleteUsersMeChannelIdentitiesByChannelIdentityIdErrors, DeleteUsersMeChannelIdentitiesByChannelIdentityIdResponses, DisplaySessionInfo, EmailBindingResponse, EmailConfigSchema, EmailCreateBindingRequest, EmailCreateProviderRequest, EmailFieldSchema, EmailOutboxItemResponse, EmailProviderMeta, EmailProviderResponse, EmailUpdateBindingRequest, EmailUpdateProviderRequest, FetchprovidersCreateRequest, FetchprovidersGetResponse, FetchprovidersProviderConfigSchema, FetchprovidersProviderFieldSchema, FetchprovidersProviderMeta, FetchprovidersProviderName, FetchprovidersUpdateRequest, GetAcpProfilesData, GetAcpProfilesResponse, GetAcpProfilesResponses, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsData, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsError, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsErrors, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsResponse, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsResponses, GetBotsByBotIdAclChannelIdentitiesData, GetBotsByBotIdAclChannelIdentitiesError, GetBotsByBotIdAclChannelIdentitiesErrors, GetBotsByBotIdAclChannelIdentitiesResponse, GetBotsByBotIdAclChannelIdentitiesResponses, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsData, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsError, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsErrors, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsResponse, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsResponses, GetBotsByBotIdAclDefaultEffectData, GetBotsByBotIdAclDefaultEffectError, GetBotsByBotIdAclDefaultEffectErrors, GetBotsByBotIdAclDefaultEffectResponse, GetBotsByBotIdAclDefaultEffectResponses, GetBotsByBotIdAclRulesData, GetBotsByBotIdAclRulesError, GetBotsByBotIdAclRulesErrors, GetBotsByBotIdAclRulesResponse, GetBotsByBotIdAclRulesResponses, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeData, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeError, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeErrors, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeResponse, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeResponses, GetBotsByBotIdAcpClaudeCodeOauthStatusData, GetBotsByBotIdAcpClaudeCodeOauthStatusError, GetBotsByBotIdAcpClaudeCodeOauthStatusErrors, GetBotsByBotIdAcpClaudeCodeOauthStatusResponse, GetBotsByBotIdAcpClaudeCodeOauthStatusResponses, GetBotsByBotIdAcpRuntimesByRuntimeIdData, GetBotsByBotIdAcpRuntimesByRuntimeIdError, GetBotsByBotIdAcpRuntimesByRuntimeIdErrors, GetBotsByBotIdAcpRuntimesByRuntimeIdResponse, GetBotsByBotIdAcpRuntimesByRuntimeIdResponses, GetBotsByBotIdBackupSummaryData, GetBotsByBotIdBackupSummaryError, GetBotsByBotIdBackupSummaryErrors, GetBotsByBotIdBackupSummaryResponse, GetBotsByBotIdBackupSummaryResponses, GetBotsByBotIdChannelManagersData, GetBotsByBotIdChannelManagersError, GetBotsByBotIdChannelManagersErrors, GetBotsByBotIdChannelManagersResponse, GetBotsByBotIdChannelManagersResponses, GetBotsByBotIdCompactionLogsData, GetBotsByBotIdCompactionLogsError, GetBotsByBotIdCompactionLogsErrors, GetBotsByBotIdCompactionLogsResponse, GetBotsByBotIdCompactionLogsResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerDisplayData, GetBotsByBotIdContainerDisplayError, GetBotsByBotIdContainerDisplayErrors, GetBotsByBotIdContainerDisplayResponse, GetBotsByBotIdContainerDisplayResponses, GetBotsByBotIdContainerDisplaySessionsData, GetBotsByBotIdContainerDisplaySessionsError, GetBotsByBotIdContainerDisplaySessionsErrors, GetBotsByBotIdContainerDisplaySessionsResponse, GetBotsByBotIdContainerDisplaySessionsResponses, GetBotsByBotIdContainerError, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsDownloadError, GetBotsByBotIdContainerFsDownloadErrors, GetBotsByBotIdContainerFsDownloadResponses, GetBotsByBotIdContainerFsError, GetBotsByBotIdContainerFsErrors, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsListError, GetBotsByBotIdContainerFsListErrors, GetBotsByBotIdContainerFsListResponse, GetBotsByBotIdContainerFsListResponses, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerFsReadError, GetBotsByBotIdContainerFsReadErrors, GetBotsByBotIdContainerFsReadResponse, GetBotsByBotIdContainerFsReadResponses, GetBotsByBotIdContainerFsResponse, GetBotsByBotIdContainerFsResponses, GetBotsByBotIdContainerMetricsData, GetBotsByBotIdContainerMetricsError, GetBotsByBotIdContainerMetricsErrors, GetBotsByBotIdContainerMetricsResponse, GetBotsByBotIdContainerMetricsResponses, GetBotsByBotIdContainerResponse, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsError, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponse, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsError, GetBotsByBotIdContainerSnapshotsErrors, GetBotsByBotIdContainerSnapshotsResponse, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdContainerTerminalData, GetBotsByBotIdContainerTerminalError, GetBotsByBotIdContainerTerminalErrors, GetBotsByBotIdContainerTerminalResponse, GetBotsByBotIdContainerTerminalResponses, GetBotsByBotIdContainerTerminalWsData, GetBotsByBotIdContainerTerminalWsError, GetBotsByBotIdContainerTerminalWsErrors, GetBotsByBotIdEmailBindingsData, GetBotsByBotIdEmailBindingsError, GetBotsByBotIdEmailBindingsErrors, GetBotsByBotIdEmailBindingsResponse, GetBotsByBotIdEmailBindingsResponses, GetBotsByBotIdEmailOutboxByIdData, GetBotsByBotIdEmailOutboxByIdError, GetBotsByBotIdEmailOutboxByIdErrors, GetBotsByBotIdEmailOutboxByIdResponse, GetBotsByBotIdEmailOutboxByIdResponses, GetBotsByBotIdEmailOutboxData, GetBotsByBotIdEmailOutboxError, GetBotsByBotIdEmailOutboxErrors, GetBotsByBotIdEmailOutboxResponse, GetBotsByBotIdEmailOutboxResponses, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdHeartbeatLogsError, GetBotsByBotIdHeartbeatLogsErrors, GetBotsByBotIdHeartbeatLogsResponse, GetBotsByBotIdHeartbeatLogsResponses, GetBotsByBotIdHooksEventsData, GetBotsByBotIdHooksEventsError, GetBotsByBotIdHooksEventsErrors, GetBotsByBotIdHooksEventsResponse, GetBotsByBotIdHooksEventsResponses, GetBotsByBotIdLocalStreamData, GetBotsByBotIdLocalStreamError, GetBotsByBotIdLocalStreamErrors, GetBotsByBotIdLocalStreamResponse, GetBotsByBotIdLocalStreamResponses, GetBotsByBotIdLocalWsData, GetBotsByBotIdLocalWsError, GetBotsByBotIdLocalWsErrors, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdError, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdOauthStatusData, GetBotsByBotIdMcpByIdOauthStatusError, GetBotsByBotIdMcpByIdOauthStatusErrors, GetBotsByBotIdMcpByIdOauthStatusResponse, GetBotsByBotIdMcpByIdOauthStatusResponses, GetBotsByBotIdMcpByIdResponse, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpError, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportError, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponse, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponse, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryError, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponse, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryStatusData, GetBotsByBotIdMemoryStatusError, GetBotsByBotIdMemoryStatusErrors, GetBotsByBotIdMemoryStatusResponse, GetBotsByBotIdMemoryStatusResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageError, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponse, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesError, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesLocateData, GetBotsByBotIdMessagesLocateError, GetBotsByBotIdMessagesLocateErrors, GetBotsByBotIdMessagesLocateResponse, GetBotsByBotIdMessagesLocateResponses, GetBotsByBotIdMessagesResponse, GetBotsByBotIdMessagesResponses, GetBotsByBotIdPluginsByIdData, GetBotsByBotIdPluginsByIdError, GetBotsByBotIdPluginsByIdErrors, GetBotsByBotIdPluginsByIdOauthStatusData, GetBotsByBotIdPluginsByIdOauthStatusError, GetBotsByBotIdPluginsByIdOauthStatusErrors, GetBotsByBotIdPluginsByIdOauthStatusResponse, GetBotsByBotIdPluginsByIdOauthStatusResponses, GetBotsByBotIdPluginsByIdResponse, GetBotsByBotIdPluginsByIdResponses, GetBotsByBotIdPluginsData, GetBotsByBotIdPluginsError, GetBotsByBotIdPluginsErrors, GetBotsByBotIdPluginsResponse, GetBotsByBotIdPluginsResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdError, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdLogsData, GetBotsByBotIdScheduleByIdLogsError, GetBotsByBotIdScheduleByIdLogsErrors, GetBotsByBotIdScheduleByIdLogsResponse, GetBotsByBotIdScheduleByIdLogsResponses, GetBotsByBotIdScheduleByIdResponse, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleError, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleLogsData, GetBotsByBotIdScheduleLogsError, GetBotsByBotIdScheduleLogsErrors, GetBotsByBotIdScheduleLogsResponse, GetBotsByBotIdScheduleLogsResponses, GetBotsByBotIdScheduleResponse, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSessionsBySessionIdAcpRuntimeData, GetBotsByBotIdSessionsBySessionIdAcpRuntimeError, GetBotsByBotIdSessionsBySessionIdAcpRuntimeErrors, GetBotsByBotIdSessionsBySessionIdAcpRuntimeResponse, GetBotsByBotIdSessionsBySessionIdAcpRuntimeResponses, GetBotsByBotIdSessionsBySessionIdData, GetBotsByBotIdSessionsBySessionIdError, GetBotsByBotIdSessionsBySessionIdErrors, GetBotsByBotIdSessionsBySessionIdMessagesEventsData, GetBotsByBotIdSessionsBySessionIdMessagesEventsError, GetBotsByBotIdSessionsBySessionIdMessagesEventsErrors, GetBotsByBotIdSessionsBySessionIdMessagesEventsResponse, GetBotsByBotIdSessionsBySessionIdMessagesEventsResponses, GetBotsByBotIdSessionsBySessionIdResponse, GetBotsByBotIdSessionsBySessionIdResponses, GetBotsByBotIdSessionsBySessionIdStatusData, GetBotsByBotIdSessionsBySessionIdStatusError, GetBotsByBotIdSessionsBySessionIdStatusErrors, GetBotsByBotIdSessionsBySessionIdStatusResponse, GetBotsByBotIdSessionsBySessionIdStatusResponses, GetBotsByBotIdSessionsData, GetBotsByBotIdSessionsError, GetBotsByBotIdSessionsErrors, GetBotsByBotIdSessionsEventsData, GetBotsByBotIdSessionsEventsError, GetBotsByBotIdSessionsEventsErrors, GetBotsByBotIdSessionsEventsResponse, GetBotsByBotIdSessionsEventsResponses, GetBotsByBotIdSessionsResponse, GetBotsByBotIdSessionsResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsError, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponse, GetBotsByBotIdSettingsResponses, GetBotsByBotIdTokenUsageData, GetBotsByBotIdTokenUsageError, GetBotsByBotIdTokenUsageErrors, GetBotsByBotIdTokenUsageRecordsData, GetBotsByBotIdTokenUsageRecordsError, GetBotsByBotIdTokenUsageRecordsErrors, GetBotsByBotIdTokenUsageRecordsResponse, GetBotsByBotIdTokenUsageRecordsResponses, GetBotsByBotIdTokenUsageResponse, GetBotsByBotIdTokenUsageResponses, GetBotsByBotIdUserAccessCandidatesData, GetBotsByBotIdUserAccessCandidatesError, GetBotsByBotIdUserAccessCandidatesErrors, GetBotsByBotIdUserAccessCandidatesResponse, GetBotsByBotIdUserAccessCandidatesResponses, GetBotsByBotIdUserAccessData, GetBotsByBotIdUserAccessError, GetBotsByBotIdUserAccessErrors, GetBotsByBotIdUserAccessResponse, GetBotsByBotIdUserAccessResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformError, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponse, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksError, GetBotsByIdChecksErrors, GetBotsByIdChecksResponse, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdError, GetBotsByIdErrors, GetBotsByIdResponse, GetBotsByIdResponses, GetBotsData, GetBotsError, GetBotsErrors, GetBotsNameAvailabilityData, GetBotsNameAvailabilityError, GetBotsNameAvailabilityErrors, GetBotsNameAvailabilityResponse, GetBotsNameAvailabilityResponses, GetBotsResponse, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformError, GetChannelsByPlatformErrors, GetChannelsByPlatformResponse, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsError, GetChannelsErrors, GetChannelsResponse, GetChannelsResponses, GetEmailOauthCallbackData, GetEmailOauthCallbackError, GetEmailOauthCallbackErrors, GetEmailOauthCallbackResponse, GetEmailOauthCallbackResponses, GetEmailProvidersByIdData, GetEmailProvidersByIdError, GetEmailProvidersByIdErrors, GetEmailProvidersByIdOauthAuthorizeData, GetEmailProvidersByIdOauthAuthorizeError, GetEmailProvidersByIdOauthAuthorizeErrors, GetEmailProvidersByIdOauthAuthorizeResponse, GetEmailProvidersByIdOauthAuthorizeResponses, GetEmailProvidersByIdOauthStatusData, GetEmailProvidersByIdOauthStatusError, GetEmailProvidersByIdOauthStatusErrors, GetEmailProvidersByIdOauthStatusResponse, GetEmailProvidersByIdOauthStatusResponses, GetEmailProvidersByIdResponse, GetEmailProvidersByIdResponses, GetEmailProvidersData, GetEmailProvidersError, GetEmailProvidersErrors, GetEmailProvidersMetaData, GetEmailProvidersMetaResponse, GetEmailProvidersMetaResponses, GetEmailProvidersResponse, GetEmailProvidersResponses, GetFetchProvidersByIdData, GetFetchProvidersByIdError, GetFetchProvidersByIdErrors, GetFetchProvidersByIdResponse, GetFetchProvidersByIdResponses, GetFetchProvidersData, GetFetchProvidersError, GetFetchProvidersErrors, GetFetchProvidersMetaData, GetFetchProvidersMetaResponse, GetFetchProvidersMetaResponses, GetFetchProvidersResponse, GetFetchProvidersResponses, GetMemoryProvidersByIdData, GetMemoryProvidersByIdError, GetMemoryProvidersByIdErrors, GetMemoryProvidersByIdResponse, GetMemoryProvidersByIdResponses, GetMemoryProvidersByIdStatusData, GetMemoryProvidersByIdStatusError, GetMemoryProvidersByIdStatusErrors, GetMemoryProvidersByIdStatusResponse, GetMemoryProvidersByIdStatusResponses, GetMemoryProvidersData, GetMemoryProvidersError, GetMemoryProvidersErrors, GetMemoryProvidersMetaData, GetMemoryProvidersMetaResponse, GetMemoryProvidersMetaResponses, GetMemoryProvidersResponse, GetMemoryProvidersResponses, GetModelsByIdData, GetModelsByIdError, GetModelsByIdErrors, GetModelsByIdResponse, GetModelsByIdResponses, GetModelsCountData, GetModelsCountError, GetModelsCountErrors, GetModelsCountResponse, GetModelsCountResponses, GetModelsData, GetModelsError, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdError, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponse, GetModelsModelByModelIdResponses, GetModelsResponse, GetModelsResponses, GetOauthMcpCallbackData, GetOauthMcpCallbackError, GetOauthMcpCallbackErrors, GetOauthMcpCallbackResponse, GetOauthMcpCallbackResponses, GetPingData, GetPingResponse, GetPingResponses, GetProvidersByIdData, GetProvidersByIdError, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsError, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponse, GetProvidersByIdModelsResponses, GetProvidersByIdOauthAuthorizeData, GetProvidersByIdOauthAuthorizeError, GetProvidersByIdOauthAuthorizeErrors, GetProvidersByIdOauthAuthorizeResponse, GetProvidersByIdOauthAuthorizeResponses, GetProvidersByIdOauthStatusData, GetProvidersByIdOauthStatusError, GetProvidersByIdOauthStatusErrors, GetProvidersByIdOauthStatusResponse, GetProvidersByIdOauthStatusResponses, GetProvidersByIdResponse, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountError, GetProvidersCountErrors, GetProvidersCountResponse, GetProvidersCountResponses, GetProvidersData, GetProvidersError, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameError, GetProvidersNameByNameErrors, GetProvidersNameByNameResponse, GetProvidersNameByNameResponses, GetProvidersOauthCallbackData, GetProvidersOauthCallbackError, GetProvidersOauthCallbackErrors, GetProvidersOauthCallbackResponse, GetProvidersOauthCallbackResponses, GetProvidersResponse, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdError, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponse, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersError, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponse, GetSearchProvidersMetaResponses, GetSearchProvidersResponse, GetSearchProvidersResponses, GetSpeechModelsByIdCapabilitiesData, GetSpeechModelsByIdCapabilitiesError, GetSpeechModelsByIdCapabilitiesErrors, GetSpeechModelsByIdCapabilitiesResponse, GetSpeechModelsByIdCapabilitiesResponses, GetSpeechModelsByIdData, GetSpeechModelsByIdError, GetSpeechModelsByIdErrors, GetSpeechModelsByIdResponse, GetSpeechModelsByIdResponses, GetSpeechModelsData, GetSpeechModelsError, GetSpeechModelsErrors, GetSpeechModelsResponse, GetSpeechModelsResponses, GetSpeechProvidersByIdData, GetSpeechProvidersByIdError, GetSpeechProvidersByIdErrors, GetSpeechProvidersByIdModelsData, GetSpeechProvidersByIdModelsError, GetSpeechProvidersByIdModelsErrors, GetSpeechProvidersByIdModelsResponse, GetSpeechProvidersByIdModelsResponses, GetSpeechProvidersByIdResponse, GetSpeechProvidersByIdResponses, GetSpeechProvidersData, GetSpeechProvidersError, GetSpeechProvidersErrors, GetSpeechProvidersMetaData, GetSpeechProvidersMetaResponse, GetSpeechProvidersMetaResponses, GetSpeechProvidersResponse, GetSpeechProvidersResponses, GetSupermarketPluginsByIdData, GetSupermarketPluginsByIdError, GetSupermarketPluginsByIdErrors, GetSupermarketPluginsByIdResponse, GetSupermarketPluginsByIdResponses, GetSupermarketPluginsData, GetSupermarketPluginsError, GetSupermarketPluginsErrors, GetSupermarketPluginsResponse, GetSupermarketPluginsResponses, GetSupermarketSkillsByIdData, GetSupermarketSkillsByIdError, GetSupermarketSkillsByIdErrors, GetSupermarketSkillsByIdResponse, GetSupermarketSkillsByIdResponses, GetSupermarketSkillsData, GetSupermarketSkillsError, GetSupermarketSkillsErrors, GetSupermarketSkillsResponse, GetSupermarketSkillsResponses, GetSupermarketTagsData, GetSupermarketTagsError, GetSupermarketTagsErrors, GetSupermarketTagsResponse, GetSupermarketTagsResponses, GetTranscriptionModelsByIdCapabilitiesData, GetTranscriptionModelsByIdCapabilitiesError, GetTranscriptionModelsByIdCapabilitiesErrors, GetTranscriptionModelsByIdCapabilitiesResponse, GetTranscriptionModelsByIdCapabilitiesResponses, GetTranscriptionModelsByIdData, GetTranscriptionModelsByIdError, GetTranscriptionModelsByIdErrors, GetTranscriptionModelsByIdResponse, GetTranscriptionModelsByIdResponses, GetTranscriptionModelsData, GetTranscriptionModelsError, GetTranscriptionModelsErrors, GetTranscriptionModelsResponse, GetTranscriptionModelsResponses, GetTranscriptionProvidersByIdData, GetTranscriptionProvidersByIdError, GetTranscriptionProvidersByIdErrors, GetTranscriptionProvidersByIdModelsData, GetTranscriptionProvidersByIdModelsError, GetTranscriptionProvidersByIdModelsErrors, GetTranscriptionProvidersByIdModelsResponse, GetTranscriptionProvidersByIdModelsResponses, GetTranscriptionProvidersByIdResponse, GetTranscriptionProvidersByIdResponses, GetTranscriptionProvidersData, GetTranscriptionProvidersError, GetTranscriptionProvidersErrors, GetTranscriptionProvidersMetaData, GetTranscriptionProvidersMetaResponse, GetTranscriptionProvidersMetaResponses, GetTranscriptionProvidersResponse, GetTranscriptionProvidersResponses, GetUsersByIdData, GetUsersByIdError, GetUsersByIdErrors, GetUsersByIdResponse, GetUsersByIdResponses, GetUsersData, GetUsersError, GetUsersErrors, GetUsersMeChannelIdentitiesData, GetUsersMeChannelIdentitiesError, GetUsersMeChannelIdentitiesErrors, GetUsersMeChannelIdentitiesResponse, GetUsersMeChannelIdentitiesResponses, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformError, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponse, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeError, GetUsersMeErrors, GetUsersMeResponse, GetUsersMeResponses, GetUsersResponse, GetUsersResponses, GithubComMemohaiMemohInternalMcpConnection, HandlersAcpClaudeCodeOAuthAuthorizeResponse, HandlersAcpClaudeCodeOAuthExchangeRequest, HandlersAcpClaudeCodeOAuthStatus, HandlersAcpRuntimeCreateRequest, HandlersAcpRuntimeModelRequest, HandlersBatchDeleteRequest, HandlersBotUserCandidate, HandlersBotUserCandidateListResponse, HandlersBotUserGrantListResponse, HandlersBrowserSessionCreateRequest, HandlersBrowserSessionCreateResponse, HandlersBrowserSessionKeepAliveResponse, HandlersCacheStats, HandlersChannelMeta, HandlersContainerCpuMetricsResponse, HandlersContainerGpuRequest, HandlersContainerMemoryMetricsResponse, HandlersContainerMetricsPayloadResponse, HandlersContainerMetricsStatusResponse, HandlersContainerResourceLimitCapabilitiesResponse, HandlersContainerResourceLimitCapabilityResponse, HandlersContainerResourceLimitObservedResponse, HandlersContainerResourceLimitValuesResponse, HandlersContainerStorageMetricsResponse, HandlersContextUsage, HandlersCreateContainerRequest, HandlersCreateContainerResponse, HandlersCreateSessionRequest, HandlersCreateSnapshotRequest, HandlersCreateSnapshotResponse, HandlersDailyTokenUsage, HandlersDisplayInfoResponse, HandlersDisplaySessionListResponse, HandlersDisplayWebRtcOfferRequest, HandlersDisplayWebRtcOfferResponse, HandlersEmailOAuthStatusResponse, HandlersErrorResponse, HandlersFsArchiveRequest, HandlersFsDeleteRequest, HandlersFsExtractRequest, HandlersFsExtractResponse, HandlersFsFileInfo, HandlersFsListResponse, HandlersFsMkdirRequest, HandlersFsOpResponse, HandlersFsReadResponse, HandlersFsRenameRequest, HandlersFsUploadResponse, HandlersFsWriteRequest, HandlersGetContainerMetricsResponse, HandlersGetContainerResourceLimitsResponse, HandlersGetContainerResponse, HandlersHookEventInfo, HandlersHooksEventsResponse, HandlersHookTestRequest, HandlersHookTestResponse, HandlersInstallPluginRequest, HandlersInstallSkillRequest, HandlersListSessionsResponse, HandlersListSnapshotsResponse, HandlersLocalChannelMessageRequest, HandlersLoginRequest, HandlersLoginResponse, HandlersMcpStdioRequest, HandlersMcpStdioResponse, HandlersMemoryAddPayload, HandlersMemoryCompactPayload, HandlersMemoryDeletePayload, HandlersMemorySearchPayload, HandlersModelTokenUsage, HandlersOauthAuthorizeRequest, HandlersOauthDiscoverRequest, HandlersOauthExchangeRequest, HandlersPingResponse, HandlersProbeResponse, HandlersRefreshResponse, HandlersRollbackRequest, HandlersSessionInfoResponse, HandlersSkillItem, HandlersSkillsActionRequest, HandlersSkillsDeleteRequest, HandlersSkillsOpResponse, HandlersSkillsResponse, HandlersSkillsUpsertRequest, HandlersSnapshotInfo, HandlersSupermarketAuthor, HandlersSupermarketPluginListResponse, HandlersSupermarketSkillEntry, HandlersSupermarketSkillListResponse, HandlersSupermarketSkillMetadata, HandlersSupermarketTagsResponse, HandlersSynthesizeRequest, HandlersSynthesizeResponse, HandlersTerminalInfoResponse, HandlersTokenUsageRecord, HandlersTokenUsageRecordsResponse, HandlersTokenUsageResponse, HandlersToolApprovalDecisionRequest, HandlersTriggerCompactResponse, HandlersUpdateContainerMetricsRequest, HandlersUpdateContainerResourceLimitsRequest, HandlersUpdateSessionRequest, HeartbeatListLogsResponse, HeartbeatLog, HooksActionResult, HooksResult, HooksToolPayload, McpAuthorizeResult, McpDiscoveryResult, McpExportResponse, McpImportRequest, McpListResponse, McpMcpServerEntry, McpOAuthStatus, McpToolDescriptor, McpUpsertRequest, MessageMessage, MessageMessageAsset, ModelsAddRequest, ModelsAddResponse, ModelsCountResponse, ModelsGetResponse, ModelsModelConfig, ModelsModelType, ModelsTestResponse, ModelsTestStatus, ModelsUpdateRequest, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelData, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelError, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelErrors, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelResponse, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelResponses, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelData, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelError, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelErrors, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelResponse, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelResponses, PatchBotsByBotIdSessionsBySessionIdData, PatchBotsByBotIdSessionsBySessionIdError, PatchBotsByBotIdSessionsBySessionIdErrors, PatchBotsByBotIdSessionsBySessionIdResponse, PatchBotsByBotIdSessionsBySessionIdResponses, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponse, PatchBotsByIdChannelByPlatformStatusResponses, PluginsAuthor, PluginsAuthRequirement, PluginsConfigVar, PluginsIcon, PluginsInstallation, PluginsListResponse, PluginsManifest, PluginsMcpResource, PluginsOAuthAuthorizeRequest, PluginsResource, PluginsSkillEntry, PluginsSkillResource, PostAuthLoginData, PostAuthLoginError, PostAuthLoginErrors, PostAuthLoginResponse, PostAuthLoginResponses, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshErrors, PostAuthRefreshResponse, PostAuthRefreshResponses, PostBotsBackupImportData, PostBotsBackupImportError, PostBotsBackupImportErrors, PostBotsBackupImportPreviewData, PostBotsBackupImportPreviewError, PostBotsBackupImportPreviewErrors, PostBotsBackupImportPreviewResponse, PostBotsBackupImportPreviewResponses, PostBotsBackupImportResponse, PostBotsBackupImportResponses, PostBotsByBotIdAclRulesData, PostBotsByBotIdAclRulesError, PostBotsByBotIdAclRulesErrors, PostBotsByBotIdAclRulesResponse, PostBotsByBotIdAclRulesResponses, PostBotsByBotIdAcpClaudeCodeOauthExchangeData, PostBotsByBotIdAcpClaudeCodeOauthExchangeError, PostBotsByBotIdAcpClaudeCodeOauthExchangeErrors, PostBotsByBotIdAcpClaudeCodeOauthExchangeResponse, PostBotsByBotIdAcpClaudeCodeOauthExchangeResponses, PostBotsByBotIdAcpRuntimesData, PostBotsByBotIdAcpRuntimesError, PostBotsByBotIdAcpRuntimesErrors, PostBotsByBotIdAcpRuntimesResponse, PostBotsByBotIdAcpRuntimesResponses, PostBotsByBotIdBackupExportData, PostBotsByBotIdBackupExportError, PostBotsByBotIdBackupExportErrors, PostBotsByBotIdBackupExportResponses, PostBotsByBotIdChannelManagersData, PostBotsByBotIdChannelManagersError, PostBotsByBotIdChannelManagersErrors, PostBotsByBotIdChannelManagersResponses, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveData, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveError, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveErrors, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveResponse, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveResponses, PostBotsByBotIdContainerBrowserSessionsData, PostBotsByBotIdContainerBrowserSessionsError, PostBotsByBotIdContainerBrowserSessionsErrors, PostBotsByBotIdContainerBrowserSessionsResponse, PostBotsByBotIdContainerBrowserSessionsResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerDataRestoreData, PostBotsByBotIdContainerDataRestoreError, PostBotsByBotIdContainerDataRestoreErrors, PostBotsByBotIdContainerDataRestoreResponse, PostBotsByBotIdContainerDataRestoreResponses, PostBotsByBotIdContainerDisplayPrepareData, PostBotsByBotIdContainerDisplayPrepareError, PostBotsByBotIdContainerDisplayPrepareErrors, PostBotsByBotIdContainerDisplayPrepareResponse, PostBotsByBotIdContainerDisplayPrepareResponses, PostBotsByBotIdContainerDisplayWebrtcOfferData, PostBotsByBotIdContainerDisplayWebrtcOfferError, PostBotsByBotIdContainerDisplayWebrtcOfferErrors, PostBotsByBotIdContainerDisplayWebrtcOfferResponse, PostBotsByBotIdContainerDisplayWebrtcOfferResponses, PostBotsByBotIdContainerError, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerFsArchiveData, PostBotsByBotIdContainerFsArchiveError, PostBotsByBotIdContainerFsArchiveErrors, PostBotsByBotIdContainerFsArchiveResponses, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteError, PostBotsByBotIdContainerFsDeleteErrors, PostBotsByBotIdContainerFsDeleteResponse, PostBotsByBotIdContainerFsDeleteResponses, PostBotsByBotIdContainerFsExtractData, PostBotsByBotIdContainerFsExtractError, PostBotsByBotIdContainerFsExtractErrors, PostBotsByBotIdContainerFsExtractResponse, PostBotsByBotIdContainerFsExtractResponses, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirError, PostBotsByBotIdContainerFsMkdirErrors, PostBotsByBotIdContainerFsMkdirResponse, PostBotsByBotIdContainerFsMkdirResponses, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameError, PostBotsByBotIdContainerFsRenameErrors, PostBotsByBotIdContainerFsRenameResponse, PostBotsByBotIdContainerFsRenameResponses, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadError, PostBotsByBotIdContainerFsUploadErrors, PostBotsByBotIdContainerFsUploadResponse, PostBotsByBotIdContainerFsUploadResponses, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteError, PostBotsByBotIdContainerFsWriteErrors, PostBotsByBotIdContainerFsWriteResponse, PostBotsByBotIdContainerFsWriteResponses, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsActionsData, PostBotsByBotIdContainerSkillsActionsError, PostBotsByBotIdContainerSkillsActionsErrors, PostBotsByBotIdContainerSkillsActionsResponse, PostBotsByBotIdContainerSkillsActionsResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerSnapshotsRollbackData, PostBotsByBotIdContainerSnapshotsRollbackError, PostBotsByBotIdContainerSnapshotsRollbackErrors, PostBotsByBotIdContainerSnapshotsRollbackResponse, PostBotsByBotIdContainerSnapshotsRollbackResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdEmailBindingsData, PostBotsByBotIdEmailBindingsError, PostBotsByBotIdEmailBindingsErrors, PostBotsByBotIdEmailBindingsResponse, PostBotsByBotIdEmailBindingsResponses, PostBotsByBotIdHooksTestData, PostBotsByBotIdHooksTestError, PostBotsByBotIdHooksTestErrors, PostBotsByBotIdHooksTestResponse, PostBotsByBotIdHooksTestResponses, PostBotsByBotIdLocalMessagesData, PostBotsByBotIdLocalMessagesError, PostBotsByBotIdLocalMessagesErrors, PostBotsByBotIdLocalMessagesResponse, PostBotsByBotIdLocalMessagesResponses, PostBotsByBotIdMcpByIdOauthAuthorizeData, PostBotsByBotIdMcpByIdOauthAuthorizeError, PostBotsByBotIdMcpByIdOauthAuthorizeErrors, PostBotsByBotIdMcpByIdOauthAuthorizeResponse, PostBotsByBotIdMcpByIdOauthAuthorizeResponses, PostBotsByBotIdMcpByIdOauthDiscoverData, PostBotsByBotIdMcpByIdOauthDiscoverError, PostBotsByBotIdMcpByIdOauthDiscoverErrors, PostBotsByBotIdMcpByIdOauthDiscoverResponse, PostBotsByBotIdMcpByIdOauthDiscoverResponses, PostBotsByBotIdMcpByIdOauthExchangeData, PostBotsByBotIdMcpByIdOauthExchangeError, PostBotsByBotIdMcpByIdOauthExchangeErrors, PostBotsByBotIdMcpByIdOauthExchangeResponse, PostBotsByBotIdMcpByIdOauthExchangeResponses, PostBotsByBotIdMcpByIdProbeData, PostBotsByBotIdMcpByIdProbeError, PostBotsByBotIdMcpByIdProbeErrors, PostBotsByBotIdMcpByIdProbeResponse, PostBotsByBotIdMcpByIdProbeResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdPluginsByIdDisableData, PostBotsByBotIdPluginsByIdDisableError, PostBotsByBotIdPluginsByIdDisableErrors, PostBotsByBotIdPluginsByIdDisableResponse, PostBotsByBotIdPluginsByIdDisableResponses, PostBotsByBotIdPluginsByIdEnableData, PostBotsByBotIdPluginsByIdEnableError, PostBotsByBotIdPluginsByIdEnableErrors, PostBotsByBotIdPluginsByIdEnableResponse, PostBotsByBotIdPluginsByIdEnableResponses, PostBotsByBotIdPluginsByIdOauthAuthorizeData, PostBotsByBotIdPluginsByIdOauthAuthorizeError, PostBotsByBotIdPluginsByIdOauthAuthorizeErrors, PostBotsByBotIdPluginsByIdOauthAuthorizeResponse, PostBotsByBotIdPluginsByIdOauthAuthorizeResponses, PostBotsByBotIdPluginsByIdUninstallData, PostBotsByBotIdPluginsByIdUninstallError, PostBotsByBotIdPluginsByIdUninstallErrors, PostBotsByBotIdPluginsByIdUninstallResponse, PostBotsByBotIdPluginsByIdUninstallResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponse, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSessionsBySessionIdAcpRuntimeData, PostBotsByBotIdSessionsBySessionIdAcpRuntimeError, PostBotsByBotIdSessionsBySessionIdAcpRuntimeErrors, PostBotsByBotIdSessionsBySessionIdAcpRuntimeResponse, PostBotsByBotIdSessionsBySessionIdAcpRuntimeResponses, PostBotsByBotIdSessionsBySessionIdCompactData, PostBotsByBotIdSessionsBySessionIdCompactError, PostBotsByBotIdSessionsBySessionIdCompactErrors, PostBotsByBotIdSessionsBySessionIdCompactResponse, PostBotsByBotIdSessionsBySessionIdCompactResponses, PostBotsByBotIdSessionsData, PostBotsByBotIdSessionsError, PostBotsByBotIdSessionsErrors, PostBotsByBotIdSessionsResponse, PostBotsByBotIdSessionsResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSupermarketInstallPluginData, PostBotsByBotIdSupermarketInstallPluginError, PostBotsByBotIdSupermarketInstallPluginErrors, PostBotsByBotIdSupermarketInstallPluginResponse, PostBotsByBotIdSupermarketInstallPluginResponses, PostBotsByBotIdSupermarketInstallSkillData, PostBotsByBotIdSupermarketInstallSkillError, PostBotsByBotIdSupermarketInstallSkillErrors, PostBotsByBotIdSupermarketInstallSkillResponse, PostBotsByBotIdSupermarketInstallSkillResponses, PostBotsByBotIdToolApprovalsByApprovalIdApproveData, PostBotsByBotIdToolApprovalsByApprovalIdApproveError, PostBotsByBotIdToolApprovalsByApprovalIdApproveErrors, PostBotsByBotIdToolApprovalsByApprovalIdApproveResponse, PostBotsByBotIdToolApprovalsByApprovalIdApproveResponses, PostBotsByBotIdToolApprovalsByApprovalIdRejectData, PostBotsByBotIdToolApprovalsByApprovalIdRejectError, PostBotsByBotIdToolApprovalsByApprovalIdRejectErrors, PostBotsByBotIdToolApprovalsByApprovalIdRejectResponse, PostBotsByBotIdToolApprovalsByApprovalIdRejectResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponse, PostBotsByBotIdToolsResponses, PostBotsByBotIdTtsSynthesizeData, PostBotsByBotIdTtsSynthesizeError, PostBotsByBotIdTtsSynthesizeErrors, PostBotsByBotIdTtsSynthesizeResponse, PostBotsByBotIdTtsSynthesizeResponses, PostBotsByBotIdUserAccessData, PostBotsByBotIdUserAccessError, PostBotsByBotIdUserAccessErrors, PostBotsByBotIdUserAccessResponse, PostBotsByBotIdUserAccessResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponse, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsError, PostBotsErrors, PostBotsResponse, PostBotsResponses, PostEmailMailgunWebhookByConfigIdData, PostEmailMailgunWebhookByConfigIdError, PostEmailMailgunWebhookByConfigIdErrors, PostEmailMailgunWebhookByConfigIdResponse, PostEmailMailgunWebhookByConfigIdResponses, PostEmailProvidersData, PostEmailProvidersError, PostEmailProvidersErrors, PostEmailProvidersResponse, PostEmailProvidersResponses, PostFetchProvidersData, PostFetchProvidersError, PostFetchProvidersErrors, PostFetchProvidersResponse, PostFetchProvidersResponses, PostMemoryProvidersData, PostMemoryProvidersError, PostMemoryProvidersErrors, PostMemoryProvidersResponse, PostMemoryProvidersResponses, PostModelsByIdTestData, PostModelsByIdTestError, PostModelsByIdTestErrors, PostModelsByIdTestResponse, PostModelsByIdTestResponses, PostModelsData, PostModelsError, PostModelsErrors, PostModelsResponse, PostModelsResponses, PostProvidersByIdImportModelsData, PostProvidersByIdImportModelsError, PostProvidersByIdImportModelsErrors, PostProvidersByIdImportModelsResponse, PostProvidersByIdImportModelsResponses, PostProvidersByIdOauthPollData, PostProvidersByIdOauthPollError, PostProvidersByIdOauthPollErrors, PostProvidersByIdOauthPollResponse, PostProvidersByIdOauthPollResponses, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestErrors, PostProvidersByIdTestResponse, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersError, PostProvidersErrors, PostProvidersResponse, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersErrors, PostSearchProvidersResponse, PostSearchProvidersResponses, PostSpeechModelsByIdTestData, PostSpeechModelsByIdTestError, PostSpeechModelsByIdTestErrors, PostSpeechModelsByIdTestResponses, PostSpeechProvidersByIdImportModelsData, PostSpeechProvidersByIdImportModelsError, PostSpeechProvidersByIdImportModelsErrors, PostSpeechProvidersByIdImportModelsResponse, PostSpeechProvidersByIdImportModelsResponses, PostTranscriptionModelsByIdTestData, PostTranscriptionModelsByIdTestError, PostTranscriptionModelsByIdTestErrors, PostTranscriptionModelsByIdTestResponse, PostTranscriptionModelsByIdTestResponses, PostTranscriptionProvidersByIdImportModelsData, PostTranscriptionProvidersByIdImportModelsError, PostTranscriptionProvidersByIdImportModelsErrors, PostTranscriptionProvidersByIdImportModelsResponse, PostTranscriptionProvidersByIdImportModelsResponses, PostUsersData, PostUsersError, PostUsersErrors, PostUsersMeChannelLinksData, PostUsersMeChannelLinksError, PostUsersMeChannelLinksErrors, PostUsersMeChannelLinksResponse, PostUsersMeChannelLinksResponses, PostUsersResponse, PostUsersResponses, ProvidersCountResponse, ProvidersCreateRequest, ProvidersGetResponse, ProvidersImportModelsResponse, ProvidersOAuthAccount, ProvidersOAuthAuthorizeResponse, ProvidersOAuthDeviceStatus, ProvidersOAuthStatus, ProvidersTestResponse, ProvidersTestStatus, ProvidersUpdateRequest, PutBotsByBotIdAclDefaultEffectData, PutBotsByBotIdAclDefaultEffectError, PutBotsByBotIdAclDefaultEffectErrors, PutBotsByBotIdAclDefaultEffectResponses, PutBotsByBotIdAclRulesByRuleIdData, PutBotsByBotIdAclRulesByRuleIdError, PutBotsByBotIdAclRulesByRuleIdErrors, PutBotsByBotIdAclRulesByRuleIdResponse, PutBotsByBotIdAclRulesByRuleIdResponses, PutBotsByBotIdContainerMetricsData, PutBotsByBotIdContainerMetricsError, PutBotsByBotIdContainerMetricsErrors, PutBotsByBotIdContainerMetricsResponse, PutBotsByBotIdContainerMetricsResponses, PutBotsByBotIdEmailBindingsByIdData, PutBotsByBotIdEmailBindingsByIdError, PutBotsByBotIdEmailBindingsByIdErrors, PutBotsByBotIdEmailBindingsByIdResponse, PutBotsByBotIdEmailBindingsByIdResponses, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSettingsResponses, PutBotsByBotIdUserAccessByGrantIdData, PutBotsByBotIdUserAccessByGrantIdError, PutBotsByBotIdUserAccessByGrantIdErrors, PutBotsByBotIdUserAccessByGrantIdResponse, PutBotsByBotIdUserAccessByGrantIdResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponse, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdError, PutBotsByIdErrors, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponse, PutBotsByIdOwnerResponses, PutBotsByIdResponse, PutBotsByIdResponses, PutEmailProvidersByIdData, PutEmailProvidersByIdError, PutEmailProvidersByIdErrors, PutEmailProvidersByIdResponse, PutEmailProvidersByIdResponses, PutFetchProvidersByIdData, PutFetchProvidersByIdError, PutFetchProvidersByIdErrors, PutFetchProvidersByIdResponse, PutFetchProvidersByIdResponses, PutMemoryProvidersByIdData, PutMemoryProvidersByIdError, PutMemoryProvidersByIdErrors, PutMemoryProvidersByIdResponse, PutMemoryProvidersByIdResponses, PutModelsByIdData, PutModelsByIdError, PutModelsByIdErrors, PutModelsByIdResponse, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponse, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdErrors, PutProvidersByIdResponse, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponse, PutSearchProvidersByIdResponses, PutSpeechModelsByIdData, PutSpeechModelsByIdError, PutSpeechModelsByIdErrors, PutSpeechModelsByIdResponse, PutSpeechModelsByIdResponses, PutTranscriptionModelsByIdData, PutTranscriptionModelsByIdError, PutTranscriptionModelsByIdErrors, PutTranscriptionModelsByIdResponse, PutTranscriptionModelsByIdResponses, PutUsersByIdData, PutUsersByIdError, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponse, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponse, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeError, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponse, PutUsersMeResponses, ScheduleCreateRequest, ScheduleListLogsResponse, ScheduleListResponse, ScheduleLog, ScheduleNullableInt, ScheduleSchedule, ScheduleUpdateRequest, SearchprovidersCreateRequest, SearchprovidersGetResponse, SearchprovidersProviderConfigSchema, SearchprovidersProviderFieldSchema, SearchprovidersProviderMeta, SearchprovidersProviderName, SearchprovidersUpdateRequest, SessionSession, SettingsSettings, SettingsToolApprovalConfig, SettingsToolApprovalExecPolicy, SettingsToolApprovalFilePolicy, SettingsUpsertRequest } from './types.gen';
+export type { AccountsAccount, AccountsCreateAccountRequest, AccountsListAccountsResponse, AccountsResetPasswordRequest, AccountsUpdateAccountRequest, AccountsUpdatePasswordRequest, AccountsUpdateProfileMetadata, AccountsUpdateProfileRequest, AclChannelIdentityCandidate, AclChannelIdentityCandidateListResponse, AclCreateRuleRequest, AclDefaultEffectResponse, AclListRulesResponse, AclObservedConversationCandidate, AclObservedConversationCandidateListResponse, AclRule, AclSourceScope, AclUpdateRuleRequest, AcpagentRuntimeStatus, AcpclientModelInfo, AcpclientModelState, AcpprofileManagedField, AcpprofileProfilesResponse, AcpprofilePublicProfile, AdaptersCompactResult, AdaptersDeleteResponse, AdaptersHealthStatus, AdaptersMemoryCompactCapability, AdaptersMemoryItem, AdaptersMemoryStatusResponse, AdaptersMessage, AdaptersProviderCollectionStatus, AdaptersProviderConfigSchema, AdaptersProviderCreateRequest, AdaptersProviderFieldSchema, AdaptersProviderGetResponse, AdaptersProviderMeta, AdaptersProviderStatusResponse, AdaptersProviderType, AdaptersProviderUpdateRequest, AdaptersRebuildResult, AdaptersSearchResponse, AdaptersUsageResponse, AudioConfigSchema, AudioFieldSchema, AudioImportModelsResponse, AudioModelCapabilities, AudioModelInfo, AudioParamConstraint, AudioProviderMetaResponse, AudioSpeechModelResponse, AudioSpeechProviderResponse, AudioTestSynthesizeRequest, AudioTestTranscriptionResponse, AudioTranscriptionModelResponse, AudioTranscriptionWord, AudioUpdateSpeechModelRequest, AudioVoiceInfo, BotbackupExportRequest, BotbackupImportMode, BotbackupImportResult, BotbackupManifest, BotbackupManifestEntry, BotbackupManifestOptions, BotbackupPreviewResult, BotbackupProfilePreview, BotbackupRestorePlan, BotbackupSection, BotbackupSectionSummary, BotbackupSummaryResult, BotsBot, BotsBotCheck, BotsCreateBotRequest, BotsCreateUserGrantRequest, BotsListBotsResponse, BotsListChecksResponse, BotsNameAvailability, BotsTransferBotRequest, BotsUpdateBotRequest, BotsUpdateUserGrantRequest, BotsUserGrant, ChannelaccessBinding, ChannelaccessIssueLinkCodeRequest, ChannelaccessLinkCode, ChannelaccessListBindingsResponse, ChannelaccessListManagersResponse, ChannelaccessManager, ChannelaccessSetManagerRequest, ChannelAction, ChannelAttachment, ChannelAttachmentType, ChannelChannelCapabilities, ChannelChannelConfig, ChannelChannelIdentityBinding, ChannelChannelType, ChannelConfigSchema, ChannelFieldSchema, ChannelFieldType, ChannelForwardRef, ChannelMessage, ChannelMessageFormat, ChannelMessagePart, ChannelMessagePartType, ChannelMessageTextStyle, ChannelReplyRef, ChannelSendRequest, ChannelTargetHint, ChannelTargetSpec, ChannelThreadRef, ChannelUpdateChannelStatusRequest, ChannelUpsertChannelIdentityConfigRequest, ChannelUpsertConfigRequest, ClientOptions, CompactionListLogsResponse, CompactionLog, DeleteBotsByBotIdAclRulesByRuleIdData, DeleteBotsByBotIdAclRulesByRuleIdError, DeleteBotsByBotIdAclRulesByRuleIdErrors, DeleteBotsByBotIdAclRulesByRuleIdResponses, DeleteBotsByBotIdAcpRuntimesByRuntimeIdData, DeleteBotsByBotIdAcpRuntimesByRuntimeIdError, DeleteBotsByBotIdAcpRuntimesByRuntimeIdErrors, DeleteBotsByBotIdAcpRuntimesByRuntimeIdResponses, DeleteBotsByBotIdChannelManagersByChannelIdentityIdData, DeleteBotsByBotIdChannelManagersByChannelIdentityIdError, DeleteBotsByBotIdChannelManagersByChannelIdentityIdErrors, DeleteBotsByBotIdChannelManagersByChannelIdentityIdResponses, DeleteBotsByBotIdCompactionLogsData, DeleteBotsByBotIdCompactionLogsError, DeleteBotsByBotIdCompactionLogsErrors, DeleteBotsByBotIdCompactionLogsResponses, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdData, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdError, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdErrors, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdResponses, DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdData, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdError, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdErrors, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdResponses, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdEmailBindingsByIdData, DeleteBotsByBotIdEmailBindingsByIdError, DeleteBotsByBotIdEmailBindingsByIdErrors, DeleteBotsByBotIdEmailBindingsByIdResponses, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsError, DeleteBotsByBotIdHeartbeatLogsErrors, DeleteBotsByBotIdHeartbeatLogsResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdOauthTokenData, DeleteBotsByBotIdMcpByIdOauthTokenError, DeleteBotsByBotIdMcpByIdOauthTokenErrors, DeleteBotsByBotIdMcpByIdOauthTokenResponses, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesError, DeleteBotsByBotIdMessagesErrors, DeleteBotsByBotIdMessagesResponses, DeleteBotsByBotIdPluginsByIdData, DeleteBotsByBotIdPluginsByIdError, DeleteBotsByBotIdPluginsByIdErrors, DeleteBotsByBotIdPluginsByIdResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdScheduleLogsData, DeleteBotsByBotIdScheduleLogsError, DeleteBotsByBotIdScheduleLogsErrors, DeleteBotsByBotIdScheduleLogsResponses, DeleteBotsByBotIdSessionsBySessionIdData, DeleteBotsByBotIdSessionsBySessionIdError, DeleteBotsByBotIdSessionsBySessionIdErrors, DeleteBotsByBotIdSessionsBySessionIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdUserAccessByGrantIdData, DeleteBotsByBotIdUserAccessByGrantIdError, DeleteBotsByBotIdUserAccessByGrantIdErrors, DeleteBotsByBotIdUserAccessByGrantIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdErrors, DeleteBotsByIdResponse, DeleteBotsByIdResponses, DeleteEmailProvidersByIdData, DeleteEmailProvidersByIdError, DeleteEmailProvidersByIdErrors, DeleteEmailProvidersByIdOauthTokenData, DeleteEmailProvidersByIdOauthTokenError, DeleteEmailProvidersByIdOauthTokenErrors, DeleteEmailProvidersByIdOauthTokenResponses, DeleteEmailProvidersByIdResponses, DeleteFetchProvidersByIdData, DeleteFetchProvidersByIdError, DeleteFetchProvidersByIdErrors, DeleteFetchProvidersByIdResponses, DeleteMemoryProvidersByIdData, DeleteMemoryProvidersByIdError, DeleteMemoryProvidersByIdErrors, DeleteMemoryProvidersByIdResponses, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteProvidersByIdErrors, DeleteProvidersByIdOauthTokenData, DeleteProvidersByIdOauthTokenError, DeleteProvidersByIdOauthTokenErrors, DeleteProvidersByIdOauthTokenResponses, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, DeleteUsersByIdData, DeleteUsersByIdError, DeleteUsersByIdErrors, DeleteUsersByIdResponses, DeleteUsersMeChannelIdentitiesByChannelIdentityIdData, DeleteUsersMeChannelIdentitiesByChannelIdentityIdError, DeleteUsersMeChannelIdentitiesByChannelIdentityIdErrors, DeleteUsersMeChannelIdentitiesByChannelIdentityIdResponses, DisplaySessionInfo, EmailBindingResponse, EmailConfigSchema, EmailCreateBindingRequest, EmailCreateProviderRequest, EmailFieldSchema, EmailOutboxItemResponse, EmailProviderMeta, EmailProviderResponse, EmailUpdateBindingRequest, EmailUpdateProviderRequest, FetchprovidersCreateRequest, FetchprovidersGetResponse, FetchprovidersProviderConfigSchema, FetchprovidersProviderFieldSchema, FetchprovidersProviderMeta, FetchprovidersProviderName, FetchprovidersUpdateRequest, GetAcpProfilesData, GetAcpProfilesResponse, GetAcpProfilesResponses, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsData, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsError, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsErrors, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsResponse, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsResponses, GetBotsByBotIdAclChannelIdentitiesData, GetBotsByBotIdAclChannelIdentitiesError, GetBotsByBotIdAclChannelIdentitiesErrors, GetBotsByBotIdAclChannelIdentitiesResponse, GetBotsByBotIdAclChannelIdentitiesResponses, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsData, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsError, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsErrors, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsResponse, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsResponses, GetBotsByBotIdAclDefaultEffectData, GetBotsByBotIdAclDefaultEffectError, GetBotsByBotIdAclDefaultEffectErrors, GetBotsByBotIdAclDefaultEffectResponse, GetBotsByBotIdAclDefaultEffectResponses, GetBotsByBotIdAclRulesData, GetBotsByBotIdAclRulesError, GetBotsByBotIdAclRulesErrors, GetBotsByBotIdAclRulesResponse, GetBotsByBotIdAclRulesResponses, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeData, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeError, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeErrors, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeResponse, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeResponses, GetBotsByBotIdAcpClaudeCodeOauthStatusData, GetBotsByBotIdAcpClaudeCodeOauthStatusError, GetBotsByBotIdAcpClaudeCodeOauthStatusErrors, GetBotsByBotIdAcpClaudeCodeOauthStatusResponse, GetBotsByBotIdAcpClaudeCodeOauthStatusResponses, GetBotsByBotIdAcpRuntimesByRuntimeIdData, GetBotsByBotIdAcpRuntimesByRuntimeIdError, GetBotsByBotIdAcpRuntimesByRuntimeIdErrors, GetBotsByBotIdAcpRuntimesByRuntimeIdResponse, GetBotsByBotIdAcpRuntimesByRuntimeIdResponses, GetBotsByBotIdBackupSummaryData, GetBotsByBotIdBackupSummaryError, GetBotsByBotIdBackupSummaryErrors, GetBotsByBotIdBackupSummaryResponse, GetBotsByBotIdBackupSummaryResponses, GetBotsByBotIdChannelManagersData, GetBotsByBotIdChannelManagersError, GetBotsByBotIdChannelManagersErrors, GetBotsByBotIdChannelManagersResponse, GetBotsByBotIdChannelManagersResponses, GetBotsByBotIdCompactionLogsData, GetBotsByBotIdCompactionLogsError, GetBotsByBotIdCompactionLogsErrors, GetBotsByBotIdCompactionLogsResponse, GetBotsByBotIdCompactionLogsResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerDisplayData, GetBotsByBotIdContainerDisplayError, GetBotsByBotIdContainerDisplayErrors, GetBotsByBotIdContainerDisplayResponse, GetBotsByBotIdContainerDisplayResponses, GetBotsByBotIdContainerDisplaySessionsData, GetBotsByBotIdContainerDisplaySessionsError, GetBotsByBotIdContainerDisplaySessionsErrors, GetBotsByBotIdContainerDisplaySessionsResponse, GetBotsByBotIdContainerDisplaySessionsResponses, GetBotsByBotIdContainerError, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsDownloadError, GetBotsByBotIdContainerFsDownloadErrors, GetBotsByBotIdContainerFsDownloadResponses, GetBotsByBotIdContainerFsError, GetBotsByBotIdContainerFsErrors, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsListError, GetBotsByBotIdContainerFsListErrors, GetBotsByBotIdContainerFsListResponse, GetBotsByBotIdContainerFsListResponses, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerFsReadError, GetBotsByBotIdContainerFsReadErrors, GetBotsByBotIdContainerFsReadResponse, GetBotsByBotIdContainerFsReadResponses, GetBotsByBotIdContainerFsResponse, GetBotsByBotIdContainerFsResponses, GetBotsByBotIdContainerMetricsData, GetBotsByBotIdContainerMetricsError, GetBotsByBotIdContainerMetricsErrors, GetBotsByBotIdContainerMetricsResponse, GetBotsByBotIdContainerMetricsResponses, GetBotsByBotIdContainerResponse, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsError, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponse, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsError, GetBotsByBotIdContainerSnapshotsErrors, GetBotsByBotIdContainerSnapshotsResponse, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdContainerTerminalData, GetBotsByBotIdContainerTerminalError, GetBotsByBotIdContainerTerminalErrors, GetBotsByBotIdContainerTerminalResponse, GetBotsByBotIdContainerTerminalResponses, GetBotsByBotIdContainerTerminalWsData, GetBotsByBotIdContainerTerminalWsError, GetBotsByBotIdContainerTerminalWsErrors, GetBotsByBotIdEmailBindingsData, GetBotsByBotIdEmailBindingsError, GetBotsByBotIdEmailBindingsErrors, GetBotsByBotIdEmailBindingsResponse, GetBotsByBotIdEmailBindingsResponses, GetBotsByBotIdEmailOutboxByIdData, GetBotsByBotIdEmailOutboxByIdError, GetBotsByBotIdEmailOutboxByIdErrors, GetBotsByBotIdEmailOutboxByIdResponse, GetBotsByBotIdEmailOutboxByIdResponses, GetBotsByBotIdEmailOutboxData, GetBotsByBotIdEmailOutboxError, GetBotsByBotIdEmailOutboxErrors, GetBotsByBotIdEmailOutboxResponse, GetBotsByBotIdEmailOutboxResponses, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdHeartbeatLogsError, GetBotsByBotIdHeartbeatLogsErrors, GetBotsByBotIdHeartbeatLogsResponse, GetBotsByBotIdHeartbeatLogsResponses, GetBotsByBotIdHooksEventsData, GetBotsByBotIdHooksEventsError, GetBotsByBotIdHooksEventsErrors, GetBotsByBotIdHooksEventsResponse, GetBotsByBotIdHooksEventsResponses, GetBotsByBotIdLocalStreamData, GetBotsByBotIdLocalStreamError, GetBotsByBotIdLocalStreamErrors, GetBotsByBotIdLocalStreamResponse, GetBotsByBotIdLocalStreamResponses, GetBotsByBotIdLocalWsData, GetBotsByBotIdLocalWsError, GetBotsByBotIdLocalWsErrors, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdError, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdOauthStatusData, GetBotsByBotIdMcpByIdOauthStatusError, GetBotsByBotIdMcpByIdOauthStatusErrors, GetBotsByBotIdMcpByIdOauthStatusResponse, GetBotsByBotIdMcpByIdOauthStatusResponses, GetBotsByBotIdMcpByIdResponse, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpError, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportError, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponse, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponse, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryError, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponse, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryStatusData, GetBotsByBotIdMemoryStatusError, GetBotsByBotIdMemoryStatusErrors, GetBotsByBotIdMemoryStatusResponse, GetBotsByBotIdMemoryStatusResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageError, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponse, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesError, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesLocateData, GetBotsByBotIdMessagesLocateError, GetBotsByBotIdMessagesLocateErrors, GetBotsByBotIdMessagesLocateResponse, GetBotsByBotIdMessagesLocateResponses, GetBotsByBotIdMessagesResponse, GetBotsByBotIdMessagesResponses, GetBotsByBotIdPluginsByIdData, GetBotsByBotIdPluginsByIdError, GetBotsByBotIdPluginsByIdErrors, GetBotsByBotIdPluginsByIdOauthStatusData, GetBotsByBotIdPluginsByIdOauthStatusError, GetBotsByBotIdPluginsByIdOauthStatusErrors, GetBotsByBotIdPluginsByIdOauthStatusResponse, GetBotsByBotIdPluginsByIdOauthStatusResponses, GetBotsByBotIdPluginsByIdResponse, GetBotsByBotIdPluginsByIdResponses, GetBotsByBotIdPluginsData, GetBotsByBotIdPluginsError, GetBotsByBotIdPluginsErrors, GetBotsByBotIdPluginsResponse, GetBotsByBotIdPluginsResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdError, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdLogsData, GetBotsByBotIdScheduleByIdLogsError, GetBotsByBotIdScheduleByIdLogsErrors, GetBotsByBotIdScheduleByIdLogsResponse, GetBotsByBotIdScheduleByIdLogsResponses, GetBotsByBotIdScheduleByIdResponse, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleError, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleLogsData, GetBotsByBotIdScheduleLogsError, GetBotsByBotIdScheduleLogsErrors, GetBotsByBotIdScheduleLogsResponse, GetBotsByBotIdScheduleLogsResponses, GetBotsByBotIdScheduleResponse, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSessionsBySessionIdAcpRuntimeData, GetBotsByBotIdSessionsBySessionIdAcpRuntimeError, GetBotsByBotIdSessionsBySessionIdAcpRuntimeErrors, GetBotsByBotIdSessionsBySessionIdAcpRuntimeResponse, GetBotsByBotIdSessionsBySessionIdAcpRuntimeResponses, GetBotsByBotIdSessionsBySessionIdData, GetBotsByBotIdSessionsBySessionIdError, GetBotsByBotIdSessionsBySessionIdErrors, GetBotsByBotIdSessionsBySessionIdMessagesEventsData, GetBotsByBotIdSessionsBySessionIdMessagesEventsError, GetBotsByBotIdSessionsBySessionIdMessagesEventsErrors, GetBotsByBotIdSessionsBySessionIdMessagesEventsResponse, GetBotsByBotIdSessionsBySessionIdMessagesEventsResponses, GetBotsByBotIdSessionsBySessionIdResponse, GetBotsByBotIdSessionsBySessionIdResponses, GetBotsByBotIdSessionsBySessionIdStatusData, GetBotsByBotIdSessionsBySessionIdStatusError, GetBotsByBotIdSessionsBySessionIdStatusErrors, GetBotsByBotIdSessionsBySessionIdStatusResponse, GetBotsByBotIdSessionsBySessionIdStatusResponses, GetBotsByBotIdSessionsData, GetBotsByBotIdSessionsError, GetBotsByBotIdSessionsErrors, GetBotsByBotIdSessionsEventsData, GetBotsByBotIdSessionsEventsError, GetBotsByBotIdSessionsEventsErrors, GetBotsByBotIdSessionsEventsResponse, GetBotsByBotIdSessionsEventsResponses, GetBotsByBotIdSessionsResponse, GetBotsByBotIdSessionsResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsError, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponse, GetBotsByBotIdSettingsResponses, GetBotsByBotIdTokenUsageData, GetBotsByBotIdTokenUsageError, GetBotsByBotIdTokenUsageErrors, GetBotsByBotIdTokenUsageRecordsData, GetBotsByBotIdTokenUsageRecordsError, GetBotsByBotIdTokenUsageRecordsErrors, GetBotsByBotIdTokenUsageRecordsResponse, GetBotsByBotIdTokenUsageRecordsResponses, GetBotsByBotIdTokenUsageResponse, GetBotsByBotIdTokenUsageResponses, GetBotsByBotIdUserAccessCandidatesData, GetBotsByBotIdUserAccessCandidatesError, GetBotsByBotIdUserAccessCandidatesErrors, GetBotsByBotIdUserAccessCandidatesResponse, GetBotsByBotIdUserAccessCandidatesResponses, GetBotsByBotIdUserAccessData, GetBotsByBotIdUserAccessError, GetBotsByBotIdUserAccessErrors, GetBotsByBotIdUserAccessResponse, GetBotsByBotIdUserAccessResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformError, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponse, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksError, GetBotsByIdChecksErrors, GetBotsByIdChecksResponse, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdError, GetBotsByIdErrors, GetBotsByIdResponse, GetBotsByIdResponses, GetBotsData, GetBotsError, GetBotsErrors, GetBotsNameAvailabilityData, GetBotsNameAvailabilityError, GetBotsNameAvailabilityErrors, GetBotsNameAvailabilityResponse, GetBotsNameAvailabilityResponses, GetBotsResponse, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformError, GetChannelsByPlatformErrors, GetChannelsByPlatformResponse, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsError, GetChannelsErrors, GetChannelsResponse, GetChannelsResponses, GetEmailOauthCallbackData, GetEmailOauthCallbackError, GetEmailOauthCallbackErrors, GetEmailOauthCallbackResponse, GetEmailOauthCallbackResponses, GetEmailProvidersByIdData, GetEmailProvidersByIdError, GetEmailProvidersByIdErrors, GetEmailProvidersByIdOauthAuthorizeData, GetEmailProvidersByIdOauthAuthorizeError, GetEmailProvidersByIdOauthAuthorizeErrors, GetEmailProvidersByIdOauthAuthorizeResponse, GetEmailProvidersByIdOauthAuthorizeResponses, GetEmailProvidersByIdOauthStatusData, GetEmailProvidersByIdOauthStatusError, GetEmailProvidersByIdOauthStatusErrors, GetEmailProvidersByIdOauthStatusResponse, GetEmailProvidersByIdOauthStatusResponses, GetEmailProvidersByIdResponse, GetEmailProvidersByIdResponses, GetEmailProvidersData, GetEmailProvidersError, GetEmailProvidersErrors, GetEmailProvidersMetaData, GetEmailProvidersMetaResponse, GetEmailProvidersMetaResponses, GetEmailProvidersResponse, GetEmailProvidersResponses, GetFetchProvidersByIdData, GetFetchProvidersByIdError, GetFetchProvidersByIdErrors, GetFetchProvidersByIdResponse, GetFetchProvidersByIdResponses, GetFetchProvidersData, GetFetchProvidersError, GetFetchProvidersErrors, GetFetchProvidersMetaData, GetFetchProvidersMetaResponse, GetFetchProvidersMetaResponses, GetFetchProvidersResponse, GetFetchProvidersResponses, GetMemoryProvidersByIdData, GetMemoryProvidersByIdError, GetMemoryProvidersByIdErrors, GetMemoryProvidersByIdResponse, GetMemoryProvidersByIdResponses, GetMemoryProvidersByIdStatusData, GetMemoryProvidersByIdStatusError, GetMemoryProvidersByIdStatusErrors, GetMemoryProvidersByIdStatusResponse, GetMemoryProvidersByIdStatusResponses, GetMemoryProvidersData, GetMemoryProvidersError, GetMemoryProvidersErrors, GetMemoryProvidersMetaData, GetMemoryProvidersMetaResponse, GetMemoryProvidersMetaResponses, GetMemoryProvidersResponse, GetMemoryProvidersResponses, GetModelsByIdData, GetModelsByIdError, GetModelsByIdErrors, GetModelsByIdResponse, GetModelsByIdResponses, GetModelsCountData, GetModelsCountError, GetModelsCountErrors, GetModelsCountResponse, GetModelsCountResponses, GetModelsData, GetModelsError, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdError, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponse, GetModelsModelByModelIdResponses, GetModelsResponse, GetModelsResponses, GetOauthMcpCallbackData, GetOauthMcpCallbackError, GetOauthMcpCallbackErrors, GetOauthMcpCallbackResponse, GetOauthMcpCallbackResponses, GetPingData, GetPingResponse, GetPingResponses, GetProvidersByIdData, GetProvidersByIdError, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsError, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponse, GetProvidersByIdModelsResponses, GetProvidersByIdOauthAuthorizeData, GetProvidersByIdOauthAuthorizeError, GetProvidersByIdOauthAuthorizeErrors, GetProvidersByIdOauthAuthorizeResponse, GetProvidersByIdOauthAuthorizeResponses, GetProvidersByIdOauthStatusData, GetProvidersByIdOauthStatusError, GetProvidersByIdOauthStatusErrors, GetProvidersByIdOauthStatusResponse, GetProvidersByIdOauthStatusResponses, GetProvidersByIdResponse, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountError, GetProvidersCountErrors, GetProvidersCountResponse, GetProvidersCountResponses, GetProvidersData, GetProvidersError, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameError, GetProvidersNameByNameErrors, GetProvidersNameByNameResponse, GetProvidersNameByNameResponses, GetProvidersOauthCallbackData, GetProvidersOauthCallbackError, GetProvidersOauthCallbackErrors, GetProvidersOauthCallbackResponse, GetProvidersOauthCallbackResponses, GetProvidersResponse, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdError, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponse, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersError, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponse, GetSearchProvidersMetaResponses, GetSearchProvidersResponse, GetSearchProvidersResponses, GetSpeechModelsByIdCapabilitiesData, GetSpeechModelsByIdCapabilitiesError, GetSpeechModelsByIdCapabilitiesErrors, GetSpeechModelsByIdCapabilitiesResponse, GetSpeechModelsByIdCapabilitiesResponses, GetSpeechModelsByIdData, GetSpeechModelsByIdError, GetSpeechModelsByIdErrors, GetSpeechModelsByIdResponse, GetSpeechModelsByIdResponses, GetSpeechModelsData, GetSpeechModelsError, GetSpeechModelsErrors, GetSpeechModelsResponse, GetSpeechModelsResponses, GetSpeechProvidersByIdData, GetSpeechProvidersByIdError, GetSpeechProvidersByIdErrors, GetSpeechProvidersByIdModelsData, GetSpeechProvidersByIdModelsError, GetSpeechProvidersByIdModelsErrors, GetSpeechProvidersByIdModelsResponse, GetSpeechProvidersByIdModelsResponses, GetSpeechProvidersByIdResponse, GetSpeechProvidersByIdResponses, GetSpeechProvidersData, GetSpeechProvidersError, GetSpeechProvidersErrors, GetSpeechProvidersMetaData, GetSpeechProvidersMetaResponse, GetSpeechProvidersMetaResponses, GetSpeechProvidersResponse, GetSpeechProvidersResponses, GetSupermarketPluginsByIdData, GetSupermarketPluginsByIdError, GetSupermarketPluginsByIdErrors, GetSupermarketPluginsByIdResponse, GetSupermarketPluginsByIdResponses, GetSupermarketPluginsData, GetSupermarketPluginsError, GetSupermarketPluginsErrors, GetSupermarketPluginsResponse, GetSupermarketPluginsResponses, GetSupermarketSkillsByIdData, GetSupermarketSkillsByIdError, GetSupermarketSkillsByIdErrors, GetSupermarketSkillsByIdResponse, GetSupermarketSkillsByIdResponses, GetSupermarketSkillsData, GetSupermarketSkillsError, GetSupermarketSkillsErrors, GetSupermarketSkillsResponse, GetSupermarketSkillsResponses, GetSupermarketTagsData, GetSupermarketTagsError, GetSupermarketTagsErrors, GetSupermarketTagsResponse, GetSupermarketTagsResponses, GetTranscriptionModelsByIdCapabilitiesData, GetTranscriptionModelsByIdCapabilitiesError, GetTranscriptionModelsByIdCapabilitiesErrors, GetTranscriptionModelsByIdCapabilitiesResponse, GetTranscriptionModelsByIdCapabilitiesResponses, GetTranscriptionModelsByIdData, GetTranscriptionModelsByIdError, GetTranscriptionModelsByIdErrors, GetTranscriptionModelsByIdResponse, GetTranscriptionModelsByIdResponses, GetTranscriptionModelsData, GetTranscriptionModelsError, GetTranscriptionModelsErrors, GetTranscriptionModelsResponse, GetTranscriptionModelsResponses, GetTranscriptionProvidersByIdData, GetTranscriptionProvidersByIdError, GetTranscriptionProvidersByIdErrors, GetTranscriptionProvidersByIdModelsData, GetTranscriptionProvidersByIdModelsError, GetTranscriptionProvidersByIdModelsErrors, GetTranscriptionProvidersByIdModelsResponse, GetTranscriptionProvidersByIdModelsResponses, GetTranscriptionProvidersByIdResponse, GetTranscriptionProvidersByIdResponses, GetTranscriptionProvidersData, GetTranscriptionProvidersError, GetTranscriptionProvidersErrors, GetTranscriptionProvidersMetaData, GetTranscriptionProvidersMetaResponse, GetTranscriptionProvidersMetaResponses, GetTranscriptionProvidersResponse, GetTranscriptionProvidersResponses, GetUsersByIdData, GetUsersByIdError, GetUsersByIdErrors, GetUsersByIdResponse, GetUsersByIdResponses, GetUsersData, GetUsersError, GetUsersErrors, GetUsersMeChannelIdentitiesData, GetUsersMeChannelIdentitiesError, GetUsersMeChannelIdentitiesErrors, GetUsersMeChannelIdentitiesResponse, GetUsersMeChannelIdentitiesResponses, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformError, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponse, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeError, GetUsersMeErrors, GetUsersMeResponse, GetUsersMeResponses, GetUsersResponse, GetUsersResponses, GithubComMemohaiMemohInternalMcpConnection, HandlersAcpClaudeCodeOAuthAuthorizeResponse, HandlersAcpClaudeCodeOAuthExchangeRequest, HandlersAcpClaudeCodeOAuthStatus, HandlersAcpRuntimeCreateRequest, HandlersAcpRuntimeModelRequest, HandlersBatchDeleteRequest, HandlersBotUserCandidate, HandlersBotUserCandidateListResponse, HandlersBotUserGrantListResponse, HandlersBrowserSessionCreateRequest, HandlersBrowserSessionCreateResponse, HandlersBrowserSessionKeepAliveResponse, HandlersCacheStats, HandlersChannelMeta, HandlersContainerCpuMetricsResponse, HandlersContainerGpuRequest, HandlersContainerMemoryMetricsResponse, HandlersContainerMetricsPayloadResponse, HandlersContainerMetricsStatusResponse, HandlersContainerResourceLimitCapabilitiesResponse, HandlersContainerResourceLimitCapabilityResponse, HandlersContainerResourceLimitObservedResponse, HandlersContainerResourceLimitValuesResponse, HandlersContainerStorageMetricsResponse, HandlersContextUsage, HandlersCreateContainerRequest, HandlersCreateContainerResponse, HandlersCreateSessionRequest, HandlersCreateSnapshotRequest, HandlersCreateSnapshotResponse, HandlersDailyTokenUsage, HandlersDisplayInfoResponse, HandlersDisplaySessionListResponse, HandlersDisplayWebRtcOfferRequest, HandlersDisplayWebRtcOfferResponse, HandlersEmailOAuthStatusResponse, HandlersErrorResponse, HandlersFsArchiveRequest, HandlersFsDeleteRequest, HandlersFsExtractRequest, HandlersFsExtractResponse, HandlersFsFileInfo, HandlersFsListResponse, HandlersFsMkdirRequest, HandlersFsOpResponse, HandlersFsReadResponse, HandlersFsRenameRequest, HandlersFsUploadResponse, HandlersFsWriteRequest, HandlersGetContainerMetricsResponse, HandlersGetContainerResourceLimitsResponse, HandlersGetContainerResponse, HandlersHookEventInfo, HandlersHooksEventsResponse, HandlersHookTestRequest, HandlersHookTestResponse, HandlersInstallPluginRequest, HandlersInstallSkillRequest, HandlersListSessionsResponse, HandlersListSnapshotsResponse, HandlersLocalChannelMessageRequest, HandlersLoginRequest, HandlersLoginResponse, HandlersMcpStdioRequest, HandlersMcpStdioResponse, HandlersMemoryAddPayload, HandlersMemoryCompactPayload, HandlersMemoryDeletePayload, HandlersMemorySearchPayload, HandlersModelTokenUsage, HandlersOauthAuthorizeRequest, HandlersOauthDiscoverRequest, HandlersOauthExchangeRequest, HandlersPingResponse, HandlersProbeResponse, HandlersRefreshResponse, HandlersRollbackRequest, HandlersSessionInfoResponse, HandlersSkillItem, HandlersSkillsActionRequest, HandlersSkillsDeleteRequest, HandlersSkillsOpResponse, HandlersSkillsResponse, HandlersSkillsUpsertRequest, HandlersSnapshotInfo, HandlersSupermarketAuthor, HandlersSupermarketPluginListResponse, HandlersSupermarketSkillEntry, HandlersSupermarketSkillListResponse, HandlersSupermarketSkillMetadata, HandlersSupermarketTagsResponse, HandlersSynthesizeRequest, HandlersSynthesizeResponse, HandlersTerminalInfoResponse, HandlersTokenUsageRecord, HandlersTokenUsageRecordsResponse, HandlersTokenUsageResponse, HandlersToolApprovalDecisionRequest, HandlersTriggerCompactResponse, HandlersUpdateContainerMetricsRequest, HandlersUpdateContainerResourceLimitsRequest, HandlersUpdateSessionRequest, HeartbeatListLogsResponse, HeartbeatLog, HooksActionResult, HooksResult, HooksToolPayload, McpAuthorizeResult, McpDiscoveryResult, McpExportResponse, McpImportRequest, McpListResponse, McpMcpServerEntry, McpOAuthStatus, McpToolDescriptor, McpUpsertRequest, MessageMessage, MessageMessageAsset, ModelsAddRequest, ModelsAddResponse, ModelsCountResponse, ModelsGetResponse, ModelsModelConfig, ModelsModelType, ModelsTestResponse, ModelsTestStatus, ModelsUpdateRequest, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelData, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelError, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelErrors, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelResponse, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelResponses, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelData, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelError, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelErrors, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelResponse, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelResponses, PatchBotsByBotIdSessionsBySessionIdData, PatchBotsByBotIdSessionsBySessionIdError, PatchBotsByBotIdSessionsBySessionIdErrors, PatchBotsByBotIdSessionsBySessionIdResponse, PatchBotsByBotIdSessionsBySessionIdResponses, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponse, PatchBotsByIdChannelByPlatformStatusResponses, PluginsAuthor, PluginsAuthRequirement, PluginsConfigVar, PluginsIcon, PluginsInstallation, PluginsListResponse, PluginsManifest, PluginsMcpResource, PluginsOAuthAuthorizeRequest, PluginsResource, PluginsSkillEntry, PluginsSkillResource, PostAuthLoginData, PostAuthLoginError, PostAuthLoginErrors, PostAuthLoginResponse, PostAuthLoginResponses, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshErrors, PostAuthRefreshResponse, PostAuthRefreshResponses, PostBotsBackupImportData, PostBotsBackupImportError, PostBotsBackupImportErrors, PostBotsBackupImportPreviewData, PostBotsBackupImportPreviewError, PostBotsBackupImportPreviewErrors, PostBotsBackupImportPreviewResponse, PostBotsBackupImportPreviewResponses, PostBotsBackupImportResponse, PostBotsBackupImportResponses, PostBotsByBotIdAclRulesData, PostBotsByBotIdAclRulesError, PostBotsByBotIdAclRulesErrors, PostBotsByBotIdAclRulesResponse, PostBotsByBotIdAclRulesResponses, PostBotsByBotIdAcpClaudeCodeOauthExchangeData, PostBotsByBotIdAcpClaudeCodeOauthExchangeError, PostBotsByBotIdAcpClaudeCodeOauthExchangeErrors, PostBotsByBotIdAcpClaudeCodeOauthExchangeResponse, PostBotsByBotIdAcpClaudeCodeOauthExchangeResponses, PostBotsByBotIdAcpRuntimesData, PostBotsByBotIdAcpRuntimesError, PostBotsByBotIdAcpRuntimesErrors, PostBotsByBotIdAcpRuntimesResponse, PostBotsByBotIdAcpRuntimesResponses, PostBotsByBotIdBackupExportData, PostBotsByBotIdBackupExportError, PostBotsByBotIdBackupExportErrors, PostBotsByBotIdBackupExportResponses, PostBotsByBotIdChannelManagersData, PostBotsByBotIdChannelManagersError, PostBotsByBotIdChannelManagersErrors, PostBotsByBotIdChannelManagersResponses, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveData, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveError, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveErrors, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveResponse, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveResponses, PostBotsByBotIdContainerBrowserSessionsData, PostBotsByBotIdContainerBrowserSessionsError, PostBotsByBotIdContainerBrowserSessionsErrors, PostBotsByBotIdContainerBrowserSessionsResponse, PostBotsByBotIdContainerBrowserSessionsResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerDataRestoreData, PostBotsByBotIdContainerDataRestoreError, PostBotsByBotIdContainerDataRestoreErrors, PostBotsByBotIdContainerDataRestoreResponse, PostBotsByBotIdContainerDataRestoreResponses, PostBotsByBotIdContainerDisplayPrepareData, PostBotsByBotIdContainerDisplayPrepareError, PostBotsByBotIdContainerDisplayPrepareErrors, PostBotsByBotIdContainerDisplayPrepareResponse, PostBotsByBotIdContainerDisplayPrepareResponses, PostBotsByBotIdContainerDisplayWebrtcOfferData, PostBotsByBotIdContainerDisplayWebrtcOfferError, PostBotsByBotIdContainerDisplayWebrtcOfferErrors, PostBotsByBotIdContainerDisplayWebrtcOfferResponse, PostBotsByBotIdContainerDisplayWebrtcOfferResponses, PostBotsByBotIdContainerError, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerFsArchiveData, PostBotsByBotIdContainerFsArchiveError, PostBotsByBotIdContainerFsArchiveErrors, PostBotsByBotIdContainerFsArchiveResponses, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteError, PostBotsByBotIdContainerFsDeleteErrors, PostBotsByBotIdContainerFsDeleteResponse, PostBotsByBotIdContainerFsDeleteResponses, PostBotsByBotIdContainerFsExtractData, PostBotsByBotIdContainerFsExtractError, PostBotsByBotIdContainerFsExtractErrors, PostBotsByBotIdContainerFsExtractResponse, PostBotsByBotIdContainerFsExtractResponses, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirError, PostBotsByBotIdContainerFsMkdirErrors, PostBotsByBotIdContainerFsMkdirResponse, PostBotsByBotIdContainerFsMkdirResponses, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameError, PostBotsByBotIdContainerFsRenameErrors, PostBotsByBotIdContainerFsRenameResponse, PostBotsByBotIdContainerFsRenameResponses, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadError, PostBotsByBotIdContainerFsUploadErrors, PostBotsByBotIdContainerFsUploadResponse, PostBotsByBotIdContainerFsUploadResponses, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteError, PostBotsByBotIdContainerFsWriteErrors, PostBotsByBotIdContainerFsWriteResponse, PostBotsByBotIdContainerFsWriteResponses, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsActionsData, PostBotsByBotIdContainerSkillsActionsError, PostBotsByBotIdContainerSkillsActionsErrors, PostBotsByBotIdContainerSkillsActionsResponse, PostBotsByBotIdContainerSkillsActionsResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerSnapshotsRollbackData, PostBotsByBotIdContainerSnapshotsRollbackError, PostBotsByBotIdContainerSnapshotsRollbackErrors, PostBotsByBotIdContainerSnapshotsRollbackResponse, PostBotsByBotIdContainerSnapshotsRollbackResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdEmailBindingsData, PostBotsByBotIdEmailBindingsError, PostBotsByBotIdEmailBindingsErrors, PostBotsByBotIdEmailBindingsResponse, PostBotsByBotIdEmailBindingsResponses, PostBotsByBotIdHooksTestData, PostBotsByBotIdHooksTestError, PostBotsByBotIdHooksTestErrors, PostBotsByBotIdHooksTestResponse, PostBotsByBotIdHooksTestResponses, PostBotsByBotIdLocalMessagesData, PostBotsByBotIdLocalMessagesError, PostBotsByBotIdLocalMessagesErrors, PostBotsByBotIdLocalMessagesResponse, PostBotsByBotIdLocalMessagesResponses, PostBotsByBotIdMcpByIdOauthAuthorizeData, PostBotsByBotIdMcpByIdOauthAuthorizeError, PostBotsByBotIdMcpByIdOauthAuthorizeErrors, PostBotsByBotIdMcpByIdOauthAuthorizeResponse, PostBotsByBotIdMcpByIdOauthAuthorizeResponses, PostBotsByBotIdMcpByIdOauthDiscoverData, PostBotsByBotIdMcpByIdOauthDiscoverError, PostBotsByBotIdMcpByIdOauthDiscoverErrors, PostBotsByBotIdMcpByIdOauthDiscoverResponse, PostBotsByBotIdMcpByIdOauthDiscoverResponses, PostBotsByBotIdMcpByIdOauthExchangeData, PostBotsByBotIdMcpByIdOauthExchangeError, PostBotsByBotIdMcpByIdOauthExchangeErrors, PostBotsByBotIdMcpByIdOauthExchangeResponse, PostBotsByBotIdMcpByIdOauthExchangeResponses, PostBotsByBotIdMcpByIdProbeData, PostBotsByBotIdMcpByIdProbeError, PostBotsByBotIdMcpByIdProbeErrors, PostBotsByBotIdMcpByIdProbeResponse, PostBotsByBotIdMcpByIdProbeResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdPluginsByIdDisableData, PostBotsByBotIdPluginsByIdDisableError, PostBotsByBotIdPluginsByIdDisableErrors, PostBotsByBotIdPluginsByIdDisableResponse, PostBotsByBotIdPluginsByIdDisableResponses, PostBotsByBotIdPluginsByIdEnableData, PostBotsByBotIdPluginsByIdEnableError, PostBotsByBotIdPluginsByIdEnableErrors, PostBotsByBotIdPluginsByIdEnableResponse, PostBotsByBotIdPluginsByIdEnableResponses, PostBotsByBotIdPluginsByIdOauthAuthorizeData, PostBotsByBotIdPluginsByIdOauthAuthorizeError, PostBotsByBotIdPluginsByIdOauthAuthorizeErrors, PostBotsByBotIdPluginsByIdOauthAuthorizeResponse, PostBotsByBotIdPluginsByIdOauthAuthorizeResponses, PostBotsByBotIdPluginsByIdUninstallData, PostBotsByBotIdPluginsByIdUninstallError, PostBotsByBotIdPluginsByIdUninstallErrors, PostBotsByBotIdPluginsByIdUninstallResponse, PostBotsByBotIdPluginsByIdUninstallResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponse, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSessionsBySessionIdAcpRuntimeData, PostBotsByBotIdSessionsBySessionIdAcpRuntimeError, PostBotsByBotIdSessionsBySessionIdAcpRuntimeErrors, PostBotsByBotIdSessionsBySessionIdAcpRuntimeResponse, PostBotsByBotIdSessionsBySessionIdAcpRuntimeResponses, PostBotsByBotIdSessionsBySessionIdCompactData, PostBotsByBotIdSessionsBySessionIdCompactError, PostBotsByBotIdSessionsBySessionIdCompactErrors, PostBotsByBotIdSessionsBySessionIdCompactResponse, PostBotsByBotIdSessionsBySessionIdCompactResponses, PostBotsByBotIdSessionsData, PostBotsByBotIdSessionsError, PostBotsByBotIdSessionsErrors, PostBotsByBotIdSessionsResponse, PostBotsByBotIdSessionsResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSupermarketInstallPluginData, PostBotsByBotIdSupermarketInstallPluginError, PostBotsByBotIdSupermarketInstallPluginErrors, PostBotsByBotIdSupermarketInstallPluginResponse, PostBotsByBotIdSupermarketInstallPluginResponses, PostBotsByBotIdSupermarketInstallSkillData, PostBotsByBotIdSupermarketInstallSkillError, PostBotsByBotIdSupermarketInstallSkillErrors, PostBotsByBotIdSupermarketInstallSkillResponse, PostBotsByBotIdSupermarketInstallSkillResponses, PostBotsByBotIdToolApprovalsByApprovalIdApproveData, PostBotsByBotIdToolApprovalsByApprovalIdApproveError, PostBotsByBotIdToolApprovalsByApprovalIdApproveErrors, PostBotsByBotIdToolApprovalsByApprovalIdApproveResponse, PostBotsByBotIdToolApprovalsByApprovalIdApproveResponses, PostBotsByBotIdToolApprovalsByApprovalIdRejectData, PostBotsByBotIdToolApprovalsByApprovalIdRejectError, PostBotsByBotIdToolApprovalsByApprovalIdRejectErrors, PostBotsByBotIdToolApprovalsByApprovalIdRejectResponse, PostBotsByBotIdToolApprovalsByApprovalIdRejectResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponse, PostBotsByBotIdToolsResponses, PostBotsByBotIdTtsSynthesizeData, PostBotsByBotIdTtsSynthesizeError, PostBotsByBotIdTtsSynthesizeErrors, PostBotsByBotIdTtsSynthesizeResponse, PostBotsByBotIdTtsSynthesizeResponses, PostBotsByBotIdUserAccessData, PostBotsByBotIdUserAccessError, PostBotsByBotIdUserAccessErrors, PostBotsByBotIdUserAccessResponse, PostBotsByBotIdUserAccessResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponse, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsError, PostBotsErrors, PostBotsResponse, PostBotsResponses, PostEmailMailgunWebhookByConfigIdData, PostEmailMailgunWebhookByConfigIdError, PostEmailMailgunWebhookByConfigIdErrors, PostEmailMailgunWebhookByConfigIdResponse, PostEmailMailgunWebhookByConfigIdResponses, PostEmailProvidersData, PostEmailProvidersError, PostEmailProvidersErrors, PostEmailProvidersResponse, PostEmailProvidersResponses, PostFetchProvidersData, PostFetchProvidersError, PostFetchProvidersErrors, PostFetchProvidersResponse, PostFetchProvidersResponses, PostMemoryProvidersData, PostMemoryProvidersError, PostMemoryProvidersErrors, PostMemoryProvidersResponse, PostMemoryProvidersResponses, PostModelsByIdTestData, PostModelsByIdTestError, PostModelsByIdTestErrors, PostModelsByIdTestResponse, PostModelsByIdTestResponses, PostModelsData, PostModelsError, PostModelsErrors, PostModelsResponse, PostModelsResponses, PostProvidersByIdImportModelsData, PostProvidersByIdImportModelsError, PostProvidersByIdImportModelsErrors, PostProvidersByIdImportModelsResponse, PostProvidersByIdImportModelsResponses, PostProvidersByIdOauthPollData, PostProvidersByIdOauthPollError, PostProvidersByIdOauthPollErrors, PostProvidersByIdOauthPollResponse, PostProvidersByIdOauthPollResponses, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestErrors, PostProvidersByIdTestResponse, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersError, PostProvidersErrors, PostProvidersResponse, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersErrors, PostSearchProvidersResponse, PostSearchProvidersResponses, PostSpeechModelsByIdTestData, PostSpeechModelsByIdTestError, PostSpeechModelsByIdTestErrors, PostSpeechModelsByIdTestResponses, PostSpeechProvidersByIdImportModelsData, PostSpeechProvidersByIdImportModelsError, PostSpeechProvidersByIdImportModelsErrors, PostSpeechProvidersByIdImportModelsResponse, PostSpeechProvidersByIdImportModelsResponses, PostTranscriptionModelsByIdTestData, PostTranscriptionModelsByIdTestError, PostTranscriptionModelsByIdTestErrors, PostTranscriptionModelsByIdTestResponse, PostTranscriptionModelsByIdTestResponses, PostTranscriptionProvidersByIdImportModelsData, PostTranscriptionProvidersByIdImportModelsError, PostTranscriptionProvidersByIdImportModelsErrors, PostTranscriptionProvidersByIdImportModelsResponse, PostTranscriptionProvidersByIdImportModelsResponses, PostUsersData, PostUsersError, PostUsersErrors, PostUsersMeChannelLinksData, PostUsersMeChannelLinksError, PostUsersMeChannelLinksErrors, PostUsersMeChannelLinksResponse, PostUsersMeChannelLinksResponses, PostUsersResponse, PostUsersResponses, ProvidersCountResponse, ProvidersCreateRequest, ProvidersGetResponse, ProvidersImportModelsResponse, ProvidersOAuthAccount, ProvidersOAuthAuthorizeResponse, ProvidersOAuthDeviceStatus, ProvidersOAuthStatus, ProvidersTestResponse, ProvidersTestStatus, ProvidersUpdateRequest, PutBotsByBotIdAclDefaultEffectData, PutBotsByBotIdAclDefaultEffectError, PutBotsByBotIdAclDefaultEffectErrors, PutBotsByBotIdAclDefaultEffectResponses, PutBotsByBotIdAclRulesByRuleIdData, PutBotsByBotIdAclRulesByRuleIdError, PutBotsByBotIdAclRulesByRuleIdErrors, PutBotsByBotIdAclRulesByRuleIdResponse, PutBotsByBotIdAclRulesByRuleIdResponses, PutBotsByBotIdContainerMetricsData, PutBotsByBotIdContainerMetricsError, PutBotsByBotIdContainerMetricsErrors, PutBotsByBotIdContainerMetricsResponse, PutBotsByBotIdContainerMetricsResponses, PutBotsByBotIdEmailBindingsByIdData, PutBotsByBotIdEmailBindingsByIdError, PutBotsByBotIdEmailBindingsByIdErrors, PutBotsByBotIdEmailBindingsByIdResponse, PutBotsByBotIdEmailBindingsByIdResponses, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSettingsResponses, PutBotsByBotIdUserAccessByGrantIdData, PutBotsByBotIdUserAccessByGrantIdError, PutBotsByBotIdUserAccessByGrantIdErrors, PutBotsByBotIdUserAccessByGrantIdResponse, PutBotsByBotIdUserAccessByGrantIdResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponse, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdError, PutBotsByIdErrors, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponse, PutBotsByIdOwnerResponses, PutBotsByIdResponse, PutBotsByIdResponses, PutEmailProvidersByIdData, PutEmailProvidersByIdError, PutEmailProvidersByIdErrors, PutEmailProvidersByIdResponse, PutEmailProvidersByIdResponses, PutFetchProvidersByIdData, PutFetchProvidersByIdError, PutFetchProvidersByIdErrors, PutFetchProvidersByIdResponse, PutFetchProvidersByIdResponses, PutMemoryProvidersByIdData, PutMemoryProvidersByIdError, PutMemoryProvidersByIdErrors, PutMemoryProvidersByIdResponse, PutMemoryProvidersByIdResponses, PutModelsByIdData, PutModelsByIdError, PutModelsByIdErrors, PutModelsByIdResponse, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponse, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdErrors, PutProvidersByIdResponse, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponse, PutSearchProvidersByIdResponses, PutSpeechModelsByIdData, PutSpeechModelsByIdError, PutSpeechModelsByIdErrors, PutSpeechModelsByIdResponse, PutSpeechModelsByIdResponses, PutTranscriptionModelsByIdData, PutTranscriptionModelsByIdError, PutTranscriptionModelsByIdErrors, PutTranscriptionModelsByIdResponse, PutTranscriptionModelsByIdResponses, PutUsersByIdData, PutUsersByIdError, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponse, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponse, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeError, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponse, PutUsersMeResponses, ScheduleCreateRequest, ScheduleListLogsResponse, ScheduleListResponse, ScheduleLog, ScheduleNullableInt, ScheduleSchedule, ScheduleUpdateRequest, SearchprovidersCreateRequest, SearchprovidersGetResponse, SearchprovidersProviderConfigSchema, SearchprovidersProviderFieldSchema, SearchprovidersProviderMeta, SearchprovidersProviderName, SearchprovidersUpdateRequest, SessionSession, SettingsSettings, SettingsToolApprovalConfig, SettingsToolApprovalExecPolicy, SettingsToolApprovalFilePolicy, SettingsUpsertRequest } from './types.gen';
diff --git a/packages/sdk/src/types.gen.ts b/packages/sdk/src/types.gen.ts
index ec8660d019..d71e414aa1 100644
--- a/packages/sdk/src/types.gen.ts
+++ b/packages/sdk/src/types.gen.ts
@@ -187,17 +187,6 @@ export type AcpprofilePublicProfile = {
supported_backends?: Array;
};
-export type AdaptersCdfPoint = {
- /**
- * cumulative weight fraction [0.0, 1.0]
- */
- cumulative?: number;
- /**
- * rank position (1-based, sorted by value desc)
- */
- k?: number;
-};
-
export type AdaptersCompactResult = {
after_count?: number;
before_count?: number;
@@ -216,7 +205,6 @@ export type AdaptersHealthStatus = {
export type AdaptersMemoryCompactCapability = {
archive?: boolean;
- native?: boolean;
reason?: string;
rebuild_index?: boolean;
semantic?: boolean;
@@ -225,7 +213,6 @@ export type AdaptersMemoryCompactCapability = {
export type AdaptersMemoryItem = {
agent_id?: string;
bot_id?: string;
- cdf_curve?: Array;
created_at?: string;
hash?: string;
id?: string;
@@ -235,7 +222,6 @@ export type AdaptersMemoryItem = {
};
run_id?: string;
score?: number;
- top_k_buckets?: Array;
updated_at?: string;
};
@@ -335,17 +321,6 @@ export type AdaptersSearchResponse = {
results?: Array;
};
-export type AdaptersTopKBucket = {
- /**
- * sparse dimension index (term hash)
- */
- index?: number;
- /**
- * weight (term frequency)
- */
- value?: number;
-};
-
export type AdaptersUsageResponse = {
avg_text_bytes?: number;
count?: number;
diff --git a/spec/docs.go b/spec/docs.go
index 5aecb2381f..1f31933711 100644
--- a/spec/docs.go
+++ b/spec/docs.go
@@ -12513,19 +12513,6 @@ const docTemplate = `{
}
}
},
- "adapters.CDFPoint": {
- "type": "object",
- "properties": {
- "cumulative": {
- "description": "cumulative weight fraction [0.0, 1.0]",
- "type": "number"
- },
- "k": {
- "description": "rank position (1-based, sorted by value desc)",
- "type": "integer"
- }
- }
- },
"adapters.CompactResult": {
"type": "object",
"properties": {
@@ -12571,9 +12558,6 @@ const docTemplate = `{
"archive": {
"type": "boolean"
},
- "native": {
- "type": "boolean"
- },
"reason": {
"type": "string"
},
@@ -12594,12 +12578,6 @@ const docTemplate = `{
"bot_id": {
"type": "string"
},
- "cdf_curve": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/adapters.CDFPoint"
- }
- },
"created_at": {
"type": "string"
},
@@ -12622,12 +12600,6 @@ const docTemplate = `{
"score": {
"type": "number"
},
- "top_k_buckets": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/adapters.TopKBucket"
- }
- },
"updated_at": {
"type": "string"
}
@@ -12867,19 +12839,6 @@ const docTemplate = `{
}
}
},
- "adapters.TopKBucket": {
- "type": "object",
- "properties": {
- "index": {
- "description": "sparse dimension index (term hash)",
- "type": "integer"
- },
- "value": {
- "description": "weight (term frequency)",
- "type": "number"
- }
- }
- },
"adapters.UsageResponse": {
"type": "object",
"properties": {
diff --git a/spec/swagger.json b/spec/swagger.json
index ad0bf419d5..3dc8889d84 100644
--- a/spec/swagger.json
+++ b/spec/swagger.json
@@ -12504,19 +12504,6 @@
}
}
},
- "adapters.CDFPoint": {
- "type": "object",
- "properties": {
- "cumulative": {
- "description": "cumulative weight fraction [0.0, 1.0]",
- "type": "number"
- },
- "k": {
- "description": "rank position (1-based, sorted by value desc)",
- "type": "integer"
- }
- }
- },
"adapters.CompactResult": {
"type": "object",
"properties": {
@@ -12562,9 +12549,6 @@
"archive": {
"type": "boolean"
},
- "native": {
- "type": "boolean"
- },
"reason": {
"type": "string"
},
@@ -12585,12 +12569,6 @@
"bot_id": {
"type": "string"
},
- "cdf_curve": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/adapters.CDFPoint"
- }
- },
"created_at": {
"type": "string"
},
@@ -12613,12 +12591,6 @@
"score": {
"type": "number"
},
- "top_k_buckets": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/adapters.TopKBucket"
- }
- },
"updated_at": {
"type": "string"
}
@@ -12858,19 +12830,6 @@
}
}
},
- "adapters.TopKBucket": {
- "type": "object",
- "properties": {
- "index": {
- "description": "sparse dimension index (term hash)",
- "type": "integer"
- },
- "value": {
- "description": "weight (term frequency)",
- "type": "number"
- }
- }
- },
"adapters.UsageResponse": {
"type": "object",
"properties": {
diff --git a/spec/swagger.yaml b/spec/swagger.yaml
index d1a6f4ea8e..b69c68d382 100644
--- a/spec/swagger.yaml
+++ b/spec/swagger.yaml
@@ -308,15 +308,6 @@ definitions:
type: string
type: array
type: object
- adapters.CDFPoint:
- properties:
- cumulative:
- description: cumulative weight fraction [0.0, 1.0]
- type: number
- k:
- description: rank position (1-based, sorted by value desc)
- type: integer
- type: object
adapters.CompactResult:
properties:
after_count:
@@ -346,8 +337,6 @@ definitions:
properties:
archive:
type: boolean
- native:
- type: boolean
reason:
type: string
rebuild_index:
@@ -361,10 +350,6 @@ definitions:
type: string
bot_id:
type: string
- cdf_curve:
- items:
- $ref: '#/definitions/adapters.CDFPoint'
- type: array
created_at:
type: string
hash:
@@ -380,10 +365,6 @@ definitions:
type: string
score:
type: number
- top_k_buckets:
- items:
- $ref: '#/definitions/adapters.TopKBucket'
- type: array
updated_at:
type: string
type: object
@@ -542,15 +523,6 @@ definitions:
$ref: '#/definitions/adapters.MemoryItem'
type: array
type: object
- adapters.TopKBucket:
- properties:
- index:
- description: sparse dimension index (term hash)
- type: integer
- value:
- description: weight (term frequency)
- type: number
- type: object
adapters.UsageResponse:
properties:
avg_text_bytes: