Full Code of MemTensor/MemOS for AI

main 1fba901b091b cached
862 files
7.6 MB
2.1M tokens
5168 symbols
1 requests
Download .txt
Showing preview only (8,185K chars total). Download the full file or copy to clipboard to get everything.
Repository: MemTensor/MemOS
Branch: main
Commit: 1fba901b091b
Files: 862
Total size: 7.6 MB

Directory structure:
gitextract_ulrpkn53/

├── .github/
│   ├── CONTRIBUTING
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.yml
│   │   ├── config.yml
│   │   └── feature-request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── openclaw-plugin-publish.yml
│       ├── python-release.yml
│       ├── python-tests.yml
│       └── stale.yml
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
├── Makefile
├── README.md
├── apps/
│   ├── MemOS-Cloud-OpenClaw-Plugin/
│   │   ├── .gitignore
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── README_ZH.md
│   │   ├── clawdbot.plugin.json
│   │   ├── index.js
│   │   ├── lib/
│   │   │   ├── check-update.js
│   │   │   └── memos-cloud-api.js
│   │   ├── moltbot.plugin.json
│   │   ├── openclaw.plugin.json
│   │   ├── package.json
│   │   └── scripts/
│   │       └── sync-version.js
│   ├── memos-local-openclaw/
│   │   ├── .gitignore
│   │   ├── README.md
│   │   ├── index.ts
│   │   ├── openclaw.plugin.json
│   │   ├── package.json
│   │   ├── plugin-impl.ts
│   │   ├── scripts/
│   │   │   ├── mock-skills.ts
│   │   │   ├── postinstall.cjs
│   │   │   ├── refresh-skill.ts
│   │   │   ├── refresh-summaries.ts
│   │   │   ├── run-accuracy-test.ts
│   │   │   ├── seed-test-data.ts
│   │   │   ├── smoke-test.ts
│   │   │   ├── start-viewer.ts
│   │   │   └── test-agent-isolation.ts
│   │   ├── skill/
│   │   │   ├── browserwing-admin/
│   │   │   │   └── SKILL.md
│   │   │   ├── browserwing-executor/
│   │   │   │   └── SKILL.md
│   │   │   └── memos-memory-guide/
│   │   │       └── SKILL.md
│   │   ├── src/
│   │   │   ├── capture/
│   │   │   │   └── index.ts
│   │   │   ├── config.ts
│   │   │   ├── embedding/
│   │   │   │   ├── index.ts
│   │   │   │   ├── local.ts
│   │   │   │   └── providers/
│   │   │   │       ├── cohere.ts
│   │   │   │       ├── gemini.ts
│   │   │   │       ├── mistral.ts
│   │   │   │       ├── openai.ts
│   │   │   │       └── voyage.ts
│   │   │   ├── index.ts
│   │   │   ├── ingest/
│   │   │   │   ├── chunker.ts
│   │   │   │   ├── dedup.ts
│   │   │   │   ├── providers/
│   │   │   │   │   ├── anthropic.ts
│   │   │   │   │   ├── bedrock.ts
│   │   │   │   │   ├── gemini.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── openai.ts
│   │   │   │   ├── task-processor.ts
│   │   │   │   └── worker.ts
│   │   │   ├── recall/
│   │   │   │   ├── engine.ts
│   │   │   │   ├── mmr.ts
│   │   │   │   ├── recency.ts
│   │   │   │   └── rrf.ts
│   │   │   ├── shared/
│   │   │   │   └── llm-call.ts
│   │   │   ├── skill/
│   │   │   │   ├── bundled-memory-guide.ts
│   │   │   │   ├── evaluator.ts
│   │   │   │   ├── evolver.ts
│   │   │   │   ├── generator.ts
│   │   │   │   ├── installer.ts
│   │   │   │   ├── upgrader.ts
│   │   │   │   └── validator.ts
│   │   │   ├── storage/
│   │   │   │   ├── ensure-binding.ts
│   │   │   │   ├── sqlite.ts
│   │   │   │   └── vector.ts
│   │   │   ├── telemetry.ts
│   │   │   ├── tools/
│   │   │   │   ├── index.ts
│   │   │   │   ├── memory-get.ts
│   │   │   │   ├── memory-search.ts
│   │   │   │   └── memory-timeline.ts
│   │   │   ├── types.ts
│   │   │   ├── update-check.ts
│   │   │   └── viewer/
│   │   │       ├── html.ts
│   │   │       └── server.ts
│   │   ├── tests/
│   │   │   ├── accuracy.test.ts
│   │   │   ├── bench/
│   │   │   │   └── README.md
│   │   │   ├── capture.test.ts
│   │   │   ├── chunker.test.ts
│   │   │   ├── integration.test.ts
│   │   │   ├── multi-agent.test.ts
│   │   │   ├── plugin-impl-access.test.ts
│   │   │   ├── policy.test.ts
│   │   │   ├── recall.test.ts
│   │   │   ├── shutdown-lifecycle.test.ts
│   │   │   ├── storage.test.ts
│   │   │   ├── task-processor.test.ts
│   │   │   └── worker-lifecycle.test.ts
│   │   ├── tsconfig.json
│   │   ├── vitest.config.ts
│   │   └── www/
│   │       ├── demo/
│   │       │   └── index.html
│   │       ├── docs/
│   │       │   ├── index.html
│   │       │   └── troubleshooting.html
│   │       └── index.html
│   └── openwork-memos-integration/
│       ├── .gitignore
│       ├── CLAUDE.md
│       ├── CONTRIBUTING.md
│       ├── LICENSE
│       ├── README.md
│       ├── SECURITY.md
│       ├── apps/
│       │   └── desktop/
│       │       ├── .eslintrc.json
│       │       ├── __tests__/
│       │       │   ├── integration/
│       │       │   │   ├── main/
│       │       │   │   │   ├── appSettings.integration.test.ts
│       │       │   │   │   ├── opencode/
│       │       │   │   │   │   ├── cli-path.integration.test.ts
│       │       │   │   │   │   └── config-generator.integration.test.ts
│       │       │   │   │   ├── permission-api.integration.test.ts
│       │       │   │   │   ├── secureStorage.integration.test.ts
│       │       │   │   │   ├── store/
│       │       │   │   │   │   └── freshInstallCleanup.integration.test.ts
│       │       │   │   │   ├── taskHistory.integration.test.ts
│       │       │   │   │   └── utils/
│       │       │   │   │       ├── bundled-node.integration.test.ts
│       │       │   │   │       └── system-path.integration.test.ts
│       │       │   │   ├── preload/
│       │       │   │   │   └── preload.integration.test.ts
│       │       │   │   └── renderer/
│       │       │   │       ├── App.integration.test.tsx
│       │       │   │       ├── components/
│       │       │   │       │   ├── Header.integration.test.tsx
│       │       │   │       │   ├── SettingsDialog.integration.test.tsx
│       │       │   │       │   ├── Sidebar.integration.test.tsx
│       │       │   │       │   ├── StreamingText.integration.test.tsx
│       │       │   │       │   ├── TaskHistory.integration.test.tsx
│       │       │   │       │   ├── TaskInputBar.integration.test.tsx
│       │       │   │       │   └── TaskLauncher.integration.test.tsx
│       │       │   │       ├── pages/
│       │       │   │       │   ├── Execution.integration.test.tsx
│       │       │   │       │   └── Home.integration.test.tsx
│       │       │   │       └── taskStore.integration.test.ts
│       │       │   ├── main/
│       │       │   │   ├── config.unit.test.ts
│       │       │   │   ├── ipc/
│       │       │   │   │   ├── handlers-utils.unit.test.ts
│       │       │   │   │   └── validation.unit.test.ts
│       │       │   │   └── opencode/
│       │       │   │       └── stream-parser.unit.test.ts
│       │       │   ├── setup.ts
│       │       │   └── unit/
│       │       │       └── main/
│       │       │           ├── ipc/
│       │       │           │   └── handlers.unit.test.ts
│       │       │           └── opencode/
│       │       │               ├── adapter.unit.test.ts
│       │       │               └── task-manager.unit.test.ts
│       │       ├── clean_dmg_install.sh
│       │       ├── e2e/
│       │       │   ├── README.md
│       │       │   ├── config/
│       │       │   │   ├── index.ts
│       │       │   │   └── timeouts.ts
│       │       │   ├── docker/
│       │       │   │   ├── Dockerfile
│       │       │   │   └── docker-compose.yml
│       │       │   ├── fixtures/
│       │       │   │   ├── electron-app.ts
│       │       │   │   └── index.ts
│       │       │   ├── pages/
│       │       │   │   ├── execution.page.ts
│       │       │   │   ├── home.page.ts
│       │       │   │   ├── index.ts
│       │       │   │   └── settings.page.ts
│       │       │   ├── playwright.config.ts
│       │       │   ├── specs/
│       │       │   │   ├── execution.spec.ts
│       │       │   │   ├── home.spec.ts
│       │       │   │   ├── settings-bedrock.spec.ts
│       │       │   │   ├── settings-providers.spec.ts
│       │       │   │   ├── settings.spec.ts
│       │       │   │   └── task-launch-guard.spec.ts
│       │       │   └── utils/
│       │       │       ├── index.ts
│       │       │       └── screenshots.ts
│       │       ├── index.html
│       │       ├── package.json
│       │       ├── postcss.config.js
│       │       ├── resources/
│       │       │   └── entitlements.mac.plist
│       │       ├── run_local_ui_prod_api.sh
│       │       ├── run_local_ui_staging_api.sh
│       │       ├── run_prod.sh
│       │       ├── run_staging.sh
│       │       ├── scripts/
│       │       │   ├── after-pack.cjs
│       │       │   ├── download-nodejs.cjs
│       │       │   ├── package.cjs
│       │       │   └── patch-electron-name.cjs
│       │       ├── skills/
│       │       │   ├── ask-user-question/
│       │       │   │   ├── SKILL.md
│       │       │   │   ├── package.json
│       │       │   │   ├── src/
│       │       │   │   │   └── index.ts
│       │       │   │   └── tsconfig.json
│       │       │   ├── dev-browser/
│       │       │   │   ├── .gitignore
│       │       │   │   ├── SKILL.md
│       │       │   │   ├── package.json
│       │       │   │   ├── references/
│       │       │   │   │   └── scraping.md
│       │       │   │   ├── scripts/
│       │       │   │   │   ├── start-relay.ts
│       │       │   │   │   └── start-server.ts
│       │       │   │   ├── server.sh
│       │       │   │   ├── src/
│       │       │   │   │   ├── client.ts
│       │       │   │   │   ├── index.ts
│       │       │   │   │   ├── relay.ts
│       │       │   │   │   ├── snapshot/
│       │       │   │   │   │   ├── __tests__/
│       │       │   │   │   │   │   └── snapshot.test.ts
│       │       │   │   │   │   ├── browser-script.ts
│       │       │   │   │   │   ├── index.ts
│       │       │   │   │   │   └── inject.ts
│       │       │   │   │   └── types.ts
│       │       │   │   ├── tsconfig.json
│       │       │   │   └── vitest.config.ts
│       │       │   ├── file-permission/
│       │       │   │   ├── package.json
│       │       │   │   ├── src/
│       │       │   │   │   └── index.ts
│       │       │   │   └── tsconfig.json
│       │       │   └── safe-file-deletion/
│       │       │       └── SKILL.md
│       │       ├── src/
│       │       │   ├── main/
│       │       │   │   ├── config.ts
│       │       │   │   ├── index.ts
│       │       │   │   ├── ipc/
│       │       │   │   │   ├── handlers.ts
│       │       │   │   │   └── validation.ts
│       │       │   │   ├── opencode/
│       │       │   │   │   ├── adapter.ts
│       │       │   │   │   ├── cli-path.ts
│       │       │   │   │   ├── config-generator.ts
│       │       │   │   │   ├── stream-parser.ts
│       │       │   │   │   └── task-manager.ts
│       │       │   │   ├── permission-api.ts
│       │       │   │   ├── services/
│       │       │   │   │   ├── memory.ts
│       │       │   │   │   └── summarizer.ts
│       │       │   │   ├── store/
│       │       │   │   │   ├── appSettings.ts
│       │       │   │   │   ├── freshInstallCleanup.ts
│       │       │   │   │   ├── providerSettings.ts
│       │       │   │   │   ├── secureStorage.ts
│       │       │   │   │   └── taskHistory.ts
│       │       │   │   ├── test-utils/
│       │       │   │   │   └── mock-task-flow.ts
│       │       │   │   └── utils/
│       │       │   │       ├── bundled-node.ts
│       │       │   │       └── system-path.ts
│       │       │   ├── preload/
│       │       │   │   └── index.ts
│       │       │   ├── renderer/
│       │       │   │   ├── App.tsx
│       │       │   │   ├── components/
│       │       │   │   │   ├── TaskLauncher/
│       │       │   │   │   │   ├── TaskLauncher.tsx
│       │       │   │   │   │   ├── TaskLauncherItem.tsx
│       │       │   │   │   │   └── index.ts
│       │       │   │   │   ├── history/
│       │       │   │   │   │   └── TaskHistory.tsx
│       │       │   │   │   ├── landing/
│       │       │   │   │   │   └── TaskInputBar.tsx
│       │       │   │   │   ├── layout/
│       │       │   │   │   │   ├── ConversationListItem.tsx
│       │       │   │   │   │   ├── Header.tsx
│       │       │   │   │   │   ├── SettingsDialog.tsx
│       │       │   │   │   │   └── Sidebar.tsx
│       │       │   │   │   ├── settings/
│       │       │   │   │   │   ├── ProviderCard.tsx
│       │       │   │   │   │   ├── ProviderGrid.tsx
│       │       │   │   │   │   ├── ProviderSettingsPanel.tsx
│       │       │   │   │   │   ├── hooks/
│       │       │   │   │   │   │   └── useProviderSettings.ts
│       │       │   │   │   │   ├── providers/
│       │       │   │   │   │   │   ├── BedrockProviderForm.tsx
│       │       │   │   │   │   │   ├── ClassicProviderForm.tsx
│       │       │   │   │   │   │   ├── LiteLLMProviderForm.tsx
│       │       │   │   │   │   │   ├── OllamaProviderForm.tsx
│       │       │   │   │   │   │   ├── OpenRouterProviderForm.tsx
│       │       │   │   │   │   │   └── index.ts
│       │       │   │   │   │   └── shared/
│       │       │   │   │   │       ├── ApiKeyInput.tsx
│       │       │   │   │   │       ├── ConnectButton.tsx
│       │       │   │   │   │       ├── ConnectedControls.tsx
│       │       │   │   │   │       ├── ConnectionStatus.tsx
│       │       │   │   │   │       ├── FormError.tsx
│       │       │   │   │   │       ├── ModelSelector.tsx
│       │       │   │   │   │       ├── ProviderFormHeader.tsx
│       │       │   │   │   │       ├── RegionSelector.tsx
│       │       │   │   │   │       └── index.ts
│       │       │   │   │   └── ui/
│       │       │   │   │       ├── avatar.tsx
│       │       │   │   │       ├── badge.tsx
│       │       │   │   │       ├── button.tsx
│       │       │   │   │       ├── card.tsx
│       │       │   │   │       ├── dialog.tsx
│       │       │   │   │       ├── dropdown-menu.tsx
│       │       │   │   │       ├── input.tsx
│       │       │   │   │       ├── label.tsx
│       │       │   │   │       ├── scroll-area.tsx
│       │       │   │   │       ├── separator.tsx
│       │       │   │   │       ├── skeleton.tsx
│       │       │   │   │       ├── streaming-text.tsx
│       │       │   │   │       └── textarea.tsx
│       │       │   │   ├── main.tsx
│       │       │   │   ├── pages/
│       │       │   │   │   ├── Execution.tsx
│       │       │   │   │   ├── History.tsx
│       │       │   │   │   └── Home.tsx
│       │       │   │   ├── stores/
│       │       │   │   │   └── taskStore.ts
│       │       │   │   └── styles/
│       │       │   │       └── globals.css
│       │       │   └── vite-env.d.ts
│       │       ├── tailwind.config.ts
│       │       ├── tsconfig.json
│       │       ├── vite.config.ts
│       │       ├── vitest.config.ts
│       │       ├── vitest.integration.config.ts
│       │       └── vitest.unit.config.ts
│       ├── docs/
│       │   └── plans/
│       │       └── 2026-01-17-safe-file-deletion-impl.md
│       ├── package.json
│       ├── packages/
│       │   └── shared/
│       │       ├── package.json
│       │       ├── src/
│       │       │   ├── index.ts
│       │       │   └── types/
│       │       │       ├── auth.ts
│       │       │       ├── index.ts
│       │       │       ├── opencode.ts
│       │       │       ├── permission.ts
│       │       │       ├── provider.ts
│       │       │       ├── providerSettings.ts
│       │       │       └── task.ts
│       │       └── tsconfig.json
│       └── pnpm-workspace.yaml
├── docker/
│   ├── Dockerfile
│   ├── Dockerfile.krolik
│   └── docker-compose.yml
├── docs/
│   ├── README.md
│   ├── openapi.json
│   └── product-api-tests.md
├── evaluation/
│   ├── .env-example
│   ├── README.md
│   ├── __init__.py
│   ├── data/
│   │   └── longmemeval/
│   │       └── .gitkeep
│   └── scripts/
│       ├── PrefEval/
│       │   ├── irrelevant_conv.py
│       │   ├── pref_eval.py
│       │   ├── pref_mem0.py
│       │   ├── pref_memobase.py
│       │   ├── pref_memos.py
│       │   ├── pref_memu.py
│       │   ├── pref_supermemory.py
│       │   ├── pref_zep.py
│       │   └── prefeval_preprocess.py
│       ├── __init__.py
│       ├── locomo/
│       │   ├── locomo_eval.py
│       │   ├── locomo_ingestion.py
│       │   ├── locomo_metric.py
│       │   ├── locomo_openai.py
│       │   ├── locomo_rag.py
│       │   ├── locomo_responses.py
│       │   ├── locomo_search.py
│       │   ├── openai_memory_locomo_eval_guide.md
│       │   ├── prompts.py
│       │   └── utils.py
│       ├── long_bench-v2/
│       │   ├── __init__.py
│       │   ├── longbench_v2_ingestion.py
│       │   ├── longbench_v2_metric.py
│       │   ├── longbench_v2_responses.py
│       │   ├── longbench_v2_search.py
│       │   └── wait_scheduler.py
│       ├── longmemeval/
│       │   ├── lme_eval.py
│       │   ├── lme_ingestion.py
│       │   ├── lme_metric.py
│       │   ├── lme_rag.py
│       │   ├── lme_responses.py
│       │   └── lme_search.py
│       ├── run_lme_eval.sh
│       ├── run_locomo_eval.sh
│       ├── run_longbench_v2_eval.sh
│       ├── run_openai_eval.sh
│       ├── run_pm_eval.sh
│       ├── run_prefeval_eval.sh
│       ├── run_rag_eval.sh
│       └── utils/
│           ├── __init__.py
│           ├── client.py
│           ├── mirix_utils.py
│           └── prompts.py
├── examples/
│   ├── api/
│   │   ├── __init__.py
│   │   └── server_router_api.py
│   ├── basic_modules/
│   │   ├── chunker.py
│   │   ├── embedder.py
│   │   ├── llm.py
│   │   ├── neo4j_example.py
│   │   ├── reranker.py
│   │   ├── textual_memory_internet_search_example.py
│   │   ├── tree_textual_memory_recall.py
│   │   ├── tree_textual_memory_relation_reason_detector.py
│   │   └── tree_textual_memory_task_goal_parser.py
│   ├── core_memories/
│   │   ├── general_textual_memory.py
│   │   ├── kv_cache_memory.py
│   │   ├── naive_textual_memory.py
│   │   ├── pref_textual_memory.py
│   │   ├── tree_textual_memory.py
│   │   └── vllm_kv_cache_memory.py
│   ├── data/
│   │   ├── config/
│   │   │   └── mem_scheduler/
│   │   │       ├── general_scheduler_config.yaml
│   │   │       ├── mem_cube_config.yaml
│   │   │       ├── mem_cube_config_neo4j.yaml
│   │   │       ├── memos_config_w_optimized_scheduler.yaml
│   │   │       └── memos_config_w_scheduler.yaml
│   │   └── mem_cube_2/
│   │       ├── README.md
│   │       ├── activation_memory.pickle
│   │       └── parametric_memory.adapter
│   ├── extras/
│   │   └── nli_e2e_example.py
│   ├── mem_agent/
│   │   └── deepsearch_example.py
│   ├── mem_chat/
│   │   └── chat_w_generated_cube_explicit_memory_only.py
│   ├── mem_cube/
│   │   ├── _deprecated/
│   │   │   ├── README.md
│   │   │   ├── load_from_folder.py
│   │   │   ├── load_from_remote.py
│   │   │   └── load_lazily.py
│   │   ├── dump_cube.py
│   │   └── load_cube.py
│   ├── mem_feedback/
│   │   └── example_feedback.py
│   ├── mem_mcp/
│   │   ├── simple_fastmcp_client.py
│   │   └── simple_fastmcp_serve.py
│   ├── mem_reader/
│   │   ├── README.md
│   │   ├── builders.py
│   │   ├── parser_demos/
│   │   │   ├── __init__.py
│   │   │   ├── _base.py
│   │   │   ├── demo_assistant.py
│   │   │   ├── demo_file_content.py
│   │   │   ├── demo_image.py
│   │   │   ├── demo_multi_modal.py
│   │   │   ├── demo_string.py
│   │   │   ├── demo_system.py
│   │   │   ├── demo_text_content.py
│   │   │   ├── demo_tool.py
│   │   │   └── demo_user.py
│   │   ├── runners/
│   │   │   ├── __init__.py
│   │   │   ├── run_multimodal.py
│   │   │   └── run_simple.py
│   │   ├── samples.py
│   │   ├── settings.py
│   │   └── utils.py
│   └── mem_scheduler/
│       ├── api_w_scheduler.py
│       ├── memos_w_scheduler.py
│       ├── redis_example.py
│       ├── run_async_tasks.py
│       ├── show_redis_status.py
│       └── try_schedule_modules.py
├── pyproject.toml
├── scripts/
│   └── check_dependencies.py
├── src/
│   ├── __init__.py
│   └── memos/
│       ├── __init__.py
│       ├── api/
│       │   ├── README_api.md
│       │   ├── __init__.py
│       │   ├── client.py
│       │   ├── config.py
│       │   ├── context/
│       │   │   └── dependencies.py
│       │   ├── exceptions.py
│       │   ├── handlers/
│       │   │   ├── __init__.py
│       │   │   ├── add_handler.py
│       │   │   ├── base_handler.py
│       │   │   ├── chat_handler.py
│       │   │   ├── component_init.py
│       │   │   ├── config_builders.py
│       │   │   ├── feedback_handler.py
│       │   │   ├── formatters_handler.py
│       │   │   ├── memory_handler.py
│       │   │   ├── scheduler_handler.py
│       │   │   ├── search_handler.py
│       │   │   └── suggestion_handler.py
│       │   ├── mcp_serve.py
│       │   ├── middleware/
│       │   │   ├── __init__.py
│       │   │   ├── auth.py
│       │   │   ├── rate_limit.py
│       │   │   └── request_context.py
│       │   ├── product_api.py
│       │   ├── product_models.py
│       │   ├── routers/
│       │   │   ├── __init__.py
│       │   │   ├── admin_router.py
│       │   │   ├── product_router.py
│       │   │   └── server_router.py
│       │   ├── server_api.py
│       │   ├── server_api_ext.py
│       │   ├── start_api.py
│       │   └── utils/
│       │       ├── __init__.py
│       │       └── api_keys.py
│       ├── chunkers/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── charactertext_chunker.py
│       │   ├── factory.py
│       │   ├── markdown_chunker.py
│       │   ├── sentence_chunker.py
│       │   └── simple_chunker.py
│       ├── cli.py
│       ├── configs/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── chunker.py
│       │   ├── embedder.py
│       │   ├── graph_db.py
│       │   ├── internet_retriever.py
│       │   ├── llm.py
│       │   ├── mem_agent.py
│       │   ├── mem_chat.py
│       │   ├── mem_cube.py
│       │   ├── mem_os.py
│       │   ├── mem_reader.py
│       │   ├── mem_scheduler.py
│       │   ├── mem_user.py
│       │   ├── memory.py
│       │   ├── parser.py
│       │   ├── reranker.py
│       │   ├── utils.py
│       │   └── vec_db.py
│       ├── context/
│       │   └── context.py
│       ├── dependency.py
│       ├── deprecation.py
│       ├── embedders/
│       │   ├── __init__.py
│       │   ├── ark.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   ├── ollama.py
│       │   ├── sentence_transformer.py
│       │   └── universal_api.py
│       ├── exceptions.py
│       ├── extras/
│       │   ├── __init__.py
│       │   └── nli_model/
│       │       ├── __init__.py
│       │       ├── client.py
│       │       ├── server/
│       │       │   ├── README.md
│       │       │   ├── __init__.py
│       │       │   ├── config.py
│       │       │   ├── handler.py
│       │       │   └── serve.py
│       │       └── types.py
│       ├── graph_dbs/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   ├── item.py
│       │   ├── nebular.py
│       │   ├── neo4j.py
│       │   ├── neo4j_community.py
│       │   ├── polardb.py
│       │   └── postgres.py
│       ├── hello_world.py
│       ├── llms/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── deepseek.py
│       │   ├── factory.py
│       │   ├── hf.py
│       │   ├── hf_singleton.py
│       │   ├── ollama.py
│       │   ├── openai.py
│       │   ├── openai_new.py
│       │   ├── qwen.py
│       │   ├── utils.py
│       │   └── vllm.py
│       ├── log.py
│       ├── mem_agent/
│       │   ├── base.py
│       │   ├── deepsearch_agent.py
│       │   └── factory.py
│       ├── mem_chat/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   └── simple.py
│       ├── mem_cube/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── general.py
│       │   ├── navie.py
│       │   └── utils.py
│       ├── mem_feedback/
│       │   ├── base.py
│       │   ├── feedback.py
│       │   ├── simple_feedback.py
│       │   └── utils.py
│       ├── mem_os/
│       │   ├── client.py
│       │   ├── core.py
│       │   ├── main.py
│       │   ├── product.py
│       │   ├── product_server.py
│       │   └── utils/
│       │       ├── default_config.py
│       │       ├── format_utils.py
│       │       └── reference_utils.py
│       ├── mem_reader/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   ├── memory.py
│       │   ├── multi_modal_struct.py
│       │   ├── read_multi_modal/
│       │   │   ├── __init__.py
│       │   │   ├── assistant_parser.py
│       │   │   ├── base.py
│       │   │   ├── file_content_parser.py
│       │   │   ├── image_parser.py
│       │   │   ├── multi_modal_parser.py
│       │   │   ├── string_parser.py
│       │   │   ├── system_parser.py
│       │   │   ├── text_content_parser.py
│       │   │   ├── tool_parser.py
│       │   │   ├── user_parser.py
│       │   │   └── utils.py
│       │   ├── read_pref_memory/
│       │   │   └── process_preference_memory.py
│       │   ├── read_skill_memory/
│       │   │   └── process_skill_memory.py
│       │   ├── simple_struct.py
│       │   ├── strategy_struct.py
│       │   └── utils.py
│       ├── mem_scheduler/
│       │   ├── __init__.py
│       │   ├── analyzer/
│       │   │   ├── __init__.py
│       │   │   ├── api_analyzer.py
│       │   │   ├── eval_analyzer.py
│       │   │   ├── mos_for_test_scheduler.py
│       │   │   └── scheduler_for_eval.py
│       │   ├── base_mixins/
│       │   │   ├── __init__.py
│       │   │   ├── memory_ops.py
│       │   │   ├── queue_ops.py
│       │   │   └── web_log_ops.py
│       │   ├── base_scheduler.py
│       │   ├── general_modules/
│       │   │   ├── __init__.py
│       │   │   ├── api_misc.py
│       │   │   ├── base.py
│       │   │   ├── init_components_for_scheduler.py
│       │   │   ├── misc.py
│       │   │   ├── scheduler_logger.py
│       │   │   └── task_threads.py
│       │   ├── general_scheduler.py
│       │   ├── memory_manage_modules/
│       │   │   ├── __init__.py
│       │   │   ├── activation_memory_manager.py
│       │   │   ├── enhancement_pipeline.py
│       │   │   ├── filter_pipeline.py
│       │   │   ├── memory_filter.py
│       │   │   ├── post_processor.py
│       │   │   ├── rerank_pipeline.py
│       │   │   ├── retriever.py
│       │   │   ├── search_pipeline.py
│       │   │   └── search_service.py
│       │   ├── monitors/
│       │   │   ├── __init__.py
│       │   │   ├── dispatcher_monitor.py
│       │   │   ├── general_monitor.py
│       │   │   └── task_schedule_monitor.py
│       │   ├── optimized_scheduler.py
│       │   ├── orm_modules/
│       │   │   ├── __init__.py
│       │   │   ├── api_redis_model.py
│       │   │   ├── base_model.py
│       │   │   ├── monitor_models.py
│       │   │   └── redis_model.py
│       │   ├── scheduler_factory.py
│       │   ├── schemas/
│       │   │   ├── __init__.py
│       │   │   ├── analyzer_schemas.py
│       │   │   ├── api_schemas.py
│       │   │   ├── general_schemas.py
│       │   │   ├── message_schemas.py
│       │   │   ├── monitor_schemas.py
│       │   │   └── task_schemas.py
│       │   ├── task_schedule_modules/
│       │   │   ├── __init__.py
│       │   │   ├── base_handler.py
│       │   │   ├── context.py
│       │   │   ├── dispatcher.py
│       │   │   ├── handlers/
│       │   │   │   ├── __init__.py
│       │   │   │   ├── add_handler.py
│       │   │   │   ├── answer_handler.py
│       │   │   │   ├── feedback_handler.py
│       │   │   │   ├── mem_read_handler.py
│       │   │   │   ├── mem_reorganize_handler.py
│       │   │   │   ├── memory_update_handler.py
│       │   │   │   ├── pref_add_handler.py
│       │   │   │   └── query_handler.py
│       │   │   ├── local_queue.py
│       │   │   ├── orchestrator.py
│       │   │   ├── redis_queue.py
│       │   │   ├── registry.py
│       │   │   └── task_queue.py
│       │   ├── utils/
│       │   │   ├── __init__.py
│       │   │   ├── api_utils.py
│       │   │   ├── config_utils.py
│       │   │   ├── db_utils.py
│       │   │   ├── filter_utils.py
│       │   │   ├── metrics.py
│       │   │   ├── misc_utils.py
│       │   │   ├── monitor_event_utils.py
│       │   │   └── status_tracker.py
│       │   └── webservice_modules/
│       │       ├── __init__.py
│       │       ├── rabbitmq_service.py
│       │       └── redis_service.py
│       ├── mem_user/
│       │   ├── factory.py
│       │   ├── mysql_persistent_user_manager.py
│       │   ├── mysql_user_manager.py
│       │   ├── persistent_factory.py
│       │   ├── persistent_user_manager.py
│       │   ├── redis_persistent_user_manager.py
│       │   └── user_manager.py
│       ├── memories/
│       │   ├── __init__.py
│       │   ├── activation/
│       │   │   ├── __init__.py
│       │   │   ├── base.py
│       │   │   ├── item.py
│       │   │   ├── kv.py
│       │   │   └── vllmkv.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   ├── parametric/
│       │   │   ├── __init__.py
│       │   │   ├── base.py
│       │   │   ├── item.py
│       │   │   └── lora.py
│       │   └── textual/
│       │       ├── __init__.py
│       │       ├── base.py
│       │       ├── general.py
│       │       ├── item.py
│       │       ├── naive.py
│       │       ├── prefer_text_memory/
│       │       │   ├── __init__.py
│       │       │   ├── adder.py
│       │       │   ├── config.py
│       │       │   ├── extractor.py
│       │       │   ├── factory.py
│       │       │   ├── retrievers.py
│       │       │   ├── spliter.py
│       │       │   └── utils.py
│       │       ├── preference.py
│       │       ├── simple_preference.py
│       │       ├── simple_tree.py
│       │       ├── tree.py
│       │       └── tree_text_memory/
│       │           ├── __init__.py
│       │           ├── organize/
│       │           │   ├── __init__.py
│       │           │   ├── handler.py
│       │           │   ├── history_manager.py
│       │           │   ├── manager.py
│       │           │   ├── relation_reason_detector.py
│       │           │   └── reorganizer.py
│       │           └── retrieve/
│       │               ├── __init__.py
│       │               ├── advanced_searcher.py
│       │               ├── bm25_util.py
│       │               ├── bochasearch.py
│       │               ├── internet_retriever.py
│       │               ├── internet_retriever_factory.py
│       │               ├── pre_update.py
│       │               ├── reasoner.py
│       │               ├── recall.py
│       │               ├── reranker.py
│       │               ├── retrieval_mid_structs.py
│       │               ├── retrieve_utils.py
│       │               ├── searcher.py
│       │               ├── task_goal_parser.py
│       │               ├── utils.py
│       │               └── xinyusearch.py
│       ├── memos_tools/
│       │   ├── dinding_report_bot.py
│       │   ├── lockfree_dict.py
│       │   ├── notification_service.py
│       │   ├── notification_utils.py
│       │   ├── singleton.py
│       │   ├── thread_safe_dict.py
│       │   └── thread_safe_dict_segment.py
│       ├── multi_mem_cube/
│       │   ├── __init__.py
│       │   ├── composite_cube.py
│       │   ├── single_cube.py
│       │   └── views.py
│       ├── parsers/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   └── markitdown.py
│       ├── reranker/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── concat.py
│       │   ├── cosine_local.py
│       │   ├── factory.py
│       │   ├── http_bge.py
│       │   ├── http_bge_strategy.py
│       │   ├── noop.py
│       │   └── strategies/
│       │       ├── __init__.py
│       │       ├── base.py
│       │       ├── concat_background.py
│       │       ├── concat_docsource.py
│       │       ├── dialogue_common.py
│       │       ├── factory.py
│       │       ├── single_turn.py
│       │       └── singleturn_outmem.py
│       ├── search/
│       │   ├── __init__.py
│       │   └── search_service.py
│       ├── settings.py
│       ├── templates/
│       │   ├── __init__.py
│       │   ├── advanced_search_prompts.py
│       │   ├── cloud_service_prompt.py
│       │   ├── instruction_completion.py
│       │   ├── mem_agent_prompts.py
│       │   ├── mem_feedback_prompts.py
│       │   ├── mem_reader_prompts.py
│       │   ├── mem_reader_strategy_prompts.py
│       │   ├── mem_scheduler_prompts.py
│       │   ├── mem_search_prompts.py
│       │   ├── mos_prompts.py
│       │   ├── prefer_complete_prompt.py
│       │   ├── skill_mem_prompt.py
│       │   ├── tool_mem_prompts.py
│       │   └── tree_reorganize_prompts.py
│       ├── types/
│       │   ├── __init__.py
│       │   ├── general_types.py
│       │   └── openai_chat_completion_types/
│       │       ├── __init__.py
│       │       ├── chat_completion_assistant_message_param.py
│       │       ├── chat_completion_content_part_image_param.py
│       │       ├── chat_completion_content_part_input_audio_param.py
│       │       ├── chat_completion_content_part_param.py
│       │       ├── chat_completion_content_part_refusal_param.py
│       │       ├── chat_completion_content_part_text_param.py
│       │       ├── chat_completion_message_custom_tool_call_param.py
│       │       ├── chat_completion_message_function_tool_call_param.py
│       │       ├── chat_completion_message_param.py
│       │       ├── chat_completion_message_tool_call_union_param.py
│       │       ├── chat_completion_system_message_param.py
│       │       ├── chat_completion_tool_message_param.py
│       │       └── chat_completion_user_message_param.py
│       ├── utils.py
│       └── vec_dbs/
│           ├── __init__.py
│           ├── base.py
│           ├── factory.py
│           ├── item.py
│           ├── milvus.py
│           └── qdrant.py
└── tests/
    ├── __init__.py
    ├── api/
    │   ├── test_product_router.py
    │   ├── test_server_router.py
    │   ├── test_start_api.py
    │   └── test_thread_context.py
    ├── chunkers/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_factory.py
    │   └── test_sentence_chunker.py
    ├── configs/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_embedder.py
    │   ├── test_llm.py
    │   ├── test_mem_chat.py
    │   ├── test_mem_cube.py
    │   ├── test_memory.py
    │   ├── test_parser.py
    │   └── test_vec_db.py
    ├── embedders/
    │   ├── __init__.py
    │   ├── test_ark.py
    │   ├── test_base.py
    │   ├── test_factory.py
    │   ├── test_ollama.py
    │   └── test_universal_api.py
    ├── extras/
    │   ├── __init__.py
    │   └── nli_model/
    │       ├── __init__.py
    │       └── test_client_integration.py
    ├── graph_dbs/
    │   ├── __init__.py
    │   ├── graph_dbs.py
    │   └── test_search_return_fields.py
    ├── llms/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_deepseek.py
    │   ├── test_factory.py
    │   ├── test_hf.py
    │   ├── test_ollama.py
    │   ├── test_openai.py
    │   └── test_qwen.py
    ├── mem_agent/
    │   └── test_deepsearch_agent.py
    ├── mem_chat/
    │   ├── __init__.py
    │   ├── test_base.py
    │   └── test_factory.py
    ├── mem_cube/
    │   ├── test_base.py
    │   └── test_general.py
    ├── mem_os/
    │   ├── test_memos.py
    │   └── test_memos_core.py
    ├── mem_reader/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_coarse_memory_type.py
    │   ├── test_factory.py
    │   ├── test_memory.py
    │   ├── test_project_id_propagation.py
    │   └── test_simple_structure.py
    ├── mem_scheduler/
    │   ├── __init__.py
    │   ├── test_config.py
    │   ├── test_dispatcher.py
    │   ├── test_retriever.py
    │   ├── test_scheduler.py
    │   └── test_version_control.py
    ├── mem_tools/
    │   └── test_thread_safe_dict.py
    ├── mem_user/
    │   └── test_mem_user.py
    ├── memories/
    │   ├── __init__.py
    │   ├── activation/
    │   │   ├── __init__.py
    │   │   ├── test_base.py
    │   │   ├── test_item.py
    │   │   └── test_kv.py
    │   ├── test_base.py
    │   ├── test_factory.py
    │   └── textual/
    │       ├── __init__.py
    │       ├── test_base.py
    │       ├── test_general.py
    │       ├── test_history_manager.py
    │       ├── test_naive.py
    │       ├── test_pre_update_retriever.py
    │       ├── test_pre_update_retriever_latency.py
    │       ├── test_tree.py
    │       ├── test_tree_manager.py
    │       ├── test_tree_reranker.py
    │       ├── test_tree_retriever.py
    │       ├── test_tree_searcher.py
    │       └── test_tree_task_goal_parser.py
    ├── parsers/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_factory.py
    │   └── test_markitdown.py
    ├── test_cli.py
    ├── test_deprecation.py
    ├── test_hello_world.py
    ├── test_log.py
    ├── test_settings.py
    ├── utils.py
    └── vec_dbs/
        ├── __init__.py
        ├── test_base.py
        ├── test_factory.py
        ├── test_item.py
        └── test_qdrant.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/CONTRIBUTING
================================================
Please read https://memos-docs.openmem.net/contribution/overview to learn how to contribute to this repository. 🌟

请阅读 https://memos-docs.openmem.net/contribution/overview 了解如何为此项目贡献代码。🌟


================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.yml
================================================
name: "\U0001F41B Bug Report"
description: Report a bug to help us improve MemOS | 报告错误以帮助我们改进 MemOS
title: "fix: "
labels: ["bug", "pending"]
body:

  - type: checkboxes
    id: checklist
    attributes:
      label: Pre-submission checklist | 提交前检查
      options:
        - label: I have searched existing issues and this hasn't been mentioned before | 我已搜索现有问题,确认此问题尚未被提及
          required: true
        - label: I have read the project documentation and confirmed this issue doesn't already exist | 我已阅读项目文档并确认此问题尚未存在
          required: true
        - label: This issue is specific to MemOS and not a general software issue | 该问题是针对 MemOS 的,而不是一般软件问题
          required: true

  - type: textarea
    id: description
    attributes:
      label: "Bug Description | 问题描述"
      placeholder: "Describe what happened and what you expected to happen"
    validations:
      required: true

  - type: textarea
    id: reproduction
    attributes:
      label: "How to Reproduce | 如何重现"
      placeholder: |
        1. Import/run '...'
        2. Call function '...'
        3. See error
    validations:
      required: true

  - type: textarea
    id: environment
    attributes:
      label: "Environment | 环境信息"
      placeholder: |
        - Python version:
        - Operating System:
        - MemOS version: (run `pip show memoryos`)
    validations:
      required: true

  - type: textarea
    id: others
    validations:
      required: false
    attributes:
      label: "Additional Context | 其他信息"

  - type: checkboxes
    id: contribution
    attributes:
      label: Willingness to Implement | 实现意愿
      options:
        - label: I'm willing to implement this myself | 我愿意自己解决
          required: false
        - label: I would like someone else to implement this | 我希望其他人来解决
          required: false


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: "\U0001F527 GitHub Pull Requests"
    url: https://github.com/MemTensor/MemOS/pulls
    about: Contribute code improvements via Pull Requests | 通过 Pull Requests 贡献代码改进
  - name: "\U0001F4AC GitHub Discussions"
    url: https://github.com/MemTensor/MemOS/discussions
    about: Participate in our GitHub Discussions to ask questions or share ideas | 加入 GitHub Discussions,提出问题或分享想法
  - name: "\U0001F3AE Discord Server"
    url: https://discord.gg/Txbx3gebZR
    about: Join our Discord Server for real-time community chat | 加入我们的 Discord 服务器进行实时社区聊天
  - name: "\U0001F4F1 WeChat Group"
    url: https://statics.memtensor.com.cn/memos/qr-code.png
    about: Scan the QR code to join our WeChat group for more discussions | 扫描二维码加入我们的微信群,进行更多讨论


================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yml
================================================
name: "\U0001F680 Feature request"
description: Submit a request for a new feature | 申请添加新功能
title: "feat: "
labels: ["enhancement", "pending"]
body:

  - type: checkboxes
    id: checklist
    attributes:
      label: Pre-submission checklist | 提交前检查
      options:
        - label: I have searched existing issues and this hasn't been mentioned before | 我已搜索现有问题,确认此问题尚未被提及
          required: true
        - label: I have read the project documentation and confirmed this issue doesn't already exist | 我已阅读项目文档并确认此问题尚未存在
          required: true
        - label: This issue is specific to MemOS and not a general software issue | 该问题是针对 MemOS 的,而不是一般软件问题
          required: true

  - type: textarea
    id: problem
    validations:
      required: true
    attributes:
      label: Problem Statement | 问题陈述
      placeholder: |
        Describe the problem you're trying to solve...
        Example: "As a developer using MemOS, I find it difficult to..."

  - type: checkboxes
    id: contribution
    attributes:
      label: Willingness to Implement | 实现意愿
      options:
        - label: I'm willing to implement this myself | 我愿意自己解决
          required: false
        - label: I would like someone else to implement this | 我希望其他人来解决
          required: false


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Description

Please include a summary of the change, the problem it solves, the implementation approach, and relevant context. List any dependencies required for this change.

Related Issue (Required):  Fixes #issue_number

## Type of change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Refactor (does not change functionality, e.g. code style improvements, linting)
- [ ] Documentation update

## How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

- [ ] Unit Test
- [ ] Test Script Or Test Steps (please provide)
- [ ] Pipeline Automated API Test (please provide)

## Checklist

- [ ] I have performed a self-review of my own code | 我已自行检查了自己的代码
- [ ] I have commented my code in hard-to-understand areas | 我已在难以理解的地方对代码进行了注释
- [ ] I have added tests that prove my fix is effective or that my feature works | 我已添加测试以证明我的修复有效或功能正常
- [ ] I have created related documentation issue/PR in [MemOS-Docs](https://github.com/MemTensor/MemOS-Docs) (if applicable) | 我已在 [MemOS-Docs](https://github.com/MemTensor/MemOS-Docs) 中创建了相关的文档 issue/PR(如果适用)
- [ ] I have linked the issue to this PR (if applicable) | 我已将 issue 链接到此 PR(如果适用)
- [ ] I have mentioned the person who will review this PR | 我已提及将审查此 PR 的人

## Reviewer Checklist
- [ ] closes #xxxx (Replace xxxx with the GitHub issue number)
- [ ] Made sure Checks passed
- [ ] Tests have been provided


================================================
FILE: .github/workflows/openclaw-plugin-publish.yml
================================================
name: OpenClaw Plugin — Build Prebuilds & Publish

on:
  workflow_dispatch:
    inputs:
      version:
        description: "Version to publish (e.g. 1.0.4 or 1.0.4-beta.1)"
        required: true
      tag:
        description: "npm dist-tag (latest for production, beta/next/alpha for testing)"
        required: true
        default: "latest"

defaults:
  run:
    working-directory: apps/memos-local-openclaw

permissions:
  contents: write

jobs:
  build-prebuilds:
    strategy:
      matrix:
        include:
          - os: macos-14
            platform: darwin-arm64
          - os: macos-13
            platform: darwin-x64
          - os: ubuntu-latest
            platform: linux-x64
          - os: windows-latest
            platform: win32-x64
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - name: Install dependencies
        run: npm install

      - name: Collect prebuild
        shell: bash
        run: |
          mkdir -p prebuilds/${{ matrix.platform }}
          cp node_modules/better-sqlite3/build/Release/better_sqlite3.node prebuilds/${{ matrix.platform }}/

      - name: Upload prebuild artifact
        uses: actions/upload-artifact@v4
        with:
          name: prebuild-${{ matrix.platform }}
          path: apps/memos-local-openclaw/prebuilds/${{ matrix.platform }}/better_sqlite3.node

  publish:
    needs: build-prebuilds
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          registry-url: https://registry.npmjs.org

      - name: Download all prebuilds
        uses: actions/download-artifact@v4
        with:
          path: apps/memos-local-openclaw/prebuilds
          pattern: prebuild-*
          merge-multiple: false

      - name: Organize prebuilds
        run: |
          cd prebuilds
          for dir in prebuild-*; do
            platform="${dir#prebuild-}"
            mkdir -p "$platform"
            mv "$dir/better_sqlite3.node" "$platform/"
            rmdir "$dir"
          done
          echo "Prebuilds collected:"
          find . -name "*.node" -exec ls -lh {} \;

      - name: Install dependencies (skip native build)
        run: npm install --ignore-scripts

      - name: Bump version
        run: npm version ${{ inputs.version }} --no-git-tag-version

      - name: Publish to npm
        run: npm publish --access public --tag ${{ inputs.tag }}
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Create git tag and push
        working-directory: .
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add apps/memos-local-openclaw/package.json
          git commit -m "release: openclaw-plugin v${{ inputs.version }}"
          git tag "openclaw-plugin-v${{ inputs.version }}"
          git push origin HEAD --tags


================================================
FILE: .github/workflows/python-release.yml
================================================
name: Upload Python Package to PyPI

on:
  release:
    types: [published]

permissions:
  contents: read

jobs:
  deploy:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    - name: Install poetry
      run: pipx install poetry
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.10'
    - name: Install dependencies
      run: |
        poetry install --no-interaction
    - name: Build package
      run: poetry build
    - name: Publish package
      uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
      with:
        user: __token__
        password: ${{ secrets.PYPI_API_TOKEN }}


================================================
FILE: .github/workflows/python-tests.yml
================================================
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python tests

permissions:
  contents: read

on:
  push:
    branches:
      - "main"
      - "dev"
      - "dev*"
      - "feat/*"
      - "test"
  pull_request:
    branches:
      - "main"
      - "dev"
      - "dev*"
      - "feat/*"
      - "test"

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        os:
          - "ubuntu-latest"
          - "windows-latest"
          - "macos-14"
          - "macos-15"
          # Ref: https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-where-your-workflow-runs/choosing-the-runner-for-a-job
        python-version:
          - "3.10"
          - "3.11"
          - "3.12"
          - "3.13"
    runs-on: ${{ matrix.os }}
    timeout-minutes: 30

    steps:
    - uses: actions/checkout@v4
    - name: Install poetry
      # This is a temporary fix to ensure compatibility with Poetry & virtualenv
      # Revert to the original installation method once the poetry==2.1.4 is released
      run: |
        echo "virtualenv==20.32.0" > constraints.txt
        pipx install poetry==2.1.3 --pip-args="--constraint=constraints.txt"
        rm constraints.txt
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'poetry'

    # Dependency and building tests
    - name: Install main dependencies
      run: |
        poetry install --no-root --no-interaction
    - name: Check no top-level optional dependencies
      run: |
        poetry run python scripts/check_dependencies.py
    - name: Build sdist and wheel
      run: poetry build
    - name: Test wheel installation on Windows
      if: startsWith(matrix.os, 'windows')
      run: |
        Get-ChildItem dist/*.whl | ForEach-Object { pip install $_.FullName }
        pip uninstall -y memoryos
    - name: Test wheel installation on Linux / Mac
      if: ${{ !startsWith(matrix.os, 'windows') }}
      run: |
        pip install dist/*.whl
        pip uninstall -y memoryos
    - name: Test sdist installation on Windows
      if: startsWith(matrix.os, 'windows')
      run: |
        Get-ChildItem dist/*.tar.gz | ForEach-Object { pip install $_.FullName }
        pip uninstall -y memoryos
    - name: Test sdist installation on Linux / Mac
      if: ${{ !startsWith(matrix.os, 'windows') }}
      run: |
        pip install dist/*.tar.gz
        pip uninstall -y memoryos

    # Ruff checks
    - name: Install test group dependencies
      run: |
        poetry install --no-interaction --with test
    - name: Ruff checks
      run: |
        poetry run ruff check
        poetry run ruff format --check

    # PyTest checks
    - name: Install all extra dependencies
      # macos-13 doesn't support torch==2.7.1
      # So, pytest won't work
      if: ${{ !startsWith(matrix.os, 'macos-13') }}
      run: |
        poetry install --no-interaction --extras all
    - name: PyTest unit tests with coverage
      if: ${{ !startsWith(matrix.os, 'macos-13') }}
      shell: bash
      run: |
        poetry run pytest tests -vv --durations=10 \
          --cov=src/memos \
          --cov-report=term-missing \
          --cov-fail-under=28


================================================
FILE: .github/workflows/stale.yml
================================================
name: "Mark stale issues and PRs"

on:
  schedule:
    - cron: '0 2 * * *' # Runs every day at 2 AM UTC

permissions:
  issues: write
  pull-requests: write

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v9
        with:
          stale-issue-message: 'This issue has been automatically marked as stale due to inactivity.'
          stale-pr-message: 'This PR has been automatically marked as stale due to inactivity.'
          close-issue-message: 'This issue has been automatically closed due to inactivity.'
          close-pr-message: 'This PR has been automatically closed due to inactivity.'
          days-before-stale: 30  # Days of inactivity before marking as stale
          days-before-close: 7  # Days of inactivity before closing stale issues/PRs
          stale-issue-label: 'stale'
          stale-pr-label: 'stale'
          exempt-issue-labels: 'do not close'
          exempt-pr-labels: 'do not close'
          remove-stale-when-updated: true


================================================
FILE: .gitignore
================================================
# MemOS home
.memos/

# Temporary files
tmp/
**/tmp_data/

# evaluation data
*.csv
*.jsonl
**settings.json**
evaluation/*tmp/
evaluation/results
evaluation/.env
!evaluation/configs-example/*.json
evaluation/configs/*
**tree_textual_memory_locomo**
**script.py**
.env
evaluation/scripts/personamem

# benchmarks
benchmarks/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
.run

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
report/
cov-report/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
#   in version control.
#   https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# ignore all office files
*.pdf
*.txt
*.docx
*.doc
*.pptx
*.xls
*.xlsx
*.json
*.pkl
*.html

# but do not ignore docs/openapi.json
!docs/openapi.json

# do not ignore apps/ config files
!apps/**/*.json
!apps/**/*.html
!apps/**/*.ts
!apps/**/*.tsx
!apps/**/*.js
!apps/**/*.cjs
!apps/**/*.css
!apps/**/*.md
!apps/**/*.yaml
!apps/**/*.yml
!apps/**/*.svg
!apps/**/*.sh


# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# auth file
*_auth.yaml

# PyCharm
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
.trae

# VSCode
.vscode*

# DS_Store
.DS_Store

# OpenWork integration assets (managed separately)
apps/openwork-memos-integration/apps/desktop/public/assets/usecases/

# Outputs and Evaluation Results
outputs

evaluation/data/temporal_locomo
test_add_pipeline.py
test_file_pipeline.py


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: trailing-whitespace
        exclude: tests/repositories/fixtures/pypi.org/metadata/.*\.metadata
      - id: end-of-file-fixer
        exclude: ^.*\.egg-info/|tests/repositories/fixtures/pypi.org/metadata/.*\.metadata
      - id: check-merge-conflict
      - id: check-case-conflict
      - id: check-json
      - id: check-toml
        exclude: tests/fixtures/invalid_lock/poetry\.lock
      - id: check-yaml
      - id: pretty-format-json
        args: [--autofix, --no-ensure-ascii, --no-sort-keys]
      - id: check-ast
      - id: debug-statements
      - id: check-docstring-first

  - repo: https://github.com/pre-commit/pre-commit
    rev: v4.2.0
    hooks:
      - id: validate_manifest

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.11.8
    hooks:
      - id: ruff
        args: [ --fix, --config=./pyproject.toml ]
      - id: ruff-format
        args: [ --config=./pyproject.toml ]

  - repo: https://github.com/python-poetry/poetry
    rev: '2.1.3'
    hooks:
    -   id: poetry-check
    -   id: poetry-lock
    -   id: poetry-install

  - repo: https://github.com/hauntsaninja/no_implicit_optional
    rev: '1.4'
    hooks:
    -   id: no_implicit_optional
        name: no_implicit_optional
        description: "A codemod to make your implicit optional type hints PEP 484 compliant"
        entry: no_implicit_optional
        language: python
        minimum_pre_commit_version: 2.9.2
        require_serial: true
        types_or: [python, pyi]


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2025 - Present MemTensor Research

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: Makefile
================================================
.PHONY: test test-report test-cov

install:
	poetry install --extras all --with dev --with test
	poetry run pre-commit install --install-hooks

clean:
	rm -rf .memos
	rm -rf .pytest_cache
	rm -rf .ruff_cache
	rm -rf tmp
	rm -rf report cov-report
	rm -f .coverage .coverage.*

test:
	poetry run pytest tests

test-report:
	poetry run pytest tests -vv --durations=10 \
		--html=report/index.html \
		--cov=src/memos \
		--cov-report=term-missing \
		--cov-report=html:cov-report/src

test-cov:
	poetry run pytest tests \
		--cov=src/memos \
		--cov-report=term-missing \
		--cov-report=html:cov-report/src

format:
	poetry run ruff check --fix
	poetry run ruff format

pre_commit:
	poetry run pre-commit run -a

serve:
	poetry run uvicorn memos.api.start_api:app

openapi:
	poetry run memos export_openapi --output docs/openapi.json


================================================
FILE: README.md
================================================
<div align="center">
  <a href="https://memos.openmem.net/">
    <img src="https://statics.memtensor.com.cn/memos/memos-banner.gif" alt="MemOS Banner">
  </a>

  <h1 align="center">
    <img src="https://statics.memtensor.com.cn/logo/memos_color_m.png" alt="MemOS Logo" width="50"/>
    MemOS 2.0: 星尘(Stardust)
    <img src="https://img.shields.io/badge/status-Preview-blue" alt="Preview Badge"/>
  </h1>

  <p>
    <a href="https://www.memtensor.com.cn/">
      <img alt="Static Badge" src="https://img.shields.io/badge/Maintained_by-MemTensor-blue">
    </a>
    <a href="https://pypi.org/project/MemoryOS">
      <img src="https://img.shields.io/pypi/v/MemoryOS?label=pypi%20package" alt="PyPI Version">
    </a>
    <a href="https://pypi.org/project/MemoryOS">
      <img src="https://img.shields.io/pypi/pyversions/MemoryOS.svg" alt="Supported Python versions">
    </a>
    <a href="https://pypi.org/project/MemoryOS">
      <img src="https://img.shields.io/badge/Platform-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey" alt="Supported Platforms">
    </a>
    <a href="https://memos-docs.openmem.net/home/overview/">
      <img src="https://img.shields.io/badge/Documentation-view-blue.svg" alt="Documentation">
    </a>
    <a href="https://arxiv.org/abs/2507.03724">
      <img src="https://img.shields.io/badge/arXiv-2507.03724-b31b1b.svg" alt="ArXiv Paper">
    </a>
    <a href="https://github.com/MemTensor/MemOS/discussions">
      <img src="https://img.shields.io/badge/GitHub-Discussions-181717.svg?logo=github" alt="GitHub Discussions">
    </a>
    <a href="https://discord.gg/Txbx3gebZR">
      <img src="https://img.shields.io/badge/Discord-join%20chat-7289DA.svg?logo=discord" alt="Discord">
    </a>
    <a href="https://statics.memtensor.com.cn/memos/qr-code.png">
      <img src="https://img.shields.io/badge/WeChat-Group-07C160.svg?logo=wechat" alt="WeChat Group">
    </a>
    <a href="https://opensource.org/license/apache-2-0/">
      <img src="https://img.shields.io/badge/License-Apache_2.0-green.svg?logo=apache" alt="License">
    </a>
    <a href="https://github.com/IAAR-Shanghai/Awesome-AI-Memory">
      <img alt="Awesome AI Memory" src="https://img.shields.io/badge/Resources-Awesome--AI--Memory-8A2BE2">
    </a>
  </p>

<p align="center">
  <strong>🎯 +43.70% Accuracy vs. OpenAI Memory</strong><br/>
  <strong>🏆 Top-tier long-term memory + personalization</strong><br/>
  <strong>💰 Saves 35.24% memory tokens</strong><br/>
  <sub>LoCoMo 75.80 • LongMemEval +40.43% • PrefEval-10 +2568% • PersonaMem +40.75%</sub>
  <!-- <a href="https://memos.openmem.net/">
    <img src="https://statics.memtensor.com.cn/memos/github_api_free_banner.gif" alt="MemOS Free API Banner">
  </a> -->

</p>

</div>

<!-- Get Free API: [Try API](https://memos-dashboard.openmem.net/quickstart/?source=github) -->

<!-- --- -->

<!-- <br> -->

## 🦞 Enhanced OpenClaw with MemOS Plugin

![](https://cdn.memtensor.com.cn/img/1770612303123_mnaisk_compressed.png)

🦞 Your lobster now has a working memory system — choose **Cloud** or **Local** to get started.

### ☁️ Cloud Plugin — Hosted Memory Service

- [**72% lower token usage**](https://x.com/MemOS_dev/status/2020854044583924111) — intelligent memory retrieval instead of loading full chat history
- [**Multi-agent memory sharing**](https://x.com/MemOS_dev/status/2020538135487062094) — multi-instance agents share memory via same user_id, automatic context handoff

Get your API key: [MemOS Dashboard](https://memos-dashboard.openmem.net/cn/login/)  
Full tutorial → [MemOS-Cloud-OpenClaw-Plugin](https://github.com/MemTensor/MemOS-Cloud-OpenClaw-Plugin)

### 🧠 Local Plugin — 100% On-Device Memory

- **Zero cloud dependency** — all data stays on your machine, persistent local SQLite storage
- **Hybrid search + task & skill evolution** — FTS5 + vector search, auto task summarization, reusable skills that self-upgrade
- **Multi-agent collaboration + Memory Viewer** — memory isolation, skill sharing, full web dashboard with 7 management pages

 🌐 [Homepage](https://memos-claw.openmem.net) · 
📖 [Documentation](https://memos-claw.openmem.net/docs/index.html) · 📦 [NPM](https://www.npmjs.com/package/@memtensor/memos-local-openclaw-plugin)

## 📌 MemOS: Memory Operating System for AI Agents

**MemOS** is a Memory Operating System for LLMs and AI agents that unifies **store / retrieve / manage** for long-term memory, enabling **context-aware and personalized** interactions with **KB**, **multi-modal**, **tool memory**, and **enterprise-grade** optimizations built in.



### Key Features

- **Unified Memory API**: A single API to add, retrieve, edit, and delete memory—structured as a graph, inspectable and editable by design, not a black-box embedding store.
- **Multi-Modal Memory**: Natively supports text, images, tool traces, and personas, retrieved and reasoned together in one memory system.
- **Multi-Cube Knowledge Base Management**: Manage multiple knowledge bases as composable memory cubes, enabling isolation, controlled sharing, and dynamic composition across users, projects, and agents.
- **Asynchronous Ingestion via MemScheduler**: Run memory operations asynchronously with millisecond-level latency for production stability under high concurrency.
- **Memory Feedback & Correction**: Refine memory with natural-language feedback—correcting, supplementing, or replacing existing memories over time.


### News

- **2026-03-08** · 🦞 **MemOS OpenClaw Plugin — Cloud & Local**  
  Official OpenClaw memory plugins launched. **Cloud Plugin**: hosted memory service with 72% lower token usage and multi-agent memory sharing ([MemOS-Cloud-OpenClaw-Plugin](https://github.com/MemTensor/MemOS-Cloud-OpenClaw-Plugin)). **Local Plugin** (`v1.0.0`): 100% on-device memory with persistent SQLite, hybrid search (FTS5 + vector), task summarization & skill evolution, multi-agent collaboration, and a full Memory Viewer dashboard.

- **2025-12-24** · 🎉 **MemOS v2.0: Stardust (星尘) Release**  
  Comprehensive KB (doc/URL parsing + cross-project sharing), memory feedback & precise deletion, multi-modal memory (images/charts), tool memory for agent planning, Redis Streams scheduling + DB optimizations, streaming/non-streaming chat, MCP upgrade, and lightweight quick/full deployment.
  <details>
    <summary>✨ <b>New Features</b></summary>

  **Knowledge Base & Memory**
  - Added knowledge base support for long-term memory from documents and URLs

  **Feedback & Memory Management**
  - Added natural language feedback and correction for memories
  - Added memory deletion API by memory ID
  - Added MCP support for memory deletion and feedback

  **Conversation & Retrieval**
  - Added chat API with memory-aware retrieval
  - Added memory filtering with custom tags (Cloud & Open Source)

  **Multimodal & Tool Memory**
  - Added tool memory for tool usage history
  - Added image memory support for conversations and documents

  </details>

  <details>
    <summary>📈 <b>Improvements</b></summary>

  **Data & Infrastructure**
  - Upgraded database for better stability and performance

  **Scheduler**
  - Rebuilt task scheduler with Redis Streams and queue isolation
  - Added task priority, auto-recovery, and quota-based scheduling

  **Deployment & Engineering**
  - Added lightweight deployment with quick and full modes

  </details>

  <details>
    <summary>🐞 <b>Bug Fixes</b></summary>

  **Memory Scheduling & Updates**
  - Fixed legacy scheduling API to ensure correct memory isolation
  - Fixed memory update logging to show new memories correctly

  </details>

- **2025-08-07** · 🎉 **MemOS v1.0.0 (MemCube) Release**
  First MemCube release with a word-game demo, LongMemEval evaluation, BochaAISearchRetriever integration, NebulaGraph support, improved search capabilities, and the official Playground launch.

  <details>
    <summary>✨ <b>New Features</b></summary>

  **Playground**
  - Expanded Playground features and algorithm performance.

  **MemCube Construction**
  - Added a text game demo based on the MemCube novel.

  **Extended Evaluation Set**
  - Added LongMemEval evaluation results and scripts.

  </details>

  <details>
    <summary>📈 <b>Improvements</b></summary>

  **Plaintext Memory**
  - Integrated internet search with Bocha.
  - Added support for Nebula database.
  - Added contextual understanding for the tree-structured plaintext memory search interface.

  </details>

  <details>
    <summary>🐞 <b>Bug Fixes</b></summary>

  **KV Cache Concatenation**
  - Fixed the concat_cache method.

  **Plaintext Memory**
  - Fixed Nebula search-related issues.

  </details>

- **2025-07-07** · 🎉 **MemOS v1.0: Stellar (星河) Preview Release**
  A SOTA Memory OS for LLMs is now open-sourced.
- **2025-07-04** · 🎉 **MemOS Paper Release**
  [MemOS: A Memory OS for AI System](https://arxiv.org/abs/2507.03724) is available on arXiv.
- **2024-07-04** · 🎉 **Memory3 Model Release at WAIC 2024**
  The Memory3 model, featuring a memory-layered architecture, was unveiled at the 2024 World Artificial Intelligence Conference.

<br>

## 🚀 Quickstart Guide

### ☁️ 1、Cloud API (Hosted)
#### Get API Key
- Sign up on the [MemOS dashboard](https://memos-dashboard.openmem.net/cn/quickstart/?source=landing)
- Go to **API Keys** and copy your key

#### Next Steps
- [MemOS Cloud Getting Started](https://memos-docs.openmem.net/memos_cloud/quick_start/)
  Connect to MemOS Cloud and enable memory in minutes.
- [MemOS Cloud Platform](https://memos.openmem.net/?from=/quickstart/)
  Explore the Cloud dashboard, features, and workflows.

### 🖥️ 2、Self-Hosted (Local/Private)
1. Get the repository.
    ```bash
    git clone https://github.com/MemTensor/MemOS.git
    cd MemOS
    pip install -r ./docker/requirements.txt
    ```
2. Configure `docker/.env.example` and copy to `MemOS/.env`
 - The `OPENAI_API_KEY`,`MOS_EMBEDDER_API_KEY`,`MEMRADER_API_KEY` and others can be applied for through [`BaiLian`](https://bailian.console.aliyun.com/?spm=a2c4g.11186623.0.0.2f2165b08fRk4l&tab=api#/api).
 - Fill in the corresponding configuration in the `MemOS/.env` file.
3. Start the service.

- Launch via Docker
  ###### Tips: Please ensure that Docker Compose is installed successfully and that you have navigated to the docker directory (via `cd docker`) before executing the following command.
  ```bash
  # Enter docker directory
  docker compose up
  ```
  ##### For detailed steps, see the[`Docker Reference`](https://docs.openmem.net/open_source/getting_started/rest_api_server/#method-1-docker-use-repository-dependency-package-imagestart-recommended-use).

- Launch via the uvicorn command line interface (CLI)
  ###### Tips: Please ensure that Neo4j and Qdrant are running before executing the following command.
  ```bash
  cd src
  uvicorn memos.api.server_api:app --host 0.0.0.0 --port 8001 --workers 1
  ```
  ##### For detailed integration steps, see the [`CLI Reference`](https://docs.openmem.net/open_source/getting_started/rest_api_server/#method-3client-install-with-CLI).



### Basic Usage (Self-Hosted)
  - Add User Message
    ```python
    import requests
    import json

    data = {
        "user_id": "8736b16e-1d20-4163-980b-a5063c3facdc",
        "mem_cube_id": "b32d0977-435d-4828-a86f-4f47f8b55bca",
        "messages": [
            {
                "role": "user",
                "content": "I like strawberry"
            }
        ],
        "async_mode": "sync"
    }
    headers = {
        "Content-Type": "application/json"
    }
    url = "http://localhost:8000/product/add"

    res = requests.post(url=url, headers=headers, data=json.dumps(data))
    print(f"result: {res.json()}")
    ```
  - Search User Memory
    ```python
    import requests
    import json

    data = {
        "query": "What do I like",
        "user_id": "8736b16e-1d20-4163-980b-a5063c3facdc",
        "mem_cube_id": "b32d0977-435d-4828-a86f-4f47f8b55bca"
    }
    headers = {
        "Content-Type": "application/json"
    }
    url = "http://localhost:8000/product/search"

    res = requests.post(url=url, headers=headers, data=json.dumps(data))
    print(f"result: {res.json()}")
    ```

<br>

## 📚 Resources

- **Awesome-AI-Memory**
 This is a curated repository dedicated to resources on memory and memory systems for large language models. It systematically collects relevant research papers, frameworks, tools, and practical insights. The repository aims to organize and present the rapidly evolving research landscape of LLM memory, bridging multiple research directions including natural language processing, information retrieval, agentic systems, and cognitive science.
- **Get started** 👉 [IAAR-Shanghai/Awesome-AI-Memory](https://github.com/IAAR-Shanghai/Awesome-AI-Memory)
- **MemOS Cloud OpenClaw Plugin**
  Official OpenClaw lifecycle plugin for MemOS Cloud. It automatically recalls context from MemOS before the agent starts and saves the conversation back to MemOS after the agent finishes.
- **Get started** 👉 [MemTensor/MemOS-Cloud-OpenClaw-Plugin](https://github.com/MemTensor/MemOS-Cloud-OpenClaw-Plugin)

<br>

## 💬 Community & Support

Join our community to ask questions, share your projects, and connect with other developers.

- **GitHub Issues**: Report bugs or request features in our <a href="https://github.com/MemTensor/MemOS/issues" target="_blank">GitHub Issues</a>.
- **GitHub Pull Requests**: Contribute code improvements via <a href="https://github.com/MemTensor/MemOS/pulls" target="_blank">Pull Requests</a>.
- **GitHub Discussions**: Participate in our <a href="https://github.com/MemTensor/MemOS/discussions" target="_blank">GitHub Discussions</a> to ask questions or share ideas.
- **Discord**: Join our <a href="https://discord.gg/Txbx3gebZR" target="_blank">Discord Server</a>.
- **WeChat**: Scan the QR code to join our WeChat group.

<div align="center">
  <img src="https://statics.memtensor.com.cn/memos/qr-code.png" alt="QR Code" width="300" />
</div>

<br>

## 📜 Citation

> [!NOTE]
> We publicly released the Short Version on **May 28, 2025**, making it the earliest work to propose the concept of a Memory Operating System for LLMs.

If you use MemOS in your research, we would appreciate citations to our papers.

```bibtex

@article{li2025memos_long,
  title={MemOS: A Memory OS for AI System},
  author={Li, Zhiyu and Song, Shichao and Xi, Chenyang and Wang, Hanyu and Tang, Chen and Niu, Simin and Chen, Ding and Yang, Jiawei and Li, Chunyu and Yu, Qingchen and Zhao, Jihao and Wang, Yezhaohui and Liu, Peng and Lin, Zehao and Wang, Pengyuan and Huo, Jiahao and Chen, Tianyi and Chen, Kai and Li, Kehang and Tao, Zhen and Ren, Junpeng and Lai, Huayi and Wu, Hao and Tang, Bo and Wang, Zhenren and Fan, Zhaoxin and Zhang, Ningyu and Zhang, Linfeng and Yan, Junchi and Yang, Mingchuan and Xu, Tong and Xu, Wei and Chen, Huajun and Wang, Haofeng and Yang, Hongkang and Zhang, Wentao and Xu, Zhi-Qin John and Chen, Siheng and Xiong, Feiyu},
  journal={arXiv preprint arXiv:2507.03724},
  year={2025},
  url={https://arxiv.org/abs/2507.03724}
}

@article{li2025memos_short,
  title={MemOS: An Operating System for Memory-Augmented Generation (MAG) in Large Language Models},
  author={Li, Zhiyu and Song, Shichao and Wang, Hanyu and Niu, Simin and Chen, Ding and Yang, Jiawei and Xi, Chenyang and Lai, Huayi and Zhao, Jihao and Wang, Yezhaohui and others},
  journal={arXiv preprint arXiv:2505.22101},
  year={2025},
  url={https://arxiv.org/abs/2505.22101}
}

@article{yang2024memory3,
author = {Yang, Hongkang and Zehao, Lin and Wenjin, Wang and Wu, Hao and Zhiyu, Li and Tang, Bo and Wenqiang, Wei and Wang, Jinbo and Zeyun, Tang and Song, Shichao and Xi, Chenyang and Yu, Yu and Kai, Chen and Xiong, Feiyu and Tang, Linpeng and Weinan, E},
title = {Memory$^3$: Language Modeling with Explicit Memory},
journal = {Journal of Machine Learning},
year = {2024},
volume = {3},
number = {3},
pages = {300--346},
issn = {2790-2048},
doi = {https://doi.org/10.4208/jml.240708},
url = {https://global-sci.com/article/91443/memory3-language-modeling-with-explicit-memory}
}
```

<br>

## 🙌 Contributing

We welcome contributions from the community! Please read our [contribution guidelines](https://memos-docs.openmem.net/open_source/contribution/overview/) to get started.

<br>

## 📄 License

MemOS is licensed under the [Apache 2.0 License](./LICENSE).


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/.gitignore
================================================
# Dependencies
node_modules

# Environment variables
.env
.env.*

# NPM
.npmrc

# System
.DS_Store
Thumbs.db

# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/README.md
================================================
# MemOS Cloud OpenClaw Plugin (Lifecycle)

Official plugin maintained by MemTensor.

A minimal OpenClaw lifecycle plugin that **recalls** memories from MemOS Cloud before each run and **adds** new messages to MemOS Cloud after each run.

## Features
- **Recall**: `before_agent_start` → `/search/memory`
- **Add**: `agent_end` → `/add/message`
- Uses **Token** auth (`Authorization: Token <MEMOS_API_KEY>`)

## Install

### Option A — NPM (Recommended)
```bash
openclaw plugins install @memtensor/memos-cloud-openclaw-plugin@latest
openclaw gateway restart
```

> **Note for Windows Users**:
> If you encounter `Error: spawn EINVAL`, this is a known issue with OpenClaw's plugin installer on Windows. Please use **Option B** (Manual Install) below.

Make sure it’s enabled in `~/.openclaw/openclaw.json`:
```json
{
  "plugins": {
    "entries": {
      "memos-cloud-openclaw-plugin": { "enabled": true }
    }
  }
}
```

### Option B — Manual Install (Workaround for Windows)
1. Download the latest `.tgz` from [NPM](https://www.npmjs.com/package/@memtensor/memos-cloud-openclaw-plugin).
2. Extract it to a local folder (e.g., `C:\Users\YourName\.openclaw\extensions\memos-cloud-openclaw-plugin`).
3. Configure `~/.openclaw/openclaw.json` (or `%USERPROFILE%\.openclaw\openclaw.json`):

```json
{
  "plugins": {
    "entries": {
      "memos-cloud-openclaw-plugin": { "enabled": true }
    },
    "load": {
      "paths": [
        "C:\\Users\\YourName\\.openclaw\\extensions\\memos-cloud-openclaw-plugin\\package"
      ]
    }
  }
}
```
*Note: The extracted folder usually contains a `package` subfolder. Point to the folder containing `package.json`.*

Restart the gateway after config changes.

## Environment Variables
The plugin tries env files in order (**openclaw → moltbot → clawdbot**). For each key, the first file with a value wins.
If none of these files exist (or the key is missing), it falls back to the process environment.

**Where to configure**
- Files (priority order):
  - `~/.openclaw/.env`
  - `~/.moltbot/.env`
  - `~/.clawdbot/.env`
- Each line is `KEY=value`

**Quick setup (shell)**
```bash
echo 'export MEMOS_API_KEY="mpg-..."' >> ~/.zshrc
source ~/.zshrc
# or

echo 'export MEMOS_API_KEY="mpg-..."' >> ~/.bashrc
source ~/.bashrc
```

**Quick setup (Windows PowerShell)**
```powershell
[System.Environment]::SetEnvironmentVariable("MEMOS_API_KEY", "mpg-...", "User")
```

If `MEMOS_API_KEY` is missing, the plugin will warn with setup instructions and the API key URL.

**Minimal config**
```env
MEMOS_API_KEY=YOUR_TOKEN
```

**Optional config**
- `MEMOS_BASE_URL` (default: `https://memos.memtensor.cn/api/openmem/v1`)
- `MEMOS_API_KEY` (required; Token auth) — get it at https://memos-dashboard.openmem.net/cn/apikeys/
- `MEMOS_USER_ID` (optional; default: `openclaw-user`)
- `MEMOS_CONVERSATION_ID` (optional override)
- `MEMOS_RECALL_GLOBAL` (default: `true`; when true, search does **not** pass conversation_id)
- `MEMOS_MULTI_AGENT_MODE` (default: `false`; enable multi-agent data isolation)
- `MEMOS_CONVERSATION_PREFIX` / `MEMOS_CONVERSATION_SUFFIX` (optional)
- `MEMOS_CONVERSATION_SUFFIX_MODE` (`none` | `counter`, default: `none`)
- `MEMOS_CONVERSATION_RESET_ON_NEW` (default: `true`, requires hooks.internal.enabled)
- `MEMOS_RECALL_FILTER_ENABLED` (default: `false`; run model-based memory filtering before injection)
- `MEMOS_RECALL_FILTER_BASE_URL` (OpenAI-compatible base URL, e.g. `http://127.0.0.1:11434/v1`)
- `MEMOS_RECALL_FILTER_API_KEY` (optional; required if your endpoint needs auth)
- `MEMOS_RECALL_FILTER_MODEL` (model name used to filter recall candidates)
- `MEMOS_RECALL_FILTER_TIMEOUT_MS` (default: `6000`)
- `MEMOS_RECALL_FILTER_RETRIES` (default: `0`)
- `MEMOS_RECALL_FILTER_CANDIDATE_LIMIT` (default: `30` per category)
- `MEMOS_RECALL_FILTER_MAX_ITEM_CHARS` (default: `500`)
- `MEMOS_RECALL_FILTER_FAIL_OPEN` (default: `true`; fallback to unfiltered recall on failure)

## Optional Plugin Config
In `plugins.entries.memos-cloud-openclaw-plugin.config`:
```json
{
  "baseUrl": "https://memos.memtensor.cn/api/openmem/v1",
  "apiKey": "YOUR_API_KEY",
  "userId": "memos_user_123",
  "conversationId": "openclaw-main",
  "queryPrefix": "important user context preferences decisions ",
  "recallEnabled": true,
  "recallGlobal": true,
  "addEnabled": true,
  "captureStrategy": "last_turn",
  "maxItemChars": 8000,
  "includeAssistant": true,
  "conversationIdPrefix": "",
  "conversationIdSuffix": "",
  "conversationSuffixMode": "none",
  "resetOnNew": true,
  "knowledgebaseIds": [],
  "memoryLimitNumber": 6,
  "preferenceLimitNumber": 6,
  "includePreference": true,
  "includeToolMemory": false,
  "toolMemoryLimitNumber": 6,
  "relativity": 0.45,
  "tags": ["openclaw"],
  "agentId": "",
  "multiAgentMode": false,
  "asyncMode": true,
  "recallFilterEnabled": false,
  "recallFilterBaseUrl": "http://127.0.0.1:11434/v1",
  "recallFilterApiKey": "",
  "recallFilterModel": "qwen2.5:7b",
  "recallFilterTimeoutMs": 6000,
  "recallFilterRetries": 0,
  "recallFilterCandidateLimit": 30,
  "recallFilterMaxItemChars": 500,
  "recallFilterFailOpen": true
}
```

## How it Works
- **Recall** (`before_agent_start`)
  - Builds a `/search/memory` request using `user_id`, `query` (= prompt + optional prefix), and optional filters.
  - Default **global recall**: when `recallGlobal=true`, it does **not** pass `conversation_id`.
  - Optional second-pass filtering: if `recallFilterEnabled=true`, candidates are sent to your configured model and only returned `keep` items are injected.
  - Injects a stable MemOS recall protocol via `appendSystemContext`, while the retrieved `<memories>` block remains in `prependContext`.

- **Add** (`agent_end`)
  - Builds a `/add/message` request with the **last turn** by default (user + assistant).
  - Sends `messages` with `user_id`, `conversation_id`, and optional `tags/info/agent_id/app_id`.

## Multi-Agent Support
The plugin provides native support for multi-agent architectures (via the `agent_id` parameter):
- **Enable Mode**: Set `"multiAgentMode": true` in config or `MEMOS_MULTI_AGENT_MODE=true` in env variables (default is `false`).
- **Dynamic Context**: When enabled, it automatically captures `ctx.agentId` during OpenClaw lifecycle hooks. (Note: the default OpenClaw agent `"main"` is ignored to preserve backwards compatibility for single-agent users).
- **Data Isolation**: The `agent_id` is automatically injected into both `/search/memory` and `/add/message` requests. This ensures completely isolated memory and message histories for different agents, even under the same user or session.
- **Static Override**: You can also force a specific agent ID by setting `"agentId": "your_agent_id"` in the plugin's `config`.

## Notes
- `conversation_id` defaults to OpenClaw `sessionKey` (unless `conversationId` is provided). **TODO**: consider binding to OpenClaw `sessionId` directly.
- Optional **prefix/suffix** via env or config; `conversationSuffixMode=counter` increments on `/new` (requires `hooks.internal.enabled`).

## Acknowledgements
- Thanks to @anatolykoptev (Contributor) — LinkedIn: https://www.linkedin.com/in/koptev?utm_source=share&utm_campaign=share_via&utm_content=profile&utm_medium=ios_app


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/README_ZH.md
================================================
# MemOS Cloud OpenClaw Plugin(Lifecycle 插件)

官方维护:MemTensor。

这是一个最小可用的 OpenClaw lifecycle 插件,功能是:
- **召回记忆**:在每轮对话前从 MemOS Cloud 检索记忆并注入上下文
- **添加记忆**:在每轮对话结束后把消息写回 MemOS Cloud

## 功能
- **Recall**:`before_agent_start` → `/search/memory`
- **Add**:`agent_end` → `/add/message`
- 使用 **Token** 认证(`Authorization: Token <MEMOS_API_KEY>`)

## 安装

### 方式 A — NPM(推荐)
```bash
openclaw plugins install @memtensor/memos-cloud-openclaw-plugin@latest
openclaw gateway restart
```

> **Windows 用户注意**:
> 如果遇到 `Error: spawn EINVAL` 报错,这是 OpenClaw Windows 安装器的已知问题。请使用下方的 **方式 B**(手动安装)。

确认 `~/.openclaw/openclaw.json` 中已启用:
```json
{
  "plugins": {
    "entries": {
      "memos-cloud-openclaw-plugin": { "enabled": true }
    }
  }
}
```

### 方式 B — 手动安装(Windows 解决方案)
1. 从 [NPM](https://www.npmjs.com/package/@memtensor/memos-cloud-openclaw-plugin) 下载最新的 `.tgz` 包。
2. 解压到本地目录(例如 `C:\Users\YourName\.openclaw\extensions\memos-cloud-openclaw-plugin`)。
3. 修改配置 `~/.openclaw/openclaw.json`(或 `%USERPROFILE%\.openclaw\openclaw.json`):

```json
{
  "plugins": {
    "entries": {
      "memos-cloud-openclaw-plugin": { "enabled": true }
    },
    "load": {
      "paths": [
        "C:\\Users\\YourName\\.openclaw\\extensions\\memos-cloud-openclaw-plugin\\package"
      ]
    }
  }
}
```
*注意:解压后的文件夹通常包含一个 `package` 子文件夹,请指向包含 `package.json` 的那层目录。*

修改配置后需要重启 gateway。

## 环境变量
插件按顺序读取 env 文件(**openclaw → moltbot → clawdbot**),每个键优先使用最先匹配到的值。
若三个文件都不存在(或该键未找到),才会回退到进程环境变量。

**配置位置**
- 文件(优先级顺序):
  - `~/.openclaw/.env`
  - `~/.moltbot/.env`
  - `~/.clawdbot/.env`
- 每行格式:`KEY=value`

**快速配置(Shell)**
```bash
echo 'export MEMOS_API_KEY="mpg-..."' >> ~/.zshrc
source ~/.zshrc
# 或者

echo 'export MEMOS_API_KEY="mpg-..."' >> ~/.bashrc
source ~/.bashrc
```

**快速配置(Windows PowerShell)**
```powershell
[System.Environment]::SetEnvironmentVariable("MEMOS_API_KEY", "mpg-...", "User")
```

若未读取到 `MEMOS_API_KEY`,插件会提示配置方式并附 API Key 获取地址。

**最小配置**
```env
MEMOS_API_KEY=YOUR_TOKEN
```

**可选配置**
- `MEMOS_BASE_URL`(默认 `https://memos.memtensor.cn/api/openmem/v1`)
- `MEMOS_API_KEY`(必填,Token 认证)—— 获取地址:https://memos-dashboard.openmem.net/cn/apikeys/
- `MEMOS_USER_ID`(可选,默认 `openclaw-user`)
- `MEMOS_CONVERSATION_ID`(可选覆盖)
- `MEMOS_RECALL_GLOBAL`(默认 `true`;为 true 时检索不传 conversation_id)
- `MEMOS_MULTI_AGENT_MODE`(默认 `false`;是否开启多 Agent 数据隔离模式)
- `MEMOS_CONVERSATION_PREFIX` / `MEMOS_CONVERSATION_SUFFIX`(可选)
- `MEMOS_CONVERSATION_SUFFIX_MODE`(`none` | `counter`,默认 `none`)
- `MEMOS_CONVERSATION_RESET_ON_NEW`(默认 `true`,需 hooks.internal.enabled)
- `MEMOS_RECALL_FILTER_ENABLED`(默认 `false`;开启后先用你指定的模型过滤召回记忆再注入)
- `MEMOS_RECALL_FILTER_BASE_URL`(OpenAI 兼容接口,例如 `http://127.0.0.1:11434/v1`)
- `MEMOS_RECALL_FILTER_API_KEY`(可选,若你的接口需要鉴权)
- `MEMOS_RECALL_FILTER_MODEL`(用于筛选记忆的模型名)
- `MEMOS_RECALL_FILTER_TIMEOUT_MS`(默认 `6000`)
- `MEMOS_RECALL_FILTER_RETRIES`(默认 `0`)
- `MEMOS_RECALL_FILTER_CANDIDATE_LIMIT`(默认每类 `30` 条)
- `MEMOS_RECALL_FILTER_MAX_ITEM_CHARS`(默认 `500`)
- `MEMOS_RECALL_FILTER_FAIL_OPEN`(默认 `true`;筛选失败时回退为“不过滤”)

## 可选插件配置
在 `plugins.entries.memos-cloud-openclaw-plugin.config` 中设置:
```json
{
  "baseUrl": "https://memos.memtensor.cn/api/openmem/v1",
  "apiKey": "YOUR_API_KEY",
  "userId": "memos_user_123",
  "conversationId": "openclaw-main",
  "queryPrefix": "important user context preferences decisions ",
  "recallEnabled": true,
  "recallGlobal": true,
  "addEnabled": true,
  "captureStrategy": "last_turn",
  "includeAssistant": true,
  "conversationIdPrefix": "",
  "conversationIdSuffix": "",
  "conversationSuffixMode": "none",
  "resetOnNew": true,
  "memoryLimitNumber": 6,
  "preferenceLimitNumber": 6,
  "knowledgebaseIds": [],
  "includePreference": true,
  "includeToolMemory": false,
  "toolMemoryLimitNumber": 6,
  "tags": ["openclaw"],
  "agentId": "",
  "multiAgentMode": false,
  "asyncMode": true,
  "recallFilterEnabled": false,
  "recallFilterBaseUrl": "http://127.0.0.1:11434/v1",
  "recallFilterApiKey": "",
  "recallFilterModel": "qwen2.5:7b",
  "recallFilterTimeoutMs": 6000,
  "recallFilterRetries": 0,
  "recallFilterCandidateLimit": 30,
  "recallFilterMaxItemChars": 500,
  "recallFilterFailOpen": true
}
```

## 工作原理
### 1) 召回(before_agent_start)
- 组装 `/search/memory` 请求
  - `user_id`、`query`(= prompt + 可选前缀)
  - 默认**全局召回**:`recallGlobal=true` 时不传 `conversation_id`
  - 可选 `filter` / `knowledgebase_ids`
- (可选)若开启 `recallFilterEnabled`,会先把 `memory/preference/tool_memory` 候选发给你配置的模型做二次筛选,只保留 `keep` 的条目
- 将稳定的 MemOS 召回协议通过 `appendSystemContext` 注入,而检索到的 `<memories>` 数据块继续通过 `prependContext` 注入

### 2) 添加(agent_end)
- 默认只写**最后一轮**(user + assistant)
- 构造 `/add/message` 请求:
  - `user_id`、`conversation_id`
  - `messages` 列表
  - 可选 `tags / info / agent_id / app_id`

## 多Agent支持(Multi-Agent)
插件内置对多Agent模式的支持(`agent_id` 参数):
- **开启模式**:需要在配置中设置 `"multiAgentMode": true` 或在环境变量中设置 `MEMOS_MULTI_AGENT_MODE=true`(默认为 `false`)。
- **动态获取**:开启后,执行生命周期钩子时会自动读取上下文中的 `ctx.agentId`。(注:OpenClaw 的默认 Agent `"main"` 会被自动忽略,以保证老用户的单 Agent 数据兼容性)。
- **数据隔离**:在调用 `/search/memory`(检索记忆)和 `/add/message`(添加记录)时会自动附带该 `agent_id`,从而保证即使是同一用户下的不同 Agent 之间,记忆和反馈数据也是完全隔离的。
- **静态配置**:如果需要,也可在上述插件的 `config` 中显式指定 `"agentId": "your_agent_id"` 作为固定值。

## 说明
- 未显式指定 `conversation_id` 时,默认使用 OpenClaw `sessionKey`。**TODO**:后续考虑直接绑定 OpenClaw `sessionId`。
- 可配置前后缀;`conversationSuffixMode=counter` 时会在 `/new` 递增(需 `hooks.internal.enabled`)。

## 致谢
- 感谢 @anatolykoptev(Contributor)— 领英:https://www.linkedin.com/in/koptev?utm_source=share&utm_campaign=share_via&utm_content=profile&utm_medium=ios_app


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/clawdbot.plugin.json
================================================
{
  "id": "memos-cloud-openclaw-plugin",
  "name": "MemOS Cloud OpenClaw Plugin",
  "description": "MemOS Cloud recall + add memory via lifecycle hooks",
  "version": "0.1.9",
  "kind": "lifecycle",
  "main": "./index.js",
  "configSchema": {
    "type": "object",
    "properties": {
      "baseUrl": {
        "type": "string",
        "description": "MemOS Cloud base URL"
      },
      "apiKey": {
        "type": "string",
        "description": "MemOS API Key (Token auth; supports ~/.openclaw/.env, ~/.moltbot/.env, ~/.clawdbot/.env; falls back to process env)"
      },
      "userId": {
        "type": "string",
        "description": "MemOS user_id (default: openclaw-user)",
        "default": "openclaw-user"
      },
      "conversationId": {
        "type": "string",
        "description": "Override conversation_id"
      },
      "conversationIdPrefix": {
        "type": "string",
        "description": "conversation_id prefix"
      },
      "conversationIdSuffix": {
        "type": "string",
        "description": "conversation_id suffix"
      },
      "conversationSuffixMode": {
        "type": "string",
        "enum": [
          "none",
          "counter"
        ],
        "default": "none"
      },
      "resetOnNew": {
        "type": "boolean",
        "default": true
      },
      "queryPrefix": {
        "type": "string",
        "description": "Prefix added to search queries"
      },
      "maxQueryChars": {
        "type": "integer",
        "description": "Max chars for search query"
      },
      "recallEnabled": {
        "type": "boolean",
        "default": true
      },
      "recallGlobal": {
        "type": "boolean",
        "default": true
      },
      "addEnabled": {
        "type": "boolean",
        "default": true
      },
      "captureStrategy": {
        "type": "string",
        "enum": [
          "last_turn",
          "full_session"
        ],
        "default": "last_turn"
      },
      "maxMessageChars": {
        "type": "integer",
        "description": "Max chars per message when adding",
        "default": 20000
      },
      "maxItemChars": {
        "type": "integer",
        "description": "Max chars per memory item when injecting prompt",
        "default": 8000
      },
      "includeAssistant": {
        "type": "boolean",
        "default": true
      },
      "memoryLimitNumber": {
        "type": "integer",
        "default": 6
      },
      "preferenceLimitNumber": {
        "type": "integer",
        "default": 6
      },
      "includePreference": {
        "type": "boolean",
        "default": true
      },
      "includeToolMemory": {
        "type": "boolean",
        "default": false
      },
      "toolMemoryLimitNumber": {
        "type": "integer",
        "default": 6
      },
      "filter": {
        "type": "object",
        "description": "MemOS search filter"
      },
      "knowledgebaseIds": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "tags": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "info": {
        "type": "object",
        "additionalProperties": true
      },
      "agentId": {
        "type": "string"
      },
      "multiAgentMode": {
        "type": "boolean",
        "default": false
      },
      "appId": {
        "type": "string"
      },
      "allowPublic": {
        "type": "boolean",
        "default": false
      },
      "allowKnowledgebaseIds": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "asyncMode": {
        "type": "boolean",
        "default": true
      },
      "timeoutMs": {
        "type": "integer",
        "default": 5000
      },
      "retries": {
        "type": "integer",
        "default": 1
      },
      "throttleMs": {
        "type": "integer",
        "default": 0
      }
    },
    "additionalProperties": false
  }
}


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/index.js
================================================
#!/usr/bin/env node
import {
  addMessage,
  buildConfig,
  extractResultData,
  extractText,
  formatRecallHookResult,
  USER_QUERY_MARKER,
  searchMemory,
} from "./lib/memos-cloud-api.js";
import { startUpdateChecker } from "./lib/check-update.js";
let lastCaptureTime = 0;
const conversationCounters = new Map();
const API_KEY_HELP_URL = "https://memos-dashboard.openmem.net/cn/apikeys/";
const ENV_FILE_SEARCH_HINTS = ["~/.openclaw/.env", "~/.moltbot/.env", "~/.clawdbot/.env"];
const MEMOS_SOURCE = "openclaw";

function warnMissingApiKey(log, context) {
  const heading = "[memos-cloud] Missing MEMOS_API_KEY (Token auth)";
  const header = `${heading}${context ? `; ${context} skipped` : ""}. Configure it with:`;
  log.warn?.(
    [
      header,
      "echo 'export MEMOS_API_KEY=\"mpg-...\"' >> ~/.zshrc",
      "source ~/.zshrc",
      "or",
      "echo 'export MEMOS_API_KEY=\"mpg-...\"' >> ~/.bashrc",
      "source ~/.bashrc",
      "or",
      "[System.Environment]::SetEnvironmentVariable(\"MEMOS_API_KEY\", \"mpg-...\", \"User\")",
      `Get API key: ${API_KEY_HELP_URL}`,
    ].join("\n"),
  );
}

function stripPrependedPrompt(content) {
  if (!content) return content;
  const idx = content.lastIndexOf(USER_QUERY_MARKER);
  if (idx === -1) return content;
  return content.slice(idx + USER_QUERY_MARKER.length).trimStart();
}

function getCounterSuffix(sessionKey) {
  if (!sessionKey) return "";
  const current = conversationCounters.get(sessionKey) ?? 0;
  return current > 0 ? `#${current}` : "";
}

function bumpConversationCounter(sessionKey) {
  if (!sessionKey) return;
  const current = conversationCounters.get(sessionKey) ?? 0;
  conversationCounters.set(sessionKey, current + 1);
}

function getEffectiveAgentId(cfg, ctx) {
  if (!cfg.multiAgentMode) {
    return cfg.agentId;
  }
  const agentId = ctx?.agentId || cfg.agentId;
  return agentId === "main" ? undefined : agentId;
}

function resolveConversationId(cfg, ctx) {
  if (cfg.conversationId) return cfg.conversationId;
  // TODO: consider binding conversation_id directly to OpenClaw sessionId (prefer ctx.sessionId).
  const agentId = getEffectiveAgentId(cfg, ctx);
  const base = ctx?.sessionKey || ctx?.sessionId || (agentId ? `openclaw:${agentId}` : "");
  const dynamicSuffix = cfg.conversationSuffixMode === "counter" ? getCounterSuffix(ctx?.sessionKey) : "";
  const prefix = cfg.conversationIdPrefix || "";
  const suffix = cfg.conversationIdSuffix || "";
  if (base) return `${prefix}${base}${dynamicSuffix}${suffix}`;
  return `${prefix}openclaw-${Date.now()}${dynamicSuffix}${suffix}`;
}

function buildSearchPayload(cfg, prompt, ctx) {
  const queryRaw = `${cfg.queryPrefix || ""}${prompt}`;
  const query =
    Number.isFinite(cfg.maxQueryChars) && cfg.maxQueryChars > 0
      ? queryRaw.slice(0, cfg.maxQueryChars)
      : queryRaw;

  const payload = {
    user_id: cfg.userId,
    query,
    source: MEMOS_SOURCE,
  };

  if (!cfg.recallGlobal) {
    const conversationId = resolveConversationId(cfg, ctx);
    if (conversationId) payload.conversation_id = conversationId;
  }

  let filterObj = cfg.filter ? JSON.parse(JSON.stringify(cfg.filter)) : null;
  const agentId = getEffectiveAgentId(cfg, ctx);

  if (agentId) {
    if (filterObj) {
      if (Array.isArray(filterObj.and)) {
        filterObj.and.push({ agent_id: agentId });
      } else {
        filterObj = { and: [filterObj, { agent_id: agentId }] };
      }
    } else {
      filterObj = { agent_id: agentId };
    }
  }

  if (filterObj) payload.filter = filterObj;

  if (cfg.knowledgebaseIds?.length) payload.knowledgebase_ids = cfg.knowledgebaseIds;

  payload.memory_limit_number = cfg.memoryLimitNumber;
  payload.include_preference = cfg.includePreference;
  payload.preference_limit_number = cfg.preferenceLimitNumber;
  payload.include_tool_memory = cfg.includeToolMemory;
  payload.tool_memory_limit_number = cfg.toolMemoryLimitNumber;
  payload.relativity = cfg.relativity;

  return payload;
}

function buildAddMessagePayload(cfg, messages, ctx) {
  const payload = {
    user_id: cfg.userId,
    conversation_id: resolveConversationId(cfg, ctx),
    messages,
    source: MEMOS_SOURCE,
  };

  const agentId = getEffectiveAgentId(cfg, ctx);
  if (agentId) payload.agent_id = agentId;
  if (cfg.appId) payload.app_id = cfg.appId;
  if (cfg.tags?.length) payload.tags = cfg.tags;

  const info = {
    source: "openclaw",
    sessionKey: ctx?.sessionKey,
    agentId: ctx?.agentId,
    ...(cfg.info || {}),
  };
  if (Object.keys(info).length > 0) payload.info = info;

  payload.allow_public = cfg.allowPublic;
  if (cfg.allowKnowledgebaseIds?.length) payload.allow_knowledgebase_ids = cfg.allowKnowledgebaseIds;
  payload.async_mode = cfg.asyncMode;

  return payload;
}

function pickLastTurnMessages(messages, cfg) {
  const lastUserIndex = messages
    .map((m, idx) => ({ m, idx }))
    .filter(({ m }) => m?.role === "user")
    .map(({ idx }) => idx)
    .pop();

  if (lastUserIndex === undefined) return [];

  const slice = messages.slice(lastUserIndex);
  const results = [];

  for (const msg of slice) {
    if (!msg || !msg.role) continue;
    if (msg.role === "user") {
      const content = stripPrependedPrompt(extractText(msg.content));
      if (content) results.push({ role: "user", content: truncate(content, cfg.maxMessageChars) });
      continue;
    }
    if (msg.role === "assistant" && cfg.includeAssistant) {
      const content = extractText(msg.content);
      if (content) results.push({ role: "assistant", content: truncate(content, cfg.maxMessageChars) });
    }
  }

  return results;
}

function pickFullSessionMessages(messages, cfg) {
  const results = [];
  for (const msg of messages) {
    if (!msg || !msg.role) continue;
    if (msg.role === "user") {
      const content = stripPrependedPrompt(extractText(msg.content));
      if (content) results.push({ role: "user", content: truncate(content, cfg.maxMessageChars) });
    }
    if (msg.role === "assistant" && cfg.includeAssistant) {
      const content = extractText(msg.content);
      if (content) results.push({ role: "assistant", content: truncate(content, cfg.maxMessageChars) });
    }
  }
  return results;
}

function truncate(text, maxLen) {
  if (!text) return "";
  if (!maxLen) return text;
  return text.length > maxLen ? `${text.slice(0, maxLen)}...` : text;
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function parseModelJson(text) {
  if (!text || typeof text !== "string") return null;
  const trimmed = text.trim();
  if (!trimmed) return null;
  try {
    return JSON.parse(trimmed);
  } catch {
    // Some models wrap JSON in markdown code fences.
  }
  const fenceMatch = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
  if (fenceMatch?.[1]) {
    try {
      return JSON.parse(fenceMatch[1].trim());
    } catch {
      return null;
    }
  }
  const first = trimmed.indexOf("{");
  const last = trimmed.lastIndexOf("}");
  if (first >= 0 && last > first) {
    try {
      return JSON.parse(trimmed.slice(first, last + 1));
    } catch {
      return null;
    }
  }
  return null;
}

function normalizeIndexList(value, maxLen) {
  if (!Array.isArray(value)) return [];
  const seen = new Set();
  const out = [];
  for (const v of value) {
    if (!Number.isInteger(v)) continue;
    if (v < 0 || v >= maxLen) continue;
    if (seen.has(v)) continue;
    seen.add(v);
    out.push(v);
  }
  return out;
}

function buildRecallCandidates(data, cfg) {
  const limit = Number.isFinite(cfg.recallFilterCandidateLimit) ? Math.max(0, cfg.recallFilterCandidateLimit) : 30;
  const maxChars = Number.isFinite(cfg.recallFilterMaxItemChars) ? Math.max(80, cfg.recallFilterMaxItemChars) : 500;
  const memoryList = Array.isArray(data?.memory_detail_list) ? data.memory_detail_list : [];
  const preferenceList = Array.isArray(data?.preference_detail_list) ? data.preference_detail_list : [];
  const toolList = Array.isArray(data?.tool_memory_detail_list) ? data.tool_memory_detail_list : [];

  const memoryCandidates = memoryList.slice(0, limit).map((item, idx) => ({
    idx,
    text: truncate(item?.memory_value || item?.memory_key || "", maxChars),
    relativity: item?.relativity,
  }));
  const preferenceCandidates = preferenceList.slice(0, limit).map((item, idx) => ({
    idx,
    text: truncate(item?.preference || "", maxChars),
    relativity: item?.relativity,
    preference_type: item?.preference_type || "",
  }));
  const toolCandidates = toolList.slice(0, limit).map((item, idx) => ({
    idx,
    text: truncate(item?.tool_value || "", maxChars),
    relativity: item?.relativity,
  }));

  return {
    memoryList,
    preferenceList,
    toolList,
    candidatePayload: {
      memory: memoryCandidates,
      preference: preferenceCandidates,
      tool_memory: toolCandidates,
    },
  };
}

function applyRecallDecision(data, decision, lists) {
  const keep = decision?.keep || {};
  const memoryIdx = normalizeIndexList(keep.memory, lists.memoryList.length);
  const preferenceIdx = normalizeIndexList(keep.preference, lists.preferenceList.length);
  const toolIdx = normalizeIndexList(keep.tool_memory, lists.toolList.length);

  return {
    ...data,
    memory_detail_list: memoryIdx.map((idx) => lists.memoryList[idx]),
    preference_detail_list: preferenceIdx.map((idx) => lists.preferenceList[idx]),
    tool_memory_detail_list: toolIdx.map((idx) => lists.toolList[idx]),
  };
}

async function callRecallFilterModel(cfg, userPrompt, candidatePayload) {
  const headers = {
    "Content-Type": "application/json",
  };
  if (cfg.recallFilterApiKey) {
    headers.Authorization = `Bearer ${cfg.recallFilterApiKey}`;
  }

  const modelInput = {
    user_query: userPrompt,
    candidate_memories: candidatePayload,
    output_schema: {
      keep: {
        memory: ["number index"],
        preference: ["number index"],
        tool_memory: ["number index"],
      },
      reason: "optional short string",
    },
  };

  const body = {
    model: cfg.recallFilterModel,
    temperature: 0,
    messages: [
      {
        role: "system",
        content:
          "You are a strict memory relevance judge. Return JSON only. Keep only items directly useful for answering current user query. If unsure, do not keep.",
      },
      {
        role: "user",
        content: JSON.stringify(modelInput),
      },
    ],
  };

  let lastError;
  const retries = Number.isFinite(cfg.recallFilterRetries) ? Math.max(0, cfg.recallFilterRetries) : 0;
  const timeoutMs = Number.isFinite(cfg.recallFilterTimeoutMs) ? Math.max(1000, cfg.recallFilterTimeoutMs) : 6000;

  for (let attempt = 0; attempt <= retries; attempt += 1) {
    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
      const res = await fetch(`${cfg.recallFilterBaseUrl}/chat/completions`, {
        method: "POST",
        headers,
        body: JSON.stringify(body),
        signal: controller.signal,
      });
      clearTimeout(timeoutId);
      if (!res.ok) {
        throw new Error(`HTTP ${res.status}`);
      }
      const json = await res.json();
      const text = json?.choices?.[0]?.message?.content || "";
      const parsed = parseModelJson(text);
      if (!parsed || typeof parsed !== "object") {
        throw new Error("invalid JSON output from recall filter model");
      }
      return parsed;
    } catch (err) {
      lastError = err;
      if (attempt < retries) {
        await sleep(120 * (attempt + 1));
      }
    }
  }
  throw lastError;
}

async function maybeFilterRecallData(cfg, data, userPrompt, log) {
  if (!cfg.recallFilterEnabled) return data;
  if (!cfg.recallFilterBaseUrl || !cfg.recallFilterModel) {
    log.warn?.("[memos-cloud] recall filter enabled but missing recallFilterBaseUrl/recallFilterModel; skip filter");
    return data;
  }
  const lists = buildRecallCandidates(data, cfg);
  const hasCandidates =
    lists.candidatePayload.memory.length > 0 ||
    lists.candidatePayload.preference.length > 0 ||
    lists.candidatePayload.tool_memory.length > 0;
  if (!hasCandidates) return data;

  try {
    const decision = await callRecallFilterModel(cfg, userPrompt, lists.candidatePayload);
    return applyRecallDecision(data, decision, lists);
  } catch (err) {
    log.warn?.(`[memos-cloud] recall filter failed: ${String(err)}`);
    return cfg.recallFilterFailOpen ? data : { ...data, memory_detail_list: [], preference_detail_list: [], tool_memory_detail_list: [] };
  }
}

export default {
  id: "memos-cloud-openclaw-plugin",
  name: "MemOS Cloud OpenClaw Plugin",
  description: "MemOS Cloud recall + add memory via lifecycle hooks",
  kind: "lifecycle",

  register(api) {
    const cfg = buildConfig(api.pluginConfig);
    const log = api.logger ?? console;

    // Start 12-hour background update interval
    startUpdateChecker(log);

    if (!cfg.envFileStatus?.found) {
      const searchPaths = cfg.envFileStatus?.searchPaths?.join(", ") ?? ENV_FILE_SEARCH_HINTS.join(", ");
      log.warn?.(`[memos-cloud] No .env found in ${searchPaths}; falling back to process env or plugin config.`);
    }

    if (cfg.conversationSuffixMode === "counter" && cfg.resetOnNew) {
      if (api.config?.hooks?.internal?.enabled !== true) {
        log.warn?.("[memos-cloud] command:new hook requires hooks.internal.enabled = true");
      }
      api.registerHook(
        ["command:new"],
        (event) => {
          if (event?.type === "command" && event?.action === "new") {
            bumpConversationCounter(event.sessionKey);
          }
        },
        {
          name: "memos-cloud-conversation-new",
          description: "Increment MemOS conversation suffix on /new",
        },
      );
    }

    api.on("before_agent_start", async (event, ctx) => {
      if (!cfg.recallEnabled) return;
      if (!event?.prompt || event.prompt.length < 3) return;
      if (!cfg.apiKey) {
        warnMissingApiKey(log, "recall");
        return;
      }

      try {
        const payload = buildSearchPayload(cfg, event.prompt, ctx);
        const result = await searchMemory(cfg, payload);
        const resultData = extractResultData(result);
        if (!resultData) return;
        const filteredData = await maybeFilterRecallData(cfg, resultData, event.prompt, log);
        const hookResult = formatRecallHookResult({ data: filteredData }, {
          wrapTagBlocks: true,
          relativity: payload.relativity,
          maxItemChars: cfg.maxItemChars,
        });
        if (!hookResult.appendSystemContext && !hookResult.prependContext) return;

        return hookResult;
      } catch (err) {
        log.warn?.(`[memos-cloud] recall failed: ${String(err)}`);
      }
    });

    api.on("agent_end", async (event, ctx) => {
      if (!cfg.addEnabled) return;
      if (!event?.success || !event?.messages?.length) return;
      if (!cfg.apiKey) {
        warnMissingApiKey(log, "add");
        return;
      }

      const now = Date.now();
      if (cfg.throttleMs && now - lastCaptureTime < cfg.throttleMs) {
        return;
      }
      lastCaptureTime = now;

      try {
        const messages =
          cfg.captureStrategy === "full_session"
            ? pickFullSessionMessages(event.messages, cfg)
            : pickLastTurnMessages(event.messages, cfg);

        if (!messages.length) return;

        const payload = buildAddMessagePayload(cfg, messages, ctx);
        await addMessage(cfg, payload);
      } catch (err) {
        log.warn?.(`[memos-cloud] add failed: ${String(err)}`);
      }
    });
  },
};


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/lib/check-update.js
================================================
import https from "https";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { spawn, exec } from "child_process";
import os from "os";

/**
 * Kill a spawned child process and its entire process tree.
 */
function killProcessTree(child) {
  try {
    if (process.platform === "win32") {
      exec(`taskkill /pid ${child.pid} /T /F`, () => {});
    } else {
      // On Unix, kill the process group
      process.kill(-child.pid, "SIGKILL");
    }
  } catch (e) {
    // Fallback: try the basic kill
    try { child.kill("SIGKILL"); } catch (_) {}
  }
}

let isUpdating = false;

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const CHECK_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours check interval
const UPDATE_TIMEOUT = 3 * 60 * 1000; // 3 minutes timeout for the CLI update command to finish
const PLUGIN_NAME = "@memtensor/memos-cloud-openclaw-plugin";
const CHECK_FILE = path.join(os.tmpdir(), "memos_openclaw_update_check.json");

const ANSI = {
  RESET: "\x1b[0m",
  GREEN: "\x1b[32m",
  YELLOW: "\x1b[33m",
  CYAN: "\x1b[36m",
  RED: "\x1b[31m"
};


function getPackageVersion() {
  try {
    const pkgPath = path.join(__dirname, "..", "package.json");
    const pkgData = fs.readFileSync(pkgPath, "utf-8");
    const pkg = JSON.parse(pkgData);
    return pkg.version;
  } catch (err) {
    return null;
  }
}

function getLatestVersion(log) {
  return new Promise((resolve, reject) => {
    const req = https.get(
      `https://registry.npmjs.org/${PLUGIN_NAME}/latest`,
      { timeout: 5000 },
      (res) => {
        if (res.statusCode !== 200) {
          req.destroy();
          return reject(new Error(`Failed to fetch version, status: ${res.statusCode}`));
        }

        let body = "";
        res.on("data", (chunk) => {
          body += chunk;
        });

        res.on("end", () => {
          try {
            const data = JSON.parse(body);
            resolve(data.version);
          } catch (err) {
            reject(err);
          }
        });
      }
    );

    req.on("error", (err) => {
      reject(err);
    });

    req.on("timeout", () => {
      req.destroy();
      reject(new Error("Timeout getting latest version"));
    });
  });
}

function compareVersions(v1, v2) {
  // Split pre-release tags (e.g. 0.1.8-beta.1 -> "0.1.8" and "beta.1")
  const split1 = v1.split("-");
  const split2 = v2.split("-");
  const parts1 = split1[0].split(".").map(Number);
  const parts2 = split2[0].split(".").map(Number);
  
  // Compare major.minor.patch
  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
    const p1 = parts1[i] || 0;
    const p2 = parts2[i] || 0;
    if (p1 > p2) return 1;
    if (p1 < p2) return -1;
  }
  
  // If base versions are equal, compare pre-release tags.
  // A version WITH a pre-release tag is LOWER than a version WITHOUT one.
  // e.g. 0.1.8-beta is less than 0.1.8. 0.1.8 is the final release.
  const hasPre1 = split1.length > 1;
  const hasPre2 = split2.length > 1;
  
  if (hasPre1 && !hasPre2) return -1; // v1 is a beta, v2 is a full release
  if (!hasPre1 && hasPre2) return 1;  // v1 is a full release, v2 is a beta
  if (!hasPre1 && !hasPre2) return 0; // both are full releases and equal
  
  // If both are pre-releases, do a basic string compare on the tag
  // "alpha" < "beta" < "rc"
  if (split1[1] > split2[1]) return 1;
  if (split1[1] < split2[1]) return -1;
  
  return 0;
}

export function startUpdateChecker(log) {
  // Only start the interval if we are in the gateway
  const isGateway = process.argv.includes("gateway");
  if (!isGateway) {
    return;
  }

  const runCheck = async () => {
    if (isUpdating) {
      log.info?.(`${ANSI.YELLOW}[memos-cloud] An update sequence is currently in progress, skipping this check.${ANSI.RESET}`);
      return;
    }

    // TRULY PREVENT LOOPS: The instant we start a check, record the time BEFORE any network or processing happens.
    // This absolutely guarantees that even if the network hangs, NPM crashes, or openclaw update causes an immediate hot reload,
    // the system has already advanced the 12-hour/1-min clock and will NOT re-enter this function on boot.
    try {
      fs.writeFileSync(CHECK_FILE, JSON.stringify({ time: Date.now() }));
    } catch (e) {
      log.warn?.(`${ANSI.RED}[memos-cloud] Failed to write timestamp file: ${e.message}${ANSI.RESET}`);
    }

    const currentVersion = getPackageVersion();
    if (!currentVersion) {
      log.warn?.(`${ANSI.RED}[memos-cloud] Could not read current version from package.json${ANSI.RESET}`);
      return;
    }

    try {
      const latestVersion = await getLatestVersion(log);

      // Normal version check
      if (compareVersions(latestVersion, currentVersion) <= 0) {
        return;
      }

      log.info?.(`${ANSI.YELLOW}[memos-cloud] Update available: ${currentVersion} -> ${latestVersion}. Updating in background...${ANSI.RESET}`);

      let dotCount = 0;
      const progressInterval = setInterval(() => {
        dotCount++;
        const dots = ".".repeat(dotCount % 4);
        log.info?.(`${ANSI.YELLOW}[memos-cloud] Update in progress for memos-cloud-openclaw-plugin${dots}${ANSI.RESET}`);
      }, 30000); // Log every 30 seconds to show it's still alive without spamming

      const cliName = (() => {
        // Check the full path of the entry script (e.g., .../moltbot/bin/index.js) or the executable
        const scriptPath = process.argv[1] ? process.argv[1].toLowerCase() : "";
        const execPath = process.execPath ? process.execPath.toLowerCase() : "";

        if (scriptPath.includes("moltbot") || execPath.includes("moltbot")) return "moltbot";
        if (scriptPath.includes("clawdbot") || execPath.includes("clawdbot")) return "clawdbot";
        return "openclaw";
      })();

      isUpdating = true;
      const spawnOpts = { shell: true };
      // On Unix, detach the process so we can kill the entire process group on timeout
      if (process.platform !== "win32") {
        spawnOpts.detached = true;
      }
      const child = spawn(cliName, ["plugins", "update", "memos-cloud-openclaw-plugin"], spawnOpts);

      // Timeout mechanism: forcefully kill the update process if it hangs for more than the configured timeout
      const updateTimeout = setTimeout(() => {
        log.warn?.(`${ANSI.RED}[memos-cloud] Update process timed out. Please try manually running: ${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`);
        killProcessTree(child);

        // Fallback: if kill failed and the close event never fires, forcefully release the lock after 5 seconds
        setTimeout(() => {
          if (isUpdating) {
            clearInterval(progressInterval);
            isUpdating = false;
          }
        }, 5000);
      }, UPDATE_TIMEOUT);

      child.stdout.on("data", (data) => {
        const outText = data.toString();
        log.info?.(`${ANSI.CYAN}[${cliName}-cli]${ANSI.RESET}\n${outText.trim()}`);
        
        // Auto-reply to any [y/N] prompts from the CLI
        if (outText.toLowerCase().includes("[y/n]")) {
          child.stdin.write("y\n");
        }
      });

      child.stderr.on("data", (data) => {
        const errText = data.toString();
        log.warn?.(`${ANSI.RED}[${cliName}-cli]${ANSI.RESET}\n${errText.trim()}`);
        
        // Some CLIs output interactive prompts to stderr instead of stdout
        if (errText.toLowerCase().includes("[y/n]")) {
          child.stdin.write("y\n");
        }
      });

      child.on("close", (code) => {
        clearTimeout(updateTimeout);
        clearInterval(progressInterval);
        isUpdating = false;

        // Wait for a brief moment to let file system sync if needed
        setTimeout(() => {
          const postUpdateVersion = getPackageVersion();
          const actuallyUpdated = (postUpdateVersion === latestVersion) && (postUpdateVersion !== currentVersion);

          if (code !== 0 || !actuallyUpdated) {
            log.warn?.(`${ANSI.RED}[memos-cloud] Auto-update failed or version did not change. Please refer to the CLI logs above, or run manually: ${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`);
          } else {
            log.info?.(`${ANSI.GREEN}[memos-cloud] Successfully updated to version ${latestVersion}. Please restart the gateway to apply changes.${ANSI.RESET}`);
          }
        }, 1000); // Small 1-second buffer for file systems
      });

    } catch (error) {
      log.warn?.(`${ANSI.RED}[memos-cloud] Update check failed entirely: ${error.message}${ANSI.RESET}`);
    }
  };

  // Check when we last ran
  let lastCheckTime = 0;
  try {
    if (fs.existsSync(CHECK_FILE)) {
      const data = JSON.parse(fs.readFileSync(CHECK_FILE, "utf-8"));
      lastCheckTime = data.time || 0;
    }
  } catch (e) {}

  const now = Date.now();
  const timeSinceLastCheck = now - lastCheckTime;

  // If the interval has passed, run it IMMEDIATELY without delay.
  // The immediate file-write at the top of runCheck() will prevent loop scenarios.
  if (timeSinceLastCheck >= CHECK_INTERVAL) {
    runCheck();
    setInterval(runCheck, CHECK_INTERVAL);
  } else {
    // If it hasn't been the full interval yet, wait the remaining time, then trigger interval
    const timeUntilNextCheck = CHECK_INTERVAL - timeSinceLastCheck;
    setTimeout(() => {
      runCheck();
      setInterval(runCheck, CHECK_INTERVAL);
    }, timeUntilNextCheck);
  }
}


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/lib/memos-cloud-api.js
================================================
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
import { setTimeout as delay } from "node:timers/promises";

const DEFAULT_BASE_URL = "https://memos.memtensor.cn/api/openmem/v1";
export const USER_QUERY_MARKER = "user\u200b原\u200b始\u200bquery\u200b:\u200b\u200b\u200b\u200b";
const ENV_SOURCES = [
  { name: "openclaw", path: join(homedir(), ".openclaw", ".env") },
  { name: "moltbot", path: join(homedir(), ".moltbot", ".env") },
  { name: "clawdbot", path: join(homedir(), ".clawdbot", ".env") },
];

let envFilesLoaded = false;
const envFileContents = new Map();
const envFileValues = new Map();

function stripQuotes(value) {
  if (!value) return value;
  const trimmed = value.trim();
  if (
    (trimmed.startsWith("\"") && trimmed.endsWith("\"")) ||
    (trimmed.startsWith("'") && trimmed.endsWith("'"))
  ) {
    return trimmed.slice(1, -1);
  }
  return trimmed;
}

export function extractResultData(result) {
  if (!result || typeof result !== "object") return null;
  return result.data ?? result.data?.data ?? result.data?.result ?? null;
}

function pad2(value) {
  return String(value).padStart(2, "0");
}

function formatTime(value) {
  if (value === undefined || value === null || value === "") return "";
  if (typeof value === "number") {
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return "";
    return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(
      date.getHours(),
    )}:${pad2(date.getMinutes())}`;
  }
  if (typeof value === "string") {
    const trimmed = value.trim();
    if (!trimmed) return "";
    if (/^\d+$/.test(trimmed)) return formatTime(Number(trimmed));
    return trimmed;
  }
  return "";
}

function parseEnvFile(content) {
  const values = new Map();
  for (const line of content.split(/\r?\n/)) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) continue;
    const idx = trimmed.indexOf("=");
    if (idx <= 0) continue;
    const key = trimmed.slice(0, idx).trim();
    const rawValue = trimmed.slice(idx + 1);
    if (!key) continue;
    values.set(key, stripQuotes(rawValue));
  }
  return values;
}

function loadEnvFiles() {
  if (envFilesLoaded) return;
  envFilesLoaded = true;
  for (const source of ENV_SOURCES) {
    try {
      const content = readFileSync(source.path, "utf-8");
      envFileContents.set(source.name, content);
      envFileValues.set(source.name, parseEnvFile(content));
    } catch {
      // ignore missing files
    }
  }
}

function loadEnvFromFiles(name) {
  for (const source of ENV_SOURCES) {
    const values = envFileValues.get(source.name);
    if (!values) continue;
    if (values.has(name)) return values.get(name);
  }
  return undefined;
}

function loadEnvVar(name) {
  loadEnvFiles();
  const fromFiles = loadEnvFromFiles(name);
  if (fromFiles !== undefined) return fromFiles;
  if (envFileContents.size === 0) return process.env[name];
  return undefined;
}

export function getEnvFileStatus() {
  loadEnvFiles();
  const sources = ENV_SOURCES.filter((source) => envFileContents.has(source.name));
  return {
    found: sources.length > 0,
    sources: sources.map((source) => source.name),
    paths: sources.map((source) => source.path),
    searchPaths: ENV_SOURCES.map((source) => source.path),
  };
}

function parseBool(value, fallback) {
  if (value === undefined || value === null || value === "") return fallback;
  if (typeof value === "boolean") return value;
  const normalized = String(value).trim().toLowerCase();
  if (["1", "true", "yes", "y", "on"].includes(normalized)) return true;
  if (["0", "false", "no", "n", "off"].includes(normalized)) return false;
  return fallback;
}

function parseNumber(value, fallback) {
  if (value === undefined || value === null || value === "") return fallback;
  const n = Number(value);
  return Number.isFinite(n) ? n : fallback;
}

export function buildConfig(pluginConfig = {}) {
  const cfg = pluginConfig ?? {};

  const baseUrl = cfg.baseUrl || loadEnvVar("MEMOS_BASE_URL") || DEFAULT_BASE_URL;
  const apiKey = cfg.apiKey || loadEnvVar("MEMOS_API_KEY") || "";
  const userId = cfg.userId || loadEnvVar("MEMOS_USER_ID") || "openclaw-user";
  const conversationId = cfg.conversationId || loadEnvVar("MEMOS_CONVERSATION_ID") || "";

  const recallGlobal = parseBool(
    cfg.recallGlobal,
    parseBool(loadEnvVar("MEMOS_RECALL_GLOBAL"), true),
  );

  const conversationIdPrefix = cfg.conversationIdPrefix ?? loadEnvVar("MEMOS_CONVERSATION_PREFIX") ?? "";
  const conversationIdSuffix = cfg.conversationIdSuffix ?? loadEnvVar("MEMOS_CONVERSATION_SUFFIX") ?? "";
  const conversationSuffixMode =
    cfg.conversationSuffixMode ?? loadEnvVar("MEMOS_CONVERSATION_SUFFIX_MODE") ?? "none";
  const resetOnNew = parseBool(
    cfg.resetOnNew,
    parseBool(loadEnvVar("MEMOS_CONVERSATION_RESET_ON_NEW"), true),
  );

  const multiAgentMode = parseBool(
    cfg.multiAgentMode,
    parseBool(loadEnvVar("MEMOS_MULTI_AGENT_MODE"), false),
  );

  const recallFilterEnabled = parseBool(
    cfg.recallFilterEnabled,
    parseBool(loadEnvVar("MEMOS_RECALL_FILTER_ENABLED"), false),
  );
  const recallFilterFailOpen = parseBool(
    cfg.recallFilterFailOpen,
    parseBool(loadEnvVar("MEMOS_RECALL_FILTER_FAIL_OPEN"), true),
  );

  return {
    baseUrl: baseUrl.replace(/\/+$/, ""),
    apiKey,
    userId,
    conversationId,
    conversationIdPrefix,
    conversationIdSuffix,
    conversationSuffixMode,
    recallGlobal,
    resetOnNew,
    envFileStatus: getEnvFileStatus(),
    queryPrefix: cfg.queryPrefix ?? "",
    maxQueryChars: cfg.maxQueryChars ?? 0,
    recallEnabled: cfg.recallEnabled !== false,
    addEnabled: cfg.addEnabled !== false,
    captureStrategy: cfg.captureStrategy ?? "last_turn",
    maxMessageChars: cfg.maxMessageChars ?? 20000,
    maxItemChars: cfg.maxItemChars ?? 8000,
    includeAssistant: cfg.includeAssistant !== false,
    memoryLimitNumber: cfg.memoryLimitNumber ?? 9,
    preferenceLimitNumber: cfg.preferenceLimitNumber ?? 6,
    includePreference: cfg.includePreference !== false,
    includeToolMemory: cfg.includeToolMemory === true,
    toolMemoryLimitNumber: cfg.toolMemoryLimitNumber ?? 6,
    relativity: cfg.relativity ?? ((() => {
      const v = loadEnvVar("MEMOS_RELATIVITY");
      return v ? parseFloat(v) : 0.45;
    })()),
    filter: cfg.filter,
    knowledgebaseIds: cfg.knowledgebaseIds ?? [],
    tags: cfg.tags ?? ["openclaw"],
    info: cfg.info ?? {},
    agentId: cfg.agentId,
    appId: cfg.appId,
    allowPublic: cfg.allowPublic ?? false,
    allowKnowledgebaseIds: cfg.allowKnowledgebaseIds ?? [],
    asyncMode: cfg.asyncMode ?? true,
    multiAgentMode,
    recallFilterEnabled,
    recallFilterBaseUrl:
      (cfg.recallFilterBaseUrl ?? loadEnvVar("MEMOS_RECALL_FILTER_BASE_URL") ?? "").replace(/\/+$/, ""),
    recallFilterApiKey: cfg.recallFilterApiKey ?? loadEnvVar("MEMOS_RECALL_FILTER_API_KEY") ?? "",
    recallFilterModel: cfg.recallFilterModel ?? loadEnvVar("MEMOS_RECALL_FILTER_MODEL") ?? "",
    recallFilterTimeoutMs: parseNumber(
      cfg.recallFilterTimeoutMs ?? loadEnvVar("MEMOS_RECALL_FILTER_TIMEOUT_MS"),
      6000,
    ),
    recallFilterRetries: parseNumber(cfg.recallFilterRetries ?? loadEnvVar("MEMOS_RECALL_FILTER_RETRIES"), 0),
    recallFilterCandidateLimit:
      parseNumber(cfg.recallFilterCandidateLimit ?? loadEnvVar("MEMOS_RECALL_FILTER_CANDIDATE_LIMIT"), 30),
    recallFilterMaxItemChars:
      parseNumber(cfg.recallFilterMaxItemChars ?? loadEnvVar("MEMOS_RECALL_FILTER_MAX_ITEM_CHARS"), 500),
    recallFilterFailOpen,
    timeoutMs: cfg.timeoutMs ?? 5000,
    retries: cfg.retries ?? 1,
    throttleMs: cfg.throttleMs ?? 0,
  };
}

export async function callApi({ baseUrl, apiKey, timeoutMs = 5000, retries = 1 }, path, body) {
  if (!apiKey) {
    throw new Error("Missing MEMOS API key (Token auth)");
  }

  const headers = {
    "Content-Type": "application/json",
    Authorization: `Token ${apiKey}`,
  };

  let lastError;
  for (let attempt = 0; attempt <= retries; attempt += 1) {
    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

      const res = await fetch(`${baseUrl}${path}`, {
        method: "POST",
        headers,
        body: JSON.stringify(body),
        signal: controller.signal,
      });

      clearTimeout(timeoutId);

      if (!res.ok) {
        throw new Error(`HTTP ${res.status}`);
      }

      return await res.json();
    } catch (err) {
      lastError = err;
      if (attempt < retries) {
        await delay(100 * (attempt + 1));
      }
    }
  }

  throw lastError;
}

export async function searchMemory(cfg, payload) {
  return callApi(cfg, "/search/memory", payload);
}

export async function addMessage(cfg, payload) {
  return callApi(cfg, "/add/message", payload);
}

export function extractText(content) {
  if (!content) return "";
  if (typeof content === "string") return content;
  if (Array.isArray(content)) {
    return content
      .filter((block) => block && typeof block === "object" && block.type === "text")
      .map((block) => block.text)
      .join(" ");
  }
  return "";
}

function normalizePreferenceType(value) {
  if (!value) return "";
  const normalized = String(value).trim().toLowerCase();
  if (!normalized) return "";
  if (normalized.includes("explicit")) return "Explicit Preference";
  if (normalized.includes("implicit")) return "Implicit Preference";
  return String(value)
    .replace(/[_-]+/g, " ")
    .replace(/\b\w/g, (ch) => ch.toUpperCase());
}

function sanitizeInlineText(text) {
  if (text === undefined || text === null) return "";
  return String(text).replace(/\r?\n+/g, " ").trim();
}

function formatMemoryLine(item, text, options = {}) {
  const cleaned = sanitizeInlineText(text);
  if (!cleaned) return "";
  const maxChars = options.maxItemChars;
  const truncated = truncate(cleaned, maxChars);
  const time = formatTime(item?.create_time);
  if (time) return `   -[${time}] ${truncated}`;
  return `   - ${truncated}`;
}

function formatPreferenceLine(item, text, options = {}) {
  const cleaned = sanitizeInlineText(text);
  if (!cleaned) return "";
  const maxChars = options.maxItemChars;
  const truncated = truncate(cleaned, maxChars);
  const time = formatTime(item?.create_time);
  const type = normalizePreferenceType(item?.preference_type);
  const typeLabel = type ? ` [${type}]` : "";
  if (time) return `   -[${time}]${typeLabel} ${truncated}`;
  return `   -${typeLabel} ${truncated}`;
}

function wrapCodeBlock(lines, options = {}) {
  if (!options.wrapTagBlocks) return lines;
  return ["```text", ...lines, "```"];
}

function buildMemorySections(data, options = {}) {
  const memoryList = data?.memory_detail_list ?? [];
  const preferenceList = data?.preference_detail_list ?? [];

  const memoryLines = memoryList
    .filter((item) => {
      const score = item?.relativity ?? 1;
      const threshold = options.relativity ?? 0;
      return score > threshold;
    })
    .map((item) => {
      const text = item?.memory_value || item?.memory_key || "";
      return formatMemoryLine(item, text, options);
    })
    .filter(Boolean);

  const preferenceLines = preferenceList
    .filter((item) => {
      const score = item?.relativity ?? 1;
      const threshold = options.relativity ?? 0;
      return score > threshold;
    })
    .map((item) => {
      const text = item?.preference || "";
      return formatPreferenceLine(item, text, options);
    })
    .filter(Boolean);

  return { memoryLines, preferenceLines };
}

const STATIC_RECALL_SYSTEM_PROMPT = [
  "# Role",
  "",
  "You are an intelligent assistant with long-term memory capabilities (MemOS Assistant). Your goal is to combine retrieved memory fragments to provide highly personalized, accurate, and logically rigorous responses.",
  "",
  "# System Context",
  "",
  "* Current Time: Use the runtime-provided current time as the baseline for freshness checks.",
  "* Additional memory context for the current turn may be prepended before the original user query as a structured `<memories>` block.",
  "",
  "# Memory Data",
  "",
  'Below is the information retrieved by MemOS, categorized into "Facts" and "Preferences".',
  "* **Facts**: May include user attributes, historical conversations, or third-party details.",
  "* **Special Note**: Content tagged with '[assistant观点]' or '[模型总结]' represents **past AI inference**, **not** direct user statements.",
  "* **Preferences**: The user's explicit or implicit requirements on response style, format, or reasoning.",
  "",
  "# Critical Protocol: Memory Safety",
  "",
  "Retrieved memories may contain **AI speculation**, **irrelevant noise**, or **wrong subject attribution**. You must strictly apply the **Four-Step Verdict**. If any step fails, **discard the memory**:",
  "",
  "1. **Source Verification**:",
  "* **Core**: Distinguish direct user statements from AI inference.",
  "* If a memory has tags like '[assistant观点]' or '[模型总结]', treat it as a **hypothesis**, not a user-grounded fact.",
  "* *Counterexample*: If memory says '[assistant观点] User loves mangoes' but the user never said that, do not assume it as fact.",
  "* **Principle: AI summaries are reference-only and have much lower authority than direct user statements.**",
  "",
  "2. **Attribution Check**:",
  "* Is the subject in memory definitely the user?",
  "* If the memory describes a **third party** (e.g., candidate, interviewee, fictional character, case data), never attribute it to the user.",
  "",
  "3. **Strong Relevance Check**:",
  "* Does the memory directly help answer the current 'Original Query'?",
  "* If it is only a keyword overlap with different context, ignore it.",
  "",
  "4. **Freshness Check**:",
  "* If memory conflicts with the user's latest intent, prioritize the current 'Original Query' as the highest source of truth.",
  "",
  "# Instructions",
  "",
  "1. **Review**: Read '<facts>' first and apply the Four-Step Verdict to remove noise and unreliable AI inference.",
  "2. **Execute**:",
  "   - Use only memories that pass filtering as context.",
  "   - Strictly follow style requirements from '<preferences>'.",
  "3. **Output**: Answer directly. Never mention internal terms such as \"memory store\", \"retrieval\", or \"AI opinions\".",
  "4. **Attention**: Additional memory context may already be provided before the original user query. Do not read from or write to local `MEMORY.md` or `memory/*` files for reference, as they may be outdated or irrelevant to the current query.",
].join("\n");

function buildMemoryPrependBlock(data, options = {}) {
  const { memoryLines, preferenceLines } = buildMemorySections(data, options);
  const hasContent = memoryLines.length > 0 || preferenceLines.length > 0;
  if (!hasContent) return "";

  const memoriesBlock = [
    "<memories>",
    "  <facts>",
    ...memoryLines,
    "  </facts>",
    "  <preferences>",
    ...preferenceLines,
    "  </preferences>",
    "</memories>",
  ];

  return [...wrapCodeBlock(memoriesBlock, options), "", USER_QUERY_MARKER].join("\n");
}

export function formatPromptBlockFromData(data, options = {}) {
  if (!data || typeof data !== "object") return "";
  return buildMemoryPrependBlock(data, options);
}

export function formatPromptBlock(result, options = {}) {
  const data = extractResultData(result);
  return formatPromptBlockFromData(data, options);
}

export function formatContextBlock(result, options = {}) {
  const data = extractResultData(result);
  if (!data) return "";

  const memoryList = data.memory_detail_list ?? [];
  const prefList = data.preference_detail_list ?? [];
  const toolList = data.tool_memory_detail_list ?? [];
  const preferenceNote = data.preference_note;

  const lines = [];
  if (memoryList.length > 0) {
    lines.push("Facts:");
    for (const item of memoryList) {
      const text = item?.memory_value || item?.memory_key || "";
      if (!text) continue;
      lines.push(`- ${truncate(text, options.maxItemChars)}`);
    }
  }

  if (prefList.length > 0) {
    lines.push("Preferences:");
    for (const item of prefList) {
      const pref = item?.preference || "";
      const type = item?.preference_type ? `(${item.preference_type}) ` : "";
      if (!pref) continue;
      lines.push(`- ${type}${truncate(pref, options.maxItemChars)}`);
    }
  }

  if (toolList.length > 0) {
    lines.push("Tool Memories:");
    for (const item of toolList) {
      const value = item?.tool_value || "";
      if (!value) continue;
      lines.push(`- ${truncate(value, options.maxItemChars)}`);
    }
  }

  if (preferenceNote) {
    lines.push(`Preference Note: ${truncate(preferenceNote, options.maxItemChars)}`);
  }

  return lines.length > 0 ? lines.join("\n") : "";
}

export function formatRecallHookResult(result, options = {}) {
  const data = extractResultData(result);
  if (!data) {
    return {
      appendSystemContext: "",
      prependContext: "",
    };
  }

  return {
    // Keep this system addendum byte-stable across turns so provider-side prefix caching can hit.
    appendSystemContext: STATIC_RECALL_SYSTEM_PROMPT,
    prependContext: buildMemoryPrependBlock(data, options),
  };
}

function truncate(text, maxLen) {
  if (!text) return "";
  const limit = maxLen || 10000;
  return text.length > limit ? `${text.slice(0, limit)}...` : text;
}


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/moltbot.plugin.json
================================================
{
  "id": "memos-cloud-openclaw-plugin",
  "name": "MemOS Cloud OpenClaw Plugin",
  "description": "MemOS Cloud recall + add memory via lifecycle hooks",
  "version": "0.1.9",
  "kind": "lifecycle",
  "main": "./index.js",
  "configSchema": {
    "type": "object",
    "properties": {
      "baseUrl": {
        "type": "string",
        "description": "MemOS Cloud base URL"
      },
      "apiKey": {
        "type": "string",
        "description": "MemOS API Key (Token auth; supports ~/.openclaw/.env, ~/.moltbot/.env, ~/.clawdbot/.env; falls back to process env)"
      },
      "userId": {
        "type": "string",
        "description": "MemOS user_id (default: openclaw-user)",
        "default": "openclaw-user"
      },
      "conversationId": {
        "type": "string",
        "description": "Override conversation_id"
      },
      "conversationIdPrefix": {
        "type": "string",
        "description": "conversation_id prefix"
      },
      "conversationIdSuffix": {
        "type": "string",
        "description": "conversation_id suffix"
      },
      "conversationSuffixMode": {
        "type": "string",
        "enum": [
          "none",
          "counter"
        ],
        "default": "none"
      },
      "resetOnNew": {
        "type": "boolean",
        "default": true
      },
      "queryPrefix": {
        "type": "string",
        "description": "Prefix added to search queries"
      },
      "maxQueryChars": {
        "type": "integer",
        "description": "Max chars for search query"
      },
      "recallEnabled": {
        "type": "boolean",
        "default": true
      },
      "recallGlobal": {
        "type": "boolean",
        "default": true
      },
      "addEnabled": {
        "type": "boolean",
        "default": true
      },
      "captureStrategy": {
        "type": "string",
        "enum": [
          "last_turn",
          "full_session"
        ],
        "default": "last_turn"
      },
      "maxMessageChars": {
        "type": "integer",
        "description": "Max chars per message when adding",
        "default": 20000
      },
      "maxItemChars": {
        "type": "integer",
        "description": "Max chars per memory item when injecting prompt",
        "default": 8000
      },
      "includeAssistant": {
        "type": "boolean",
        "default": true
      },
      "memoryLimitNumber": {
        "type": "integer",
        "default": 6
      },
      "preferenceLimitNumber": {
        "type": "integer",
        "default": 6
      },
      "includePreference": {
        "type": "boolean",
        "default": true
      },
      "includeToolMemory": {
        "type": "boolean",
        "default": false
      },
      "toolMemoryLimitNumber": {
        "type": "integer",
        "default": 6
      },
      "filter": {
        "type": "object",
        "description": "MemOS search filter"
      },
      "knowledgebaseIds": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "tags": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "info": {
        "type": "object",
        "additionalProperties": true
      },
      "agentId": {
        "type": "string"
      },
      "multiAgentMode": {
        "type": "boolean",
        "default": false
      },
      "appId": {
        "type": "string"
      },
      "allowPublic": {
        "type": "boolean",
        "default": false
      },
      "allowKnowledgebaseIds": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "asyncMode": {
        "type": "boolean",
        "default": true
      },
      "timeoutMs": {
        "type": "integer",
        "default": 5000
      },
      "retries": {
        "type": "integer",
        "default": 1
      },
      "throttleMs": {
        "type": "integer",
        "default": 0
      }
    },
    "additionalProperties": false
  }
}


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/openclaw.plugin.json
================================================
{
  "id": "memos-cloud-openclaw-plugin",
  "name": "MemOS Cloud OpenClaw Plugin",
  "description": "MemOS Cloud recall + add memory via lifecycle hooks",
  "version": "0.1.9",
  "kind": "lifecycle",
  "main": "./index.js",
  "configSchema": {
    "type": "object",
    "properties": {
      "baseUrl": {
        "type": "string",
        "description": "MemOS Cloud base URL"
      },
      "apiKey": {
        "type": "string",
        "description": "MemOS API Key (Token auth; supports ~/.openclaw/.env, ~/.moltbot/.env, ~/.clawdbot/.env; falls back to process env)"
      },
      "userId": {
        "type": "string",
        "description": "MemOS user_id (default: openclaw-user)",
        "default": "openclaw-user"
      },
      "conversationId": {
        "type": "string",
        "description": "Override conversation_id"
      },
      "conversationIdPrefix": {
        "type": "string",
        "description": "conversation_id prefix"
      },
      "conversationIdSuffix": {
        "type": "string",
        "description": "conversation_id suffix"
      },
      "conversationSuffixMode": {
        "type": "string",
        "enum": [
          "none",
          "counter"
        ],
        "default": "none"
      },
      "resetOnNew": {
        "type": "boolean",
        "default": true
      },
      "queryPrefix": {
        "type": "string",
        "description": "Prefix added to search queries"
      },
      "maxQueryChars": {
        "type": "integer",
        "description": "Max chars for search query"
      },
      "recallEnabled": {
        "type": "boolean",
        "default": true
      },
      "recallGlobal": {
        "type": "boolean",
        "default": true
      },
      "addEnabled": {
        "type": "boolean",
        "default": true
      },
      "captureStrategy": {
        "type": "string",
        "enum": [
          "last_turn",
          "full_session"
        ],
        "default": "last_turn"
      },
      "maxMessageChars": {
        "type": "integer",
        "description": "Max chars per message when adding",
        "default": 20000
      },
      "maxItemChars": {
        "type": "integer",
        "description": "Max chars per memory item when injecting prompt",
        "default": 8000
      },
      "includeAssistant": {
        "type": "boolean",
        "default": true
      },
      "memoryLimitNumber": {
        "type": "integer",
        "default": 6
      },
      "preferenceLimitNumber": {
        "type": "integer",
        "default": 6
      },
      "includePreference": {
        "type": "boolean",
        "default": true
      },
      "includeToolMemory": {
        "type": "boolean",
        "default": false
      },
      "toolMemoryLimitNumber": {
        "type": "integer",
        "default": 6
      },
      "filter": {
        "type": "object",
        "description": "MemOS search filter"
      },
      "knowledgebaseIds": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "tags": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "info": {
        "type": "object",
        "additionalProperties": true
      },
      "agentId": {
        "type": "string"
      },
      "multiAgentMode": {
        "type": "boolean",
        "default": false
      },
      "appId": {
        "type": "string"
      },
      "allowPublic": {
        "type": "boolean",
        "default": false
      },
      "allowKnowledgebaseIds": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "asyncMode": {
        "type": "boolean",
        "default": true
      },
      "timeoutMs": {
        "type": "integer",
        "default": 5000
      },
      "retries": {
        "type": "integer",
        "default": 1
      },
      "throttleMs": {
        "type": "integer",
        "default": 0
      }
    },
    "additionalProperties": false
  }
}


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/package.json
================================================
{
  "name": "@memtensor/memos-cloud-openclaw-plugin",
  "version": "0.1.9",
  "description": "OpenClaw lifecycle plugin for MemOS Cloud (add + recall memory)",
  "scripts": {
    "sync-version": "node scripts/sync-version.js",
    "version": "npm run sync-version && git add openclaw.plugin.json moltbot.plugin.json clawdbot.plugin.json",
    "publish-beta": "npm publish --tag beta",
    "publish-beta-patch": "npm version prepatch --preid=beta && npm publish --tag beta",
    "publish-latest": "npm version $(node -p \"require('./package.json').version.split('-')[0]\") && npm publish",
    "publish-latest-patch": "npm version patch && npm publish"
  },
  "keywords": [
    "memos",
    "memos-cloud",
    "openclaw",
    "plugin",
    "memory"
  ],
  "homepage": "https://github.com/MemTensor/MemOS-Cloud-OpenClaw-Plugin#readme",
  "bugs": {
    "url": "https://github.com/MemTensor/MemOS-Cloud-OpenClaw-Plugin/issues"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/MemTensor/MemOS-Cloud-OpenClaw-Plugin.git"
  },
  "type": "module",
  "author": "MemTensor",
  "license": "MIT",
  "openclaw": {
    "extensions": [
      "./index.js"
    ]
  },
  "clawdbot": {
    "extensions": [
      "./index.js"
    ]
  },
  "moltbot": {
    "extensions": [
      "./index.js"
    ]
  }
}


================================================
FILE: apps/MemOS-Cloud-OpenClaw-Plugin/scripts/sync-version.js
================================================
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Read the updated package.json to get the new version
const packageJsonPath = path.resolve(__dirname, '../package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const newVersion = packageJson.version;

console.log(`Syncing version to ${newVersion}...`);

const filesToUpdate = [
  'openclaw.plugin.json',
  'moltbot.plugin.json',
  'clawdbot.plugin.json'
];

filesToUpdate.forEach(fileName => {
  const filePath = path.resolve(__dirname, '..', fileName);
  
  if (fs.existsSync(filePath)) {
    try {
      const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
      
      if (content.version !== newVersion) {
        content.version = newVersion;
        // Write back with 2 spaces indentation and a newline at the end
        fs.writeFileSync(filePath, JSON.stringify(content, null, 2) + '\n', 'utf8');
        console.log(`Updated ${fileName} to version ${newVersion}`);
      } else {
        console.log(`${fileName} is already at version ${newVersion}`);
      }
    } catch (error) {
      console.error(`Error updating ${fileName}:`, error.message);
      process.exit(1);
    }
  } else {
    console.warn(`Warning: ${fileName} not found, skipping.`);
  }
});

console.log('Version sync complete.');


================================================
FILE: apps/memos-local-openclaw/.gitignore
================================================
node_modules/
dist/
*.tsbuildinfo
.env

# OS files
.DS_Store
Thumbs.db

# IDE
.vscode/
.idea/

# Generated / non-essential
package-lock.json
.installed-version
ppt/

# Prebuilt native binaries (included in npm package via `files`, not in git)
prebuilds/

# Database files
*.sqlite
*.sqlite-journal
*.db


================================================
FILE: apps/memos-local-openclaw/README.md
================================================
# 🧠 MemOS — OpenClaw Memory Plugin

[![npm version](https://img.shields.io/npm/v/@memtensor/memos-local-openclaw-plugin)](https://www.npmjs.com/package/@memtensor/memos-local-openclaw-plugin)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/MemTensor/MemOS/blob/main/LICENSE)
[![Node.js >= 18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org/)
[![GitHub](https://img.shields.io/badge/GitHub-Source-181717?logo=github)](https://github.com/MemTensor/MemOS/tree/main/apps/memos-local-openclaw)

Persistent local conversation memory for [OpenClaw](https://github.com/nicepkg/openclaw) AI Agents. Every conversation is automatically captured, semantically indexed, and instantly recallable — with **task summarization & skill evolution**, and **multi-agent collaborative memory**.

**Full-write | Hybrid Search | Task Summarization & Skill Evolution | Multi-Agent Collaboration | Memory Viewer**

> **Homepage:**  🌐 [Homepage](https://memos-claw.openmem.net) · 📖 [Documentation](https://memos-claw.openmem.net/docs/index.html) · 📦 [NPM](https://www.npmjs.com/package/@memtensor/memos-local-openclaw-plugin)

## Why MemOS

| Problem | Solution |
|---------|----------|
| Agent forgets everything between sessions | **Persistent memory** — every conversation auto-captured to local SQLite |
| Fragmented context, repeated mistakes | **Task summarization & skill evolution** — conversations organized into structured tasks, then distilled into reusable skills that auto-upgrade |
| Multi-agent teams work in isolation | **Multi-agent collaboration** — memory isolation + public memory + skill sharing enables collective evolution |
| No visibility into what the agent remembers | **Memory Viewer** — full visualization of all memories, tasks, and skills |
| Privacy concerns with cloud storage | **100% local** — zero cloud uploads, anonymous opt-out telemetry only, password-protected |

## Features

### Memory Engine
- **Auto-capture** — Stores user, assistant, and tool messages after each agent turn via `agent_end` event (consecutive assistant messages merged into one)
- **Smart deduplication** — Exact content-hash skip; then Top-5 similar chunks (threshold 0.75) with LLM judge: DUPLICATE (skip), UPDATE (merge summary + append content), or NEW (create). Evolved chunks track merge history.
- **Semantic chunking** — Splits by code blocks, function bodies, paragraphs; never cuts mid-function
- **Hybrid retrieval** — FTS5 keyword + vector semantic dual-channel search with RRF fusion
- **MMR diversity** — Maximal Marginal Relevance reranking prevents near-duplicate results
- **Recency decay** — Configurable time-based decay (half-life: 14 days) biases recent memories
- **Multi-provider embedding** — OpenAI-compatible, Gemini, Cohere, Voyage, Mistral, or local offline (Xenova/all-MiniLM-L6-v2)

### Task Summarization & Skill Evolution
- **Auto task boundary detection** — Per-turn LLM topic judgment (warm-up: 1 user turn) + 2-hour idle timeout segments conversations into tasks. Strongly biased toward SAME to avoid over-splitting related topics
- **Structured summaries** — LLM generates Goal, Key Steps, Result, Key Details for each completed task
- **Key detail preservation** — Code, commands, URLs, file paths, error messages retained in summaries
- **Quality filtering** — Tasks with too few chunks, too few turns, or trivial content are auto-skipped
- **Task status** — `active` (in progress), `completed` (with LLM summary), `skipped` (too brief, excluded from search)
- **Task/Skill CRUD** — Edit title/summary, delete tasks and skills, retry skill generation from task cards
- **Automatic evaluation** — After task completion, rule filter + LLM evaluates if the task is worth distilling into a skill
- **Skill generation** — Multi-step LLM pipeline creates SKILL.md + scripts + references + evals from real execution records
- **Skill upgrading** — When similar tasks appear, existing skills are auto-upgraded (refine / extend / fix)
- **Quality scoring** — 0-10 quality assessment; scores below 6 marked as draft
- **Version management** — Full version history with changelog, change summary, and upgrade type tracking
- **Auto-install** — Generated skills can be auto-installed into the workspace for immediate use
- **Dedicated model** — Optional separate LLM model for skill generation (e.g., Claude 4.6 for higher quality)
- **LLM fallback chain** — `skillSummarizer` → `summarizer` → OpenClaw native model (auto-detected from `openclaw.json`). If all configured models fail, the next in chain is tried automatically

### Multi-Agent Collaboration
- **Memory isolation** — Each agent's memories are tagged with `owner`. During search, agents only see their own private memories and explicitly shared `public` memories
- **Public memory** — `memory_write_public` tool allows agents to write shared knowledge accessible to all agents (e.g., team decisions, conventions, shared configs)
- **Skill sharing** — Skills have a `visibility` toggle (`private`/`public`). Public skills are discoverable by all agents via `skill_search`
- **Skill discovery** — `skill_search` combines FTS (name + description) and vector search (description embedding) with RRF fusion, followed by LLM relevance judgment. Supports `scope` parameter: `mix` (default), `self`, or `public`
- **Publish/unpublish** — `skill_publish` / `skill_unpublish` tools toggle skill visibility. Other agents can search, preview, and install public skills
- **Agent-aware capture** — `agent_end` event extracts `agentId` to tag all captured messages with the correct owner

### Memory Migration — Reconnect 🦐
- **One-click import** — Seamlessly migrate OpenClaw's native built-in memories (SQLite + JSONL) into the MemOS intelligent memory system
- **Smart deduplication** — Vector similarity + LLM judgment prevents duplicate imports; similar content auto-merged
- **Resume anytime** — Pause and resume at any time; refreshing the page auto-restores progress; already processed items are skipped
- **Post-import processing** — Optionally generate task summaries and evolve skills from imported memories; serial processing within each agent, parallel across agents
- **Agent parallelism** — Configurable concurrency (1–8) for parallel processing across agents; sessions within each agent are processed serially
- **Source tagging** — All migrated memories are tagged with 🦐, visually distinguishing them from conversation-generated memories
- **Real-time progress** — Live progress bar, stats (stored/skipped/merged/errors), and scrolling log via SSE

### Memory Viewer
- **7 management pages** — Memories, Tasks, Skills, Analytics, **Logs**, **Import**, Settings
- **Full CRUD** — Create, edit, delete, search memories; evolution badges and merge history on memory cards
- **Task browser** — Status filters, chat-bubble chunk view, structured summaries, skill generation status; edit/delete/retry-skill buttons on cards
- **Skill browser** — Version history, quality scores, visibility toggle, one-click download as ZIP; edit/delete/publish buttons on cards
- **Analytics dashboard** — Daily read/write activity, memory breakdown charts
- **Logs** — Tool call log (memory_search, auto_recall, memory_add, etc.) with input/output and duration; filter by tool, auto-refresh
- **Online configuration** — Modify embedding, summarizer, skill evolution settings via web UI
- **Security** — Password-protected, localhost-only (127.0.0.1), session cookies
- **i18n** — Chinese / English toggle
- **Themes** — Light / Dark mode

### Privacy & Security
- **100% on-device** — All data in local SQLite, no cloud uploads
- **Anonymous telemetry** — Enabled by default, opt-out via config. Only sends tool names, latencies, and version info. Never sends memory content, queries, or personal data. See [Telemetry](#telemetry) section.
- **Viewer security** — Binds to 127.0.0.1 only, password-protected with session cookies
- **Auto-recall + Skill** — Each turn, relevant memories are injected via `before_agent_start` hook (invisible to user). When nothing is recalled (e.g. long or unclear query), the agent is prompted to call `memory_search` with a self-generated short query. The bundled skill `memos-memory-guide` documents all tools and when to use them.

## Quick Start

### 1. Install

**Step 0 — Prepare build environment (macOS / Linux):**

This plugin uses `better-sqlite3`, a native C/C++ module. On **macOS** and **Linux**, prebuilt binaries may not be available, so **install C++ build tools first** to ensure a smooth installation:

```bash
# macOS
xcode-select --install

# Linux (Ubuntu / Debian)
sudo apt install build-essential python3
```

> **Windows users:** `better-sqlite3` ships prebuilt binaries for Windows + Node.js LTS, so you can usually skip this step and go directly to Step 1. If installation still fails, install [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) (select "C++ build tools" workload).
>
> Already have build tools? Skip to Step 1. Not sure? Run the install command above — it's safe to re-run.
>
> **Still having issues?** See the [Troubleshooting](#troubleshooting) section, the [detailed troubleshooting guide](https://memtensor.github.io/MemOS/apps/memos-local-openclaw/docs/troubleshooting.html), or the [official better-sqlite3 troubleshooting docs](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md).

**Step 1 — Install the plugin:**

```bash
openclaw plugins install @memtensor/memos-local-openclaw-plugin
```

The plugin is installed under `~/.openclaw/extensions/memos-local-openclaw-plugin` and registered as `memos-local-openclaw-plugin`. Dependencies and `better-sqlite3` native module are built automatically during installation.

> **Note:** The Memory Viewer starts only when the **OpenClaw gateway** is running. After install, **configure** `openclaw.json` (step 2) and **start the gateway** (step 3); the viewer will then be available at `http://127.0.0.1:18799`.
>
> **Installation failed?** If `better-sqlite3` compilation fails during install, manually rebuild after ensuring build tools are installed:
> ```bash
> cd ~/.openclaw/extensions/memos-local-openclaw-plugin && npm rebuild better-sqlite3
> ```

**From source (development):**

```bash
git clone https://github.com/MemTensor/MemOS.git
cd MemOS/apps/memos-local-openclaw
npm install && npm run build
openclaw plugins install .
```

### 2. Configure

Add the plugin config to `~/.openclaw/openclaw.json`:

```jsonc
{
  "agents": {
    "defaults": {
      // IMPORTANT: Disable OpenClaw's built-in memory to avoid conflicts
      "memorySearch": {
        "enabled": false
      }
    }
  },
  "plugins": {
    "slots": {
      "memory": "memos-local-openclaw-plugin"
    },
    "entries": {
      "memos-local-openclaw-plugin": {
        "enabled": true,
        "config": {
          "embedding": {
            "provider": "openai_compatible",
            "endpoint": "https://your-api-endpoint/v1",
            "apiKey": "sk-••••••",
            "model": "bge-m3"
          },
          "summarizer": {
            "provider": "openai_compatible",
            "endpoint": "https://your-api-endpoint/v1",
            "apiKey": "sk-••••••",
            "model": "gpt-4o-mini",
            "temperature": 0
          }
        }
      }
    }
  }
}
```

> **Critical:** You must set `agents.defaults.memorySearch.enabled` to `false`. Otherwise OpenClaw's built-in memory search runs alongside this plugin, causing duplicate retrieval and wasted tokens.

#### Embedding Provider Options

| Provider | `provider` value | Example `model` | Notes |
|---|---|---|---|
| OpenAI / compatible | `openai_compatible` | `bge-m3`, `text-embedding-3-small` | Any OpenAI-compatible API |
| Gemini | `gemini` | `text-embedding-004` | Requires `apiKey` |
| Cohere | `cohere` | `embed-english-v3.0` | Separates document/query embedding |
| Voyage | `voyage` | `voyage-2` | |
| Mistral | `mistral` | `mistral-embed` | |
| Local (offline) | `local` | — | Uses `Xenova/all-MiniLM-L6-v2`, no API needed |

> **No embedding config?** The plugin falls back to the local model automatically. You can start with zero configuration and add a cloud provider later for better quality.

#### Summarizer Provider Options

| Provider | `provider` value | Example `model` |
|---|---|---|
| OpenAI / compatible | `openai_compatible` | `gpt-4o-mini` |
| Anthropic | `anthropic` | `claude-3-haiku-20240307` |
| Gemini | `gemini` | `gemini-1.5-flash` |
| AWS Bedrock | `bedrock` | `anthropic.claude-3-haiku-20240307-v1:0` |

> **No summarizer config?** The plugin automatically falls back to the OpenClaw native model (auto-detected from `~/.openclaw/openclaw.json`). If that is also unavailable, a rule-based fallback generates summaries from the first sentence + key entities. Good enough to start.

#### Skill Evolution Configuration (Optional)

You can optionally configure a dedicated model for skill generation (for higher quality skills):

```jsonc
{
  "config": {
    "skillSummarizer": {
      "provider": "anthropic",
      "apiKey": "sk-ant-xxx",
      "model": "claude-sonnet-4-20250514",
      "temperature": 0
    },
    "skillEvolution": {
      "enabled": true,
      "autoEvaluate": true,
      "autoInstall": false
    }
  }
}
```

**LLM fallback chain:** `skillSummarizer` → `summarizer` → OpenClaw native model (auto-detected from `~/.openclaw/openclaw.json`). If `skillSummarizer` is not configured, the plugin tries the regular `summarizer`, then falls back to the OpenClaw native model. Each step in the chain is tried automatically if the previous one fails.

#### Environment Variable Support

Use `${ENV_VAR}` placeholders in config to avoid hardcoding keys:

```jsonc
{
  "apiKey": "${OPENAI_API_KEY}"
}
```

### 3. Start or Restart the Gateway

```bash
openclaw gateway stop    # if already running
openclaw gateway install # ensure LaunchAgent is installed (macOS)
openclaw gateway start
```

Once the gateway is up, the plugin loads and starts the Memory Viewer at `http://127.0.0.1:18799`.

### 4. Verify Installation

```bash
tail -20 ~/.openclaw/logs/gateway.log
```

You should see:

```
memos-local: initialized (db: ~/.openclaw/memos-local/memos.db)
memos-local: started (embedding: openai_compatible)
╔══════════════════════════════════════════╗
║  MemOS Memory Viewer                     ║
║  → http://127.0.0.1:18799               ║
║  Open in browser to manage memories       ║
╚══════════════════════════════════════════╝
```

### 5. Verify Memory is Working

**Step A** — Have a conversation with your OpenClaw agent about anything.

**Step B** — Open the Memory Viewer at `http://127.0.0.1:18799` and check that the conversation appears.

**Step C** — In a new conversation, ask the agent to recall what you discussed:

```
You: 你还记得我之前让你帮我处理过什么事情吗?
Agent: (calls memory_search) 是的,我们之前讨论过...
```

## How It Works

### Three Intelligent Pipelines

MemOS Lite operates through three interconnected pipelines that form a continuous learning loop:

```
Conversation → Memory Write Pipeline → Task Generation Pipeline → Skill Evolution Pipeline
                                                                          ↓
                              Smart Retrieval Pipeline ← ← ← ← ← ← ← ← ←
```

### Pipeline 1: Memory Write (auto on every agent turn)

```
Conversation → Capture (filter roles, strip system prompts)
→ Semantic chunking (code blocks, paragraphs, error stacks)
→ Content hash dedup → LLM summarize each chunk
→ Vector embedding → Store (SQLite + FTS5 + Vector)
```

- System messages are skipped; tool results from the plugin's own tools are not re-stored
- Evidence wrapper blocks (`[STORED_MEMORY]...[/STORED_MEMORY]`) are stripped to prevent feedback loops
- Content hash (SHA-256, first 16 hex chars) prevents duplicate chunk ingestion within the same session+role

### Pipeline 2: Task Generation (auto after memory write)

```
New chunks → Group into user-turns → Process one turn at a time
→ Warm-up (first user turn): assign directly
→ Each subsequent user turn: LLM topic judge (context vs new message)
  → "NEW"? → Finalize current task, create new task
  → "SAME"? → Assign to current task
→ Time gap > 2h? → Always split regardless of topic
→ Finalize: Chunks ≥ 4 & turns ≥ 2? → LLM structured summary → status = "completed"
  → Otherwise → status = "skipped" (excluded from search)
```

**Why Tasks matter:**
- Raw memory chunks are fragmented — a single conversation about "deploying Nginx" might span 20 chunks
- Task summarization organizes these fragments into a structured record: Goal → Steps → Result → Key Details
- When the agent searches memory, it can quickly locate the complete experience via `task_summary`, not just fragments
- Task summaries preserve code, commands, URLs, configs, and error messages

### Pipeline 3: Skill Evolution (auto after task completion)

```
Completed task → Rule filter (min chunks, non-trivial content)
→ Search for related existing skills
  → Related skill found (confidence ≥ 0.7)?
    → Evaluate upgrade (refine/extend/fix) → Merge new experience → Version bump
  → No related skill (or confidence < 0.3)?
    → Evaluate create → Generate SKILL.md + scripts + evals
    → Quality score (0-10) → Install if score ≥ 6
```

**Why Skills matter:**
- Without skills, agents rediscover solutions every time they encounter similar problems
- Skills crystallize successful executions into reusable guides with steps, pitfall warnings, and verification checks
- Skills auto-upgrade when new tasks bring improved approaches — getting faster, more accurate, and more token-efficient
- The evolution is automatic: task completes → evaluate → create/upgrade → install

### Pipeline 4: Smart Retrieval

**Auto-recall (every turn):** The plugin hooks `before_agent_start`, runs a memory search with the user's message, then uses an LLM to filter which candidates are relevant and whether they are sufficient to answer. The filtered memories are injected into the agent's system context (invisible to the user). If no memories are found or the query is long/unclear, the agent is prompted to call `memory_search` with a self-generated short query.

**On-demand search (`memory_search`):**
```
Query → FTS5 + Vector dual recall → RRF Fusion → MMR Rerank
→ Recency Decay → Score Filter → Top-K (e.g. 20)
→ LLM relevance filter (minimum information) → Dedup by excerpt overlap
→ Return excerpts + chunkId / task_id (no summaries)
  → sufficient=false → suggest task_summary(taskId), skill_get(taskId), memory_timeline(chunkId)
```

- **RRF (Reciprocal Rank Fusion):** Merges FTS5 and vector search rankings into a unified score
- **MMR (Maximal Marginal Relevance):** Re-ranks to balance relevance with diversity
- **Recency Decay:** Recent memories get a boost (half-life: 14 days by default)
- **LLM filter:** Only memories that are genuinely useful for the query are returned; sufficiency determines whether follow-up tool tips are appended

## Retrieval Strategy

1. **Auto-recall (hook)** — On every turn, the plugin runs a memory search using the user's message and injects LLM-filtered relevant memories into the agent's context (via `before_agent_start`). The agent sees this as system context; the user does not.
2. **When nothing is recalled** — If the user's message is long, vague, or no matches are found, the plugin injects a short hint telling the agent to call **`memory_search`** with a **self-generated short query** (e.g. key topics or a rephrased question).
3. **Bundled skill** — The plugin installs `memos-memory-guide` into `~/.openclaw/workspace/skills/memos-memory-guide/` and `~/.openclaw/skills/memos-memory-guide/`. This skill documents all memory tools, when to call them, and how to write good search queries. Add `skills.load.extraDirs: ["~/.openclaw/skills"]` in `openclaw.json` if you want the skill to appear in the OpenClaw skills dashboard.
4. **Search results** — `memory_search` returns **excerpts** (original content snippets) and IDs (`chunkId`, `task_id`), not summaries. The agent uses `memory_get(chunkId)` for full original text, `task_summary(taskId)` for structured task context, `memory_timeline(chunkId)` for surrounding conversation, and `skill_get(skillId|taskId)` for reusable experience guides.

## Agent Tools

The plugin provides **12 smart tools** (11 registered tools + auto-recall) and auto-installs the **memos-memory-guide** skill:

| Tool | Purpose | When to Use |
|------|---------|-------------|
| `auto_recall` | Automatically injects relevant memories into agent context each turn (via `before_agent_start` hook) | Runs automatically — no manual call needed |
| `memory_search` | Search memories (auto-filtered to current agent + public); returns excerpts + `chunkId` / `task_id` | When auto-recall returned nothing or you need a different query |
| `memory_get` | Get full original text of a memory chunk | When you need to verify exact details from a search hit |
| `memory_timeline` | Surrounding conversation around a chunk | When you need the exact dialogue before/after a hit |
| `memory_write_public` | Write a memory to the shared public space (owner="public") | When the agent discovers knowledge all agents should access |
| `task_summary` | Full structured summary of a completed task | When a hit has `task_id` and you need the full story (goal, steps, result) |
| `skill_get` | Get skill content by `skillId` or `taskId` | When a hit has a linked task/skill and you want the reusable experience guide |
| `skill_install` | Install a skill into the agent workspace | When the skill should be permanently available for future turns |
| `skill_search` | Search skills via FTS + vector + LLM relevance; scope: `mix` / `self` / `public` | When an agent needs to discover existing skills for a task |
| `skill_publish` | Set a skill's visibility to public | When a skill should be discoverable by other agents |
| `skill_unpublish` | Set a skill's visibility back to private | When a skill should no longer be shared |
| `memory_viewer` | Get the URL of the Memory Viewer web UI | When the user asks where to view or manage their memories |

### Search Parameters

| Parameter | Default | Range | Description |
|-----------|---------|-------|-------------|
| `query` | — | — | Natural language search query (keep it short and focused) |
| `maxResults` | 20 | 1–20 | Maximum candidates before LLM filter |
| `minScore` | 0.45 | 0.35–1.0 | Minimum relevance score |
| `role` | — | `user` / `assistant` / `tool` | Filter by message role (e.g. `user` to find what the user said) |

> **Viewer search** uses a stricter threshold (`minScore` 0.64) for vector results. When no semantic matches are found, it falls back to FTS5 keyword search and returns the top 20 keyword-based results.

## Memory Viewer

Open `http://127.0.0.1:18799` in your browser after starting the gateway.

**Pages:**

| Page | Features |
|------|----------|
| **Memories** | Timeline view, pagination, session/role/kind/date filters, CRUD, semantic search; evolution badges and merge history on cards |
| **Tasks** | Task list with status filters (active/completed/skipped), chat-bubble chunk view, structured summaries, skill generation status |
| **Skills** | Skill list with status badges, version history with changelogs, quality scores, related tasks, one-click ZIP download |
| **Analytics** | Daily write/read activity charts, memory/task/skill totals, role breakdown |
| **Logs** | Tool call log (memory_search, auto_recall, memory_add, etc.) with input/output, duration, and tool filter; auto-refresh |
| **Import** | 🦐 OpenClaw native memory migration — scan, one-click import with real-time SSE progress, smart dedup, pause/resume; post-processing for task & skill generation |
| **Settings** | Online configuration for embedding model, summarizer model, skill evolution settings, viewer port |

**Viewer won't open?**

- The viewer is started by the plugin when the **gateway** starts. It does **not** run at install time.
- Ensure the gateway is running: `openclaw gateway start`
- Ensure the plugin is enabled in `~/.openclaw/openclaw.json`
- Check the log: `tail -30 ~/.openclaw/logs/gateway.log` — look for `MemOS Memory Viewer`

**Forgot password?** Click "Forgot password?" on the login page and use the reset token:

```bash
grep "password reset token:" ~/.openclaw/logs/gateway.log 2>/dev/null | tail -1
```

Copy the 32-character hex string after `password reset token:`.

## Advanced Configuration

All optional — shown with defaults:

```jsonc
{
  "config": {
    "recall": {
      "maxResultsDefault": 6,     // Default search results
      "maxResultsMax": 20,        // Max search results
      "minScoreDefault": 0.45,    // Default min score threshold
      "minScoreFloor": 0.35,      // Lowest allowed min score
      "rrfK": 60,                 // RRF fusion constant
      "mmrLambda": 0.7,           // MMR relevance vs diversity (0-1)
      "recencyHalfLifeDays": 14,  // Time decay half-life
      "vectorSearchMaxChunks": 0  // 0 = search all (default). Set 200000–300000 only if search is slow on huge DBs
    },
    "dedup": {
      "similarityThreshold": 0.75,  // Cosine similarity for smart-dedup candidates (Top-5)
      "enableSmartMerge": true,     // LLM judge: DUPLICATE / UPDATE / NEW
      "maxCandidates": 5            // Max similar chunks to send to LLM
    },
    "skillEvolution": {
      "enabled": true,            // Enable skill evolution
      "autoEvaluate": true,       // Auto-evaluate tasks for skill generation
      "minChunksForEval": 6,      // Min chunks for a task to be evaluated
      "minConfidence": 0.7,       // Min LLM confidence to create/upgrade skill
      "autoInstall": false        // Auto-install generated skills
    },
    "viewerPort": 18799,          // Memory Viewer port
    "telemetry": {
      "enabled": true              // Anonymous usage analytics (default: true, set false to opt-out)
    }
  }
}
```

## Telemetry

MemOS Lite collects **anonymous** usage analytics to help us understand how the plugin is used and improve it. Telemetry is **enabled by default** and can be disabled at any time.

### What is collected

- Plugin version, OS, Node.js version, architecture
- Tool call names and latencies (e.g. "memory_search took 120ms")
- Aggregate counts (chunks ingested, skills installed)
- Daily active ping

### What is NEVER collected

- Memory content, search queries, or conversation text
- API keys, file paths, or any personally identifiable information
- Any data stored in your local database

### How to disable

Add `telemetry` to your plugin config in `~/.openclaw/openclaw.json`:

```jsonc
{
  "plugins": {
    "entries": {
      "memos-local-openclaw-plugin": {
        "enabled": true,
        "config": {
          "telemetry": {
            "enabled": false
          }
          // ... other config
        }
      }
    }
  }
}
```

Or set the environment variable:

```bash
TELEMETRY_ENABLED=false
```

### Technical details

- Uses Aliyun ARMS RUM for event collection
- Each installation gets a random anonymous UUID (stored at `~/.openclaw/memos-local/.anonymous-id`)
- Events are batched and sent in the background; failures are silently ignored
- The anonymous ID is never linked to any personal information

## Upgrade

```bash
openclaw plugins update memos-local-openclaw-plugin
```

The plugin will automatically install dependencies, clean up legacy versions, and rebuild the native SQLite module. After update, restart the gateway:

```bash
openclaw gateway stop && openclaw gateway start
```

> **Tip:** To update all plugins at once: `openclaw plugins update --all`

**If `openclaw plugins update` doesn't work** (plugin not in install registry), reinstall:

```bash
rm -rf ~/.openclaw/extensions/memos-local-openclaw-plugin
openclaw plugins install @memtensor/memos-local-openclaw-plugin
```

> **Note:** `openclaw plugins install` requires the target directory to not exist. If you see `plugin already exists`, delete the directory first. Your memory data is stored separately at `~/.openclaw/memos-local/memos.db` and will not be affected.

## Troubleshooting

> 📖 **详细排查指南 / Detailed troubleshooting guide:** [docs/troubleshooting.html](https://memtensor.github.io/MemOS/apps/memos-local-openclaw/docs/troubleshooting.html) — 包含逐步排查流程、日志查看方法、完全重装步骤等。
>
> 📦 **better-sqlite3 official troubleshooting:** [better-sqlite3 Troubleshooting](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md) — the upstream guide for native module build issues.

### Common Issues

1. **Note the exact error** — e.g. `plugin not found`, `Cannot find module 'xxx'`, `Invalid config`.

2. **Check plugin status**
   ```bash
   openclaw plugins list
   ```
   - Status is **error** → note the error message
   - Not listed → not installed or not placed in `~/.openclaw/extensions/memos-local-openclaw-plugin`

3. **Check gateway logs**
   ```bash
   tail -50 ~/.openclaw/logs/gateway.log
   ```
   Search for `memos-local`, `failed to load`, `Error`, `Cannot find module`.

4. **Check environment**
   - Node version: `node -v` (requires **>= 18**)
   - Plugin directory exists: `ls ~/.openclaw/extensions/memos-local-openclaw-plugin/package.json`
   - Dependencies installed: `ls ~/.openclaw/extensions/memos-local-openclaw-plugin/node_modules/@sinclair/typebox`
     If missing: `cd ~/.openclaw/extensions/memos-local-openclaw-plugin && npm install --omit=dev`

5. **Check configuration** — Open `~/.openclaw/openclaw.json` and verify:
   - `agents.defaults.memorySearch.enabled` = `false` (disable built-in memory)
   - `plugins.slots.memory` = `"memos-local-openclaw-plugin"`
   - `plugins.entries.memos-local-openclaw-plugin.enabled` = `true`

6. **better-sqlite3 native module error** — `Could not locate the bindings file` means the native SQLite addon was not compiled for your Node.js version.
   ```bash
   cd ~/.openclaw/extensions/memos-local-openclaw-plugin
   npm rebuild better-sqlite3
   ```
   If rebuild fails, install C++ build tools first:
   - **macOS:** `xcode-select --install` (if you see `xcrun: error: invalid active developer path`, run this first)
   - **Linux:** `sudo apt install build-essential python3`
   - **Windows:** Usually not needed — `better-sqlite3` provides prebuilt binaries for Windows + Node.js LTS. If it still fails, install [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) (select "C++ build tools" workload)

   Then retry `npm rebuild better-sqlite3` and restart the gateway.

   > **Still failing?** Check the official [better-sqlite3 troubleshooting guide](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md) for platform-specific solutions. For non-LTS Node.js versions (e.g., v25.x), prebuilt binaries may not be available and compilation from source is required.

7. **Memory conflict with built-in search** — If the agent calls both the built-in memory search and the plugin's `memory_search`, it means `agents.defaults.memorySearch.enabled` is not set to `false`.

8. **Skills not generating** — Check:
   - `skillEvolution.enabled` is `true`
   - Tasks have enough content (default requires >= 6 chunks)
   - LLM model is accessible (check gateway log for `judgeNewTopic failed` or `SkillEvolver` errors)
   - The LLM fallback chain will try: `skillSummarizer` → `summarizer` → OpenClaw native model. If all fail, skill generation is skipped
   - Look for `SkillEvolver` output in the gateway log

9. **LLM calls failing** — All LLM-dependent features (summarization, topic detection, skill generation) use a fallback chain. If the configured model returns an error, the next model in the chain is tried automatically. Check the gateway log for messages like `failed (model), trying next`. If all models fail, the operation falls back to rule-based logic or is skipped.

## Data Location

| File | Path |
|---|---|
| Database | `~/.openclaw/memos-local/memos.db` |
| Viewer auth | `~/.openclaw/memos-local/viewer-auth.json` |
| Gateway log | `~/.openclaw/logs/gateway.log` |
| Plugin code | `~/.openclaw/extensions/memos-local-openclaw-plugin/` |
| Memory-guide skill | `~/.openclaw/workspace/skills/memos-memory-guide/SKILL.md` (and `~/.openclaw/skills/memos-memory-guide/`) |
| Generated skills | `~/.openclaw/memos-local/skills-store/<skill-name>/` |
| Installed skills | `~/.openclaw/workspace/skills/<skill-name>/` |

## Development Guide

This section is for contributors who want to develop, test, or modify the plugin from source.

### Prerequisites

- **Node.js >= 18** (`node -v`)
- **npm >= 9** (`npm -v`)
- **C++ build tools** (for `better-sqlite3` native module):
  - macOS: `xcode-select --install`
  - Linux: `sudo apt install build-essential python3`
  - Windows: usually not needed (prebuilt binaries available for LTS Node.js); if build fails, install [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
- **OpenClaw CLI** installed and available in PATH (`openclaw --version`)

> **`better-sqlite3` build issues?** This is the most common installation problem on macOS and Linux. If `npm install` fails, first install the C++ build tools above, then run `npm rebuild better-sqlite3`. For detailed platform-specific solutions, see the [official better-sqlite3 troubleshooting guide](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md) and our [installation troubleshooting page](https://memtensor.github.io/MemOS/apps/memos-local-openclaw/docs/troubleshooting.html).

### Clone & Setup

```bash
git clone https://github.com/MemTensor/MemOS.git
cd MemOS/apps/memos-local-openclaw
npm install
```

> `npm install` triggers the `postinstall` script which automatically rebuilds `better-sqlite3` for your Node.js version.

### Project Structure

```
apps/memos-local-openclaw/
├── index.ts                 # Plugin entry — hooks, tool registration, lifecycle
├── plugin-impl.ts           # OpenClaw plugin SDK implementation
├── src/
│   ├── index.ts             # Module re-exports
│   ├── config.ts            # Configuration schema & defaults
│   ├── types.ts             # TypeScript type definitions
│   ├── capture/index.ts     # Message capture & filtering logic
│   ├── embedding/           # Embedding providers (OpenAI, Gemini, Cohere, etc.)
│   ├── ingest/
│   │   ├── chunker.ts       # Semantic chunking (code blocks, paragraphs)
│   │   ├── dedup.ts         # Content-hash + vector deduplication
│   │   ├── worker.ts        # Async ingestion pipeline
│   │   ├── task-processor.ts # Task boundary detection & summarization
│   │   └── providers/       # LLM providers for summarization
│   ├── recall/
│   │   ├── engine.ts        # Hybrid retrieval engine (FTS5 + Vector)
│   │   ├── rrf.ts           # Reciprocal Rank Fusion
│   │   ├── mmr.ts           # Maximal Marginal Relevance
│   │   └── recency.ts       # Time-decay scoring
│   ├── shared/
│   │   └── llm-call.ts      # LLM fallback chain utility (callLLMWithFallback, buildSkillConfigChain)
│   ├── skill/               # Skill evolution pipeline (evaluator, generator, upgrader)
│   ├── storage/
│   │   ├── sqlite.ts        # SQLite database layer (chunks, tasks, skills, FTS5)
│   │   └── vector.ts        # Vector similarity search
│   ├── tools/               # Tool implementations (memory-search, memory-get, etc.)
│   ├── viewer/              # Memory Viewer web server & HTML templates
│   └── telemetry.ts         # Anonymous usage analytics
├── tests/                   # Test suite (vitest)
├── scripts/                 # Utility scripts (seed data, smoke test, viewer)
├── skill/                   # Bundled skill definitions (SKILL.md files)
├── openclaw.plugin.json     # Plugin metadata for OpenClaw registry
├── package.json             # Dependencies & scripts
├── tsconfig.json            # TypeScript configuration
└── vitest.config.ts         # Test runner configuration
```

**Files NOT in the repository** (generated locally, excluded via `.gitignore`):

| Directory / File | Purpose | How to generate |
|---|---|---|
| `node_modules/` | npm dependencies | `npm install` |
| `dist/` | Compiled JavaScript output | `npm run build` |
| `package-lock.json` | Dependency lock file | `npm install` (auto-generated) |
| `www/` | Memory Viewer static site (local preview) | Started automatically by the plugin |
| `docs/` | Documentation HTML pages | Built from source or viewed at the hosted URL |
| `ppt/` | Presentation files (internal use) | Not needed for development |
| `.env` | Local environment variables | Copy from `.env.example` |

### Build

```bash
npm run build       # Compile TypeScript → dist/
npm run dev         # Watch mode — auto-recompile on save
```

The build output goes to `dist/` (CommonJS modules with declarations and source maps).

### Configure for Local Development

1. **Copy the environment template:**

```bash
cp .env.example .env
```

2. **Edit `.env`** with your API keys (or leave blank for local-only mode):

```bash
# Embedding — leave blank to use local offline model
EMBEDDING_PROVIDER=openai_compatible
EMBEDDING_API_KEY=your-key
EMBEDDING_ENDPOINT=https://your-api.com/v1
EMBEDDING_MODEL=bge-m3

# Summarizer — leave blank for rule-based fallback
SUMMARIZER_PROVIDER=openai_compatible
SUMMARIZER_API_KEY=your-key
SUMMARIZER_ENDPOINT=https://api.openai.com/v1
SUMMARIZER_MODEL=gpt-4o-mini
```

3. **Install the plugin locally into OpenClaw:**

```bash
npm run build
openclaw plugins install .
```

4. **Configure OpenClaw** — Add the plugin to `~/.openclaw/openclaw.json` (see [Configure](#2-configure) section above).

5. **Start the gateway:**

```bash
openclaw gateway stop    # stop existing
openclaw gateway start   # start with new plugin
```

### Testing

Run the full test suite:

```bash
npm test              # Run all tests once
npm run test:watch    # Watch mode — re-run on file changes
```

Test coverage includes:

| Test File | Coverage |
|---|---|
| `tests/policy.test.ts` | Retrieval strategy, search filtering, evidence extraction, instruction stripping |
| `tests/recall.test.ts` | RRF fusion, recency decay correctness |
| `tests/capture.test.ts` | Message filtering, evidence block stripping, self-tool exclusion |
| `tests/storage.test.ts` | SQLite CRUD, FTS5, vector storage, content hash dedup |
| `tests/chunker.test.ts` | Semantic chunking for code blocks, paragraphs, function bodies |
| `tests/task-processor.test.ts` | Task boundary detection, skip logic, summary generation |
| `tests/multi-agent.test.ts` | Multi-agent memory isolation, owner filtering, public sharing |
| `tests/integration.test.ts` | End-to-end ingestion and retrieval pipeline |

> Tests use an **in-memory SQLite database** — no external services or API keys required.

### Development Workflow

1. **Make changes** to files in `src/` or `index.ts`
2. **Run tests** to verify: `npm test`
3. **Build** to check TypeScript compilation: `npm run build`
4. **Test with OpenClaw** locally:
   ```bash
   openclaw plugins install .   # re-install from local source
   openclaw gateway stop && openclaw gateway start
   tail -f ~/.openclaw/logs/gateway.log   # watch logs
   ```
5. **Open Memory Viewer** at `http://127.0.0.1:18799` to verify UI changes

### Publishing to npm

```bash
npm run build                    # Compile TypeScript
npm publish --access public      # Publish to npm registry
```

After publishing, users can install with:
```bash
openclaw plugins install @memtensor/memos-local-openclaw-plugin
```

### Utility Scripts

| Script | Command | Purpose |
|---|---|---|
| Seed test data | `npx tsx scripts/seed-test-data.ts` | Populate local DB with sample memories, tasks, and skills |
| Smoke test | `npx tsx scripts/smoke-test.ts` | Quick end-to-end verification of plugin functionality |
| Start viewer | `npx tsx scripts/start-viewer.ts` | Start Memory Viewer standalone (without gateway) |
| Refresh skills | `npx tsx scripts/refresh-skill.ts` | Re-evaluate and regenerate skills from existing tasks |
| Refresh summaries | `npx tsx scripts/refresh-summaries.ts` | Re-generate task summaries for completed tasks |
| Mock skills | `npx tsx scripts/mock-skills.ts` | Generate mock skill data for testing |

## License

MIT — See [LICENSE](../../LICENSE) for details.


================================================
FILE: apps/memos-local-openclaw/index.ts
================================================
/**
 * OpenClaw Plugin Entry — memos-local
 *
 * Full-write local memory with hybrid retrieval (RRF + MMR + recency).
 * Provides: memory_search, memory_get, memory_timeline, task_summary, skill_get, skill_install, memory_viewer
 */

import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { Type } from "@sinclair/typebox";
import * as fs from "fs";
import * as path from "path";
import { fileURLToPath } from "url";
import { buildContext } from "./src/config";
import { ensureSqliteBinding } from "./src/storage/ensure-binding";
import { SqliteStore } from "./src/storage/sqlite";
import { Embedder } from "./src/embedding";
import { IngestWorker } from "./src/ingest/worker";
import { RecallEngine } from "./src/recall/engine";
import { captureMessages, stripInboundMetadata } from "./src/capture";
import { DEFAULTS } from "./src/types";
import { ViewerServer } from "./src/viewer/server";
import { SkillEvolver } from "./src/skill/evolver";
import { SkillInstaller } from "./src/skill/installer";
import { Summarizer } from "./src/ingest/providers";
import { MEMORY_GUIDE_SKILL_MD } from "./src/skill/bundled-memory-guide";
import { Telemetry } from "./src/telemetry";


/** Remove near-duplicate hits based on summary word overlap (>70%). Keeps first (highest-scored) hit. */
function deduplicateHits<T extends { summary: string }>(hits: T[]): T[] {
  const kept: T[] = [];
  for (const hit of hits) {
    const dominated = kept.some((k) => {
      const a = k.summary.toLowerCase();
      const b = hit.summary.toLowerCase();
      if (a === b) return true;
      const wordsA = new Set(a.split(/\s+/).filter(w => w.length > 1));
      const wordsB = new Set(b.split(/\s+/).filter(w => w.length > 1));
      if (wordsA.size === 0 || wordsB.size === 0) return false;
      let overlap = 0;
      for (const w of wordsB) { if (wordsA.has(w)) overlap++; }
      return overlap / Math.min(wordsA.size, wordsB.size) > 0.7;
    });
    if (!dominated) kept.push(hit);
  }
  return kept;
}

const pluginConfigSchema = {
  type: "object" as const,
  additionalProperties: true,
  properties: {
    viewerPort: {
      type: "number" as const,
      description: "Memory Viewer HTTP port (default 18799)",
    },
    telemetry: {
      type: "object" as const,
      description: "Anonymous usage analytics (opt-out). No memory content or personal data is ever sent.",
      properties: {
        enabled: {
          type: "boolean" as const,
          description: "Enable anonymous telemetry (default: true). Set to false to opt-out.",
        },
      },
    },
  },
};

const memosLocalPlugin = {
  id: "memos-local-openclaw-plugin",
  name: "MemOS Local Memory",
  description:
    "Full-write local conversation memory with hybrid search (RRF + MMR + recency). " +
    "Provides memory_search, memory_get, task_summary, memory_timeline, memory_viewer for layered retrieval.",
  kind: "memory" as const,
  configSchema: pluginConfigSchema,

  register(api: OpenClawPluginApi) {
    // ─── Ensure better-sqlite3 native module is available ───
    const pluginDir = path.dirname(fileURLToPath(import.meta.url));

    function normalizeFsPath(p: string): string {
      return path.resolve(p).replace(/\\/g, "/").toLowerCase();
    }

    let sqliteReady = false;

    function trySqliteLoad(): boolean {
      try {
        const resolved = require.resolve("better-sqlite3", { paths: [pluginDir] });
        const resolvedNorm = normalizeFsPath(resolved);
        const pluginNorm = normalizeFsPath(pluginDir);
        if (!resolvedNorm.startsWith(pluginNorm + "/") && resolvedNorm !== pluginNorm) {
          api.logger.warn(`memos-local: better-sqlite3 resolved outside plugin dir: ${resolved}`);
          return false;
        }
        require(resolved);
        return true;
      } catch {
        return false;
      }
    }

    sqliteReady = trySqliteLoad();

    if (!sqliteReady) {
      api.logger.warn(`memos-local: better-sqlite3 not found in ${pluginDir}, attempting auto-rebuild ...`);

      try {
        const { spawnSync } = require("child_process");
        const rebuildResult = spawnSync("npm", ["rebuild", "better-sqlite3"], {
          cwd: pluginDir,
          stdio: "pipe",
          shell: true,
          timeout: 120_000,
        });

        const stdout = rebuildResult.stdout?.toString() || "";
        const stderr = rebuildResult.stderr?.toString() || "";
        if (stdout) api.logger.info(`memos-local: rebuild stdout: ${stdout.slice(0, 500)}`);
        if (stderr) api.logger.warn(`memos-local: rebuild stderr: ${stderr.slice(0, 500)}`);

        if (rebuildResult.status === 0) {
          Object.keys(require.cache)
            .filter(k => k.includes("better-sqlite3") || k.includes("better_sqlite3"))
            .forEach(k => delete require.cache[k]);
          sqliteReady = trySqliteLoad();
          if (sqliteReady) {
            api.logger.info("memos-local: better-sqlite3 auto-rebuild succeeded!");
          } else {
            api.logger.warn("memos-local: rebuild exited 0 but module still not loadable from plugin dir");
          }
        } else {
          api.logger.warn(`memos-local: rebuild exited with code ${rebuildResult.status}`);
        }
      } catch (rebuildErr) {
        api.logger.warn(`memos-local: auto-rebuild error: ${rebuildErr}`);
      }

      if (!sqliteReady) {
        const nodeVer = process.version;
        const nodeMajor = parseInt(process.versions?.node?.split(".")[0] ?? "0", 10);
        const isNode25Plus = nodeMajor >= 25;
        const lines = [
          "",
          "╔══════════════════════════════════════════════════════════════╗",
          "║  MemOS Local Memory — better-sqlite3 native module missing  ║",
          "╠══════════════════════════════════════════════════════════════╣",
          "║                                                            ║",
          "║  Auto-rebuild failed (Node " + nodeVer + "). Run manually:              ║",
          "║                                                            ║",
          `║  cd ${pluginDir}`,
          "║  npm rebuild better-sqlite3                                ║",
          "║  openclaw gateway stop && openclaw gateway start           ║",
          "║                                                            ║",
          "║  If rebuild fails, install build tools first:              ║",
          "║  macOS:  xcode-select --install                            ║",
          "║  Linux:  sudo apt install build-essential python3          ║",
        ];
        if (isNode25Plus) {
          lines.push("║                                                            ║");
          lines.push("║  Node 25+ has no prebuild: build tools required, or use    ║");
          lines.push("║  Node LTS (20/22): nvm install 22 && nvm use 22            ║");
        }
        lines.push("║                                                            ║");
        lines.push("╚══════════════════════════════════════════════════════════════╝");
        lines.push("");
        api.logger.warn(lines.join("\n"));
        throw new Error(
          `better-sqlite3 native module not found (Node ${nodeVer}). Auto-rebuild failed. Fix: install build tools, then cd ${pluginDir} && npm rebuild better-sqlite3. Or use Node LTS (20/22).`
        );
      }
    }

    const pluginCfg = (api.pluginConfig ?? {}) as Record<string, unknown>;
    const stateDir = api.resolvePath("~/.openclaw");
    const ctx = buildContext(stateDir, process.cwd(), pluginCfg as any, {
      debug: (msg: string) => api.logger.info(`[debug] ${msg}`),
      info: (msg: string) => api.logger.info(msg),
      warn: (msg: string) => api.logger.warn(msg),
      error: (msg: string) => api.logger.warn(`[error] ${msg}`),
    });

    ensureSqliteBinding(ctx.log);

    const store = new SqliteStore(ctx.config.storage!.dbPath!, ctx.log);
    const embedder = new Embedder(ctx.config.embedding, ctx.log);
    const worker = new IngestWorker(store, embedder, ctx);
    const engine = new RecallEngine(store, embedder, ctx);
    const evidenceTag = ctx.config.capture?.evidenceWrapperTag ?? DEFAULTS.evidenceWrapperTag;

    const workspaceDir = api.resolvePath("~/.openclaw/workspace");
    const skillCtx = { ...ctx, workspaceDir };
    const skillEvolver = new SkillEvolver(store, engine, skillCtx);
    skillEvolver.onSkillEvolved = (name, type) => telemetry.trackSkillEvolved(name, type);
    const skillInstaller = new SkillInstaller(store, skillCtx);

    let pluginVersion = "0.0.0";
    try {
      const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf-8"));
      pluginVersion = pkg.version ?? pluginVersion;
    } catch {}
    const telemetry = new Telemetry(ctx.config.telemetry ?? {}, stateDir, pluginVersion, ctx.log);

    // Install bundled memory-guide skill so OpenClaw loads it (write from embedded content so it works regardless of deploy layout)
    const workspaceSkillsDir = path.join(workspaceDir, "skills");
    const memosGuideDest = path.join(workspaceSkillsDir, "memos-memory-guide");
    fs.mkdirSync(memosGuideDest, { recursive: true });
    fs.writeFileSync(path.join(memosGuideDest, "SKILL.md"), MEMORY_GUIDE_SKILL_MD, "utf-8");
    ctx.log.info(`memos-local: installed bundled skill memos-memory-guide → ${memosGuideDest}`);

    // Also ensure managed skills dir has it so dashboard/other loaders can see it
    const managedSkillsDir = path.join(stateDir, "skills");
    const managedMemosGuide = path.join(managedSkillsDir, "memos-memory-guide");
    try {
      fs.mkdirSync(managedMemosGuide, { recursive: true });
      fs.writeFileSync(path.join(managedMemosGuide, "SKILL.md"), MEMORY_GUIDE_SKILL_MD, "utf-8");
      ctx.log.info(`memos-local: installed bundled skill memos-memory-guide → ${managedMemosGuide} (managed)`);
    } catch (e) {
      ctx.log.warn(`memos-local: could not write to managed skills dir: ${e}`);
    }

    // Ensure plugin tools are enabled in openclaw.json tools.allow
    try {
      const openclawJsonPath = path.join(stateDir, "openclaw.json");
      if (fs.existsSync(openclawJsonPath)) {
        const raw = fs.readFileSync(openclawJsonPath, "utf-8");
        const cfg = JSON.parse(raw);
        const allow: string[] | undefined = cfg?.tools?.allow;
        if (Array.isArray(allow) && allow.length > 0 && !allow.includes("group:plugins")) {
          const lastEntry = JSON.stringify(allow[allow.length - 1]);
          const patched = raw.replace(
            new RegExp(`(${lastEntry})(\\s*\\])`),
            `$1,\n      "group:plugins"$2`,
          );
          if (patched !== raw && patched.includes("group:plugins")) {
            fs.writeFileSync(openclawJsonPath, patched, "utf-8");
            ctx.log.info("memos-local: added 'group:plugins' to tools.allow in openclaw.json");
          }
        }
      }
    } catch (e) {
      ctx.log.warn(`memos-local: could not patch tools.allow: ${e}`);
    }

    worker.getTaskProcessor().onTaskCompleted((task) => {
      skillEvolver.onTaskCompleted(task).catch((err) => {
        ctx.log.warn(`SkillEvolver async error: ${err}`);
      });
    });

    const summarizer = new Summarizer(ctx.config.summarizer, ctx.log);

    api.logger.info(`memos-local: initialized (db: ${ctx.config.storage!.dbPath})`);

    // Current agent ID — updated by hooks, read by tools for owner isolation.
    // Falls back to "main" when no hook has fired yet (single-agent setups).
    let currentAgentId = "main";

    // ─── Check allowPromptInjection policy ───
    // When allowPromptInjection=false, the prompt mutation fields (such as prependContext) in the hook return value
    // will be stripped by the framework. Skip auto-recall to avoid unnecessary LLM/embedding calls.
    const pluginEntry = (api.config as any)?.plugins?.entries?.[api.id];
    const allowPromptInjection = pluginEntry?.hooks?.allowPromptInjection !== false;
    if (!allowPromptInjection) {
      api.logger.info("memos-local: allowPromptInjection=false, auto-recall disabled");
    }
    else {
      api.logger.info("memos-local: allowPromptInjection=true, auto-recall enabled");
    }

    const trackTool = (toolName: string, fn: (...args: any[]) => Promise<any>) =>
      async (...args: any[]) => {
        const t0 = performance.now();
        let ok = true;
        let result: any;
        const inputParams = args.length > 1 ? args[1] : args[0];
        try {
          result = await fn(...args);
          return result;
        } catch (e) {
          ok = false;
          telemetry.trackError(toolName, (e as Error)?.name ?? "unknown");
          throw e;
        } finally {
          const dur = performance.now() - t0;
          store.recordToolCall(toolName, dur, ok);
          telemetry.trackToolCalled(toolName, dur, ok);
          try {
            let outputText: string;
            const det = result?.details;
            if (det && Array.isArray(det.candidates)) {
              outputText = JSON.stringify({
                candidates: det.candidates,
                filtered: det.hits ?? det.filtered ?? [],
              });
            } else {
              outputText = result?.content?.[0]?.text ?? JSON.stringify(result ?? "");
            }
            store.recordApiLog(toolName, { ...inputParams, type: "tool_call" }, outputText, dur, ok);
          } catch (_) { /* best-effort */ }
        }
      };

    // ─── Tool: memory_search ───

    api.registerTool(
      {
        name: "memory_search",
        label: "Memory Search",
        description:
          "Search long-term conversation memory for past conversations, user preferences, decisions, and experiences. " +
          "Relevant memories are automatically injected at the start of each turn, but call this tool when you need " +
          "to search with a different query or the auto-recalled context is insufficient. " +
          "Pass only a short natural-language query (2-5 key words).",
        parameters: Type.Object({
          query: Type.String({ description: "Short natural language search query (2-5 key words)" }),
        }),
        execute: trackTool("memory_search", async (_toolCallId: any, params: any) => {
          const { query } = params as { query: string };
          const role = undefined;
          const minScore = undefined;

          const agentId = currentAgentId;
          const ownerFilter = [`agent:${agentId}`, "public"];
          const effectiveMaxResults = 10;
          ctx.log.debug(`memory_search query="${query}" maxResults=${effectiveMaxResults} minScore=${minScore ?? 0.45} role=${role ?? "all"} owner=agent:${agentId}`);
          const result = await engine.search({ query, maxResults: effectiveMaxResults, minScore, role, ownerFilter });
          ctx.log.debug(`memory_search raw candidates: ${result.hits.length}`);

          const rawCandidates = result.hits.map((h) => ({
            chunkId: h.ref.chunkId,
            role: h.source.role,
            score: h.score,
            summary: h.summary,
            original_excerpt: (h.original_excerpt ?? "").slice(0, 200),
          }));

          if (result.hits.length === 0) {
            return {
              content: [{ type: "text", text: result.meta.note ?? "No relevant memories found." }],
              details: { candidates: [], meta: result.meta },
            };
          }

          // LLM relevance + sufficiency filtering
          let filteredHits = result.hits;
          let sufficient = false;

          const candidates = result.hits.map((h, i) => ({
            index: i + 1,
            role: h.source.role,
            content: (h.original_excerpt ?? "").slice(0, 300),
            time: h.source.ts ? new Date(h.source.ts).toISOString().slice(0, 16) : "",
          }));

          const filterResult = await summarizer.filterRelevant(query, candidates);
          if (filterResult !== null) {
            sufficient = filterResult.sufficient;
            if (filterResult.relevant.length > 0) {
              const indexSet = new Set(filterResult.relevant);
              filteredHits = result.hits.filter((_, i) => indexSet.has(i + 1));
              ctx.log.debug(`memory_search LLM filter: ${result.hits.length} → ${filteredHits.length} hits, sufficient=${sufficient}`);
            } else {
              return {
                content: [{ type: "text", text: "No relevant memories found for this query." }],
                details: { candidates: rawCandidates, filtered: [], meta: result.meta },
              };
            }
          }

          if (filteredHits.length === 0) {
            return {
              content: [{ type: "text", text: "No relevant memories found for this query." }],
              details: { candidates: rawCandidates, filtered: [], meta: result.meta },
            };
          }

          const beforeDedup = filteredHits.length;
          filteredHits = deduplicateHits(filteredHits);
          ctx.log.debug(`memory_search dedup: ${beforeDedup} → ${filteredHits.length}`);

          const lines = filteredHits.map((h, i) => {
            const excerpt = h.original_excerpt;
            const parts = [`${i + 1}. [${h.source.role}]`];
            if (excerpt) parts.push(`   ${excerpt}`);
            parts.push(`   chunkId="${h.ref.chunkId}"`);
            if (h.taskId) {
              const task = store.getTask(h.taskId);
              if (task && task.status !== "skipped") {
                parts.push(`   task_id="${h.taskId}"`);
              }
            }
            return parts.join("\n");
          });

          let tipsText = "";
          if (!sufficient) {
            const hasTask = filteredHits.some((h) => {
              if (!h.taskId) return false;
              const t = store.getTask(h.taskId);
              return t && t.status !== "skipped";
            });

            const tips: string[] = [];
            if (hasTask) {
              tips.push("→ call task_summary(taskId) for full task context");
              tips.push("→ call skill_get(taskId=...) if the task has a proven experience guide");
            }
            tips.push("→ call memory_timeline(chunkId) to expand surrounding conversation");

            if (tips.length > 0) {
              tipsText = "\n\nThese memories may not be enough. You can fetch more context:\n" + tips.join("\n");
            }
          }

          return {
            content: [
              {
                type: "text",
                text: `Found ${filteredHits.length} relevant memories:\n\n${lines.join("\n\n")}${tipsText}`,
              },
            ],
            details: {
              candidates: rawCandidates,
              hits: filteredHits.map((h) => {
                let effectiveTaskId = h.taskId;
                if (effectiveTaskId) {
                  const t = store.getTask(effectiveTaskId);
                  if (t && t.status === "skipped") effectiveTaskId = null;
                }
                return {
                  chunkId: h.ref.chunkId,
                  taskId: effectiveTaskId,
                  skillId: h.skillId,
                  role: h.source.role,
                  score: h.score,
                  summary: h.summary,
                  original_excerpt: (h.original_excerpt ?? "").slice(0, 200),
                };
              }),
              meta: result.meta,
            },
          };
        }),
      },
      { name: "memory_search" },
    );

    // ─── Tool: memory_timeline ───

    api.registerTool(
      {
        name: "memory_timeline",
        label: "Memory Timeline",
        description:
          "Expand context around a memory search hit. Pass the chunkId from a search result " +
          "to read the surrounding conversation messages.",
        parameters: Type.Object({
          chunkId: Type.String({ description: "The chunkId from a memory_search hit" }),
          window: Type.Optional(Type.Number({ description: "Context window ±N (default 2)" })),
        }),
        execute: trackTool("memory_timeline", async (_toolCallId: any, params: any) => {
          ctx.log.debug(`memory_timeline called (agent=${currentAgentId})`);
          const { chunkId, window: win } = params as {
            chunkId: string;
            window?: number;
          };

          const ownerFilter = [`agent:${currentAgentId}`, "public"];
          const anchorChunk = store.getChunkForOwners(chunkId, ownerFilter);
          if (!anchorChunk) {
            return {
              content: [{ type: "text", text: `Chunk not found: ${chunkId}` }],
              details: { error: "not_found" },
            };
          }

          const w = win ?? DEFAULTS.timelineWindowDefault;
          const neighbors = store.getNeighborChunks(anchorChunk.sessionKey, anchorChunk.turnId, anchorChunk.seq, w, ownerFilter);
          const anchorTs = anchorChunk?.createdAt ?? 0;

          const entries = neighbors.map((chunk) => {
            let relation: "before" | "current" | "after" = "before";
            if (chunk.id === chunkId) relation = "current";
            else if (chunk.createdAt > anchorTs) relation = "after";

            return {
              relation,
              role: chunk.role,
              excerpt: chunk.content,
              ts: chunk.createdAt,
            };
          });

          const rl = (r: string) => r === "user" ? "USER" : r === "assistant" ? "ASSISTANT" : r.toUpperCase();
          const text = entries
            .map((e) => `[${e.relation}] ${rl(e.role)}: ${e.excerpt}`)
            .join("\n");

          return {
            content: [{ type: "text", text: `Timeline (${entries.length} entries):\n\n${text}` }],
            details: { entries, anchorRef: { sessionKey: anchorChunk.sessionKey, chunkId, turnId: anchorChunk.turnId, seq: anchorChunk.seq } },
          };
        }),
      },
      { name: "memory_timeline" },
    );

    // ─── Tool: memory_get ───

    api.registerTool(
      {
        name: "memory_get",
        label: "Memory Get",
        description:
          "Get the full original text of a memory chunk. Use to verify exact details from a search hit.",
        parameters: Type.Object({
          chunkId: Type.String({ description: "From search hit ref.chunkId" }),
          maxChars: Type.Optional(
            Type.Number({ description: `Max chars (default ${DEFAULTS.getMaxCharsDefault}, max ${DEFAULTS.getMaxCharsMax})` }),
          ),
        }),
        execute: trackTool("memory_get", async (_toolCallId: any, params: any) => {
          const { chunkId, maxChars } = params as { chunkId: string; maxChars?: number };
          const limit = Math.min(maxChars ?? DEFAULTS.getMaxCharsDefault, DEFAULTS.getMaxCharsMax);

          const ownerFilter = [`agent:${currentAgentId}`, "public"];
          const chunk = store.getChunkForOwners(chunkId, ownerFilter);
          if (!chunk) {
            return {
              content: [{ type: "text", text: `Chunk not found: ${chunkId}` }],
              details: { error: "not_found" },
            };
          }

          const content = chunk.content;

          const who = chunk.role === "user" ? "USER said" : chunk.role === "assistant" ? "ASSISTANT replied" : chunk.role === "tool" ? "TOOL returned" : chunk.role.toUpperCase();

          return {
            content: [{ type: "text", text: `[${who}] (session: ${chunk.sessionKey})\n\n${content}` }],
            details: {
              ref: { sessionKey: chunk.sessionKey, chunkId: chunk.id, turnId: chunk.turnId, seq: chunk.seq },
              source: { ts: chunk.createdAt, role: chunk.role, sessionKey: chunk.sessionKey },
            },
          };
        }),
      },
      { name: "memory_get" },
    );

    // ─── Tool: task_summary ───

    api.registerTool(
      {
        name: "task_summary",
        label: "Task Summary",
        description:
          "Get the detailed summary of a complete task. Use this when memory_search returns a hit " +
          "with a task_id and you need the full context of that task. The summary preserves all " +
          "critical information: URLs, file paths, commands, error codes, step-by-step instructions.",
        parameters: Type.Object({
          taskId: Type.String({ description: "The task_id from a memory_search hit" }),
        }),
        execute: trackTool("task_summary", async (_toolCallId: any, params: any) => {
          const { taskId } = params as { taskId: string };
          ctx.log.debug(`task_summary called for task=${taskId}`);

          const task = store.getTask(taskId);
          if (!task) {
            return {
              content: [{ type: "text", text: `Task not found: ${taskId}` }],
              details: { error: "not_found" },
            };
          }

          if (task.status === "skipped") {
            return {
              content: [{ type: "text", text: `Task "${task.title}" was too brief to generate a summary. Reason: ${task.summary || "conversation too short"}. Use memory_get to read individual chunks instead.` }],
              details: { taskId, status: task.status },
            };
          }

          if (!task.summary) {
            const chunks = store.getChunksByTask(taskId);
            if (chunks.length === 0) {
              return {
                content: [{ type: "text", text: `Task ${taskId} has no content yet.` }],
                details: { taskId, status: task.s
Download .txt
gitextract_ulrpkn53/

├── .github/
│   ├── CONTRIBUTING
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.yml
│   │   ├── config.yml
│   │   └── feature-request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── openclaw-plugin-publish.yml
│       ├── python-release.yml
│       ├── python-tests.yml
│       └── stale.yml
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
├── Makefile
├── README.md
├── apps/
│   ├── MemOS-Cloud-OpenClaw-Plugin/
│   │   ├── .gitignore
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── README_ZH.md
│   │   ├── clawdbot.plugin.json
│   │   ├── index.js
│   │   ├── lib/
│   │   │   ├── check-update.js
│   │   │   └── memos-cloud-api.js
│   │   ├── moltbot.plugin.json
│   │   ├── openclaw.plugin.json
│   │   ├── package.json
│   │   └── scripts/
│   │       └── sync-version.js
│   ├── memos-local-openclaw/
│   │   ├── .gitignore
│   │   ├── README.md
│   │   ├── index.ts
│   │   ├── openclaw.plugin.json
│   │   ├── package.json
│   │   ├── plugin-impl.ts
│   │   ├── scripts/
│   │   │   ├── mock-skills.ts
│   │   │   ├── postinstall.cjs
│   │   │   ├── refresh-skill.ts
│   │   │   ├── refresh-summaries.ts
│   │   │   ├── run-accuracy-test.ts
│   │   │   ├── seed-test-data.ts
│   │   │   ├── smoke-test.ts
│   │   │   ├── start-viewer.ts
│   │   │   └── test-agent-isolation.ts
│   │   ├── skill/
│   │   │   ├── browserwing-admin/
│   │   │   │   └── SKILL.md
│   │   │   ├── browserwing-executor/
│   │   │   │   └── SKILL.md
│   │   │   └── memos-memory-guide/
│   │   │       └── SKILL.md
│   │   ├── src/
│   │   │   ├── capture/
│   │   │   │   └── index.ts
│   │   │   ├── config.ts
│   │   │   ├── embedding/
│   │   │   │   ├── index.ts
│   │   │   │   ├── local.ts
│   │   │   │   └── providers/
│   │   │   │       ├── cohere.ts
│   │   │   │       ├── gemini.ts
│   │   │   │       ├── mistral.ts
│   │   │   │       ├── openai.ts
│   │   │   │       └── voyage.ts
│   │   │   ├── index.ts
│   │   │   ├── ingest/
│   │   │   │   ├── chunker.ts
│   │   │   │   ├── dedup.ts
│   │   │   │   ├── providers/
│   │   │   │   │   ├── anthropic.ts
│   │   │   │   │   ├── bedrock.ts
│   │   │   │   │   ├── gemini.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── openai.ts
│   │   │   │   ├── task-processor.ts
│   │   │   │   └── worker.ts
│   │   │   ├── recall/
│   │   │   │   ├── engine.ts
│   │   │   │   ├── mmr.ts
│   │   │   │   ├── recency.ts
│   │   │   │   └── rrf.ts
│   │   │   ├── shared/
│   │   │   │   └── llm-call.ts
│   │   │   ├── skill/
│   │   │   │   ├── bundled-memory-guide.ts
│   │   │   │   ├── evaluator.ts
│   │   │   │   ├── evolver.ts
│   │   │   │   ├── generator.ts
│   │   │   │   ├── installer.ts
│   │   │   │   ├── upgrader.ts
│   │   │   │   └── validator.ts
│   │   │   ├── storage/
│   │   │   │   ├── ensure-binding.ts
│   │   │   │   ├── sqlite.ts
│   │   │   │   └── vector.ts
│   │   │   ├── telemetry.ts
│   │   │   ├── tools/
│   │   │   │   ├── index.ts
│   │   │   │   ├── memory-get.ts
│   │   │   │   ├── memory-search.ts
│   │   │   │   └── memory-timeline.ts
│   │   │   ├── types.ts
│   │   │   ├── update-check.ts
│   │   │   └── viewer/
│   │   │       ├── html.ts
│   │   │       └── server.ts
│   │   ├── tests/
│   │   │   ├── accuracy.test.ts
│   │   │   ├── bench/
│   │   │   │   └── README.md
│   │   │   ├── capture.test.ts
│   │   │   ├── chunker.test.ts
│   │   │   ├── integration.test.ts
│   │   │   ├── multi-agent.test.ts
│   │   │   ├── plugin-impl-access.test.ts
│   │   │   ├── policy.test.ts
│   │   │   ├── recall.test.ts
│   │   │   ├── shutdown-lifecycle.test.ts
│   │   │   ├── storage.test.ts
│   │   │   ├── task-processor.test.ts
│   │   │   └── worker-lifecycle.test.ts
│   │   ├── tsconfig.json
│   │   ├── vitest.config.ts
│   │   └── www/
│   │       ├── demo/
│   │       │   └── index.html
│   │       ├── docs/
│   │       │   ├── index.html
│   │       │   └── troubleshooting.html
│   │       └── index.html
│   └── openwork-memos-integration/
│       ├── .gitignore
│       ├── CLAUDE.md
│       ├── CONTRIBUTING.md
│       ├── LICENSE
│       ├── README.md
│       ├── SECURITY.md
│       ├── apps/
│       │   └── desktop/
│       │       ├── .eslintrc.json
│       │       ├── __tests__/
│       │       │   ├── integration/
│       │       │   │   ├── main/
│       │       │   │   │   ├── appSettings.integration.test.ts
│       │       │   │   │   ├── opencode/
│       │       │   │   │   │   ├── cli-path.integration.test.ts
│       │       │   │   │   │   └── config-generator.integration.test.ts
│       │       │   │   │   ├── permission-api.integration.test.ts
│       │       │   │   │   ├── secureStorage.integration.test.ts
│       │       │   │   │   ├── store/
│       │       │   │   │   │   └── freshInstallCleanup.integration.test.ts
│       │       │   │   │   ├── taskHistory.integration.test.ts
│       │       │   │   │   └── utils/
│       │       │   │   │       ├── bundled-node.integration.test.ts
│       │       │   │   │       └── system-path.integration.test.ts
│       │       │   │   ├── preload/
│       │       │   │   │   └── preload.integration.test.ts
│       │       │   │   └── renderer/
│       │       │   │       ├── App.integration.test.tsx
│       │       │   │       ├── components/
│       │       │   │       │   ├── Header.integration.test.tsx
│       │       │   │       │   ├── SettingsDialog.integration.test.tsx
│       │       │   │       │   ├── Sidebar.integration.test.tsx
│       │       │   │       │   ├── StreamingText.integration.test.tsx
│       │       │   │       │   ├── TaskHistory.integration.test.tsx
│       │       │   │       │   ├── TaskInputBar.integration.test.tsx
│       │       │   │       │   └── TaskLauncher.integration.test.tsx
│       │       │   │       ├── pages/
│       │       │   │       │   ├── Execution.integration.test.tsx
│       │       │   │       │   └── Home.integration.test.tsx
│       │       │   │       └── taskStore.integration.test.ts
│       │       │   ├── main/
│       │       │   │   ├── config.unit.test.ts
│       │       │   │   ├── ipc/
│       │       │   │   │   ├── handlers-utils.unit.test.ts
│       │       │   │   │   └── validation.unit.test.ts
│       │       │   │   └── opencode/
│       │       │   │       └── stream-parser.unit.test.ts
│       │       │   ├── setup.ts
│       │       │   └── unit/
│       │       │       └── main/
│       │       │           ├── ipc/
│       │       │           │   └── handlers.unit.test.ts
│       │       │           └── opencode/
│       │       │               ├── adapter.unit.test.ts
│       │       │               └── task-manager.unit.test.ts
│       │       ├── clean_dmg_install.sh
│       │       ├── e2e/
│       │       │   ├── README.md
│       │       │   ├── config/
│       │       │   │   ├── index.ts
│       │       │   │   └── timeouts.ts
│       │       │   ├── docker/
│       │       │   │   ├── Dockerfile
│       │       │   │   └── docker-compose.yml
│       │       │   ├── fixtures/
│       │       │   │   ├── electron-app.ts
│       │       │   │   └── index.ts
│       │       │   ├── pages/
│       │       │   │   ├── execution.page.ts
│       │       │   │   ├── home.page.ts
│       │       │   │   ├── index.ts
│       │       │   │   └── settings.page.ts
│       │       │   ├── playwright.config.ts
│       │       │   ├── specs/
│       │       │   │   ├── execution.spec.ts
│       │       │   │   ├── home.spec.ts
│       │       │   │   ├── settings-bedrock.spec.ts
│       │       │   │   ├── settings-providers.spec.ts
│       │       │   │   ├── settings.spec.ts
│       │       │   │   └── task-launch-guard.spec.ts
│       │       │   └── utils/
│       │       │       ├── index.ts
│       │       │       └── screenshots.ts
│       │       ├── index.html
│       │       ├── package.json
│       │       ├── postcss.config.js
│       │       ├── resources/
│       │       │   └── entitlements.mac.plist
│       │       ├── run_local_ui_prod_api.sh
│       │       ├── run_local_ui_staging_api.sh
│       │       ├── run_prod.sh
│       │       ├── run_staging.sh
│       │       ├── scripts/
│       │       │   ├── after-pack.cjs
│       │       │   ├── download-nodejs.cjs
│       │       │   ├── package.cjs
│       │       │   └── patch-electron-name.cjs
│       │       ├── skills/
│       │       │   ├── ask-user-question/
│       │       │   │   ├── SKILL.md
│       │       │   │   ├── package.json
│       │       │   │   ├── src/
│       │       │   │   │   └── index.ts
│       │       │   │   └── tsconfig.json
│       │       │   ├── dev-browser/
│       │       │   │   ├── .gitignore
│       │       │   │   ├── SKILL.md
│       │       │   │   ├── package.json
│       │       │   │   ├── references/
│       │       │   │   │   └── scraping.md
│       │       │   │   ├── scripts/
│       │       │   │   │   ├── start-relay.ts
│       │       │   │   │   └── start-server.ts
│       │       │   │   ├── server.sh
│       │       │   │   ├── src/
│       │       │   │   │   ├── client.ts
│       │       │   │   │   ├── index.ts
│       │       │   │   │   ├── relay.ts
│       │       │   │   │   ├── snapshot/
│       │       │   │   │   │   ├── __tests__/
│       │       │   │   │   │   │   └── snapshot.test.ts
│       │       │   │   │   │   ├── browser-script.ts
│       │       │   │   │   │   ├── index.ts
│       │       │   │   │   │   └── inject.ts
│       │       │   │   │   └── types.ts
│       │       │   │   ├── tsconfig.json
│       │       │   │   └── vitest.config.ts
│       │       │   ├── file-permission/
│       │       │   │   ├── package.json
│       │       │   │   ├── src/
│       │       │   │   │   └── index.ts
│       │       │   │   └── tsconfig.json
│       │       │   └── safe-file-deletion/
│       │       │       └── SKILL.md
│       │       ├── src/
│       │       │   ├── main/
│       │       │   │   ├── config.ts
│       │       │   │   ├── index.ts
│       │       │   │   ├── ipc/
│       │       │   │   │   ├── handlers.ts
│       │       │   │   │   └── validation.ts
│       │       │   │   ├── opencode/
│       │       │   │   │   ├── adapter.ts
│       │       │   │   │   ├── cli-path.ts
│       │       │   │   │   ├── config-generator.ts
│       │       │   │   │   ├── stream-parser.ts
│       │       │   │   │   └── task-manager.ts
│       │       │   │   ├── permission-api.ts
│       │       │   │   ├── services/
│       │       │   │   │   ├── memory.ts
│       │       │   │   │   └── summarizer.ts
│       │       │   │   ├── store/
│       │       │   │   │   ├── appSettings.ts
│       │       │   │   │   ├── freshInstallCleanup.ts
│       │       │   │   │   ├── providerSettings.ts
│       │       │   │   │   ├── secureStorage.ts
│       │       │   │   │   └── taskHistory.ts
│       │       │   │   ├── test-utils/
│       │       │   │   │   └── mock-task-flow.ts
│       │       │   │   └── utils/
│       │       │   │       ├── bundled-node.ts
│       │       │   │       └── system-path.ts
│       │       │   ├── preload/
│       │       │   │   └── index.ts
│       │       │   ├── renderer/
│       │       │   │   ├── App.tsx
│       │       │   │   ├── components/
│       │       │   │   │   ├── TaskLauncher/
│       │       │   │   │   │   ├── TaskLauncher.tsx
│       │       │   │   │   │   ├── TaskLauncherItem.tsx
│       │       │   │   │   │   └── index.ts
│       │       │   │   │   ├── history/
│       │       │   │   │   │   └── TaskHistory.tsx
│       │       │   │   │   ├── landing/
│       │       │   │   │   │   └── TaskInputBar.tsx
│       │       │   │   │   ├── layout/
│       │       │   │   │   │   ├── ConversationListItem.tsx
│       │       │   │   │   │   ├── Header.tsx
│       │       │   │   │   │   ├── SettingsDialog.tsx
│       │       │   │   │   │   └── Sidebar.tsx
│       │       │   │   │   ├── settings/
│       │       │   │   │   │   ├── ProviderCard.tsx
│       │       │   │   │   │   ├── ProviderGrid.tsx
│       │       │   │   │   │   ├── ProviderSettingsPanel.tsx
│       │       │   │   │   │   ├── hooks/
│       │       │   │   │   │   │   └── useProviderSettings.ts
│       │       │   │   │   │   ├── providers/
│       │       │   │   │   │   │   ├── BedrockProviderForm.tsx
│       │       │   │   │   │   │   ├── ClassicProviderForm.tsx
│       │       │   │   │   │   │   ├── LiteLLMProviderForm.tsx
│       │       │   │   │   │   │   ├── OllamaProviderForm.tsx
│       │       │   │   │   │   │   ├── OpenRouterProviderForm.tsx
│       │       │   │   │   │   │   └── index.ts
│       │       │   │   │   │   └── shared/
│       │       │   │   │   │       ├── ApiKeyInput.tsx
│       │       │   │   │   │       ├── ConnectButton.tsx
│       │       │   │   │   │       ├── ConnectedControls.tsx
│       │       │   │   │   │       ├── ConnectionStatus.tsx
│       │       │   │   │   │       ├── FormError.tsx
│       │       │   │   │   │       ├── ModelSelector.tsx
│       │       │   │   │   │       ├── ProviderFormHeader.tsx
│       │       │   │   │   │       ├── RegionSelector.tsx
│       │       │   │   │   │       └── index.ts
│       │       │   │   │   └── ui/
│       │       │   │   │       ├── avatar.tsx
│       │       │   │   │       ├── badge.tsx
│       │       │   │   │       ├── button.tsx
│       │       │   │   │       ├── card.tsx
│       │       │   │   │       ├── dialog.tsx
│       │       │   │   │       ├── dropdown-menu.tsx
│       │       │   │   │       ├── input.tsx
│       │       │   │   │       ├── label.tsx
│       │       │   │   │       ├── scroll-area.tsx
│       │       │   │   │       ├── separator.tsx
│       │       │   │   │       ├── skeleton.tsx
│       │       │   │   │       ├── streaming-text.tsx
│       │       │   │   │       └── textarea.tsx
│       │       │   │   ├── main.tsx
│       │       │   │   ├── pages/
│       │       │   │   │   ├── Execution.tsx
│       │       │   │   │   ├── History.tsx
│       │       │   │   │   └── Home.tsx
│       │       │   │   ├── stores/
│       │       │   │   │   └── taskStore.ts
│       │       │   │   └── styles/
│       │       │   │       └── globals.css
│       │       │   └── vite-env.d.ts
│       │       ├── tailwind.config.ts
│       │       ├── tsconfig.json
│       │       ├── vite.config.ts
│       │       ├── vitest.config.ts
│       │       ├── vitest.integration.config.ts
│       │       └── vitest.unit.config.ts
│       ├── docs/
│       │   └── plans/
│       │       └── 2026-01-17-safe-file-deletion-impl.md
│       ├── package.json
│       ├── packages/
│       │   └── shared/
│       │       ├── package.json
│       │       ├── src/
│       │       │   ├── index.ts
│       │       │   └── types/
│       │       │       ├── auth.ts
│       │       │       ├── index.ts
│       │       │       ├── opencode.ts
│       │       │       ├── permission.ts
│       │       │       ├── provider.ts
│       │       │       ├── providerSettings.ts
│       │       │       └── task.ts
│       │       └── tsconfig.json
│       └── pnpm-workspace.yaml
├── docker/
│   ├── Dockerfile
│   ├── Dockerfile.krolik
│   └── docker-compose.yml
├── docs/
│   ├── README.md
│   ├── openapi.json
│   └── product-api-tests.md
├── evaluation/
│   ├── .env-example
│   ├── README.md
│   ├── __init__.py
│   ├── data/
│   │   └── longmemeval/
│   │       └── .gitkeep
│   └── scripts/
│       ├── PrefEval/
│       │   ├── irrelevant_conv.py
│       │   ├── pref_eval.py
│       │   ├── pref_mem0.py
│       │   ├── pref_memobase.py
│       │   ├── pref_memos.py
│       │   ├── pref_memu.py
│       │   ├── pref_supermemory.py
│       │   ├── pref_zep.py
│       │   └── prefeval_preprocess.py
│       ├── __init__.py
│       ├── locomo/
│       │   ├── locomo_eval.py
│       │   ├── locomo_ingestion.py
│       │   ├── locomo_metric.py
│       │   ├── locomo_openai.py
│       │   ├── locomo_rag.py
│       │   ├── locomo_responses.py
│       │   ├── locomo_search.py
│       │   ├── openai_memory_locomo_eval_guide.md
│       │   ├── prompts.py
│       │   └── utils.py
│       ├── long_bench-v2/
│       │   ├── __init__.py
│       │   ├── longbench_v2_ingestion.py
│       │   ├── longbench_v2_metric.py
│       │   ├── longbench_v2_responses.py
│       │   ├── longbench_v2_search.py
│       │   └── wait_scheduler.py
│       ├── longmemeval/
│       │   ├── lme_eval.py
│       │   ├── lme_ingestion.py
│       │   ├── lme_metric.py
│       │   ├── lme_rag.py
│       │   ├── lme_responses.py
│       │   └── lme_search.py
│       ├── run_lme_eval.sh
│       ├── run_locomo_eval.sh
│       ├── run_longbench_v2_eval.sh
│       ├── run_openai_eval.sh
│       ├── run_pm_eval.sh
│       ├── run_prefeval_eval.sh
│       ├── run_rag_eval.sh
│       └── utils/
│           ├── __init__.py
│           ├── client.py
│           ├── mirix_utils.py
│           └── prompts.py
├── examples/
│   ├── api/
│   │   ├── __init__.py
│   │   └── server_router_api.py
│   ├── basic_modules/
│   │   ├── chunker.py
│   │   ├── embedder.py
│   │   ├── llm.py
│   │   ├── neo4j_example.py
│   │   ├── reranker.py
│   │   ├── textual_memory_internet_search_example.py
│   │   ├── tree_textual_memory_recall.py
│   │   ├── tree_textual_memory_relation_reason_detector.py
│   │   └── tree_textual_memory_task_goal_parser.py
│   ├── core_memories/
│   │   ├── general_textual_memory.py
│   │   ├── kv_cache_memory.py
│   │   ├── naive_textual_memory.py
│   │   ├── pref_textual_memory.py
│   │   ├── tree_textual_memory.py
│   │   └── vllm_kv_cache_memory.py
│   ├── data/
│   │   ├── config/
│   │   │   └── mem_scheduler/
│   │   │       ├── general_scheduler_config.yaml
│   │   │       ├── mem_cube_config.yaml
│   │   │       ├── mem_cube_config_neo4j.yaml
│   │   │       ├── memos_config_w_optimized_scheduler.yaml
│   │   │       └── memos_config_w_scheduler.yaml
│   │   └── mem_cube_2/
│   │       ├── README.md
│   │       ├── activation_memory.pickle
│   │       └── parametric_memory.adapter
│   ├── extras/
│   │   └── nli_e2e_example.py
│   ├── mem_agent/
│   │   └── deepsearch_example.py
│   ├── mem_chat/
│   │   └── chat_w_generated_cube_explicit_memory_only.py
│   ├── mem_cube/
│   │   ├── _deprecated/
│   │   │   ├── README.md
│   │   │   ├── load_from_folder.py
│   │   │   ├── load_from_remote.py
│   │   │   └── load_lazily.py
│   │   ├── dump_cube.py
│   │   └── load_cube.py
│   ├── mem_feedback/
│   │   └── example_feedback.py
│   ├── mem_mcp/
│   │   ├── simple_fastmcp_client.py
│   │   └── simple_fastmcp_serve.py
│   ├── mem_reader/
│   │   ├── README.md
│   │   ├── builders.py
│   │   ├── parser_demos/
│   │   │   ├── __init__.py
│   │   │   ├── _base.py
│   │   │   ├── demo_assistant.py
│   │   │   ├── demo_file_content.py
│   │   │   ├── demo_image.py
│   │   │   ├── demo_multi_modal.py
│   │   │   ├── demo_string.py
│   │   │   ├── demo_system.py
│   │   │   ├── demo_text_content.py
│   │   │   ├── demo_tool.py
│   │   │   └── demo_user.py
│   │   ├── runners/
│   │   │   ├── __init__.py
│   │   │   ├── run_multimodal.py
│   │   │   └── run_simple.py
│   │   ├── samples.py
│   │   ├── settings.py
│   │   └── utils.py
│   └── mem_scheduler/
│       ├── api_w_scheduler.py
│       ├── memos_w_scheduler.py
│       ├── redis_example.py
│       ├── run_async_tasks.py
│       ├── show_redis_status.py
│       └── try_schedule_modules.py
├── pyproject.toml
├── scripts/
│   └── check_dependencies.py
├── src/
│   ├── __init__.py
│   └── memos/
│       ├── __init__.py
│       ├── api/
│       │   ├── README_api.md
│       │   ├── __init__.py
│       │   ├── client.py
│       │   ├── config.py
│       │   ├── context/
│       │   │   └── dependencies.py
│       │   ├── exceptions.py
│       │   ├── handlers/
│       │   │   ├── __init__.py
│       │   │   ├── add_handler.py
│       │   │   ├── base_handler.py
│       │   │   ├── chat_handler.py
│       │   │   ├── component_init.py
│       │   │   ├── config_builders.py
│       │   │   ├── feedback_handler.py
│       │   │   ├── formatters_handler.py
│       │   │   ├── memory_handler.py
│       │   │   ├── scheduler_handler.py
│       │   │   ├── search_handler.py
│       │   │   └── suggestion_handler.py
│       │   ├── mcp_serve.py
│       │   ├── middleware/
│       │   │   ├── __init__.py
│       │   │   ├── auth.py
│       │   │   ├── rate_limit.py
│       │   │   └── request_context.py
│       │   ├── product_api.py
│       │   ├── product_models.py
│       │   ├── routers/
│       │   │   ├── __init__.py
│       │   │   ├── admin_router.py
│       │   │   ├── product_router.py
│       │   │   └── server_router.py
│       │   ├── server_api.py
│       │   ├── server_api_ext.py
│       │   ├── start_api.py
│       │   └── utils/
│       │       ├── __init__.py
│       │       └── api_keys.py
│       ├── chunkers/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── charactertext_chunker.py
│       │   ├── factory.py
│       │   ├── markdown_chunker.py
│       │   ├── sentence_chunker.py
│       │   └── simple_chunker.py
│       ├── cli.py
│       ├── configs/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── chunker.py
│       │   ├── embedder.py
│       │   ├── graph_db.py
│       │   ├── internet_retriever.py
│       │   ├── llm.py
│       │   ├── mem_agent.py
│       │   ├── mem_chat.py
│       │   ├── mem_cube.py
│       │   ├── mem_os.py
│       │   ├── mem_reader.py
│       │   ├── mem_scheduler.py
│       │   ├── mem_user.py
│       │   ├── memory.py
│       │   ├── parser.py
│       │   ├── reranker.py
│       │   ├── utils.py
│       │   └── vec_db.py
│       ├── context/
│       │   └── context.py
│       ├── dependency.py
│       ├── deprecation.py
│       ├── embedders/
│       │   ├── __init__.py
│       │   ├── ark.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   ├── ollama.py
│       │   ├── sentence_transformer.py
│       │   └── universal_api.py
│       ├── exceptions.py
│       ├── extras/
│       │   ├── __init__.py
│       │   └── nli_model/
│       │       ├── __init__.py
│       │       ├── client.py
│       │       ├── server/
│       │       │   ├── README.md
│       │       │   ├── __init__.py
│       │       │   ├── config.py
│       │       │   ├── handler.py
│       │       │   └── serve.py
│       │       └── types.py
│       ├── graph_dbs/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   ├── item.py
│       │   ├── nebular.py
│       │   ├── neo4j.py
│       │   ├── neo4j_community.py
│       │   ├── polardb.py
│       │   └── postgres.py
│       ├── hello_world.py
│       ├── llms/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── deepseek.py
│       │   ├── factory.py
│       │   ├── hf.py
│       │   ├── hf_singleton.py
│       │   ├── ollama.py
│       │   ├── openai.py
│       │   ├── openai_new.py
│       │   ├── qwen.py
│       │   ├── utils.py
│       │   └── vllm.py
│       ├── log.py
│       ├── mem_agent/
│       │   ├── base.py
│       │   ├── deepsearch_agent.py
│       │   └── factory.py
│       ├── mem_chat/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   └── simple.py
│       ├── mem_cube/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── general.py
│       │   ├── navie.py
│       │   └── utils.py
│       ├── mem_feedback/
│       │   ├── base.py
│       │   ├── feedback.py
│       │   ├── simple_feedback.py
│       │   └── utils.py
│       ├── mem_os/
│       │   ├── client.py
│       │   ├── core.py
│       │   ├── main.py
│       │   ├── product.py
│       │   ├── product_server.py
│       │   └── utils/
│       │       ├── default_config.py
│       │       ├── format_utils.py
│       │       └── reference_utils.py
│       ├── mem_reader/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   ├── memory.py
│       │   ├── multi_modal_struct.py
│       │   ├── read_multi_modal/
│       │   │   ├── __init__.py
│       │   │   ├── assistant_parser.py
│       │   │   ├── base.py
│       │   │   ├── file_content_parser.py
│       │   │   ├── image_parser.py
│       │   │   ├── multi_modal_parser.py
│       │   │   ├── string_parser.py
│       │   │   ├── system_parser.py
│       │   │   ├── text_content_parser.py
│       │   │   ├── tool_parser.py
│       │   │   ├── user_parser.py
│       │   │   └── utils.py
│       │   ├── read_pref_memory/
│       │   │   └── process_preference_memory.py
│       │   ├── read_skill_memory/
│       │   │   └── process_skill_memory.py
│       │   ├── simple_struct.py
│       │   ├── strategy_struct.py
│       │   └── utils.py
│       ├── mem_scheduler/
│       │   ├── __init__.py
│       │   ├── analyzer/
│       │   │   ├── __init__.py
│       │   │   ├── api_analyzer.py
│       │   │   ├── eval_analyzer.py
│       │   │   ├── mos_for_test_scheduler.py
│       │   │   └── scheduler_for_eval.py
│       │   ├── base_mixins/
│       │   │   ├── __init__.py
│       │   │   ├── memory_ops.py
│       │   │   ├── queue_ops.py
│       │   │   └── web_log_ops.py
│       │   ├── base_scheduler.py
│       │   ├── general_modules/
│       │   │   ├── __init__.py
│       │   │   ├── api_misc.py
│       │   │   ├── base.py
│       │   │   ├── init_components_for_scheduler.py
│       │   │   ├── misc.py
│       │   │   ├── scheduler_logger.py
│       │   │   └── task_threads.py
│       │   ├── general_scheduler.py
│       │   ├── memory_manage_modules/
│       │   │   ├── __init__.py
│       │   │   ├── activation_memory_manager.py
│       │   │   ├── enhancement_pipeline.py
│       │   │   ├── filter_pipeline.py
│       │   │   ├── memory_filter.py
│       │   │   ├── post_processor.py
│       │   │   ├── rerank_pipeline.py
│       │   │   ├── retriever.py
│       │   │   ├── search_pipeline.py
│       │   │   └── search_service.py
│       │   ├── monitors/
│       │   │   ├── __init__.py
│       │   │   ├── dispatcher_monitor.py
│       │   │   ├── general_monitor.py
│       │   │   └── task_schedule_monitor.py
│       │   ├── optimized_scheduler.py
│       │   ├── orm_modules/
│       │   │   ├── __init__.py
│       │   │   ├── api_redis_model.py
│       │   │   ├── base_model.py
│       │   │   ├── monitor_models.py
│       │   │   └── redis_model.py
│       │   ├── scheduler_factory.py
│       │   ├── schemas/
│       │   │   ├── __init__.py
│       │   │   ├── analyzer_schemas.py
│       │   │   ├── api_schemas.py
│       │   │   ├── general_schemas.py
│       │   │   ├── message_schemas.py
│       │   │   ├── monitor_schemas.py
│       │   │   └── task_schemas.py
│       │   ├── task_schedule_modules/
│       │   │   ├── __init__.py
│       │   │   ├── base_handler.py
│       │   │   ├── context.py
│       │   │   ├── dispatcher.py
│       │   │   ├── handlers/
│       │   │   │   ├── __init__.py
│       │   │   │   ├── add_handler.py
│       │   │   │   ├── answer_handler.py
│       │   │   │   ├── feedback_handler.py
│       │   │   │   ├── mem_read_handler.py
│       │   │   │   ├── mem_reorganize_handler.py
│       │   │   │   ├── memory_update_handler.py
│       │   │   │   ├── pref_add_handler.py
│       │   │   │   └── query_handler.py
│       │   │   ├── local_queue.py
│       │   │   ├── orchestrator.py
│       │   │   ├── redis_queue.py
│       │   │   ├── registry.py
│       │   │   └── task_queue.py
│       │   ├── utils/
│       │   │   ├── __init__.py
│       │   │   ├── api_utils.py
│       │   │   ├── config_utils.py
│       │   │   ├── db_utils.py
│       │   │   ├── filter_utils.py
│       │   │   ├── metrics.py
│       │   │   ├── misc_utils.py
│       │   │   ├── monitor_event_utils.py
│       │   │   └── status_tracker.py
│       │   └── webservice_modules/
│       │       ├── __init__.py
│       │       ├── rabbitmq_service.py
│       │       └── redis_service.py
│       ├── mem_user/
│       │   ├── factory.py
│       │   ├── mysql_persistent_user_manager.py
│       │   ├── mysql_user_manager.py
│       │   ├── persistent_factory.py
│       │   ├── persistent_user_manager.py
│       │   ├── redis_persistent_user_manager.py
│       │   └── user_manager.py
│       ├── memories/
│       │   ├── __init__.py
│       │   ├── activation/
│       │   │   ├── __init__.py
│       │   │   ├── base.py
│       │   │   ├── item.py
│       │   │   ├── kv.py
│       │   │   └── vllmkv.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   ├── parametric/
│       │   │   ├── __init__.py
│       │   │   ├── base.py
│       │   │   ├── item.py
│       │   │   └── lora.py
│       │   └── textual/
│       │       ├── __init__.py
│       │       ├── base.py
│       │       ├── general.py
│       │       ├── item.py
│       │       ├── naive.py
│       │       ├── prefer_text_memory/
│       │       │   ├── __init__.py
│       │       │   ├── adder.py
│       │       │   ├── config.py
│       │       │   ├── extractor.py
│       │       │   ├── factory.py
│       │       │   ├── retrievers.py
│       │       │   ├── spliter.py
│       │       │   └── utils.py
│       │       ├── preference.py
│       │       ├── simple_preference.py
│       │       ├── simple_tree.py
│       │       ├── tree.py
│       │       └── tree_text_memory/
│       │           ├── __init__.py
│       │           ├── organize/
│       │           │   ├── __init__.py
│       │           │   ├── handler.py
│       │           │   ├── history_manager.py
│       │           │   ├── manager.py
│       │           │   ├── relation_reason_detector.py
│       │           │   └── reorganizer.py
│       │           └── retrieve/
│       │               ├── __init__.py
│       │               ├── advanced_searcher.py
│       │               ├── bm25_util.py
│       │               ├── bochasearch.py
│       │               ├── internet_retriever.py
│       │               ├── internet_retriever_factory.py
│       │               ├── pre_update.py
│       │               ├── reasoner.py
│       │               ├── recall.py
│       │               ├── reranker.py
│       │               ├── retrieval_mid_structs.py
│       │               ├── retrieve_utils.py
│       │               ├── searcher.py
│       │               ├── task_goal_parser.py
│       │               ├── utils.py
│       │               └── xinyusearch.py
│       ├── memos_tools/
│       │   ├── dinding_report_bot.py
│       │   ├── lockfree_dict.py
│       │   ├── notification_service.py
│       │   ├── notification_utils.py
│       │   ├── singleton.py
│       │   ├── thread_safe_dict.py
│       │   └── thread_safe_dict_segment.py
│       ├── multi_mem_cube/
│       │   ├── __init__.py
│       │   ├── composite_cube.py
│       │   ├── single_cube.py
│       │   └── views.py
│       ├── parsers/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── factory.py
│       │   └── markitdown.py
│       ├── reranker/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── concat.py
│       │   ├── cosine_local.py
│       │   ├── factory.py
│       │   ├── http_bge.py
│       │   ├── http_bge_strategy.py
│       │   ├── noop.py
│       │   └── strategies/
│       │       ├── __init__.py
│       │       ├── base.py
│       │       ├── concat_background.py
│       │       ├── concat_docsource.py
│       │       ├── dialogue_common.py
│       │       ├── factory.py
│       │       ├── single_turn.py
│       │       └── singleturn_outmem.py
│       ├── search/
│       │   ├── __init__.py
│       │   └── search_service.py
│       ├── settings.py
│       ├── templates/
│       │   ├── __init__.py
│       │   ├── advanced_search_prompts.py
│       │   ├── cloud_service_prompt.py
│       │   ├── instruction_completion.py
│       │   ├── mem_agent_prompts.py
│       │   ├── mem_feedback_prompts.py
│       │   ├── mem_reader_prompts.py
│       │   ├── mem_reader_strategy_prompts.py
│       │   ├── mem_scheduler_prompts.py
│       │   ├── mem_search_prompts.py
│       │   ├── mos_prompts.py
│       │   ├── prefer_complete_prompt.py
│       │   ├── skill_mem_prompt.py
│       │   ├── tool_mem_prompts.py
│       │   └── tree_reorganize_prompts.py
│       ├── types/
│       │   ├── __init__.py
│       │   ├── general_types.py
│       │   └── openai_chat_completion_types/
│       │       ├── __init__.py
│       │       ├── chat_completion_assistant_message_param.py
│       │       ├── chat_completion_content_part_image_param.py
│       │       ├── chat_completion_content_part_input_audio_param.py
│       │       ├── chat_completion_content_part_param.py
│       │       ├── chat_completion_content_part_refusal_param.py
│       │       ├── chat_completion_content_part_text_param.py
│       │       ├── chat_completion_message_custom_tool_call_param.py
│       │       ├── chat_completion_message_function_tool_call_param.py
│       │       ├── chat_completion_message_param.py
│       │       ├── chat_completion_message_tool_call_union_param.py
│       │       ├── chat_completion_system_message_param.py
│       │       ├── chat_completion_tool_message_param.py
│       │       └── chat_completion_user_message_param.py
│       ├── utils.py
│       └── vec_dbs/
│           ├── __init__.py
│           ├── base.py
│           ├── factory.py
│           ├── item.py
│           ├── milvus.py
│           └── qdrant.py
└── tests/
    ├── __init__.py
    ├── api/
    │   ├── test_product_router.py
    │   ├── test_server_router.py
    │   ├── test_start_api.py
    │   └── test_thread_context.py
    ├── chunkers/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_factory.py
    │   └── test_sentence_chunker.py
    ├── configs/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_embedder.py
    │   ├── test_llm.py
    │   ├── test_mem_chat.py
    │   ├── test_mem_cube.py
    │   ├── test_memory.py
    │   ├── test_parser.py
    │   └── test_vec_db.py
    ├── embedders/
    │   ├── __init__.py
    │   ├── test_ark.py
    │   ├── test_base.py
    │   ├── test_factory.py
    │   ├── test_ollama.py
    │   └── test_universal_api.py
    ├── extras/
    │   ├── __init__.py
    │   └── nli_model/
    │       ├── __init__.py
    │       └── test_client_integration.py
    ├── graph_dbs/
    │   ├── __init__.py
    │   ├── graph_dbs.py
    │   └── test_search_return_fields.py
    ├── llms/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_deepseek.py
    │   ├── test_factory.py
    │   ├── test_hf.py
    │   ├── test_ollama.py
    │   ├── test_openai.py
    │   └── test_qwen.py
    ├── mem_agent/
    │   └── test_deepsearch_agent.py
    ├── mem_chat/
    │   ├── __init__.py
    │   ├── test_base.py
    │   └── test_factory.py
    ├── mem_cube/
    │   ├── test_base.py
    │   └── test_general.py
    ├── mem_os/
    │   ├── test_memos.py
    │   └── test_memos_core.py
    ├── mem_reader/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_coarse_memory_type.py
    │   ├── test_factory.py
    │   ├── test_memory.py
    │   ├── test_project_id_propagation.py
    │   └── test_simple_structure.py
    ├── mem_scheduler/
    │   ├── __init__.py
    │   ├── test_config.py
    │   ├── test_dispatcher.py
    │   ├── test_retriever.py
    │   ├── test_scheduler.py
    │   └── test_version_control.py
    ├── mem_tools/
    │   └── test_thread_safe_dict.py
    ├── mem_user/
    │   └── test_mem_user.py
    ├── memories/
    │   ├── __init__.py
    │   ├── activation/
    │   │   ├── __init__.py
    │   │   ├── test_base.py
    │   │   ├── test_item.py
    │   │   └── test_kv.py
    │   ├── test_base.py
    │   ├── test_factory.py
    │   └── textual/
    │       ├── __init__.py
    │       ├── test_base.py
    │       ├── test_general.py
    │       ├── test_history_manager.py
    │       ├── test_naive.py
    │       ├── test_pre_update_retriever.py
    │       ├── test_pre_update_retriever_latency.py
    │       ├── test_tree.py
    │       ├── test_tree_manager.py
    │       ├── test_tree_reranker.py
    │       ├── test_tree_retriever.py
    │       ├── test_tree_searcher.py
    │       └── test_tree_task_goal_parser.py
    ├── parsers/
    │   ├── __init__.py
    │   ├── test_base.py
    │   ├── test_factory.py
    │   └── test_markitdown.py
    ├── test_cli.py
    ├── test_deprecation.py
    ├── test_hello_world.py
    ├── test_log.py
    ├── test_settings.py
    ├── utils.py
    └── vec_dbs/
        ├── __init__.py
        ├── test_base.py
        ├── test_factory.py
        ├── test_item.py
        └── test_qdrant.py
Download .txt
Showing preview only (467K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (5168 symbols across 598 files)

FILE: apps/MemOS-Cloud-OpenClaw-Plugin/index.js
  constant API_KEY_HELP_URL (line 14) | const API_KEY_HELP_URL = "https://memos-dashboard.openmem.net/cn/apikeys/";
  constant ENV_FILE_SEARCH_HINTS (line 15) | const ENV_FILE_SEARCH_HINTS = ["~/.openclaw/.env", "~/.moltbot/.env", "~...
  constant MEMOS_SOURCE (line 16) | const MEMOS_SOURCE = "openclaw";
  function warnMissingApiKey (line 18) | function warnMissingApiKey(log, context) {
  function stripPrependedPrompt (line 36) | function stripPrependedPrompt(content) {
  function getCounterSuffix (line 43) | function getCounterSuffix(sessionKey) {
  function bumpConversationCounter (line 49) | function bumpConversationCounter(sessionKey) {
  function getEffectiveAgentId (line 55) | function getEffectiveAgentId(cfg, ctx) {
  function resolveConversationId (line 63) | function resolveConversationId(cfg, ctx) {
  function buildSearchPayload (line 75) | function buildSearchPayload(cfg, prompt, ctx) {
  function buildAddMessagePayload (line 122) | function buildAddMessagePayload(cfg, messages, ctx) {
  function pickLastTurnMessages (line 150) | function pickLastTurnMessages(messages, cfg) {
  function pickFullSessionMessages (line 178) | function pickFullSessionMessages(messages, cfg) {
  function truncate (line 194) | function truncate(text, maxLen) {
  function sleep (line 200) | function sleep(ms) {
  function parseModelJson (line 204) | function parseModelJson(text) {
  function normalizeIndexList (line 233) | function normalizeIndexList(value, maxLen) {
  function buildRecallCandidates (line 247) | function buildRecallCandidates(data, cfg) {
  function applyRecallDecision (line 283) | function applyRecallDecision(data, decision, lists) {
  function callRecallFilterModel (line 297) | async function callRecallFilterModel(cfg, userPrompt, candidatePayload) {
  function maybeFilterRecallData (line 369) | async function maybeFilterRecallData(cfg, data, userPrompt, log) {
  method register (line 397) | register(api) {

FILE: apps/MemOS-Cloud-OpenClaw-Plugin/lib/check-update.js
  function killProcessTree (line 11) | function killProcessTree(child) {
  constant CHECK_INTERVAL (line 29) | const CHECK_INTERVAL = 12 * 60 * 60 * 1000;
  constant UPDATE_TIMEOUT (line 30) | const UPDATE_TIMEOUT = 3 * 60 * 1000;
  constant PLUGIN_NAME (line 31) | const PLUGIN_NAME = "@memtensor/memos-cloud-openclaw-plugin";
  constant CHECK_FILE (line 32) | const CHECK_FILE = path.join(os.tmpdir(), "memos_openclaw_update_check.j...
  constant ANSI (line 34) | const ANSI = {
  function getPackageVersion (line 43) | function getPackageVersion() {
  function getLatestVersion (line 54) | function getLatestVersion(log) {
  function compareVersions (line 92) | function compareVersions(v1, v2) {
  function startUpdateChecker (line 125) | function startUpdateChecker(log) {

FILE: apps/MemOS-Cloud-OpenClaw-Plugin/lib/memos-cloud-api.js
  constant DEFAULT_BASE_URL (line 6) | const DEFAULT_BASE_URL = "https://memos.memtensor.cn/api/openmem/v1";
  constant USER_QUERY_MARKER (line 7) | const USER_QUERY_MARKER = "user\u200b原\u200b始\u200bquery\u200b:\u200b\u2...
  constant ENV_SOURCES (line 8) | const ENV_SOURCES = [
  function stripQuotes (line 18) | function stripQuotes(value) {
  function extractResultData (line 30) | function extractResultData(result) {
  function pad2 (line 35) | function pad2(value) {
  function formatTime (line 39) | function formatTime(value) {
  function parseEnvFile (line 57) | function parseEnvFile(content) {
  function loadEnvFiles (line 72) | function loadEnvFiles() {
  function loadEnvFromFiles (line 86) | function loadEnvFromFiles(name) {
  function loadEnvVar (line 95) | function loadEnvVar(name) {
  function getEnvFileStatus (line 103) | function getEnvFileStatus() {
  function parseBool (line 114) | function parseBool(value, fallback) {
  function parseNumber (line 123) | function parseNumber(value, fallback) {
  function buildConfig (line 129) | function buildConfig(pluginConfig = {}) {
  function callApi (line 224) | async function callApi({ baseUrl, apiKey, timeoutMs = 5000, retries = 1 ...
  function searchMemory (line 265) | async function searchMemory(cfg, payload) {
  function addMessage (line 269) | async function addMessage(cfg, payload) {
  function extractText (line 273) | function extractText(content) {
  function normalizePreferenceType (line 285) | function normalizePreferenceType(value) {
  function sanitizeInlineText (line 296) | function sanitizeInlineText(text) {
  function formatMemoryLine (line 301) | function formatMemoryLine(item, text, options = {}) {
  function formatPreferenceLine (line 311) | function formatPreferenceLine(item, text, options = {}) {
  function wrapCodeBlock (line 323) | function wrapCodeBlock(lines, options = {}) {
  function buildMemorySections (line 328) | function buildMemorySections(data, options = {}) {
  constant STATIC_RECALL_SYSTEM_PROMPT (line 359) | const STATIC_RECALL_SYSTEM_PROMPT = [
  function buildMemoryPrependBlock (line 407) | function buildMemoryPrependBlock(data, options = {}) {
  function formatPromptBlockFromData (line 426) | function formatPromptBlockFromData(data, options = {}) {
  function formatPromptBlock (line 431) | function formatPromptBlock(result, options = {}) {
  function formatContextBlock (line 436) | function formatContextBlock(result, options = {}) {
  function formatRecallHookResult (line 481) | function formatRecallHookResult(result, options = {}) {
  function truncate (line 497) | function truncate(text, maxLen) {

FILE: apps/memos-local-openclaw/index.ts
  function deduplicateHits (line 30) | function deduplicateHits<T extends { summary: string }>(hits: T[]): T[] {
  method register (line 79) | register(api: OpenClawPluginApi) {

FILE: apps/memos-local-openclaw/plugin-impl.ts
  function ownerFilterFor (line 17) | function ownerFilterFor(agentId: string | undefined): string[] {
  method register (line 68) | register(api: OpenClawPluginApi) {

FILE: apps/memos-local-openclaw/scripts/postinstall.cjs
  constant RESET (line 8) | const RESET = "\x1b[0m";
  constant GREEN (line 9) | const GREEN = "\x1b[32m";
  constant YELLOW (line 10) | const YELLOW = "\x1b[33m";
  constant RED (line 11) | const RED = "\x1b[31m";
  constant CYAN (line 12) | const CYAN = "\x1b[36m";
  constant BOLD (line 13) | const BOLD = "\x1b[1m";
  constant DIM (line 14) | const DIM = "\x1b[2m";
  function log (line 16) | function log(msg) { console.log(`  ${CYAN}[memos-local]${RESET} ${msg}`); }
  function warn (line 17) | function warn(msg) { console.log(`  ${YELLOW}⚠ [memos-local]${RESET} ${m...
  function ok (line 18) | function ok(msg) { console.log(`  ${GREEN}✔ [memos-local]${RESET} ${msg}...
  function fail (line 19) | function fail(msg) { console.log(`  ${RED}✖ [memos-local]${RESET} ${msg}...
  function phase (line 21) | function phase(n, title) {
  function cleanStaleArtifacts (line 44) | function cleanStaleArtifacts() {
  function ensureDependencies (line 112) | function ensureDependencies() {
  function cleanupLegacy (line 165) | function cleanupLegacy() {
  function installBundledSkill (line 276) | function installBundledSkill() {
  function findSqliteBinding (line 347) | function findSqliteBinding() {
  function sqliteBindingsExist (line 372) | function sqliteBindingsExist() {

FILE: apps/memos-local-openclaw/scripts/refresh-summaries.ts
  constant TASK_SUMMARY_PROMPT (line 6) | const TASK_SUMMARY_PROMPT = `You create a DETAILED task summary from a m...
  function parseTitleFromSummary (line 43) | function parseTitleFromSummary(summary: string): { title: string; body: ...
  function main (line 53) | async function main() {

FILE: apps/memos-local-openclaw/scripts/run-accuracy-test.ts
  constant FULL_MODE (line 28) | const FULL_MODE = args.includes("--full");
  constant SKIP_INGEST (line 29) | const SKIP_INGEST = args.includes("--skip-ingest");
  constant WORKERS (line 30) | const WORKERS = Number(args.find((_, i, a) => a[i - 1] === "--workers") ...
  constant INGEST_DELAY_MS (line 31) | const INGEST_DELAY_MS = 3000;
  function loadConfig (line 35) | function loadConfig() {
  type TestResult (line 47) | interface TestResult {
  constant RUN_ID (line 56) | const RUN_ID = Date.now();
  constant SESSION_PREFIX (line 57) | const SESSION_PREFIX = `acc-${RUN_ID}`;
  function mkSession (line 60) | function mkSession(label: string) {
  function log (line 64) | function log(msg: string) {
  class ProgressTracker (line 71) | class ProgressTracker {
    method constructor (line 77) | constructor(phaseName: string, total: number) {
    method tick (line 82) | tick(label: string) {
    method summary (line 101) | summary(): string {
  function fmtDur (line 107) | function fmtDur(ms: number): string {
  function hitContains (line 115) | function hitContains(hits: any[], keyword: string): boolean {
  function sendViaGateway (line 125) | function sendViaGateway(sessionId: string, message: string): boolean {
  type ConversationCase (line 144) | interface ConversationCase {
  function buildTestCases (line 152) | function buildTestCases(): ConversationCase[] {
  type SearchCase (line 473) | interface SearchCase {
  function buildSearchCases (line 482) | function buildSearchCases(): SearchCase[] {
  function registerSessionsInStore (line 517) | function registerSessionsInStore(cases: ConversationCase[]) {
  function ingestPhase (line 570) | async function ingestPhase(cases: ConversationCase[]) {
  function runSearchTests (line 615) | async function runSearchTests(plugin: MemosLocalPlugin, cases: SearchCas...
  function runDedupChecks (line 667) | async function runDedupChecks(plugin: MemosLocalPlugin, tracker: Progres...
  function runSummaryChecks (line 691) | async function runSummaryChecks(plugin: MemosLocalPlugin, tracker: Progr...
  function runTopicChecks (line 716) | async function runTopicChecks(plugin: MemosLocalPlugin, tracker: Progres...
  function printReport (line 749) | function printReport(totalMs: number, ingestStats?: { successCount: numb...
  function main (line 786) | async function main() {

FILE: apps/memos-local-openclaw/scripts/seed-test-data.ts
  constant DB_PATH (line 17) | const DB_PATH = path.join(os.homedir(), ".openclaw", "memos-local", "mem...
  constant HOUR (line 23) | const HOUR = 3600_000;
  constant MIN (line 24) | const MIN = 60_000;
  function seedTask (line 266) | function seedTask(

FILE: apps/memos-local-openclaw/scripts/smoke-test.ts
  constant GREEN (line 29) | const GREEN = "\x1b[32m";
  constant RED (line 30) | const RED = "\x1b[31m";
  constant CYAN (line 31) | const CYAN = "\x1b[36m";
  constant YELLOW (line 32) | const YELLOW = "\x1b[33m";
  constant RESET (line 33) | const RESET = "\x1b[0m";
  constant BOLD (line 34) | const BOLD = "\x1b[1m";
  function ok (line 36) | function ok(msg: string) { console.log(`${GREEN}  ✓ ${msg}${RESET}`); }
  function fail (line 37) | function fail(msg: string) { console.log(`${RED}  ✗ ${msg}${RESET}`); }
  function section (line 38) | function section(msg: string) { console.log(`\n${BOLD}${CYAN}━━━ ${msg} ...
  function info (line 39) | function info(msg: string) { console.log(`${YELLOW}  ℹ ${msg}${RESET}`); }
  function main (line 41) | async function main() {

FILE: apps/memos-local-openclaw/scripts/start-viewer.ts
  function main (line 40) | async function main() {

FILE: apps/memos-local-openclaw/scripts/test-agent-isolation.ts
  constant RUN_ID (line 20) | const RUN_ID = Date.now();
  constant AGENT_A (line 21) | const AGENT_A = "iso-test-alpha";
  constant AGENT_B (line 22) | const AGENT_B = "iso-test-beta";
  constant UNIQUE_A (line 24) | const UNIQUE_A = `AlphaUniqueKey${RUN_ID}`;
  constant UNIQUE_B (line 25) | const UNIQUE_B = `BetaUniqueKey${RUN_ID}`;
  constant MSG_A1 (line 27) | const MSG_A1 = `我正在用 ${UNIQUE_A} 部署一个私有 Redis 缓存集群,配置主从复制和哨兵模式,端口 6379。`;
  constant MSG_A2 (line 28) | const MSG_A2 = `${UNIQUE_A} 的 Redis 集群已经部署完成,延迟从 50ms 降到了 3ms,命中率 95%。`;
  constant MSG_B1 (line 30) | const MSG_B1 = `帮我设置 ${UNIQUE_B} 的 PostgreSQL 数据库迁移方案,从 v14 升级到 v16,数据量约...
  constant MSG_B2 (line 31) | const MSG_B2 = `${UNIQUE_B} 的 PostgreSQL 迁移完成了,用了 pg_upgrade --link 模式,停...
  function log (line 36) | function log(msg: string) {
  function assert (line 41) | function assert(name: string, condition: boolean, detail: string) {
  function main (line 53) | async function main() {

FILE: apps/memos-local-openclaw/src/capture/index.ts
  constant SKIP_ROLES (line 3) | const SKIP_ROLES: Set<Role> = new Set(["system"]);
  constant SYSTEM_BOILERPLATE_RE (line 5) | const SYSTEM_BOILERPLATE_RE = /^A new session was started via \/new or \...
  constant SELF_TOOLS (line 7) | const SELF_TOOLS = new Set([
  constant INBOUND_META_SENTINELS (line 20) | const INBOUND_META_SENTINELS = [
  constant SENTINEL_FAST_RE (line 29) | const SENTINEL_FAST_RE = new RegExp(
  constant ENVELOPE_PREFIX_RE (line 33) | const ENVELOPE_PREFIX_RE =
  function captureMessages (line 43) | function captureMessages(
  function stripInboundMetadata (line 104) | function stripInboundMetadata(text: string): string {
  constant THINKING_TAG_RE (line 154) | const THINKING_TAG_RE = /<think[\s>][\s\S]*?<\/think>\s*/gi;
  function stripThinkingTags (line 156) | function stripThinkingTags(text: string): string {
  function stripEnvelopePrefix (line 160) | function stripEnvelopePrefix(text: string): string {
  function stripMemoryInjection (line 171) | function stripMemoryInjection(text: string): string {
  function stripEvidenceWrappers (line 262) | function stripEvidenceWrappers(text: string, evidenceTag: string): string {

FILE: apps/memos-local-openclaw/src/config.ts
  constant ENV_RE (line 4) | const ENV_RE = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
  function resolveEnvVars (line 6) | function resolveEnvVars(value: string): string {
  function deepResolveEnv (line 10) | function deepResolveEnv<T>(obj: T): T {
  function resolveConfig (line 23) | function resolveConfig(raw: Partial<MemosLocalConfig> | undefined, state...
  function buildContext (line 58) | function buildContext(

FILE: apps/memos-local-openclaw/src/embedding/index.ts
  class Embedder (line 10) | class Embedder {
    method constructor (line 11) | constructor(
    method provider (line 16) | get provider(): string {
    method dimensions (line 20) | get dimensions(): number {
    method embed (line 25) | async embed(texts: string[]): Promise<number[][]> {
    method embedQuery (line 38) | async embedQuery(text: string): Promise<number[]> {
    method embedBatch (line 46) | private async embedBatch(texts: string[]): Promise<number[][]> {

FILE: apps/memos-local-openclaw/src/embedding/local.ts
  function getExtractor (line 6) | function getExtractor(log: Logger): Promise<any> {
  function embedLocal (line 26) | async function embedLocal(texts: string[], log: Logger): Promise<number[...

FILE: apps/memos-local-openclaw/src/embedding/providers/cohere.ts
  function embedCohere (line 3) | async function embedCohere(
  function embedCohereQuery (line 37) | async function embedCohereQuery(

FILE: apps/memos-local-openclaw/src/embedding/providers/gemini.ts
  function embedGemini (line 3) | async function embedGemini(

FILE: apps/memos-local-openclaw/src/embedding/providers/mistral.ts
  function embedMistral (line 3) | async function embedMistral(

FILE: apps/memos-local-openclaw/src/embedding/providers/openai.ts
  function embedOpenAI (line 3) | async function embedOpenAI(
  function normalizeEmbeddingEndpoint (line 38) | function normalizeEmbeddingEndpoint(url: string): string {

FILE: apps/memos-local-openclaw/src/embedding/providers/voyage.ts
  function embedVoyage (line 3) | async function embedVoyage(

FILE: apps/memos-local-openclaw/src/index.ts
  type MemosLocalPlugin (line 12) | interface MemosLocalPlugin {
  type PluginInitOptions (line 21) | interface PluginInitOptions {
  function initPlugin (line 51) | function initPlugin(opts: PluginInitOptions = {}): MemosLocalPlugin {
  function defaultStateDir (line 105) | function defaultStateDir(): string {

FILE: apps/memos-local-openclaw/src/ingest/chunker.ts
  type RawChunk (line 1) | interface RawChunk {
  constant MAX_CHUNK_CHARS (line 6) | const MAX_CHUNK_CHARS = 3000;
  constant MIN_CHUNK_CHARS (line 7) | const MIN_CHUNK_CHARS = 40;
  constant IDEAL_CHUNK_CHARS (line 8) | const IDEAL_CHUNK_CHARS = 1500;
  constant FENCED_CODE_RE (line 10) | const FENCED_CODE_RE = /^(`{3,})[^\n]*\n[\s\S]*?^\1\s*$/gm;
  constant FUNC_OPEN_RE (line 12) | const FUNC_OPEN_RE =
  constant BLOCK_CLOSE_RE (line 14) | const BLOCK_CLOSE_RE = /^[ \t]*[}\]]\s*;?\s*$/;
  constant ERROR_STACK_RE (line 16) | const ERROR_STACK_RE =
  constant LIST_BLOCK_RE (line 18) | const LIST_BLOCK_RE = /(?:^[\s]*[-*•]\s+.+\n?){3,}/gm;
  constant COMMAND_LINE_RE (line 19) | const COMMAND_LINE_RE = /^(?:\$|>|#)\s+.+$/gm;
  function chunkText (line 29) | function chunkText(text: string): RawChunk[] {
  function extractBraceBlocks (line 86) | function extractBraceBlocks(
  function countBraces (line 147) | function countBraces(line: string): number {
  function mergeSmallChunks (line 156) | function mergeSmallChunks(chunks: RawChunk[]): RawChunk[] {
  function splitOversized (line 181) | function splitOversized(chunks: RawChunk[]): RawChunk[] {
  function splitAtSentenceBoundary (line 193) | function splitAtSentenceBoundary(text: string): RawChunk[] {

FILE: apps/memos-local-openclaw/src/ingest/dedup.ts
  function findDuplicate (line 12) | function findDuplicate(
  function findTopSimilar (line 44) | function findTopSimilar(

FILE: apps/memos-local-openclaw/src/ingest/providers/anthropic.ts
  constant SYSTEM_PROMPT (line 3) | const SYSTEM_PROMPT = `You generate a retrieval-friendly title.
  constant TASK_SUMMARY_PROMPT (line 19) | const TASK_SUMMARY_PROMPT = `You create a DETAILED task summary from a m...
  function summarizeTaskAnthropic (line 57) | async function summarizeTaskAnthropic(
  constant TASK_TITLE_PROMPT (line 93) | const TASK_TITLE_PROMPT = `Generate a short title for a conversation task.
  function generateTaskTitleAnthropic (line 105) | async function generateTaskTitleAnthropic(
  constant TOPIC_JUDGE_PROMPT (line 141) | const TOPIC_JUDGE_PROMPT = `You are a conversation topic boundary detect...
  function judgeNewTopicAnthropic (line 167) | async function judgeNewTopicAnthropic(
  constant FILTER_RELEVANT_PROMPT (line 208) | const FILTER_RELEVANT_PROMPT = `You are a memory relevance judge.
  function filterRelevantAnthropic (line 229) | async function filterRelevantAnthropic(
  function parseFilterResult (line 275) | function parseFilterResult(raw: string, log: Logger): FilterResult {
  function summarizeAnthropic (line 292) | async function summarizeAnthropic(
  function judgeDedupAnthropic (line 336) | async function judgeDedupAnthropic(

FILE: apps/memos-local-openclaw/src/ingest/providers/bedrock.ts
  constant SYSTEM_PROMPT (line 3) | const SYSTEM_PROMPT = `You generate a retrieval-friendly title.
  constant TASK_SUMMARY_PROMPT (line 19) | const TASK_SUMMARY_PROMPT = `You create a DETAILED task summary from a m...
  function summarizeTaskBedrock (line 57) | async function summarizeTaskBedrock(
  constant TASK_TITLE_PROMPT (line 94) | const TASK_TITLE_PROMPT = `Generate a short title for a conversation task.
  function generateTaskTitleBedrock (line 106) | async function generateTaskTitleBedrock(
  constant TOPIC_JUDGE_PROMPT (line 143) | const TOPIC_JUDGE_PROMPT = `You are a conversation topic boundary detect...
  function judgeNewTopicBedrock (line 169) | async function judgeNewTopicBedrock(
  constant FILTER_RELEVANT_PROMPT (line 211) | const FILTER_RELEVANT_PROMPT = `You are a memory relevance judge.
  function filterRelevantBedrock (line 232) | async function filterRelevantBedrock(
  function parseFilterResult (line 279) | function parseFilterResult(raw: string, log: Logger): FilterResult {
  function summarizeBedrock (line 296) | async function summarizeBedrock(
  function judgeDedupBedrock (line 344) | async function judgeDedupBedrock(

FILE: apps/memos-local-openclaw/src/ingest/providers/gemini.ts
  constant SYSTEM_PROMPT (line 3) | const SYSTEM_PROMPT = `You generate a retrieval-friendly title.
  constant TASK_SUMMARY_PROMPT (line 19) | const TASK_SUMMARY_PROMPT = `You create a DETAILED task summary from a m...
  function summarizeTaskGemini (line 57) | async function summarizeTaskGemini(
  constant TASK_TITLE_PROMPT (line 93) | const TASK_TITLE_PROMPT = `Generate a short title for a conversation task.
  function generateTaskTitleGemini (line 105) | async function generateTaskTitleGemini(
  constant TOPIC_JUDGE_PROMPT (line 141) | const TOPIC_JUDGE_PROMPT = `You are a conversation topic boundary detect...
  function judgeNewTopicGemini (line 167) | async function judgeNewTopicGemini(
  constant FILTER_RELEVANT_PROMPT (line 208) | const FILTER_RELEVANT_PROMPT = `You are a memory relevance judge.
  function filterRelevantGemini (line 229) | async function filterRelevantGemini(
  function parseFilterResult (line 275) | function parseFilterResult(raw: string, log: Logger): FilterResult {
  function summarizeGemini (line 292) | async function summarizeGemini(
  function judgeDedupGemini (line 336) | async function judgeDedupGemini(

FILE: apps/memos-local-openclaw/src/ingest/providers/index.ts
  function detectProvider (line 14) | function detectProvider(
  function normalizeEndpointForProvider (line 31) | function normalizeEndpointForProvider(
  function loadOpenClawFallbackConfig (line 49) | function loadOpenClawFallbackConfig(log: Logger): SummarizerConfig | und...
  type ModelHealthEntry (line 92) | interface ModelHealthEntry {
  class ModelHealthTracker (line 103) | class ModelHealthTracker {
    method recordSuccess (line 107) | recordSuccess(role: string, model: string): void {
    method recordError (line 124) | recordError(role: string, model: string, error: string): void {
    method getAll (line 134) | getAll(): ModelHealthEntry[] {
    method getOrCreate (line 138) | private getOrCreate(role: string): ModelHealthEntry {
  class Summarizer (line 150) | class Summarizer {
    method constructor (line 154) | constructor(
    method getConfigChain (line 167) | private getConfigChain(): SummarizerConfig[] {
    method tryChain (line 179) | private async tryChain<T>(
    method summarize (line 199) | async summarize(text: string): Promise<string> {
    method summarizeTask (line 249) | async summarizeTask(text: string): Promise<string> {
    method generateTaskTitle (line 258) | async generateTaskTitle(text: string): Promise<string> {
    method judgeNewTopic (line 264) | async judgeNewTopic(currentContext: string, newMessage: string): Promi...
    method filterRelevant (line 286) | async filterRelevant(
    method judgeDedup (line 297) | async judgeDedup(
    method getStrongConfig (line 308) | getStrongConfig(): SummarizerConfig | undefined {
  function callSummarize (line 315) | function callSummarize(cfg: SummarizerConfig, text: string, log: Logger)...
  function callSummarizeTask (line 338) | function callSummarizeTask(cfg: SummarizerConfig, text: string, log: Log...
  function callGenerateTaskTitle (line 361) | function callGenerateTaskTitle(cfg: SummarizerConfig, text: string, log:...
  function callTopicJudge (line 384) | function callTopicJudge(cfg: SummarizerConfig, currentContext: string, n...
  function callFilterRelevant (line 407) | function callFilterRelevant(cfg: SummarizerConfig, query: string, candid...
  function callJudgeDedup (line 430) | function callJudgeDedup(cfg: SummarizerConfig, newSummary: string, candi...
  function ruleFallback (line 455) | function ruleFallback(text: string): string {
  function taskFallback (line 460) | function taskFallback(text: string): string {
  function stripMarkdown (line 465) | function stripMarkdown(text: string): string {
  function wordCount (line 476) | function wordCount(text: string): number {

FILE: apps/memos-local-openclaw/src/ingest/providers/openai.ts
  constant SYSTEM_PROMPT (line 3) | const SYSTEM_PROMPT = `You generate a retrieval-friendly title.
  constant TASK_SUMMARY_PROMPT (line 19) | const TASK_SUMMARY_PROMPT = `You create a DETAILED task summary from a m...
  function summarizeTaskOpenAI (line 57) | async function summarizeTaskOpenAI(
  constant TASK_TITLE_PROMPT (line 94) | const TASK_TITLE_PROMPT = `Generate a short title for a conversation task.
  function generateTaskTitleOpenAI (line 106) | async function generateTaskTitleOpenAI(
  function summarizeOpenAI (line 143) | async function summarizeOpenAI(
  constant TOPIC_JUDGE_PROMPT (line 181) | const TOPIC_JUDGE_PROMPT = `You are a conversation topic boundary detect...
  function judgeNewTopicOpenAI (line 207) | async function judgeNewTopicOpenAI(
  constant FILTER_RELEVANT_PROMPT (line 249) | const FILTER_RELEVANT_PROMPT = `You are a memory relevance judge.
  type FilterResult (line 267) | interface FilterResult {
  function filterRelevantOpenAI (line 272) | async function filterRelevantOpenAI(
  function parseFilterResult (line 319) | function parseFilterResult(raw: string, log: Logger): FilterResult {
  constant DEDUP_JUDGE_PROMPT (line 338) | const DEDUP_JUDGE_PROMPT = `You are a memory deduplication system.
  type DedupResult (line 360) | interface DedupResult {
  function judgeDedupOpenAI (line 367) | async function judgeDedupOpenAI(
  function parseDedupResult (line 410) | function parseDedupResult(raw: string, log: Logger): DedupResult {
  function normalizeChatEndpoint (line 429) | function normalizeChatEndpoint(url: string): string {

FILE: apps/memos-local-openclaw/src/ingest/task-processor.ts
  constant TRIVIAL_PATTERNS (line 7) | const TRIVIAL_PATTERNS = [
  constant SKIP_REASONS (line 13) | const SKIP_REASONS = {
  class TaskProcessor (line 30) | class TaskProcessor {
    method constructor (line 37) | constructor(
    method onTaskCompleted (line 45) | onTaskCompleted(cb: (task: Task) => void): void {
    method onChunksIngested (line 53) | async onChunksIngested(sessionKey: string, latestTimestamp: number, ow...
    method drainPending (line 65) | private async drainPending(): Promise<void> {
    method detectAndProcess (line 82) | private async detectAndProcess(sessionKey: string, latestTimestamp: nu...
    method processChunksIncrementally (line 114) | private async processChunksIncrementally(
    method groupIntoTurns (line 213) | private groupIntoTurns(chunks: Chunk[]): Chunk[][] {
    method buildContextSummary (line 238) | private buildContextSummary(chunks: Chunk[]): string {
    method createNewTaskReturn (line 263) | private async createNewTaskReturn(sessionKey: string, timestamp: numbe...
    method createNewTask (line 281) | private async createNewTask(sessionKey: string, timestamp: number, own...
    method assignChunksToTask (line 286) | private assignChunksToTask(chunks: Chunk[], taskId: string): void {
    method assignUnassignedChunks (line 295) | private assignUnassignedChunks(sessionKey: string, taskId: string): vo...
    method finalizeTask (line 300) | async finalizeTask(task: Task): Promise<void> {
    method shouldSkipSummary (line 368) | private shouldSkipSummary(chunks: Chunk[]): string | null {
    method looksLikeTrivialContent (line 429) | private looksLikeTrivialContent(text: string): boolean {
    method buildConversationText (line 442) | private buildConversationText(chunks: Chunk[]): string {
    method parseTitleFromSummary (line 456) | private parseTitleFromSummary(summary: string): { title: string; body:...
    method generateTitle (line 466) | private async generateTitle(chunks: Chunk[], fallback: string): Promis...
    method extractTitle (line 482) | private extractTitle(chunks: Chunk[]): string {
    method humanReadableSkipReason (line 494) | private humanReadableSkipReason(reason: string, chunks: Chunk[]): stri...
    method fallbackSummary (line 522) | private fallbackSummary(chunks: Chunk[]): string {

FILE: apps/memos-local-openclaw/src/ingest/worker.ts
  class IngestWorker (line 10) | class IngestWorker {
    method constructor (line 17) | constructor(
    method getTaskProcessor (line 26) | getTaskProcessor(): TaskProcessor { return this.taskProcessor; }
    method enqueue (line 28) | enqueue(messages: ConversationMessage[]): void {
    method flush (line 39) | async flush(): Promise<void> {
    method processQueue (line 46) | private async processQueue(): Promise<void> {
    method ingestMessage (line 123) | private async ingestMessage(msg: ConversationMessage): Promise<
    method storeChunk (line 129) | private async storeChunk(

FILE: apps/memos-local-openclaw/src/recall/engine.ts
  type SkillSearchScope (line 10) | type SkillSearchScope = "mix" | "self" | "public";
  type RecallOptions (line 12) | interface RecallOptions {
  constant MAX_RECENT_QUERIES (line 20) | const MAX_RECENT_QUERIES = 20;
  class RecallEngine (line 22) | class RecallEngine {
    method constructor (line 25) | constructor(
    method search (line 31) | async search(opts: RecallOptions): Promise<SearchResult> {
    method checkRepeat (line 173) | private checkRepeat(query: string, maxResults: number, minScore: numbe...
    method recordQuery (line 191) | private recordQuery(query: string, maxResults: number, minScore: numbe...
    method searchSkills (line 205) | async searchSkills(query: string, scope: SkillSearchScope, currentOwne...
    method judgeSkillRelevance (line 266) | private async judgeSkillRelevance(
  function makeExcerpt (line 291) | function makeExcerpt(content: string): string {

FILE: apps/memos-local-openclaw/src/recall/mmr.ts
  function mmrRerank (line 12) | function mmrRerank(

FILE: apps/memos-local-openclaw/src/recall/recency.ts
  function applyRecencyDecay (line 12) | function applyRecencyDecay(

FILE: apps/memos-local-openclaw/src/recall/rrf.ts
  type RankedItem (line 11) | interface RankedItem {
  function rrfFuse (line 16) | function rrfFuse(

FILE: apps/memos-local-openclaw/src/shared/llm-call.ts
  function detectProvider (line 8) | function detectProvider(providerKey: string | undefined, baseUrl: string...
  function defaultEndpointForProvider (line 22) | function defaultEndpointForProvider(provider: SummaryProvider, baseUrl: ...
  function loadOpenClawFallbackConfig (line 38) | function loadOpenClawFallbackConfig(log: Logger): SummarizerConfig | und...
  function buildSkillConfigChain (line 82) | function buildSkillConfigChain(ctx: PluginContext): SummarizerConfig[] {
  type LLMCallOptions (line 93) | interface LLMCallOptions {
  function normalizeOpenAIEndpoint (line 99) | function normalizeOpenAIEndpoint(url: string): string {
  function normalizeAnthropicEndpoint (line 106) | function normalizeAnthropicEndpoint(url: string): string {
  function isAnthropicProvider (line 113) | function isAnthropicProvider(cfg: SummarizerConfig): boolean {
  function callLLMOnce (line 121) | async function callLLMOnce(
  function callLLMOnceAnthropic (line 132) | async function callLLMOnceAnthropic(
  function callLLMOnceOpenAI (line 169) | async function callLLMOnceOpenAI(
  function callLLMWithFallback (line 209) | async function callLLMWithFallback(

FILE: apps/memos-local-openclaw/src/skill/bundled-memory-guide.ts
  constant MEMORY_GUIDE_SKILL_MD (line 9) | const MEMORY_GUIDE_SKILL_MD: string = fs.readFileSync(skillPath, "utf-8");

FILE: apps/memos-local-openclaw/src/skill/evaluator.ts
  type CreateEvalResult (line 5) | interface CreateEvalResult {
  type UpgradeEvalResult (line 13) | interface UpgradeEvalResult {
  constant CREATE_EVAL_PROMPT (line 22) | const CREATE_EVAL_PROMPT = `You are a strict experience evaluation exper...
  constant UPGRADE_EVAL_PROMPT (line 66) | const UPGRADE_EVAL_PROMPT = `You are a skill upgrade evaluation expert.
  class SkillEvaluator (line 107) | class SkillEvaluator {
    method constructor (line 108) | constructor(private ctx: PluginContext) {}
    method passesRuleFilter (line 110) | passesRuleFilter(chunks: Chunk[], task: Task): { pass: boolean; skipRe...
    method evaluateCreate (line 137) | async evaluateCreate(task: Task): Promise<CreateEvalResult> {
    method evaluateUpgrade (line 158) | async evaluateUpgrade(task: Task, skill: Skill, skillContent: string):...
    method parseJSON (line 182) | private parseJSON<T>(raw: string, fallback: T): T {

FILE: apps/memos-local-openclaw/src/skill/evolver.ts
  type SkillEvolvedCallback (line 15) | type SkillEvolvedCallback = (skillName: string, upgradeType: "created" |...
  class SkillEvolver (line 17) | class SkillEvolver {
    method constructor (line 26) | constructor(
    method recoverOrphanedTasks (line 38) | async recoverOrphanedTasks(): Promise<number> {
    method onTaskCompleted (line 53) | async onTaskCompleted(task: Task): Promise<void> {
    method drain (line 67) | private async drain(task: Task): Promise<void> {
    method processOne (line 80) | private async processOne(task: Task): Promise<void> {
    method process (line 89) | private async process(task: Task): Promise<void> {
    method findRelatedSkill (line 119) | private async findRelatedSkill(task: Task): Promise<Skill | null> {
    method judgeSkillRelatedToTask (line 192) | private async judgeSkillRelatedToTask(
    method parseJudgeSkillResult (line 242) | private parseJudgeSkillResult(raw: string, maxIndex: number): { select...
    method handleExistingSkill (line 257) | private async handleExistingSkill(task: Task, chunks: Chunk[], skill: ...
    method handleNewSkill (line 302) | private async handleNewSkill(task: Task, chunks: Chunk[]): Promise<voi...
    method markChunksWithSkill (line 327) | private markChunksWithSkill(chunks: Chunk[], skillId: string): void {
    method readSkillContent (line 334) | private readSkillContent(skill: Skill): string | null {

FILE: apps/memos-local-openclaw/src/skill/generator.ts
  constant STEP1_SKILL_MD_PROMPT (line 21) | const STEP1_SKILL_MD_PROMPT = `You are a Skill creation expert. Your job...
  constant STEP2_SCRIPTS_PROMPT (line 107) | const STEP2_SCRIPTS_PROMPT = `Based on the following SKILL.md and task r...
  constant STEP3_EVALS_PROMPT (line 133) | const STEP3_EVALS_PROMPT = `Based on the following skill, generate reali...
  constant STEP2B_REFS_PROMPT (line 159) | const STEP2B_REFS_PROMPT = `Based on the following SKILL.md and task rec...
  class SkillGenerator (line 181) | class SkillGenerator {
    method constructor (line 185) | constructor(
    method generate (line 195) | async generate(task: Task, chunks: Chunk[], evalResult: CreateEvalResu...
    method detectLanguage (line 338) | private detectLanguage(text: string): string {
    method step1GenerateSkillMd (line 345) | private async step1GenerateSkillMd(task: Task, conversationText: strin...
    method step2ExtractScripts (line 370) | private async step2ExtractScripts(
    method step2bExtractReferences (line 392) | private async step2bExtractReferences(
    method step3GenerateEvals (line 414) | private async step3GenerateEvals(
    method verifyEvals (line 436) | private async verifyEvals(
    method parseJSONArray (line 472) | private parseJSONArray<T>(raw: string): T[] {
    method buildConversationText (line 484) | private buildConversationText(chunks: Chunk[]): string {
    method parseDescription (line 494) | private parseDescription(content: string): string {

FILE: apps/memos-local-openclaw/src/skill/installer.ts
  class SkillInstaller (line 6) | class SkillInstaller {
    method constructor (line 9) | constructor(
    method install (line 16) | install(skillId: string): { installed: boolean; path: string; message:...
    method uninstall (line 37) | uninstall(skillId: string): void {
    method syncIfInstalled (line 49) | syncIfInstalled(skillName: string): void {

FILE: apps/memos-local-openclaw/src/skill/upgrader.ts
  constant UPGRADE_PROMPT (line 10) | const UPGRADE_PROMPT = `You are a Skill upgrade expert. You're merging n...
  class SkillUpgrader (line 71) | class SkillUpgrader {
    method constructor (line 74) | constructor(
    method upgrade (line 81) | async upgrade(task: Task, skill: Skill, evalResult: UpgradeEvalResult)...
    method readCurrentContent (line 151) | private readCurrentContent(skill: Skill): string | null {
    method callUpgradeLLM (line 161) | private async callUpgradeLLM(
    method parseDescription (line 212) | private parseDescription(content: string): string {

FILE: apps/memos-local-openclaw/src/skill/validator.ts
  type ValidationResult (line 7) | interface ValidationResult {
  class SkillValidator (line 15) | class SkillValidator {
    method constructor (line 16) | constructor(private ctx: PluginContext) {}
    method validate (line 22) | async validate(dirPath: string, opts?: { skipLLM?: boolean; previousCo...
    method validateFormat (line 50) | private validateFormat(dirPath: string, result: ValidationResult): void {
    method regressionCheck (line 115) | private regressionCheck(dirPath: string, previousContent: string, resu...
    method assessQuality (line 136) | private async assessQuality(dirPath: string, result: ValidationResult)...
  constant QUALITY_PROMPT (line 175) | const QUALITY_PROMPT = `You are a skill quality reviewer. Evaluate the f...

FILE: apps/memos-local-openclaw/src/storage/ensure-binding.ts
  function ensureSqliteBinding (line 15) | function ensureSqliteBinding(log?: { info: (msg: string) => void; warn: ...

FILE: apps/memos-local-openclaw/src/storage/sqlite.ts
  class SqliteStore (line 7) | class SqliteStore {
    method constructor (line 10) | constructor(dbPath: string, private log: Logger) {
    method migrate (line 20) | private migrate(): void {
    method migrateChunksIndexesForRecall (line 116) | private migrateChunksIndexesForRecall(): void {
    method migrateOwnerFields (line 120) | private migrateOwnerFields(): void {
    method migrateSkillVisibility (line 135) | private migrateSkillVisibility(): void {
    method migrateSkillEmbeddingsAndFts (line 149) | private migrateSkillEmbeddingsAndFts(): void {
    method migrateFtsToTrigram (line 199) | private migrateFtsToTrigram(): void {
    method migrateTaskId (line 274) | private migrateTaskId(): void {
    method migrateContentHash (line 283) | private migrateContentHash(): void {
    method migrateSkillTables (line 301) | private migrateSkillTables(): void {
    method migrateSkillId (line 344) | private migrateSkillId(): void {
    method migrateSkillQualityScore (line 353) | private migrateSkillQualityScore(): void {
    method migrateTaskSkillMeta (line 371) | private migrateTaskSkillMeta(): void {
    method setTaskSkillMeta (line 380) | setTaskSkillMeta(taskId: string, meta: { skillStatus: string; skillRea...
    method getTasksBySkillStatus (line 385) | getTasksBySkillStatus(statuses: string[]): Task[] {
    method migrateMergeFields (line 393) | private migrateMergeFields(): void {
    method migrateApiLogs (line 403) | private migrateApiLogs(): void {
    method migrateDedupStatus (line 419) | private migrateDedupStatus(): void {
    method recordApiLog (line 430) | recordApiLog(toolName: string, input: unknown, output: string, duratio...
    method getApiLogs (line 437) | getApiLogs(limit: number = 50, offset: number = 0, toolFilter?: string...
    method getApiLogToolNames (line 468) | getApiLogToolNames(): string[] {
    method recordMergeHit (line 473) | recordMergeHit(chunkId: string, action: "DUPLICATE" | "UPDATE", reason...
    method updateChunkSummaryAndContent (line 491) | updateChunkSummaryAndContent(chunkId: string, newSummary: string, appe...
    method migrateToolCalls (line 497) | private migrateToolCalls(): void {
    method recordToolCall (line 511) | recordToolCall(toolName: string, durationMs: number, success: boolean)...
    method getToolMetrics (line 517) | getToolMetrics(minutes: number): {
    method recordViewerEvent (line 599) | recordViewerEvent(eventType: string): void {
    method getMetrics (line 606) | getMetrics(days: number): {
    method insertChunk (line 664) | insertChunk(chunk: Chunk): void {
    method markDedupStatus (line 689) | markDedupStatus(chunkId: string, status: "duplicate" | "merged", targe...
    method updateSummary (line 695) | updateSummary(chunkId: string, summary: string): void {
    method upsertEmbedding (line 703) | upsertEmbedding(chunkId: string, vector: number[]): void {
    method deleteEmbedding (line 711) | deleteEmbedding(chunkId: string): void {
    method getChunk (line 717) | getChunk(chunkId: string): Chunk | null {
    method getChunkForOwners (line 722) | getChunkForOwners(chunkId: string, ownerFilter?: string[]): Chunk | nu...
    method getChunksByRef (line 732) | getChunksByRef(ref: ChunkRef, ownerFilter?: string[]): Chunk | null {
    method getNeighborChunks (line 736) | getNeighborChunks(sessionKey: string, turnId: string, seq: number, win...
    method ftsSearch (line 767) | ftsSearch(query: string, limit: number, ownerFilter?: string[]): Array...
    method patternSearch (line 804) | patternSearch(patterns: string[], opts: { role?: string; limit?: numbe...
    method getAllEmbeddings (line 837) | getAllEmbeddings(ownerFilter?: string[]): Array<{ chunkId: string; vec...
    method getRecentEmbeddings (line 857) | getRecentEmbeddings(limit: number, ownerFilter?: string[]): Array<{ ch...
    method getEmbedding (line 883) | getEmbedding(chunkId: string): number[] | null {
    method updateChunk (line 893) | updateChunk(chunkId: string, fields: { summary?: string; content?: str...
    method findPollutedUserChunks (line 933) | findPollutedUserChunks(): Array<{ id: string; preview: string; reason:...
    method fixMixedUserChunks (line 960) | fixMixedUserChunks(): number {
    method deleteChunk (line 984) | deleteChunk(chunkId: string): boolean {
    method deleteSession (line 989) | deleteSession(sessionKey: string): number {
    method deleteAll (line 994) | deleteAll(): number {
    method deleteTask (line 1020) | deleteTask(taskId: string): boolean {
    method deleteSkill (line 1027) | deleteSkill(skillId: string): boolean {
    method insertTask (line 1038) | insertTask(task: Task): void {
    method getTask (line 1045) | getTask(taskId: string): Task | null {
    method getActiveTask (line 1050) | getActiveTask(sessionKey: string, owner?: string): Task | null {
    method hasTaskForSession (line 1063) | hasTaskForSession(sessionKey: string): boolean {
    method hasSkillForSessionTask (line 1070) | hasSkillForSessionTask(sessionKey: string): boolean {
    method getCompletedTasksForSession (line 1077) | getCompletedTasksForSession(sessionKey: string): Task[] {
    method getAllActiveTasks (line 1084) | getAllActiveTasks(owner?: string): Task[] {
    method updateTask (line 1097) | updateTask(taskId: string, fields: { title?: string; summary?: string;...
    method getChunksByTask (line 1112) | getChunksByTask(taskId: string): Chunk[] {
    method listTasks (line 1117) | listTasks(opts: { status?: string; limit?: number; offset?: number; ow...
    method countChunksByTask (line 1136) | countChunksByTask(taskId: string): number {
    method setChunkTaskId (line 1141) | setChunkTaskId(chunkId: string, taskId: string): void {
    method getUnassignedChunks (line 1145) | getUnassignedChunks(sessionKey: string, owner?: string): Chunk[] {
    method chunkExistsByContent (line 1163) | chunkExistsByContent(sessionKey: string, role: string, content: string...
    method findActiveChunkByHash (line 1175) | findActiveChunkByHash(content: string, owner?: string): string | null {
    method getRecentChunkIds (line 1193) | getRecentChunkIds(limit: number): string[] {
    method countChunks (line 1200) | countChunks(): number {
    method insertSkill (line 1207) | insertSkill(skill: Skill): void {
    method getSkill (line 1214) | getSkill(skillId: string): Skill | null {
    method getSkillByName (line 1219) | getSkillByName(name: string): Skill | null {
    method updateSkill (line 1224) | updateSkill(skillId: string, fields: { description?: string; version?:...
    method listSkills (line 1239) | listSkills(opts: { status?: string } = {}): Skill[] {
    method setSkillVisibility (line 1248) | setSkillVisibility(skillId: string, visibility: SkillVisibility): void {
    method upsertSkillEmbedding (line 1253) | upsertSkillEmbedding(skillId: string, vector: number[]): void {
    method getSkillEmbedding (line 1261) | getSkillEmbedding(skillId: string): number[] | null {
    method getSkillEmbeddings (line 1269) | getSkillEmbeddings(scope: "self" | "public" | "mix", currentOwner: str...
    method skillFtsSearch (line 1293) | skillFtsSearch(query: string, limit: number, scope: "self" | "public" ...
    method listPublicSkills (line 1331) | listPublicSkills(): Skill[] {
    method insertSkillVersion (line 1338) | insertSkillVersion(sv: SkillVersion): void {
    method getLatestSkillVersion (line 1345) | getLatestSkillVersion(skillId: string): SkillVersion | null {
    method getSkillVersions (line 1350) | getSkillVersions(skillId: string): SkillVersion[] {
    method getSkillVersion (line 1355) | getSkillVersion(skillId: string, version: number): SkillVersion | null {
    method linkTaskSkill (line 1362) | linkTaskSkill(taskId: string, skillId: string, relation: TaskSkillRela...
    method getSkillsByTask (line 1373) | getSkillsByTask(taskId: string): Array<{ skill: Skill; relation: TaskS...
    method getTasksBySkill (line 1386) | getTasksBySkill(skillId: string): Array<{ task: Task; relation: TaskSk...
    method countSkills (line 1399) | countSkills(status?: string): number {
    method setChunkSkillId (line 1408) | setChunkSkillId(chunkId: string, skillId: string): void {
    method getDistinctSessionKeys (line 1412) | getDistinctSessionKeys(): string[] {
    method getSessionOwnerMap (line 1417) | getSessionOwnerMap(sessionKeys: string[]): Map<string, string> {
    method close (line 1428) | close(): void {
  function sanitizeFtsQuery (line 1440) | function sanitizeFtsQuery(raw: string): string {
  constant FTS_RESERVED (line 1451) | const FTS_RESERVED = new Set(["AND", "OR", "NOT", "NEAR"]);
  type ChunkRow (line 1455) | interface ChunkRow {
  function rowToChunk (line 1477) | function rowToChunk(row: ChunkRow): Chunk {
  type TaskRow (line 1502) | interface TaskRow {
  function rowToTask (line 1514) | function rowToTask(row: TaskRow): Task {
  type SkillRow (line 1528) | interface SkillRow {
  function rowToSkill (line 1545) | function rowToSkill(row: SkillRow): Skill {
  type SkillVersionRow (line 1564) | interface SkillVersionRow {
  function rowToSkillVersion (line 1578) | function rowToSkillVersion(row: SkillVersionRow): SkillVersion {
  function contentHash (line 1594) | function contentHash(content: string): string {

FILE: apps/memos-local-openclaw/src/storage/vector.ts
  function cosineSimilarity (line 3) | function cosineSimilarity(a: number[], b: number[]): number {
  type VectorHit (line 17) | interface VectorHit {
  function vectorSearch (line 26) | function vectorSearch(

FILE: apps/memos-local-openclaw/src/telemetry.ts
  type TelemetryConfig (line 17) | interface TelemetryConfig {
  constant ARMS_ENDPOINT (line 21) | const ARMS_ENDPOINT =
  constant ARMS_PID (line 27) | const ARMS_PID = "a3u72ukxmr@066657d42a13a9a9f337f";
  constant ARMS_ENV (line 28) | const ARMS_ENV = "prod";
  constant FLUSH_AT (line 30) | const FLUSH_AT = 10;
  constant FLUSH_INTERVAL_MS (line 31) | const FLUSH_INTERVAL_MS = 30_000;
  constant SEND_TIMEOUT_MS (line 32) | const SEND_TIMEOUT_MS = 30_000;
  constant SESSION_TTL_MS (line 33) | const SESSION_TTL_MS = 30 * 60_000;
  type ArmsEvent (line 34) | interface ArmsEvent {
  class Telemetry (line 46) | class Telemetry {
    method constructor (line 58) | constructor(config: TelemetryConfig, stateDir: string, pluginVersion: ...
    method loadOrCreateAnonymousId (line 76) | private loadOrCreateAnonymousId(stateDir: string): string {
    method loadOrCreateSessionId (line 99) | private loadOrCreateSessionId(stateDir: string): string {
    method touchSession (line 118) | private touchSession(filePath: string, id: string): void {
    method loadOrCreateFirstSeen (line 125) | private loadOrCreateFirstSeen(stateDir: string): string {
    method capture (line 139) | private capture(event: string, properties?: Record<string, unknown>): ...
    method buildPayload (line 174) | private buildPayload(events: ArmsEvent[]): Record<string, unknown> {
    method flush (line 191) | private async flush(): Promise<void> {
    method trackPluginStarted (line 211) | trackPluginStarted(embeddingProvider: string, summarizerProvider: stri...
    method trackToolCalled (line 219) | trackToolCalled(toolName: string, latencyMs: number, success: boolean)...
    method trackMemoryIngested (line 226) | trackMemoryIngested(chunkCount: number): void {
    method trackSkillInstalled (line 232) | trackSkillInstalled(skillName: string): void {
    method trackSkillEvolved (line 238) | trackSkillEvolved(skillName: string, upgradeType: "created" | "upgrade...
    method trackViewerOpened (line 245) | trackViewerOpened(): void {
    method trackAutoRecall (line 249) | trackAutoRecall(hitCount: number, latencyMs: number): void {
    method trackError (line 257) | trackError(source: string, errorType: string): void {
    method maybeSendDailyPing (line 264) | private maybeSendDailyPing(): void {
    method shutdown (line 274) | async shutdown(): Promise<void> {

FILE: apps/memos-local-openclaw/src/tools/memory-get.ts
  function resolveOwnerFilter (line 5) | function resolveOwnerFilter(owner: unknown): string[] {
  function createMemoryGetTool (line 10) | function createMemoryGetTool(store: SqliteStore): ToolDefinition {

FILE: apps/memos-local-openclaw/src/tools/memory-search.ts
  function resolveOwnerFilter (line 4) | function resolveOwnerFilter(owner: unknown): string[] {
  function createMemorySearchTool (line 9) | function createMemorySearchTool(engine: RecallEngine): ToolDefinition {

FILE: apps/memos-local-openclaw/src/tools/memory-timeline.ts
  function resolveOwnerFilter (line 5) | function resolveOwnerFilter(owner: unknown): string[] {
  function createMemoryTimelineTool (line 10) | function createMemoryTimelineTool(store: SqliteStore): ToolDefinition {

FILE: apps/memos-local-openclaw/src/types.ts
  type Role (line 3) | type Role = "user" | "assistant" | "system" | "tool";
  type ConversationMessage (line 5) | interface ConversationMessage {
  type DedupStatus (line 17) | type DedupStatus = "active" | "duplicate" | "merged";
  type Chunk (line 19) | interface Chunk {
  type TaskStatus (line 44) | type TaskStatus = "active" | "completed" | "skipped";
  type Task (line 46) | interface Task {
  type ChunkKind (line 58) | type ChunkKind = "paragraph";
  type ChunkRef (line 60) | interface ChunkRef {
  type SearchHit (line 69) | interface SearchHit {
  type SkillSearchHit (line 84) | interface SkillSearchHit {
  type SearchResult (line 94) | interface SearchResult {
  type TimelineEntry (line 104) | interface TimelineEntry {
  type TimelineResult (line 112) | interface TimelineResult {
  type GetResult (line 117) | interface GetResult {
  type RankedCandidate (line 129) | interface RankedCandidate {
  type SummaryProvider (line 141) | type SummaryProvider =
  type EmbeddingProvider (line 155) | type EmbeddingProvider =
  type ProviderConfig (line 165) | interface ProviderConfig {
  type SummarizerConfig (line 175) | interface SummarizerConfig extends ProviderConfig {
  type EmbeddingConfig (line 179) | interface EmbeddingConfig extends ProviderConfig {
  type SkillStatus (line 188) | type SkillStatus = "active" | "archived" | "draft";
  type SkillUpgradeType (line 189) | type SkillUpgradeType = "create" | "refine" | "extend" | "fix";
  type TaskSkillRelation (line 190) | type TaskSkillRelation = "generated_from" | "evolved_from" | "applied_to";
  type SkillVisibility (line 192) | type SkillVisibility = "private" | "public";
  type Skill (line 194) | interface Skill {
  type SkillVersion (line 211) | interface SkillVersion {
  type SkillGenerateOutput (line 225) | interface SkillGenerateOutput {
  type TaskSkillLink (line 232) | interface TaskSkillLink {
  type SkillEvolutionConfig (line 242) | interface SkillEvolutionConfig {
  type TelemetryConfig (line 252) | interface TelemetryConfig {
  type MemosLocalConfig (line 256) | interface MemosLocalConfig {
  constant DEFAULTS (line 285) | const DEFAULTS = {
  type PluginContext (line 316) | interface PluginContext {
  type Logger (line 323) | interface Logger {
  type ToolDefinition (line 330) | interface ToolDefinition {

FILE: apps/memos-local-openclaw/src/update-check.ts
  type UpdateCheckResult (line 9) | interface UpdateCheckResult {
  function isPrerelease (line 23) | function isPrerelease(v: string): boolean {
  function computeUpdateCheck (line 30) | async function computeUpdateCheck(

FILE: apps/memos-local-openclaw/src/viewer/html.ts
  function viewerHTML (line 1) | function viewerHTML(pluginVersion?: string): string {

FILE: apps/memos-local-openclaw/src/viewer/server.ts
  function normalizeTimestamp (line 21) | function normalizeTimestamp(ts: number): number {
  type ViewerServerOptions (line 26) | interface ViewerServerOptions {
  type AuthState (line 35) | interface AuthState {
  class ViewerServer (line 40) | class ViewerServer {
    method constructor (line 83) | constructor(opts: ViewerServerOptions) {
    method start (line 96) | start(): Promise<string> {
    method autoCleanupPolluted (line 116) | private autoCleanupPolluted(): void {
    method stop (line 132) | stop(): void {
    method getResetToken (line 137) | getResetToken(): string {
    method loadAuth (line 143) | private loadAuth(): void {
    method saveAuth (line 154) | private saveAuth(): void {
    method hashPassword (line 163) | private hashPassword(pw: string): string {
    method createSession (line 167) | private createSession(): string {
    method isValidSession (line 173) | private isValidSession(req: http.IncomingMessage): boolean {
    method needsSetup (line 183) | private get needsSetup(): boolean {
    method handleRequest (line 189) | private handleRequest(req: http.IncomingMessage, res: http.ServerRespo...
    method handleSetup (line 277) | private handleSetup(req: http.IncomingMessage, res: http.ServerRespons...
    method handleLogin (line 306) | private handleLogin(req: http.IncomingMessage, res: http.ServerRespons...
    method handleLogout (line 328) | private handleLogout(req: http.IncomingMessage, res: http.ServerRespon...
    method handlePasswordReset (line 339) | private handlePasswordReset(req: http.IncomingMessage, res: http.Serve...
    method serveViewer (line 373) | private serveViewer(res: http.ServerResponse): void {
    method serveMemories (line 380) | private serveMemories(res: http.ServerResponse, url: URL): void {
    method serveMetrics (line 422) | private serveMetrics(res: http.ServerResponse, url: URL): void {
    method serveToolMetrics (line 428) | private serveToolMetrics(res: http.ServerResponse, url: URL): void {
    method serveTasks (line 434) | private serveTasks(res: http.ServerResponse, url: URL): void {
    method serveTaskDetail (line 460) | private serveTaskDetail(res: http.ServerResponse, urlPath: string): vo...
    method serveStats (line 504) | private serveStats(res: http.ServerResponse): void {
    method serveSearch (line 567) | private async serveSearch(_req: http.IncomingMessage, res: http.Server...
    method serveSkills (line 664) | private serveSkills(res: http.ServerResponse, url: URL): void {
    method serveSkillDetail (line 674) | private serveSkillDetail(res: http.ServerResponse, urlPath: string): v...
    method serveSkillFiles (line 714) | private serveSkillFiles(res: http.ServerResponse, urlPath: string): vo...
    method walkDir (line 732) | private walkDir(dir: string, root: string): Array<{ path: string; type...
    method serveSkillDownload (line 755) | private serveSkillDownload(res: http.ServerResponse, urlPath: string):...
    method handleSkillVisibility (line 796) | private handleSkillVisibility(req: http.IncomingMessage, res: http.Ser...
    method handleTaskRetrySkill (line 827) | private handleTaskRetrySkill(_req: http.IncomingMessage, res: http.Ser...
    method handleTaskDelete (line 852) | private handleTaskDelete(res: http.ServerResponse, urlPath: string): v...
    method handleTaskUpdate (line 859) | private handleTaskUpdate(req: http.IncomingMessage, res: http.ServerRe...
    method handleSkillDelete (line 880) | private handleSkillDelete(res: http.ServerResponse, urlPath: string): ...
    method handleSkillUpdate (line 896) | private handleSkillUpdate(req: http.IncomingMessage, res: http.ServerR...
    method serveMemoryDetail (line 920) | private serveMemoryDetail(res: http.ServerResponse, urlPath: string): ...
    method handleUpdate (line 934) | private handleUpdate(req: http.IncomingMessage, res: http.ServerRespon...
    method handleDelete (line 954) | private handleDelete(res: http.ServerResponse, urlPath: string): void {
    method handleDeleteSession (line 960) | private handleDeleteSession(res: http.ServerResponse, url: URL): void {
    method handleDeleteAll (line 967) | private handleDeleteAll(res: http.ServerResponse): void {
    method getOpenClawConfigPath (line 993) | private getOpenClawConfigPath(): string {
    method serveConfig (line 999) | private serveConfig(res: http.ServerResponse): void {
    method handleSaveConfig (line 1029) | private handleSaveConfig(req: http.IncomingMessage, res: http.ServerRe...
    method handleTestModel (line 1071) | private handleTestModel(req: http.IncomingMessage, res: http.ServerRes...
    method serveModelHealth (line 1094) | private serveModelHealth(res: http.ServerResponse): void {
    method serveFallbackModel (line 1098) | private serveFallbackModel(res: http.ServerResponse): void {
    method findPluginPackageJson (line 1127) | private findPluginPackageJson(): string | null {
    method handleUpdateCheck (line 1142) | private async handleUpdateCheck(res: http.ServerResponse): Promise<voi...
    method handleUpdateInstall (line 1177) | private handleUpdateInstall(req: http.IncomingMessage, res: http.Serve...
    method testEmbeddingModel (line 1303) | private async testEmbeddingModel(provider: string, model: string, endp...
    method testChatModel (line 1373) | private async testChatModel(provider: string, model: string, endpoint:...
    method serveLogs (line 1423) | private serveLogs(res: http.ServerResponse, url: URL): void {
    method serveLogTools (line 1433) | private serveLogTools(res: http.ServerResponse): void {
    method getOpenClawHome (line 1440) | private getOpenClawHome(): string {
    method handleCleanupPolluted (line 1445) | private handleCleanupPolluted(res: http.ServerResponse): void {
    method handleCleanupPolluted (line 1467) | private handleCleanupPolluted(res: http.ServerResponse): void {
    method handleMigrateScan (line 1489) | private handleMigrateScan(res: http.ServerResponse): void {
    method broadcastSSE (line 1604) | private broadcastSSE(event: string, data: unknown): void {
    method handleMigrateStatus (line 1611) | private handleMigrateStatus(res: http.ServerResponse): void {
    method handleMigrateStop (line 1618) | private handleMigrateStop(res: http.ServerResponse): void {
    method handleMigrateStream (line 1627) | private handleMigrateStream(res: http.ServerResponse): void {
    method handleMigrateStart (line 1651) | private handleMigrateStart(req: http.IncomingMessage, res: http.Server...
    method runMigration (line 1728) | private async runMigration(
    method handlePostprocess (line 2125) | private handlePostprocess(req: http.IncomingMessage, res: http.ServerR...
    method handlePostprocessStream (line 2181) | private handlePostprocessStream(res: http.ServerResponse): void {
    method handlePostprocessStop (line 2202) | private handlePostprocessStop(res: http.ServerResponse): void {
    method handlePostprocessStatus (line 2207) | private handlePostprocessStatus(res: http.ServerResponse): void {
    method broadcastPPSSE (line 2217) | private broadcastPPSSE(event: string, data: unknown): void {
    method runPostprocess (line 2224) | private async runPostprocess(
    method readBody (line 2385) | private readBody(req: http.IncomingMessage, cb: (body: string) => void...
    method jsonResponse (line 2391) | private jsonResponse(res: http.ServerResponse, data: unknown): void {

FILE: apps/memos-local-openclaw/tests/accuracy.test.ts
  function loadProductionConfig (line 26) | function loadProductionConfig(): Partial<MemosLocalConfig> {
  constant TOTAL_TESTS (line 39) | const TOTAL_TESTS = 14;
  function fmtDuration (line 44) | function fmtDuration(ms: number): string {
  function printProgress (line 51) | function printProgress(testName: string) {
  constant SESSION_PREFIX (line 76) | const SESSION_PREFIX = "test-accuracy";
  function nextSession (line 79) | function nextSession(label: string): string {
  type TestResult (line 83) | interface TestResult {
  function record (line 91) | function record(category: string, name: string, pass: boolean, detail: s...

FILE: apps/memos-local-openclaw/tests/multi-agent.test.ts
  function makeChunk (line 29) | function makeChunk(overrides: Partial<Chunk> = {}): Chunk {
  function makeSkill (line 134) | function makeSkill(overrides: Partial<Skill> = {}): Skill {

FILE: apps/memos-local-openclaw/tests/plugin-impl-access.test.ts
  function makeApi (line 7) | function makeApi(stateDir: string) {
  function waitFor (line 37) | async function waitFor(predicate: () => Promise<boolean> | boolean, time...

FILE: apps/memos-local-openclaw/tests/shutdown-lifecycle.test.ts
  class MockStore (line 23) | class MockStore {
    method close (line 24) | close(): void {
    method close (line 67) | close(): void {
  class MockWorker (line 29) | class MockWorker {
    method enqueue (line 30) | enqueue(): void {}
    method flush (line 31) | flush(): Promise<void> {
    method enqueue (line 73) | enqueue(): void {}
    method flush (line 74) | flush(): Promise<void> {
  class MockStore (line 66) | class MockStore {
    method close (line 24) | close(): void {
    method close (line 67) | close(): void {
  class MockWorker (line 72) | class MockWorker {
    method enqueue (line 30) | enqueue(): void {}
    method flush (line 31) | flush(): Promise<void> {
    method enqueue (line 73) | enqueue(): void {}
    method flush (line 74) | flush(): Promise<void> {
  class MockViewer (line 80) | class MockViewer {
    method start (line 81) | async start(): Promise<string> { return "http://127.0.0.1:18799"; }
    method stop (line 82) | stop(): void { events.push("viewer-stop"); }
    method getResetToken (line 83) | getResetToken(): string { return "token"; }
  method search (line 91) | async search() { return { hits: [], meta: {} }; }
  method searchSkills (line 91) | async searchSkills() { return []; }

FILE: apps/memos-local-openclaw/tests/storage.test.ts
  function makeChunk (line 29) | function makeChunk(overrides: Partial<Chunk> = {}): Chunk {

FILE: apps/memos-local-openclaw/tests/task-processor.test.ts
  function makeCtx (line 21) | function makeCtx(): PluginContext {
  function insertTestChunk (line 41) | function insertTestChunk(overrides: Partial<Chunk> & { id: string }): vo...
  function insertChunk (line 363) | function insertChunk(overrides: Partial<Chunk> & { id: string }): void {

FILE: apps/memos-local-openclaw/tests/worker-lifecycle.test.ts
  function makeCtx (line 16) | function makeCtx(tmpDir: string): PluginContext {
  function makeMessage (line 36) | function makeMessage(id: string, sessionKey = "s1"): ConversationMessage {

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/taskHistory.integration.test.ts
  function createMockTask (line 36) | function createMockTask(id: string, prompt: string = 'Test task'): Task {
  function createMockMessage (line 47) | function createMockMessage(

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/Sidebar.integration.test.tsx
  function createMockTask (line 30) | function createMockTask(

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/TaskHistory.integration.test.tsx
  function createMockTask (line 32) | function createMockTask(

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/TaskLauncher.integration.test.tsx
  function createMockTask (line 26) | function createMockTask(

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/pages/Execution.integration.test.tsx
  function createMockTask (line 29) | function createMockTask(
  function createMockMessage (line 45) | function createMockMessage(
  function renderWithRouter (line 166) | function renderWithRouter(taskId: string = 'task-123') {

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/pages/Home.integration.test.tsx
  function createMockTask (line 30) | function createMockTask(

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/taskStore.integration.test.ts
  function createMockTask (line 11) | function createMockTask(id: string, prompt: string = 'Test task', status...
  function createMockMessage (line 22) | function createMockMessage(

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/main/ipc/handlers-utils.unit.test.ts
  constant MAX_TEXT_LENGTH (line 21) | const MAX_TEXT_LENGTH = 8000;
  function sanitizeString (line 26) | function sanitizeString(input: unknown, field: string, maxLength = MAX_T...
  function createTaskId (line 43) | function createTaskId(): string {
  function createMessageId (line 50) | function createMessageId(): string {
  function extractScreenshots (line 57) | function extractScreenshots(output: string): {
  function sanitizeToolOutput (line 102) | function sanitizeToolOutput(text: string, isError: boolean): string {

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/main/ipc/validation.unit.test.ts
  class CustomError (line 186) | class CustomError extends Error {
    method constructor (line 188) | constructor(message: string, code: number) {

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/unit/main/ipc/handlers.unit.test.ts
  type MockedIpcMain (line 267) | type MockedIpcMain = typeof ipcMain & {
  function invokeHandler (line 278) | async function invokeHandler(channel: string, ...args: unknown[]): Promi...

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/unit/main/opencode/adapter.unit.test.ts
  class MockPty (line 62) | class MockPty extends EventEmitter {
    method simulateData (line 72) | simulateData(data: string) {
    method simulateExit (line 78) | simulateExit(exitCode: number, signal?: number) {
    method onData (line 84) | onData(callback: (data: string) => void) {
    method onExit (line 89) | onExit(callback: (params: { exitCode: number; signal?: number }) => vo...

FILE: apps/openwork-memos-integration/apps/desktop/__tests__/unit/main/opencode/task-manager.unit.test.ts
  class MockOpenCodeAdapter (line 60) | class MockOpenCodeAdapter extends EventEmitter {
    method constructor (line 66) | constructor(taskId?: string) {
    method getTaskId (line 82) | getTaskId() {
    method getSessionId (line 86) | getSessionId() {
    method isAdapterDisposed (line 90) | isAdapterDisposed() {
    method startTask (line 94) | async startTask(config: TaskConfig) {
    method cancelTask (line 98) | async cancelTask() {
    method interruptTask (line 102) | async interruptTask() {
    method sendResponse (line 106) | async sendResponse(response: string) {
    method dispose (line 111) | dispose() {
    method simulateComplete (line 117) | simulateComplete(result: TaskResult) {
    method simulateError (line 121) | simulateError(error: Error) {
    method simulateMessage (line 125) | simulateMessage(message: OpenCodeMessage) {
    method simulateProgress (line 129) | simulateProgress(progress: { stage: string; message?: string }) {
    method simulatePermissionRequest (line 133) | simulatePermissionRequest(request: PermissionRequest) {
  method constructor (line 146) | constructor() {
  function createMockCallbacks (line 186) | function createMockCallbacks() {

FILE: apps/openwork-memos-integration/apps/desktop/e2e/config/timeouts.ts
  constant TEST_TIMEOUTS (line 5) | const TEST_TIMEOUTS = {
  constant TEST_SCENARIOS (line 35) | const TEST_SCENARIOS = {
  type TestScenario (line 62) | type TestScenario = keyof typeof TEST_SCENARIOS;

FILE: apps/openwork-memos-integration/apps/desktop/e2e/fixtures/electron-app.ts
  type ElectronFixtures (line 12) | type ElectronFixtures = {

FILE: apps/openwork-memos-integration/apps/desktop/e2e/pages/execution.page.ts
  class ExecutionPage (line 4) | class ExecutionPage {
    method constructor (line 5) | constructor(private page: Page) {}
    method statusBadge (line 7) | get statusBadge() {
    method cancelButton (line 11) | get cancelButton() {
    method thinkingIndicator (line 15) | get thinkingIndicator() {
    method followUpInput (line 19) | get followUpInput() {
    method stopButton (line 23) | get stopButton() {
    method permissionModal (line 27) | get permissionModal() {
    method allowButton (line 31) | get allowButton() {
    method denyButton (line 35) | get denyButton() {
    method questionOptions (line 40) | get questionOptions() {
    method customResponseInput (line 45) | get customResponseInput() {
    method backToOptionsButton (line 50) | get backToOptionsButton() {
    method selectQuestionOption (line 55) | async selectQuestionOption(index: number) {
    method waitForComplete (line 59) | async waitForComplete() {

FILE: apps/openwork-memos-integration/apps/desktop/e2e/pages/home.page.ts
  class HomePage (line 3) | class HomePage {
    method constructor (line 4) | constructor(private page: Page) {}
    method title (line 6) | get title() {
    method taskInput (line 10) | get taskInput() {
    method submitButton (line 14) | get submitButton() {
    method examplesToggle (line 18) | get examplesToggle() {
    method getExampleCard (line 22) | getExampleCard(index: number) {
    method expandExamples (line 26) | async expandExamples() {
    method enterTask (line 30) | async enterTask(text: string) {
    method submitTask (line 34) | async submitTask() {

FILE: apps/openwork-memos-integration/apps/desktop/e2e/pages/settings.page.ts
  class SettingsPage (line 4) | class SettingsPage {
    method constructor (line 5) | constructor(private page: Page) {}
    method providerGrid (line 9) | get providerGrid() {
    method providerSearchInput (line 13) | get providerSearchInput() {
    method showAllButton (line 17) | get showAllButton() {
    method hideButton (line 21) | get hideButton() {
    method getProviderCard (line 25) | getProviderCard(providerId: string) {
    method getProviderConnectedBadge (line 29) | getProviderConnectedBadge(providerId: string) {
    method connectionStatus (line 35) | get connectionStatus() {
    method disconnectButton (line 39) | get disconnectButton() {
    method connectButton (line 43) | get connectButton() {
    method modelSelector (line 49) | get modelSelector() {
    method modelSelectorError (line 53) | get modelSelectorError() {
    method apiKeyInput (line 59) | get apiKeyInput() {
    method apiKeyHelpLink (line 63) | get apiKeyHelpLink() {
    method bedrockAccessKeyTab (line 69) | get bedrockAccessKeyTab() {
    method bedrockAwsProfileTab (line 73) | get bedrockAwsProfileTab() {
    method bedrockAccessKeyIdInput (line 77) | get bedrockAccessKeyIdInput() {
    method bedrockSecretKeyInput (line 81) | get bedrockSecretKeyInput() {
    method bedrockSessionTokenInput (line 85) | get bedrockSessionTokenInput() {
    method bedrockProfileNameInput (line 89) | get bedrockProfileNameInput() {
    method bedrockRegionSelect (line 93) | get bedrockRegionSelect() {
    method ollamaServerUrlInput (line 99) | get ollamaServerUrlInput() {
    method ollamaConnectionError (line 103) | get ollamaConnectionError() {
    method litellmServerUrlInput (line 109) | get litellmServerUrlInput() {
    method litellmApiKeyInput (line 113) | get litellmApiKeyInput() {
    method openrouterFetchModelsButton (line 119) | get openrouterFetchModelsButton() {
    method debugModeToggle (line 125) | get debugModeToggle() {
    method settingsDialog (line 131) | get settingsDialog() {
    method doneButton (line 135) | get doneButton() {
    method closeWarning (line 139) | get closeWarning() {
    method closeAnywayButton (line 143) | get closeAnywayButton() {
    method sidebarSettingsButton (line 147) | get sidebarSettingsButton() {
    method navigateToSettings (line 153) | async navigateToSettings() {
    method selectProvider (line 158) | async selectProvider(providerId: string) {
    method searchProvider (line 164) | async searchProvider(query: string) {
    method clearSearch (line 168) | async clearSearch() {
    method toggleShowAll (line 172) | async toggleShowAll() {
    method enterApiKey (line 181) | async enterApiKey(key: string) {
    method clickConnect (line 185) | async clickConnect() {
    method clickDisconnect (line 189) | async clickDisconnect() {
    method selectModel (line 193) | async selectModel(modelId: string) {
    method toggleDebugMode (line 197) | async toggleDebugMode() {
    method closeDialog (line 201) | async closeDialog() {
    method pressEscapeToClose (line 205) | async pressEscapeToClose() {
    method selectBedrockAccessKeyTab (line 210) | async selectBedrockAccessKeyTab() {
    method selectBedrockAwsProfileTab (line 214) | async selectBedrockAwsProfileTab() {
    method enterBedrockAccessKeyCredentials (line 218) | async enterBedrockAccessKeyCredentials(accessKeyId: string, secretKey:...
    method enterBedrockProfileCredentials (line 226) | async enterBedrockProfileCredentials(profileName: string) {
    method selectBedrockRegion (line 230) | async selectBedrockRegion(region: string) {
    method enterOllamaServerUrl (line 235) | async enterOllamaServerUrl(url: string) {
    method enterLiteLLMServerUrl (line 240) | async enterLiteLLMServerUrl(url: string) {
    method enterLiteLLMApiKey (line 244) | async enterLiteLLMApiKey(key: string) {

FILE: apps/openwork-memos-integration/apps/desktop/e2e/utils/screenshots.ts
  type ScreenshotMetadata (line 17) | interface ScreenshotMetadata {
  type CaptureResult (line 26) | interface CaptureResult {
  function captureForAI (line 46) | async function captureForAI(
  function sanitizeFilename (line 102) | function sanitizeFilename(input: string): string {

FILE: apps/openwork-memos-integration/apps/desktop/scripts/after-pack.cjs
  constant NODE_VERSION (line 14) | const NODE_VERSION = '20.18.1';
  constant ARCH_MAP (line 20) | const ARCH_MAP = {
  constant PLATFORM_MAP (line 31) | const PLATFORM_MAP = {
  function getNodeDirName (line 40) | function getNodeDirName(platform, arch) {
  function copyNodeBinary (line 97) | async function copyNodeBinary(context, platform, arch) {
  constant NODEJS_EXCLUDE_DIRS (line 170) | const NODEJS_EXCLUDE_DIRS = ['include'];
  function copyDirRecursive (line 179) | function copyDirRecursive(src, dest, rootDest = dest, excludeDirs = []) {
  function resignMacApp (line 235) | async function resignMacApp(context) {

FILE: apps/openwork-memos-integration/apps/desktop/scripts/download-nodejs.cjs
  constant NODE_VERSION (line 17) | const NODE_VERSION = '20.18.1';
  constant BASE_URL (line 18) | const BASE_URL = `https://nodejs.org/dist/v${NODE_VERSION}`;
  constant PLATFORMS (line 20) | const PLATFORMS = [
  constant RESOURCES_DIR (line 35) | const RESOURCES_DIR = path.join(__dirname, '..', 'resources', 'nodejs');
  function downloadFile (line 40) | function downloadFile(url, destPath) {
  function verifyChecksum (line 92) | function verifyChecksum(filePath, expectedHash) {
  function extractArchive (line 109) | function extractArchive(archivePath, destDir, type) {
  function main (line 140) | async function main() {

FILE: apps/openwork-memos-integration/apps/desktop/scripts/patch-electron-name.cjs
  constant APP_NAME (line 8) | const APP_NAME = 'Openwork';

FILE: apps/openwork-memos-integration/apps/desktop/skills/ask-user-question/src/index.ts
  constant QUESTION_API_PORT (line 17) | const QUESTION_API_PORT = process.env.QUESTION_API_PORT || '9227';
  constant QUESTION_API_URL (line 18) | const QUESTION_API_URL = `http://localhost:${QUESTION_API_PORT}/question`;
  type QuestionOption (line 20) | interface QuestionOption {
  type AskUserQuestionInput (line 25) | interface AskUserQuestionInput {
  function main (line 187) | async function main() {

FILE: apps/openwork-memos-integration/apps/desktop/skills/dev-browser/scripts/start-relay.ts
  constant PORT (line 10) | const PORT = parseInt(process.env.PORT || "9224", 10);
  constant HOST (line 11) | const HOST = process.env.HOST || "127.0.0.1";
  function main (line 13) | async function main() {

FILE: apps/openwork-memos-integration/apps/desktop/skills/dev-browser/scripts/start-server.ts
  function getDataDir (line 12) | function getDataDir(): string {
  constant ACCOMPLISH_HTTP_PORT (line 34) | const ACCOMPLISH_HTTP_PORT = 9224;
  constant ACCOMPLISH_CDP_PORT (line 35) | const ACCOMPLISH_CDP_PORT = 9225;
  function installPlaywrightChromium (line 87) | function installPlaywrightChromium(): void {
  function startServer (line 123) | async function startServer(retry = false): Promise<void> {

FILE: apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/client.ts
  function fetchWithRetry (line 15) | async function fetchWithRetry(
  type WaitForPageLoadOptions (line 47) | interface WaitForPageLoadOptions {
  type WaitForPageLoadResult (line 61) | interface WaitForPageLoadResult {
  type PageLoadState (line 74) | interface PageLoadState {
  type PendingRequest (line 80) | interface PendingRequest {
  function waitForPageLoad (line 95) | async function waitForPageLoad(
  function getPageLoadState (line 155) | async function getPageLoadState(page: Page): Promise<PageLoadState> {
  type ServerInfo (line 244) | interface ServerInfo {
  type PageOptions (line 253) | interface PageOptions {
  type DevBrowserClient (line 258) | interface DevBrowserClient {
  function connect (line 281) | async function connect(serverUrl = "http://localhost:9224"): Promise<Dev...

FILE: apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/index.ts
  type DevBrowserServer (line 18) | interface DevBrowserServer {
  function fetchWithRetry (line 25) | async function fetchWithRetry(
  function withTimeout (line 47) | function withTimeout<T>(promise: Promise<T>, ms: number, message: string...
  function serve (line 56) | async function serve(options: ServeOptions = {}): Promise<DevBrowserServ...

FILE: apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/relay.ts
  type RelayOptions (line 18) | interface RelayOptions {
  type RelayServer (line 23) | interface RelayServer {
  type TargetInfo (line 29) | interface TargetInfo {
  type ConnectedTarget (line 37) | interface ConnectedTarget {
  type PlaywrightClient (line 43) | interface PlaywrightClient {
  type ExtensionCommandMessage (line 50) | interface ExtensionCommandMessage {
  type ExtensionResponseMessage (line 60) | interface ExtensionResponseMessage {
  type ExtensionEventMessage (line 66) | interface ExtensionEventMessage {
  type ExtensionMessage (line 75) | type ExtensionMessage =
  type CDPCommand (line 81) | interface CDPCommand {
  type CDPResponse (line 88) | interface CDPResponse {
  type CDPEvent (line 95) | interface CDPEvent {
  function serveRelay (line 105) | async function serveRelay(options: RelayOptions = {}): Promise<RelayServ...

FILE: apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/snapshot/__tests__/snapshot.test.ts
  function setContent (line 28) | async function setContent(html: string): Promise<void> {
  function getSnapshot (line 32) | async function getSnapshot(): Promise<string> {
  function selectRef (line 45) | async function selectRef(ref: string): Promise<unknown> {

FILE: apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/snapshot/browser-script.ts
  function getSnapshotScript (line 25) | function getSnapshotScript(): string {
  function getDomUtilsCode (line 52) | function getDomUtilsCode(): string {
  function getYamlCode (line 163) | function getYamlCode(): string {
  function getRoleUtilsCode (line 206) | function getRoleUtilsCode(): string {
  function getAriaSnapshotCode (line 628) | function getAriaSnapshotCode(): string {
  function clearSnapshotScriptCache (line 875) | function clearSnapshotScriptCache(): void {

FILE: apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/types.ts
  type ServeOptions (line 3) | interface ServeOptions {
  type ViewportSize (line 13) | interface ViewportSize {
  type GetPageRequest (line 18) | interface GetPageRequest {
  type GetPageResponse (line 24) | interface GetPageResponse {
  type ListPagesResponse (line 30) | interface ListPagesResponse {
  type ServerInfoResponse (line 34) | interface ServerInfoResponse {

FILE: apps/openwork-memos-integration/apps/desktop/skills/file-permission/src/index.ts
  constant PERMISSION_API_PORT (line 18) | const PERMISSION_API_PORT = process.env.PERMISSION_API_PORT || '9226';
  constant PERMISSION_API_URL (line 19) | const PERMISSION_API_URL = `http://localhost:${PERMISSION_API_PORT}/perm...
  type FilePermissionInput (line 21) | interface FilePermissionInput {
  function main (line 129) | async function main() {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/config.ts
  constant PRODUCTION_API_URL (line 3) | const PRODUCTION_API_URL = 'https://lite.accomplish.ai';
  type DesktopConfig (line 12) | type DesktopConfig = z.infer<typeof desktopConfigSchema>;
  function getDesktopConfig (line 16) | function getDesktopConfig(): DesktopConfig {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/index.ts
  constant MAIN_DIST (line 62) | const MAIN_DIST = path.join(process.env.APP_ROOT, 'dist-electron');
  constant RENDERER_DIST (line 63) | const RENDERER_DIST = path.join(process.env.APP_ROOT, 'dist');
  constant VITE_DEV_SERVER_URL (line 64) | const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL;
  function getPreloadPath (line 69) | function getPreloadPath(): string {
  function createWindow (line 73) | function createWindow() {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/ipc/handlers.ts
  constant MAX_TEXT_LENGTH (line 85) | const MAX_TEXT_LENGTH = 8000;
  constant ALLOWED_API_KEY_PROVIDERS (line 86) | const ALLOWED_API_KEY_PROVIDERS = new Set(['anthropic', 'openai', 'openr...
  constant API_KEY_VALIDATION_TIMEOUT_MS (line 87) | const API_KEY_VALIDATION_TIMEOUT_MS = 15000;
  type OllamaModel (line 89) | interface OllamaModel {
  function fetchWithTimeout (line 98) | async function fetchWithTimeout(
  constant MESSAGE_BATCH_DELAY_MS (line 115) | const MESSAGE_BATCH_DELAY_MS = 50;
  type MessageBatcher (line 118) | interface MessageBatcher {
  function createMessageBatcher (line 127) | function createMessageBatcher(
  function queueMessage (line 162) | function queueMessage(
  function flushAndCleanupBatcher (line 185) | function flushAndCleanupBatcher(taskId: string): void {
  function assertTrustedWindow (line 193) | function assertTrustedWindow(window: BrowserWindow | null): BrowserWindow {
  function sanitizeString (line 206) | function sanitizeString(input: unknown, field: string, maxLength = MAX_T...
  function applyMemoryContext (line 220) | function applyMemoryContext(config: TaskConfig, memoryContext: string | ...
  function validateTaskConfig (line 238) | function validateTaskConfig(config: TaskConfig): TaskConfig {
  function isE2ESkipAuthEnabled (line 275) | function isE2ESkipAuthEnabled(): boolean {
  function handle (line 283) | function handle<Args extends unknown[], ReturnType = unknown>(
  function registerIPCHandlers (line 300) | function registerIPCHandlers(): void {
  function createTaskId (line 1552) | function createTaskId(): string {
  function createMessageId (line 1556) | function createMessageId(): string {
  function extractScreenshots (line 1564) | function extractScreenshots(output: string): {
  function sanitizeToolOutput (line 1612) | function sanitizeToolOutput(text: string, isError: boolean): string {
  function toTaskMessage (line 1657) | function toTaskMessage(message: OpenCodeMessage): TaskMessage | null {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/ipc/validation.ts
  function validate (line 30) | function validate<TSchema extends z.ZodTypeAny>(
  function normalizeIpcError (line 42) | function normalizeIpcError(error: unknown): Error {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/opencode/adapter.ts
  class OpenCodeCliNotFoundError (line 29) | class OpenCodeCliNotFoundError extends Error {
    method constructor (line 30) | constructor() {
  function isOpenCodeCliInstalled (line 41) | async function isOpenCodeCliInstalled(): Promise<boolean> {
  function getOpenCodeCliVersion (line 48) | async function getOpenCodeCliVersion(): Promise<string | null> {
  type OpenCodeAdapterEvents (line 52) | interface OpenCodeAdapterEvents {
  class OpenCodeAdapter (line 63) | class OpenCodeAdapter extends EventEmitter<OpenCodeAdapterEvents> {
    method constructor (line 77) | constructor(taskId?: string) {
    method startTask (line 87) | async startTask(config: TaskConfig): Promise<Task> {
    method resumeSession (line 222) | async resumeSession(sessionId: string, prompt: string): Promise<Task> {
    method sendResponse (line 233) | async sendResponse(response: string): Promise<void> {
    method cancelTask (line 245) | async cancelTask(): Promise<void> {
    method interruptTask (line 258) | async interruptTask(): Promise<void> {
    method getSessionId (line 275) | getSessionId(): string | null {
    method getTaskId (line 282) | getTaskId(): string | null {
    method isAdapterDisposed (line 289) | isAdapterDisposed(): boolean {
    method dispose (line 297) | dispose(): void {
    method buildEnvironment (line 333) | private async buildEnvironment(): Promise<NodeJS.ProcessEnv> {
    method buildCliArgs (line 445) | private async buildCliArgs(config: TaskConfig): Promise<string[]> {
    method setupStreamParsing (line 486) | private setupStreamParsing(): void {
    method handleMessage (line 500) | private handleMessage(message: OpenCodeMessage): void {
    method handleAskUserQuestion (line 643) | private handleAskUserQuestion(input: AskUserQuestionInput): void {
    method handleProcessExit (line 663) | private handleProcessExit(code: number | null): void {
    method generateTaskId (line 689) | private generateTaskId(): string {
    method generateMessageId (line 693) | private generateMessageId(): string {
    method generateRequestId (line 697) | private generateRequestId(): string {
    method getPlatformShell (line 710) | private getPlatformShell(): string {
    method getShellArgs (line 741) | private getShellArgs(command: string): string[] {
  type AskUserQuestionInput (line 752) | interface AskUserQuestionInput {
  function createAdapter (line 765) | function createAdapter(taskId?: string): OpenCodeAdapter {
  function getOpenCodeAdapter (line 779) | function getOpenCodeAdapter(): OpenCodeAdapter {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/opencode/cli-path.ts
  function getNvmOpenCodePaths (line 9) | function getNvmOpenCodePaths(): string[] {
  function getOpenCodeCliPath (line 37) | function getOpenCodeCliPath(): { command: string; args: string[] } {
  function isOpenCodeOnPath (line 104) | function isOpenCodeOnPath(): boolean {
  function isOpenCodeBundled (line 117) | function isOpenCodeBundled(): boolean {
  function getBundledOpenCodeVersion (line 178) | function getBundledOpenCodeVersion(): string | null {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/opencode/config-generator.ts
  constant ACCOMPLISH_AGENT_NAME (line 12) | const ACCOMPLISH_AGENT_NAME = 'accomplish';
  function getSkillsPath (line 26) | function getSkillsPath(): string {
  function getOpenCodeConfigDir (line 41) | function getOpenCodeConfigDir(): string {
  constant ACCOMPLISH_SYSTEM_PROMPT_TEMPLATE (line 49) | const ACCOMPLISH_SYSTEM_PROMPT_TEMPLATE = `<identity>
  type AgentConfig (line 318) | interface AgentConfig {
  type McpServerConfig (line 324) | interface McpServerConfig {
  type OllamaProviderModelConfig (line 333) | interface OllamaProviderModelConfig {
  type OllamaProviderConfig (line 338) | interface OllamaProviderConfig {
  type BedrockProviderConfig (line 347) | interface BedrockProviderConfig {
  type OpenRouterProviderModelConfig (line 354) | interface OpenRouterProviderModelConfig {
  type OpenRouterProviderConfig (line 359) | interface OpenRouterProviderConfig {
  type LiteLLMProviderModelConfig (line 368) | interface LiteLLMProviderModelConfig {
  type LiteLLMProviderConfig (line 373) | interface LiteLLMProviderConfig {
  type ZaiProviderModelConfig (line 383) | interface ZaiProviderModelConfig {
  type ZaiProviderConfig (line 388) | interface ZaiProviderConfig {
  type ProviderConfig (line 397) | type ProviderConfig = OllamaProviderConfig | BedrockProviderConfig | Ope...
  type OpenCodeConfig (line 399) | interface OpenCodeConfig {
  function generateOpenCodeConfig (line 415) | async function generateOpenCodeConfig(systemPromptAppend?: string): Prom...
  function getOpenCodeConfigPath (line 660) | function getOpenCodeConfigPath(): string {
  function getOpenCodeAuthPath (line 668) | function getOpenCodeAuthPath(): string {
  function syncApiKeysToOpenCodeAuth (line 680) | async function syncApiKeysToOpenCodeAuth(): Promise<void> {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/opencode/stream-parser.ts
  type StreamParserEvents (line 4) | interface StreamParserEvents {
  constant MAX_BUFFER_SIZE (line 10) | const MAX_BUFFER_SIZE = 10 * 1024 * 1024;
  class StreamParser (line 15) | class StreamParser extends EventEmitter<StreamParserEvents> {
    method feed (line 21) | feed(chunk: string): void {
    method parseBuffer (line 37) | private parseBuffer(): void {
    method isTerminalDecoration (line 54) | private isTerminalDecoration(line: string): boolean {
    method parseLine (line 72) | private parseLine(line: string): void {
    method flush (line 132) | flush(): void {
    method reset (line 142) | reset(): void {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/opencode/task-manager.ts
  function isSystemChromeInstalled (line 28) | function isSystemChromeInstalled(): boolean {
  function isPlaywrightInstalled (line 47) | function isPlaywrightInstalled(): boolean {
  function installPlaywrightChromium (line 78) | async function installPlaywrightChromium(
  function ensureDevBrowserServer (line 149) | async function ensureDevBrowserServer(
  type TaskCallbacks (line 208) | interface TaskCallbacks {
  type ManagedTask (line 221) | interface ManagedTask {
  type QueuedTask (line 232) | interface QueuedTask {
  constant DEFAULT_MAX_CONCURRENT_TASKS (line 243) | const DEFAULT_MAX_CONCURRENT_TASKS = 10;
  class TaskManager (line 251) | class TaskManager {
    method constructor (line 256) | constructor(options?: { maxConcurrentTasks?: number }) {
    method startTask (line 264) | async startTask(
    method queueTask (line 293) | private queueTask(
    method executeTask (line 328) | private async executeTask(
    method processQueue (line 430) | private async processQueue(): Promise<void> {
    method cancelTask (line 455) | async cancelTask(taskId: string): Promise<void> {
    method interruptTask (line 487) | async interruptTask(taskId: string): Promise<void> {
    method cancelQueuedTask (line 502) | cancelQueuedTask(taskId: string): boolean {
    method hasRunningTask (line 516) | hasRunningTask(): boolean {
    method isTaskQueued (line 523) | isTaskQueued(taskId: string): boolean {
    method getQueuePosition (line 530) | getQueuePosition(taskId: string): number {
    method getQueueLength (line 538) | getQueueLength(): number {
    method sendResponse (line 545) | async sendResponse(taskId: string, response: string): Promise<void> {
    method getSessionId (line 557) | getSessionId(taskId: string): string | null {
    method hasActiveTask (line 565) | hasActiveTask(taskId: string): boolean {
    method getActiveTaskCount (line 572) | getActiveTaskCount(): number {
    method getActiveTaskIds (line 579) | getActiveTaskIds(): string[] {
    method getActiveTaskId (line 587) | getActiveTaskId(): string | null {
    method cleanupTask (line 595) | private cleanupTask(taskId: string): void {
    method dispose (line 609) | dispose(): void {
  function getTaskManager (line 634) | function getTaskManager(): TaskManager {
  function disposeTaskManager (line 645) | function disposeTaskManager(): void {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/permission-api.ts
  constant PERMISSION_API_PORT (line 13) | const PERMISSION_API_PORT = 9226;
  constant QUESTION_API_PORT (line 14) | const QUESTION_API_PORT = 9227;
  type PendingPermission (line 16) | interface PendingPermission {
  type PendingQuestion (line 21) | interface PendingQuestion {
  function initPermissionApi (line 39) | function initPermissionApi(
  function resolvePermission (line 51) | function resolvePermission(requestId: string, allowed: boolean): boolean {
  function resolveQuestion (line 67) | function resolveQuestion(
  function generateRequestId (line 85) | function generateRequestId(): string {
  function generateQuestionRequestId (line 92) | function generateQuestionRequestId(): string {
  function startPermissionApiServer (line 99) | function startPermissionApiServer(): http.Server {
  function startQuestionApiServer (line 228) | function startQuestionApiServer(): http.Server {
  function isFilePermissionRequest (line 347) | function isFilePermissionRequest(requestId: string): boolean {
  function isQuestionRequest (line 354) | function isQuestionRequest(requestId: string): boolean {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/services/memory.ts
  constant DEFAULT_BASE_URL (line 5) | const DEFAULT_BASE_URL = 'https://memos.memtensor.cn/api/openmem/v1';
  constant DEFAULT_TOP_K (line 6) | const DEFAULT_TOP_K = 5;
  constant DEFAULT_TIMEOUT_MS (line 7) | const DEFAULT_TIMEOUT_MS = 6000;
  constant DEFAULT_MAX_CONTEXT_LENGTH (line 8) | const DEFAULT_MAX_CONTEXT_LENGTH = 3000;
  constant DEFAULT_MAX_MESSAGE_COUNT (line 9) | const DEFAULT_MAX_MESSAGE_COUNT = 8;
  constant DEFAULT_MAX_MESSAGE_LENGTH (line 10) | const DEFAULT_MAX_MESSAGE_LENGTH = 2000;
  type MemoryMessage (line 12) | interface MemoryMessage {
  type MemoryConfig (line 17) | interface MemoryConfig {
  function getEnv (line 30) | function getEnv(): Record<string, string | undefined> {
  function resolveMemoryConfig (line 35) | function resolveMemoryConfig(): MemoryConfig {
  function resolveMemoryUserId (line 70) | function resolveMemoryUserId(): string {
  function buildUrl (line 76) | function buildUrl(baseUrl: string, path: string): string {
  function buildAuthHeaders (line 82) | function buildAuthHeaders(config: MemoryConfig): Record<string, string> {
  function fetchWithTimeout (line 98) | async function fetchWithTimeout(
  function normalizeText (line 113) | function normalizeText(value: unknown): string | null {
  function extractMemoryTexts (line 119) | function extractMemoryTexts(payload: unknown): string[] {
  function formatMemoryContext (line 194) | function formatMemoryContext(entries: string[], maxLength: number): stri...
  function toMemoryMessages (line 209) | function toMemoryMessages(messages: TaskMessage[], taskPrompt?: string, ...
  function getMemoryContextForPrompt (line 238) | async function getMemoryContextForPrompt(
  function rememberTask (line 281) | async function rememberTask(task: {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/services/summarizer.ts
  constant SUMMARY_PROMPT (line 10) | const SUMMARY_PROMPT = `Generate a very short title (3-5 words max) that...
  function generateTaskSummary (line 21) | async function generateTaskSummary(prompt: string): Promise<string> {
  function callProvider (line 46) | async function callProvider(
  function callAnthropic (line 65) | async function callAnthropic(apiKey: string, prompt: string): Promise<st...
  function callOpenAI (line 96) | async function callOpenAI(apiKey: string, prompt: string): Promise<strin...
  function callGoogle (line 126) | async function callGoogle(apiKey: string, prompt: string): Promise<strin...
  function callXAI (line 158) | async function callXAI(apiKey: string, prompt: string): Promise<string> {
  function cleanSummary (line 191) | function cleanSummary(text: string): string {
  function truncatePrompt (line 206) | function truncatePrompt(prompt: string, maxLength = 30): string {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/store/appSettings.ts
  type AppSettingsSchema (line 8) | interface AppSettingsSchema {
  function getDebugMode (line 41) | function getDebugMode(): boolean {
  function setDebugMode (line 48) | function setDebugMode(enabled: boolean): void {
  function getOnboardingComplete (line 55) | function getOnboardingComplete(): boolean {
  function setOnboardingComplete (line 62) | function setOnboardingComplete(complete: boolean): void {
  function getSelectedModel (line 69) | function getSelectedModel(): SelectedModel | null {
  function setSelectedModel (line 76) | function setSelectedModel(model: SelectedModel): void {
  function getOllamaConfig (line 83) | function getOllamaConfig(): OllamaConfig | null {
  function setOllamaConfig (line 90) | function setOllamaConfig(config: OllamaConfig | null): void {
  function getLiteLLMConfig (line 97) | function getLiteLLMConfig(): LiteLLMConfig | null {
  function setLiteLLMConfig (line 104) | function setLiteLLMConfig(config: LiteLLMConfig | null): void {
  function getMemoryUserId (line 111) | function getMemoryUserId(): string {
  function getAppSettings (line 123) | function getAppSettings(): AppSettingsSchema {
  function clearAppSettings (line 138) | function clearAppSettings(): void {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/store/freshInstallCleanup.ts
  type InstallMarker (line 21) | interface InstallMarker {
  function getKnownUserDataDirs (line 30) | function getKnownUserDataDirs(): string[] {
  function getMarkerPath (line 46) | function getMarkerPath(): string {
  function getAppBundleMtime (line 55) | function getAppBundleMtime(): Date | null {
  function readInstallMarker (line 86) | function readInstallMarker(): InstallMarker | null {
  function writeInstallMarker (line 104) | function writeInstallMarker(marker: InstallMarker): void {
  function hasExistingUserData (line 124) | function hasExistingUserData(): boolean {
  function clearPreviousInstallData (line 136) | function clearPreviousInstallData(): void {
  function checkAndCleanupFreshInstall (line 205) | async function checkAndCleanupFreshInstall(): Promise<boolean> {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/store/providerSettings.ts
  constant DEFAULT_SETTINGS (line 6) | const DEFAULT_SETTINGS: ProviderSettings = {
  function getProviderSettings (line 17) | function getProviderSettings(): ProviderSettings {
  function setActiveProvider (line 25) | function setActiveProvider(providerId: ProviderId | null): void {
  function getActiveProviderId (line 29) | function getActiveProviderId(): ProviderId | null {
  function getConnectedProvider (line 33) | function getConnectedProvider(providerId: ProviderId): ConnectedProvider...
  function setConnectedProvider (line 38) | function setConnectedProvider(providerId: ProviderId, provider: Connecte...
  function removeConnectedProvider (line 46) | function removeConnectedProvider(providerId: ProviderId): void {
  function updateProviderModel (line 57) | function updateProviderModel(providerId: ProviderId, modelId: string | n...
  function setProviderDebugMode (line 67) | function setProviderDebugMode(enabled: boolean): void {
  function getProviderDebugMode (line 71) | function getProviderDebugMode(): boolean {
  function clearProviderSettings (line 75) | function clearProviderSettings(): void {
  function getActiveProviderModel (line 83) | function getActiveProviderModel(): { provider: ProviderId; model: string...
  function hasReadyProvider (line 110) | function hasReadyProvider(): boolean {
  function getConnectedProviderIds (line 120) | function getConnectedProviderIds(): ProviderId[] {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/store/secureStorage.ts
  type SecureStorageSchema (line 23) | interface SecureStorageSchema {
  function getSecureStore (line 34) | function getSecureStore(): Store<SecureStorageSchema> {
  function getSalt (line 48) | function getSalt(): Buffer {
  function getDerivedKey (line 68) | function getDerivedKey(): Buffer {
  function encryptValue (line 100) | function encryptValue(value: string): string {
  function decryptValue (line 118) | function decryptValue(encryptedData: string): string | null {
  function storeApiKey (line 148) | function storeApiKey(provider: string, apiKey: string): void {
  function getApiKey (line 159) | function getApiKey(provider: string): string | null {
  function deleteApiKey (line 175) | function deleteApiKey(provider: string): boolean {
  type ApiKeyProvider (line 190) | type ApiKeyProvider = 'anthropic' | 'openai' | 'openrouter' | 'google' |...
  function getAllApiKeys (line 195) | async function getAllApiKeys(): Promise<Record<ApiKeyProvider, string | ...
  function storeBedrockCredentials (line 215) | function storeBedrockCredentials(credentials: string): void {
  function getBedrockCredentials (line 222) | function getBedrockCredentials(): Record<string, string> | null {
  function hasAnyApiKey (line 235) | async function hasAnyApiKey(): Promise<boolean> {
  function listStoredCredentials (line 244) | function listStoredCredentials(): Array<{ account: string; password: str...
  function clearSecureStorage (line 265) | function clearSecureStorage(): void {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/store/taskHistory.ts
  type StoredTask (line 7) | interface StoredTask {
  type TaskHistorySchema (line 20) | interface TaskHistorySchema {
  constant PERSIST_DEBOUNCE_MS (line 33) | const PERSIST_DEBOUNCE_MS = 250;
  function getCurrentTasks (line 37) | function getCurrentTasks(): StoredTask[] {
  function schedulePersist (line 41) | function schedulePersist(tasks: StoredTask[]): void {
  function flushPendingTasks (line 59) | function flushPendingTasks(): void {
  function getTasks (line 73) | function getTasks(): StoredTask[] {
  function getTask (line 80) | function getTask(taskId: string): StoredTask | undefined {
  function saveTask (line 88) | function saveTask(task: Task): void {
  function updateTaskStatus (line 124) | function updateTaskStatus(
  function addTaskMessage (line 144) | function addTaskMessage(taskId: string, message: TaskMessage): void {
  function updateTaskSessionId (line 157) | function updateTaskSessionId(taskId: string, sessionId: string): void {
  function updateTaskSummary (line 170) | function updateTaskSummary(taskId: string, summary: string): void {
  function deleteTask (line 183) | function deleteTask(taskId: string): void {
  function clearHistory (line 192) | function clearHistory(): void {
  function setMaxHistoryItems (line 199) | function setMaxHistoryItems(max: number): void {
  function clearTaskHistoryStore (line 214) | function clearTaskHistoryStore(): void {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/test-utils/mock-task-flow.ts
  type MockScenario (line 13) | type MockScenario =
  type MockTaskConfig (line 21) | interface MockTaskConfig {
  function isMockTaskEventsEnabled (line 37) | function isMockTaskEventsEnabled(): boolean {
  constant SCENARIO_KEYWORDS (line 52) | const SCENARIO_KEYWORDS: Record<MockScenario, string[]> = {
  function detectScenarioFromPrompt (line 65) | function detectScenarioFromPrompt(prompt: string): MockScenario {
  function createMessageId (line 93) | function createMessageId(): string {
  function sleep (line 97) | function sleep(ms: number): Promise<void> {
  function executeMockTaskFlow (line 109) | async function executeMockTaskFlow(
  function executeScenario (line 151) | async function executeScenario(
  function executeSuccessScenario (line 184) | async function executeSuccessScenario(
  function executeToolScenario (line 211) | async function executeToolScenario(
  function executePermissionScenario (line 260) | function executePermissionScenario(
  function executeQuestionScenario (line 278) | function executeQuestionScenario(
  function executeErrorScenario (line 299) | function executeErrorScenario(
  function executeInterruptedScenario (line 313) | async function executeInterruptedScenario(
  function createMockTask (line 347) | function createMockTask(taskId: string, prompt: string): Task {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/utils/bundled-node.ts
  constant NODE_VERSION (line 12) | const NODE_VERSION = '20.18.1';
  type BundledNodePaths (line 14) | interface BundledNodePaths {
  function getBundledNodePaths (line 35) | function getBundledNodePaths(): BundledNodePaths | null {
  function isBundledNodeAvailable (line 71) | function isBundledNodeAvailable(): boolean {
  function getNodePath (line 87) | function getNodePath(): string {
  function getNpmPath (line 104) | function getNpmPath(): string {
  function getNpxPath (line 120) | function getNpxPath(): string {
  function logBundledNodeInfo (line 134) | function logBundledNodeInfo(): void {

FILE: apps/openwork-memos-integration/apps/desktop/src/main/utils/system-path.ts
  function getNvmNodePaths (line 22) | function getNvmNodePaths(): string[] {
  function getFnmNodePaths (line 52) | function getFnmNodePaths(): string[] {
  function getCommonNodePaths (line 81) | function getCommonNodePaths(): string[] {
  function getSystemPathFromPathHelper (line 117) | function getSystemPathFromPathHelper(): string | null {
  function getExtendedNodePath (line 153) | function getExtendedNodePath(basePath?: string): string {
  function findCommandInPath (line 206) | function findCommandInPath(command: string, searchPath: string): string ...

FILE: apps/openwork-memos-integration/apps/desktop/src/preload/index.ts
  type AccomplishAPI (line 220) | type AccomplishAPI = typeof accomplishAPI;

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/App.tsx
  type AppStatus (line 20) | type AppStatus = 'loading' | 'ready' | 'error';
  function App (line 22) | function App() {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/TaskLauncher/TaskLauncher.tsx
  function TaskLauncher (line 15) | function TaskLauncher() {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/TaskLauncher/TaskLauncherItem.tsx
  type TaskLauncherItemProps (line 7) | interface TaskLauncherItemProps {
  function formatRelativeDate (line 13) | function formatRelativeDate(dateString: string): string {
  function getStatusIcon (line 26) | function getStatusIcon(status: Task['status']) {
  function TaskLauncherItem (line 42) | function TaskLauncherItem({ task, isSelected, onClick }: TaskLauncherIte...

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/history/TaskHistory.tsx
  type TaskHistoryProps (line 6) | interface TaskHistoryProps {
  function TaskHistory (line 11) | function TaskHistory({ limit, showTitle = true }: TaskHistoryProps) {
  function TaskHistoryItem (line 70) | function TaskHistoryItem({
  function getTimeAgo (line 121) | function getTimeAgo(dateString: string): string {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/landing/TaskInputBar.tsx
  type TaskInputBarProps (line 8) | interface TaskInputBarProps {
  function TaskInputBar (line 19) | function TaskInputBar({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/layout/ConversationListItem.tsx
  type ConversationListItemProps (line 9) | interface ConversationListItemProps {
  function ConversationListItem (line 13) | function ConversationListItem({ task }: ConversationListItemProps) {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/layout/Header.tsx
  function Header (line 3) | function Header() {
  function NavLink (line 40) | function NavLink({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/layout/SettingsDialog.tsx
  type SettingsDialogProps (line 17) | interface SettingsDialogProps {
  constant API_KEY_PROVIDERS (line 24) | const API_KEY_PROVIDERS = [
  type ProviderId (line 35) | type ProviderId = typeof API_KEY_PROVIDERS[number]['id'];
  constant OPENROUTER_PROVIDER_PRIORITY (line 38) | const OPENROUTER_PROVIDER_PRIORITY = [
  constant LITELLM_PROVIDER_PRIORITY (line 52) | const LITELLM_PROVIDER_PRIORITY = [
  function SettingsDialog (line 65) | function SettingsDialog({ open, onOpenChange, onApiKeySaved }: SettingsD...

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/layout/Sidebar.tsx
  function Sidebar (line 17) | function Sidebar() {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/ProviderCard.tsx
  constant PROVIDER_LOGOS (line 22) | const PROVIDER_LOGOS: Record<ProviderId, string> = {
  type ProviderCardProps (line 35) | interface ProviderCardProps {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/ProviderGrid.tsx
  constant PROVIDER_ORDER (line 9) | const PROVIDER_ORDER: ProviderId[] = [
  type ProviderGridProps (line 22) | interface ProviderGridProps {
  function ProviderGrid (line 30) | function ProviderGrid({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/ProviderSettingsPanel.tsx
  type ProviderSettingsPanelProps (line 13) | interface ProviderSettingsPanelProps {
  function ProviderSettingsPanel (line 22) | function ProviderSettingsPanel({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/hooks/useProviderSettings.ts
  function useProviderSettings (line 11) | function useProviderSettings() {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/providers/BedrockProviderForm.tsx
  type BedrockProviderFormProps (line 19) | interface BedrockProviderFormProps {
  function BedrockProviderForm (line 27) | function BedrockProviderForm({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/providers/ClassicProviderForm.tsx
  constant PROVIDER_LOGOS (line 23) | const PROVIDER_LOGOS: Record<string, string> = {
  type ClassicProviderFormProps (line 32) | interface ClassicProviderFormProps {
  function ClassicProviderForm (line 41) | function ClassicProviderForm({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/providers/LiteLLMProviderForm.tsx
  type LiteLLMProviderFormProps (line 16) | interface LiteLLMProviderFormProps {
  function LiteLLMProviderForm (line 24) | function LiteLLMProviderForm({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/providers/OllamaProviderForm.tsx
  type OllamaProviderFormProps (line 17) | interface OllamaProviderFormProps {
  function OllamaProviderForm (line 25) | function OllamaProviderForm({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/providers/OpenRouterProviderForm.tsx
  type OpenRouterProviderFormProps (line 18) | interface OpenRouterProviderFormProps {
  function OpenRouterProviderForm (line 26) | function OpenRouterProviderForm({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/shared/ApiKeyInput.tsx
  type ApiKeyInputProps (line 3) | interface ApiKeyInputProps {
  function ApiKeyInput (line 13) | function ApiKeyInput({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/shared/ConnectButton.tsx
  type ConnectButtonProps (line 5) | interface ConnectButtonProps {
  function ConnectButton (line 11) | function ConnectButton({ onClick, connecting, disabled }: ConnectButtonP...

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/shared/ConnectedControls.tsx
  type ConnectedControlsProps (line 5) | interface ConnectedControlsProps {
  function ConnectedControls (line 9) | function ConnectedControls({ onDisconnect }: ConnectedControlsProps) {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/shared/ConnectionStatus.tsx
  type ConnectionStatusProps (line 5) | interface ConnectionStatusProps {
  function ConnectionStatus (line 10) | function ConnectionStatus({ status, onDisconnect }: ConnectionStatusProp...

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/shared/FormError.tsx
  type FormErrorProps (line 3) | interface FormErrorProps {
  function FormError (line 7) | function FormError({ error }: FormErrorProps) {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/shared/ModelSelector.tsx
  type Model (line 5) | interface Model {
  type ModelSelectorProps (line 10) | interface ModelSelectorProps {
  function ModelSelector (line 20) | function ModelSelector({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/shared/ProviderFormHeader.tsx
  type ProviderFormHeaderProps (line 3) | interface ProviderFormHeaderProps {
  function ProviderFormHeader (line 8) | function ProviderFormHeader({ logoSrc, providerName }: ProviderFormHeade...

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/settings/shared/RegionSelector.tsx
  constant AWS_REGIONS (line 3) | const AWS_REGIONS = [
  type RegionSelectorProps (line 19) | interface RegionSelectorProps {
  function RegionSelector (line 24) | function RegionSelector({ value, onChange }: RegionSelectorProps) {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/avatar.tsx
  function Avatar (line 8) | function Avatar({
  function AvatarImage (line 24) | function AvatarImage({
  function AvatarFallback (line 37) | function AvatarFallback({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/badge.tsx
  function Badge (line 28) | function Badge({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/button.tsx
  function Button (line 39) | function Button({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<'div'>) {
  function CardHeader (line 18) | function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
  function CardTitle (line 31) | function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function CardDescription (line 41) | function CardDescription({ className, ...props }: React.ComponentProps<'...
  function CardAction (line 51) | function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
  function CardContent (line 64) | function CardContent({ className, ...props }: React.ComponentProps<'div'...
  function CardFooter (line 74) | function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/dialog.tsx
  function Dialog (line 11) | function Dialog({
  function DialogTrigger (line 17) | function DialogTrigger({
  function DialogPortal (line 25) | function DialogPortal({
  function DialogClose (line 33) | function DialogClose({
  function DialogHeader (line 95) | function DialogHeader({
  function DialogFooter (line 108) | function DialogFooter({
  function DialogTitle (line 124) | function DialogTitle({
  function DialogDescription (line 137) | function DialogDescription({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/dropdown-menu.tsx
  function DropdownMenu (line 9) | function DropdownMenu({
  function DropdownMenuPortal (line 15) | function DropdownMenuPortal({
  function DropdownMenuTrigger (line 23) | function DropdownMenuTrigger({
  function DropdownMenuContent (line 34) | function DropdownMenuContent({
  function DropdownMenuGroup (line 54) | function DropdownMenuGroup({
  function DropdownMenuItem (line 62) | function DropdownMenuItem({
  function DropdownMenuCheckboxItem (line 85) | function DropdownMenuCheckboxItem({
  function DropdownMenuRadioGroup (line 111) | function DropdownMenuRadioGroup({
  function DropdownMenuRadioItem (line 122) | function DropdownMenuRadioItem({
  function DropdownMenuLabel (line 146) | function DropdownMenuLabel({
  function DropdownMenuSeparator (line 163) | function DropdownMenuSeparator({
  function DropdownMenuShortcut (line 176) | function DropdownMenuShortcut({
  function DropdownMenuSub (line 189) | function DropdownMenuSub({
  function DropdownMenuSubTrigger (line 195) | function DropdownMenuSubTrigger({
  function DropdownMenuSubContent (line 219) | function DropdownMenuSubContent({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<'inpu...

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/label.tsx
  function Label (line 13) | function Label({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/scroll-area.tsx
  type ScrollAreaProps (line 4) | interface ScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/separator.tsx
  function Separator (line 8) | function Separator({

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/streaming-text.tsx
  type StreamingTextProps (line 9) | interface StreamingTextProps {
  function StreamingText (line 23) | function StreamingText({
  function useStreamingState (line 110) | function useStreamingState(

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/components/ui/textarea.tsx
  function Textarea (line 5) | function Textarea({ className, ...props }: React.ComponentProps<'textare...

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/pages/Execution.tsx
  type DebugLogEntry (line 23) | interface DebugLogEntry {
  constant TOOL_PROGRESS_MAP (line 41) | const TOOL_PROGRESS_MAP: Record<string, { label: string; icon: typeof Fi...
  function debounce (line 57) | function debounce<T extends (...args: unknown[]) => void>(fn: T, ms: num...
  function getOperationBadgeClasses (line 66) | function getOperationBadgeClasses(operation?: string): string {
  function isDeleteOperation (line 79) | function isDeleteOperation(request: { type: string; fileOperation?: stri...
  function getDisplayFilePaths (line 84) | function getDisplayFilePaths(request: { filePath?: string; filePaths?: s...
  function ExecutionPage (line 94) | function ExecutionPage() {
  type MessageBubbleProps (line 1133) | interface MessageBubbleProps {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/pages/History.tsx
  function HistoryPage (line 4) | function HistoryPage() {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/pages/Home.tsx
  constant USE_CASE_EXAMPLES (line 26) | const USE_CASE_EXAMPLES = [
  function HomePage (line 83) | function HomePage() {

FILE: apps/openwork-memos-integration/apps/desktop/src/renderer/stores/taskStore.ts
  type TaskUpdateBatchEvent (line 14) | interface TaskUpdateBatchEvent {
  type SetupProgressEvent (line 20) | interface SetupProgressEvent {
  type TaskState (line 26) | interface TaskState {
  function createMessageId (line 67) | function createMessageId(): string {

FILE: apps/openwork-memos-integration/apps/desktop/vite.config.ts
  method onstart (line 17) | onstart({ startup }) {
  method onstart (line 32) | onstart({ reload }) {

FILE: apps/openwork-memos-integration/packages/shared/src/types/auth.ts
  type User (line 5) | interface User {
  type Session (line 14) | interface Session {
  type AuthTokens (line 23) | interface AuthTokens {
  type ApiKeyConfig (line 29) | interface ApiKeyConfig {
  type BedrockAccessKeyCredentials (line 39) | interface BedrockAccessKeyCredentials {
  type BedrockProfileCredentials (line 47) | interface BedrockProfileCredentials {
  type BedrockCredentials (line 53) | type BedrockCredentials = BedrockAccessKeyCredentials | BedrockProfileCr...
  type QuotaStatus (line 55) | interface QuotaStatus {

FILE: apps/openwork-memos-integration/packages/shared/src/types/opencode.ts
  type OpenCodeMessageBase (line 6) | interface OpenCodeMessageBase {
  type OpenCodeStepStartMessage (line 13) | interface OpenCodeStepStartMessage extends OpenCodeMessageBase {
  type OpenCodeTextMessage (line 25) | interface OpenCodeTextMessage extends OpenCodeMessageBase {
  type OpenCodeToolCallMessage (line 41) | interface OpenCodeToolCallMessage extends OpenCodeMessageBase {
  type OpenCodeToolUseMessage (line 58) | interface OpenCodeToolUseMessage extends OpenCodeMessageBase {
  type OpenCodeToolResultMessage (line 80) | interface OpenCodeToolResultMessage extends OpenCodeMessageBase {
  type OpenCodeStepFinishMessage (line 98) | interface OpenCodeStepFinishMessage extends OpenCodeMessageBase {
  type OpenCodeErrorMessage (line 121) | interface OpenCodeErrorMessage extends OpenCodeMessageBase {
  type OpenCodeMessage (line 128) | type OpenCodeMessage =
  type NormalizedMessage (line 140) | interface NormalizedMessage {
  type ClaudeMessage (line 161) | type ClaudeMessage = OpenCodeMessage;
  type ClaudeMessageBase (line 162) | type ClaudeMessageBase = OpenCodeMessageBase;

FILE: apps/openwork-memos-integration/packages/shared/src/types/permission.ts
  type FileOperation (line 6) | type FileOperation = 'create' | 'delete' | 'rename' | 'move' | 'modify' ...
  type PermissionRequest (line 8) | interface PermissionRequest {
  type PermissionOption (line 39) | interface PermissionOption {
  type PermissionResponse (line 44) | interface PermissionResponse {

FILE: apps/openwork-memos-integration/packages/shared/src/types/provider.ts
  type ProviderType (line 5) | type ProviderType = 'anthropic' | 'openai' | 'openrouter' | 'google' | '...
  type ProviderConfig (line 7) | interface ProviderConfig {
  type ModelConfig (line 16) | interface ModelConfig {
  type SelectedModel (line 26) | interface SelectedModel {
  type OllamaModelInfo (line 35) | interface OllamaModelInfo {
  type OllamaConfig (line 44) | interface OllamaConfig {
  type OpenRouterModel (line 54) | interface OpenRouterModel {
  type OpenRouterConfig (line 64) | interface OpenRouterConfig {
  type LiteLLMModel (line 72) | interface LiteLLMModel {
  type LiteLLMConfig (line 82) | interface LiteLLMConfig {
  constant DEFAULT_PROVIDERS (line 92) | const DEFAULT_PROVIDERS: ProviderConfig[] = [
  constant DEFAULT_MODEL (line 272) | const DEFAULT_MODEL: SelectedModel = {

FILE: apps/openwork-memos-integration/packages/shared/src/types/providerSettings.ts
  type ProviderId (line 3) | type ProviderId =
  type ProviderCategory (line 15) | type ProviderCategory = 'classic' | 'aws' | 'local' | 'proxy' | 'hybrid';
  type ProviderMeta (line 17) | interface ProviderMeta {
  constant PROVIDER_META (line 26) | const PROVIDER_META: Record<ProviderId, ProviderMeta> = {
  type ConnectionStatus (line 39) | type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'e...
  type ApiKeyCredentials (line 41) | interface ApiKeyCredentials {
  type BedrockProviderCredentials (line 46) | interface BedrockProviderCredentials {
  type OllamaCredentials (line 54) | interface OllamaCredentials {
  type OpenRouterCredentials (line 59) | interface OpenRouterCredentials {
  type LiteLLMCredentials (line 64) | interface LiteLLMCredentials {
  type ProviderCredentials (line 71) | type ProviderCredentials =
  type ConnectedProvider (line 78) | interface ConnectedProvider {
  type ProviderSettings (line 87) | interface ProviderSettings {
  function isProviderReady (line 93) | function isProviderReady(provider: ConnectedProvider | undefined): boole...
  function hasAnyReadyProvider (line 98) | function hasAnyReadyProvider(settings: ProviderSettings | null | undefin...
  function getActiveProvider (line 103) | function getActiveProvider(settings: ProviderSettings | null | undefined...
  constant DEFAULT_MODELS (line 112) | const DEFAULT_MODELS: Partial<Record<ProviderId, string>> = {
  function getDefaultModelForProvider (line 123) | function getDefaultModelForProvider(providerId: ProviderId): string | nu...

FILE: apps/openwork-memos-integration/packages/shared/src/types/task.ts
  type TaskStatus (line 5) | type TaskStatus =
  type TaskConfig (line 15) | interface TaskConfig {
  type Task (line 32) | interface Task {
  type TaskAttachment (line 46) | interface TaskAttachment {
  type TaskMessage (line 52) | interface TaskMessage {
  type TaskResult (line 63) | interface TaskResult {
  type TaskProgress (line 70) | interface TaskProgress {
  type TaskUpdateEvent (line 79) | interface TaskUpdateEvent {

FILE: evaluation/scripts/PrefEval/pref_eval.py
  function call_gpt4o_mini_async (line 23) | async def call_gpt4o_mini_async(client: OpenAI, prompt: str) -> str:
  function parse_xml_response (line 41) | def parse_xml_response(response: str, tag: str) -> str:
  function evaluate_violate_preference_async (line 46) | async def evaluate_violate_preference_async(
  function evaluate_acknowledge_preference_async (line 78) | async def evaluate_acknowledge_preference_async(
  function evaluate_hallucinate_preference_async (line 108) | async def evaluate_hallucinate_preference_async(
  function evaluate_helpful_response_async (line 141) | async def evaluate_helpful_response_async(
  function classify_error_type (line 181) | def classify_error_type(evaluation_results: dict[str, Any]) -> str:
  function process_line (line 199) | async def process_line(line: str, client: OpenAI, semaphore: asyncio.Sem...
  function log_summary (line 230) | def log_summary(error_counter: Counter, total_samples: int) -> dict[str,...
  function generate_excel_summary (line 253) | def generate_excel_summary(
  function main (line 307) | async def main(concurrency_limit: int, input_file: str, output_file: str...

FILE: evaluation/scripts/PrefEval/pref_mem0.py
  function add_memory_for_line (line 31) | def add_memory_for_line(
  function search_memory_for_line (line 83) | def search_memory_for_line(line_data: tuple, mem_client, top_k_value: in...
  function generate_response_for_line (line 129) | def generate_response_for_line(line_data: tuple, openai_client: OpenAI) ...
  function main (line 175) | def main():

FILE: evaluation/scripts/PrefEval/pref_memobase.py
  function add_memory_for_line (line 30) | def add_memory_for_line(
  function search_memory_for_line (line 91) | def search_memory_for_line(line_data: tuple, mem_client, top_k_value: in...
  function generate_response_for_line (line 136) | def generate_response_for_line(line_data: tuple, openai_client: OpenAI) ...
  function main (line 182) | def main():

FILE: evaluation/scripts/PrefEval/pref_memos.py
  function add_memory_for_line (line 31) | def add_memory_for_line(
  function search_memory_for_line (line 77) | def search_memory_for_line(line_data, mem_client, top_k_value):
  function generate_response_for_line (line 129) | def generate_response_for_line(line_data, openai_client, lib):
  function main (line 177) | def main():

FILE: evaluation/scripts/PrefEval/pref_memu.py
  function add_memory_for_line (line 32) | def add_memory_for_line(
  function search_memory_for_line (line 83) | def search_memory_for_line(line_data: tuple, mem_client, top_k_value: in...
  function generate_response_for_line (line 130) | def generate_response_for_line(line_data: tuple, openai_client: OpenAI) ...
  function main (line 176) | def main():

FILE: evaluation/scripts/PrefEval/pref_supermemory.py
  function add_memory_for_line (line 30) | def add_memory_for_line(
  function search_memory_for_line (line 80) | def search_memory_for_line(line_data: tuple, mem_client, top_k_value: in...
  function generate_response_for_line (line 125) | def generate_response_for_line(line_data: tuple, openai_client: OpenAI) ...
  function main (line 171) | def main():

FILE: evaluation/scripts/PrefEval/pref_zep.py
  function add_memory_for_line (line 32) | def add_memory_for_line(
  function search_memory_for_line (line 84) | def search_memory_for_line(line_data: tuple, mem_client, top_k_value: in...
  function generate_response_for_line (line 131) | def generate_response_for_line(line_data: tuple, openai_client: OpenAI) ...
  function main (line 177) | def main():

FILE: evaluation/scripts/PrefEval/prefeval_preprocess.py
  function convert_dataset_to_jsonl (line 7) | def convert_dataset_to_jsonl(dataset_name, output_dir="./scripts/PrefEva...
  function restructure_conversation_in_json (line 29) | def restructure_conversation_in_json(data):
  function process_jsonl_file (line 63) | def process_jsonl_file(input_filepath, output_filepath):
  function main (line 94) | def main():

FILE: evaluation/scripts/locomo/locomo_eval.py
  class LLMGrade (line 46) | class LLMGrade(BaseModel):
  function extract_label_json (line 51) | def extract_label_json(text: str) -> str | None:
  function locomo_grader (line 74) | async def locomo_grader(llm_client, question: str, gold_answer: str, res...
  function calculate_rouge_scores (line 123) | def calculate_rouge_scores(gold_answer, response):
  function calculate_bleu_scores (line 136) | def calculate_bleu_scores(gold_tokens, response_tokens):
  function calculate_meteor_score (line 155) | def calculate_meteor_score(gold_tokens, response_tokens):
  function calculate_semantic_similarity (line 163) | def calculate_semantic_similarity(gold_answer, response):
  function calculate_f1_score (line 178) | def calculate_f1_score(gold_tokens, response_tokens):
  function calculate_nlp_metrics (line 197) | def calculate_nlp_metrics(gold_answer, response, context, options=None):
  function convert_numpy_types (line 227) | def convert_numpy_types(obj):
  function process_group_responses (line 238) | async def process_group_responses(group_id, group_responses, oai_client,...
  function process_single_group (line 279) | async def process_single_group(group_id, group_responses, oai_client, op...
  function main (line 294) | async def main(frame, version="default", options=None, num_runs=1, max_w...

FILE: evaluation/scripts/locomo/locomo_ingestion.py
  function ingest_session (line 22) | def ingest_session(client, session, frame, version, metadata):
  function process_user (line 91) | def process_user(conv_idx, frame, locomo_df, version, success_records, f):
  function main (line 168) | def main(frame, version="default", num_workers=4):

FILE: evaluation/scripts/locomo/locomo_metric.py
  function calculate_scores (line 50) | def calculate_scores(data):
  function save_to_excel (line 303) | def save_to_excel(results, output_path):

FILE: evaluation/scripts/locomo/locomo_openai.py
  class OpenAIPredict (line 64) | class OpenAIPredict:
    method __init__ (line 65) | def __init__(self, model="gpt-4o-mini"):
    method search_memory (line 72) | def search_memory(self, idx):
    method process_question (line 78) | def process_question(self, val, idx):
    method answer_question (line 102) | def answer_question(self, idx, question):
    method process_data_file (line 117) | def process_data_file(self, file_path, output_file_path):
  function main (line 156) | def main(version):

FILE: evaluation/scripts/locomo/locomo_rag.py
  class RAGManager (line 38) | class RAGManager:
    method __init__ (line 39) | def __init__(self, data_path="data/locomo/locomo10_rag.json", chunk_si...
    method generate_response (line 46) | def generate_response(self, question, context):
    method clean_chat_history (line 88) | def clean_chat_history(self, chat_history):
    method calculate_embedding (line 95) | def calculate_embedding(self, document):
    method calculate_similarity (line 99) | def calculate_similarity(self, embedding1, embedding2):
    method search (line 104) | def search(self, query, chunks, embeddings, k=1):
    method create_chunks (line 132) | def create_chunks(self, chat_history, chunk_size=500):
    method process_all_conversations (line 162) | def process_all_conversations(self, output_file_path):
  class Experiment (line 205) | class Experiment:
    method __init__ (line 206) | def __init__(self, technique_type, chunk_size):
    method run (line 210) | def run(self):
  function process_item (line 216) | def process_item(item_data):
  function rename_json_keys (line 249) | def rename_json_keys(file_path):
  function generate_response_file (line 262) | def generate_response_file(file_path):
  function main (line 302) | def main():

FILE: evaluation/scripts/locomo/locomo_responses.py
  function locomo_response (line 26) | async def locomo_response(frame, llm_client, context: str, question: str...
  function process_qa (line 54) | async def process_qa(frame, qa, search_result, oai_client):
  function main (line 79) | async def main(frame, version="default"):

FILE: evaluation/scripts/locomo/locomo_search.py
  function mem0_search (line 25) | def mem0_search(client, query, speaker_a_user_id, speaker_b_user_id, top...
  function mem0_graph_search (line 51) | def mem0_graph_search(
  function memos_api_search (line 99) | def memos_api_search(
  function memobase_search (line 128) | def memobase_search(
  function memu_search (line 147) | def memu_search(client, query, speaker_a_user_id, speaker_b_user_id, top...
  function supermemory_search (line 167) | def supermemory_search(
  function search_query (line 186) | def search_query(client, query, metadata, frame, version, top_k=20):
  function load_existing_results (line 223) | def load_existing_results(frame, version, group_idx):
  function process_user (line 236) | def process_user(conv_idx, locomo_df, frame, version, top_k=20, num_work...
  function main (line 319) | def main(frame, version="default", num_workers=1, top_k=20):

FILE: evaluation/scripts/locomo/utils.py
  function filter_memory_data (line 1) | def filter_memory_data(memories_data):

FILE: evaluation/scripts/long_bench-v2/longbench_v2_ingestion.py
  function ingest_sample (line 22) | def ingest_sample(
  function load_dataset_from_local (line 63) | def load_dataset_from_local():
  function main (line 83) | def main(frame, version="default", num_workers=10, max_samples=None):

FILE: evaluation/scripts/long_bench-v2/longbench_v2_metric.py
  function calculate_accuracy (line 6) | def calculate_accuracy(responses):
  function main (line 84) | def main(frame, version="default"):

FILE: evaluation/scripts/long_bench-v2/longbench_v2_responses.py
  function extract_answer (line 42) | def extract_answer(response):
  function llm_answer (line 59) | def llm_answer(llm_client, memories, question, choices):
  function process_sample (line 104) | def process_sample(search_result, llm_client, success_records, record_fi...
  function main (line 194) | def main(frame, version="default", num_workers=10):

FILE: evaluation/scripts/long_bench-v2/longbench_v2_search.py
  function memos_api_search (line 23) | def memos_api_search(client, query, user_id, top_k, frame):
  function process_sample (line 54) | def process_sample(
  function load_dataset_from_local (line 103) | def load_dataset_from_local():
  function main (line 123) | def main(frame, version="default", num_workers=10, top_k=20, max_samples...

FILE: evaluation/scripts/long_bench-v2/wait_scheduler.py
  function wait_until_completed (line 9) | def wait_until_completed(params: dict, interval: float = 2.0, timeout: f...

FILE: evaluation/scripts/longmemeval/lme_eval.py
  class LLMGrade (line 51) | class LLMGrade(BaseModel):
  function calculate_rouge_scores (line 56) | def calculate_rouge_scores(golden_answer, response):
  function calculate_bleu_scores (line 69) | def calculate_bleu_scores(gold_tokens, response_tokens):
  function calculate_meteor_score (line 88) | def calculate_meteor_score(gold_tokens, response_tokens):
  function calculate_semantic_similarity (line 96) | def calculate_semantic_similarity(golden_answer, response):
  function calculate_f1_score (line 111) | def calculate_f1_score(gold_tokens, response_tokens):
  function calculate_nlp_metrics (line 130) | def calculate_nlp_metrics(golden_answer, response, context, options=None):
  function lme_grader (line 161) | def lme_grader(llm_client, question, golden_answer, response):
  function process_qa (line 183) | async def process_qa(
  function convert_numpy_types (line 246) | def convert_numpy_types(obj):
  function evaluate_accuracy (line 257) | def evaluate_accuracy(results, num_runs):
  function main (line 276) | async def main(frame, version, nlp_options, num_runs=3, num_workers=5):

FILE: evaluation/scripts/longmemeval/lme_ingestion.py
  function ingest_session (line 16) | def ingest_session(session, date, user_id, session_id, frame, client):
  function ingest_conv (line 63) | def ingest_conv(lme_df, version, conv_idx, frame, success_records, f):
  function main (line 120) | def main(frame, version, num_workers=2):

FILE: evaluation/scripts/longmemeval/lme_metric.py
  function save_to_excel (line 8) | def save_to_excel(results, output_path):
  function calculate_scores (line 37) | def calculate_scores(data, grade_path, output_path):

FILE: evaluation/scripts/longmemeval/lme_rag.py
  class RAGFullContext (line 29) | class RAGFullContext(RAGManager):
    method __init__ (line 30) | def __init__(self, data_path="data/longmemeval/longmemeval_s.json", ch...
    method get_dataset (line 33) | def get_dataset(self):
    method split_chunks (line 38) | def split_chunks(self, message_content, chunk_size):
    method split_chunks2 (line 66) | def split_chunks2(self, message_content, chunk_size):
  function rag_search (line 98) | def rag_search(client, user_id, query, top_k, frame):
  function process_user (line 147) | def process_user(lme_df, conv_idx, frame, version, chunk_size, num_chunk...
  function load_existing_results (line 208) | def load_existing_results(frame, version, group_idx):
  function main (line 221) | def main(frame, version, chunk_size, num_chunks, top_k=20, num_workers=2):

FILE: evaluation/scripts/longmemeval/lme_responses.py
  function lme_response (line 18) | def lme_response(llm_client, context, question, question_date):
  function process_qa (line 36) | def process_qa(user_id, search_result, llm_client):
  function main (line 67) | def main(frame, version, num_workers=4):

FILE: evaluation/scripts/longmemeval/lme_search.py
  function mem0_search (line 23) | def mem0_search(client, query, user_id, top_k):
  function memos_search (line 44) | def memos_search(client, query, user_id, top_k):
  function memobase_search (line 56) | def memobase_search(client, query, user_id, top_k):
  function memu_search (line 63) | def memu_search(client, query, user_id, top_k):
  function supermemory_search (line 71) | def supermemory_search(client, query, user_id, top_k):
  function process_user (line 78) | def process_user(lme_df, conv_idx, frame, version, top_k=20):
  function load_existing_results (line 165) | def load_existing_results(frame, version, group_idx):
  function main (line 176) | def main(frame, version, top_k=20, num_workers=2):

FILE: evaluation/scripts/utils/client.py
  class ZepClient (line 19) | class ZepClient:
    method __init__ (line 20) | def __init__(self):
    method add (line 26) | def add(self, messages, user_id, timestamp):
    method search (line 36) | def search(self, query, user_id, top_k):
  class Mem0Client (line 51) | class Mem0Client:
    method __init__ (line 52) | def __init__(self, enable_graph=False):
    method add (line 58) | def add(self, messages, user_id, timestamp, batch_size=2):
    method search (line 84) | def search(self, query, user_id, top_k):
  class MemobaseClient (line 95) | class MemobaseClient:
    method __init__ (line 96) | def __init__(self):
    method add (line 103) | def add(self, messages, user_id, batch_size=2):
    method search (line 123) | def search(self, query, user_id, top_k):
    method delete_user (line 134) | def delete_user(self, user_id):
    method string_to_uuid (line 141) | def string_to_uuid(self, s: str, salt="memobase_client"):
  class MemosApiClient (line 145) | class MemosApiClient:
    method __init__ (line 146) | def __init__(self):
    method add (line 150) | def add(self, messages, user_id, conv_id, batch_size: int = 9999):
    method search (line 174) | def search(self, query, user_id, top_k):
  class MemosApiOnlineClient (line 198) | class MemosApiOnlineClient:
    method __init__ (line 199) | def __init__(self):
    method add (line 203) | def add(self, messages, user_id, conv_id=None, batch_size: int = 9999):
    method search (line 228) | def search(self, query, user_id, top_k):
  class SupermemoryClient (line 277) | class SupermemoryClient:
    method __init__ (line 278) | def __init__(self):
    method add (line 283) | def add(self, messages, user_id):
    method search (line 298) | def search(self, query, user_id, top_k):
  class MemuClient (line 319) | class MemuClient:
    method __init__ (line 320) | def __init__(self):
    method add (line 328) | def add(self, messages, user_id, iso_date):
    method search (line 342) | def search(self, query, user_id, top_k):
    method wait_for_completion (line 349) | def wait_for_completion(self, task_id):

FILE: evaluation/scripts/utils/mirix_utils.py
  function get_mirix_client (line 8) | def get_mirix_client(config_path, load_from=None):

FILE: examples/api/server_router_api.py
  function call_add_api (line 46) | def call_add_api(name: str, payload: dict):
  function example_01a_string_message_minimal (line 80) | def example_01a_string_message_minimal():
  function example_01b_standard_chat_triplet (line 96) | def example_01b_standard_chat_triplet():
  function example_02a_assistant_with_tool_calls (line 144) | def example_02a_assistant_with_tool_calls():
  function example_02b_tool_message_with_result (line 177) | def example_02b_tool_message_with_result():
  function example_02c_tool_description_input_output (line 218) | def example_02c_tool_description_input_output():
  function example_03_multimodal_text_and_image (line 258) | def example_03_multimodal_text_and_image():
  function example_04a_pure_text_input_items (line 298) | def example_04a_pure_text_input_items():
  function example_04b_pure_file_input_by_file_id (line 323) | def example_04b_pure_file_input_by_file_id():
  function example_04c_pure_file_input_by_file_data (line 351) | def example_04c_pure_file_input_by_file_data():
  function example_04d_pure_file_input_by_oss_url (line 378) | def example_04d_pure_file_input_by_oss_url():
  function example_05_deprecated_memory_content_and_doc_path (line 408) | def example_05_deprecated_memory_content_and_doc_path():
  function example_06a_feedback_add (line 437) | def example_06a_feedback_add():
  function example_06b_family_travel_conversation (line 466) | def example_06b_family_travel_conversation():
  function example_06c_add_with_chat_history (line 515) | def example_06c_add_with_chat_history():
  function example_07a_search_memories (line 561) | def example_07a_search_memories():
  function example_07b_chat_complete (line 595) | def example_07b_chat_complete():

FILE: examples/basic_modules/chunker.py
  function main (line 5) | def main():

FILE: examples/basic_modules/neo4j_example.py
  function embed_memory_item (line 26) | def embed_memory_item(memory: str) -> list[float]:
  function get_neo4j_graph (line 30) | def get_neo4j_graph(db_name: str = "paper"):
  function example_multi_db (line 47) | def example_multi_db(db_name: str = "paper"):
  function example_shared_db (line 278) | def example_shared_db(db_name: str = "shared-traval-group"):
  function run_user_session (line 370) | def run_user_session(
  function example_complex_shared_db (line 523) | def example_complex_shared_db(db_name: str = "shared-traval-group-comple...
  function example_complex_shared_db_search_filter (line 557) | def example_complex_shared_db_search_filter(db):
  function example_complex_shared_db_delete_memory (line 597) | def example_complex_shared_db_delete_memory(db):

FILE: examples/basic_modules/reranker.py
  function make_item (line 18) | def make_item(text: str) -> TextualMemoryItem:
  function show_ranked (line 43) | def show_ranked(title: str, ranked: list[tuple[TextualMemoryItem, float]...
  function main (line 50) | def main():

FILE: examples/core_memories/kv_cache_memory.py
  function get_cache_info (line 10) | def get_cache_info(cache):
  function serialize_item (line 45) | def serialize_item(obj):

FILE: examples/core_memories/vllm_kv_cache_memory.py
  function main (line 11) | def main():

FILE: examples/extras/nli_e2e_example.py
  function run_server (line 16) | def run_server():
  function main (line 22) | def main():

FILE: examples/mem_agent/deepsearch_example.py
  function build_minimal_components (line 39) | def build_minimal_components():
  function factory_initialization (line 150) | def factory_initialization() -> tuple[DeepSearchMemAgent, dict[str, Any]]:
  function main (line 181) | def main():

FILE: examples/mem_chat/chat_w_generated_cube_explicit_memory_only.py
  function get_mem_chat_config (line 14) | def get_mem_chat_config() -> MemChatConfigFactory:
  function get_mem_cube_config (line 56) | def get_mem_cube_config() -> GeneralMemCubeConfig:
  function main (line 113) | def main():

FILE: examples/mem_feedback/example_feedback.py
  function init_components (line 10) | def init_components():
  function main (line 153) | def main():

FILE: examples/mem_mcp/simple_fastmcp_client.py
  function main (line 9) | async def main():

FILE: examples/mem_mcp/simple_fastmcp_serve.py
  function add_memory (line 22) | def add_memory(memory_content: str, user_id: str, cube_id: str | None = ...
  function search_memories (line 38) | def search_memories(query: str, user_id: str, cube_ids: str | None = None):
  function chat (line 51) | def chat(query: str, user_id: str):

FILE: examples/mem_reader/builders.py
  function build_llm_and_embedder (line 25) | def build_llm_and_embedder() -> tuple[Any, Any]:
  function build_file_parser (line 39) | def build_file_parser() -> Any:
  function build_simple_reader (line 58) | def build_simple_reader() -> SimpleStructMemReader:
  function build_multimodal_reader (line 70) | def build_multimodal_reader() -> MultiModalStructMemReader:

FILE: examples/mem_reader/parser_demos/_base.py
  class BaseParserDemo (line 10) | class BaseParserDemo:
    method __init__ (line 13) | def __init__(self):
    method create_parser (line 19) | def create_parser(self):
    method run (line 23) | def run(self):
    method demo_source_creation (line 27) | def demo_source_creation(
    method demo_rebuild (line 48) | def demo_rebuild(self, source: SourceMessage | list[SourceMessage]):
    method demo_parse_fast (line 62) | def demo_parse_fast(self, message: Any, info: dict):

FILE: examples/mem_reader/parser_demos/demo_assistant.py
  class AssistantParserDemo (line 9) | class AssistantParserDemo(BaseParserDemo):
    method create_parser (line 10) | def create_parser(self):
    method run (line 29) | def run(self):

FILE: examples/mem_reader/parser_demos/demo_file_content.py
  class FileContentParserDemo (line 10) | class FileContentParserDemo(BaseParserDemo):
    method create_parser (line 11) | def create_parser(self):
    method run (line 21) | def run(self):

FILE: examples/mem_reader/parser_demos/demo_image.py
  class ImageParserDemo (line 14) | class ImageParserDemo(BaseParserDemo):
    method create_parser (line 15) | def create_parser(self):
    method run (line 18) | def run(self):

FILE: examples/mem_reader/parser_demos/demo_multi_modal.py
  class MultiModalParserDemo (line 9) | class MultiModalParserDemo(BaseParserDemo):
    method create_parser (line 10) | def create_parser(self):
    method run (line 14) | def run(self):
    method parser_selection (line 19) | def parser_selection(self):
    method parser_instances (line 305) | def parser_instances(self):

FILE: examples/mem_reader/parser_demos/demo_string.py
  class StringParserDemo (line 9) | class StringParserDemo(BaseParserDemo):
    method create_parser (line 10) | def create_parser(self):
    method run (line 13) | def run(self):

FILE: examples/mem_reader/parser_demos/demo_system.py
  class SystemParserDemo (line 9) | class SystemParserDemo(BaseParserDemo):
    method create_parser (line 10) | def create_parser(self):
    method run (line 13) | def run(self):

FILE: examples/mem_reader/parser_demos/demo_text_content.py
  class TextContentParserDemo (line 9) | class TextContentParserDemo(BaseParserDemo):
    method create_parser (line 10) | def create_parser(self):
    method run (line 13) | def run(self):

FILE: examples/mem_reader/parser_demos/demo_tool.py
  class ToolParserDemo (line 9) | class ToolParserDemo(BaseParserDemo):
    method create_parser (line 10) | def create_parser(self):
    method run (line 13) | def run(self):

FILE: examples/mem_reader/parser_demos/demo_user.py
  class UserParserDemo (line 9) | class UserParserDemo(BaseParserDemo):
    method create_parser (line 10) | def create_parser(self):
    method run (line 13) | def run(self):

FILE: examples/mem_reader/runners/run_multimodal.py
  function run_multimodal_reader (line 25) | def run_multimodal_reader():

FILE: examples/mem_reader/runners/run_simple.py
  function _print_memory_sets (line 12) | def _print_memory_sets(title: str, memories):
  function run_simple_reader (line 24) | def run_simple_reader():

FILE: examples/mem_reader/samples.py
  class TestCase (line 12) | class TestCase:
    method get_info (line 20) | def get_info(self) -> dict[str, Any]:

FILE: examples/mem_reader/settings.py
  function get_llm_config (line 18) | def get_llm_config() -> dict[str, Any]:
  function get_embedder_config (line 59) | def get_embedder_config() -> dict[str, Any]:
  function get_chunker_config (line 88) | def get_chunker_config() -> dict[str, Any]:
  function get_reader_config (line 101) | def get_reader_config() -> dict[str, Any]:

FILE: examples/mem_reader/utils.py
  function _truncate (line 11) | def _truncate(s: str, max_len: int | None) -> str:
  function sanitize_for_print (line 17) | def sanitize_for_print(obj: Any, *, max_str_len: int | None = 500) -> Any:
  function pretty_print_dict (line 34) | def pretty_print_dict(d: dict, *, max_str_len: int | None = 500):
  function print_memory_item (line 61) | def print_memory_item(

FILE: examples/mem_scheduler/api_w_scheduler.py
  function run_with_scheduler_api (line 33) | def run_with_scheduler_api():

FILE: examples/mem_scheduler/memos_w_scheduler.py
  function init_task (line 44) | def init_task():
  function custom_query_handler (line 93) | def custom_query_handler(messages: list[ScheduleMessageItem]):
  function custom_mem_update_handler (line 104) | def custom_mem_update_handler(messages: list[ScheduleMessageItem]):
  function run_with_scheduler (line 112) | async def run_with_scheduler():

FILE: examples/mem_scheduler/redis_example.py
  function service_run (line 33) | def service_run():

FILE: examples/mem_scheduler/run_async_tasks.py
  function my_test_handler (line 30) | def my_test_handler(messages: list[ScheduleMessageItem]):
  function submit_tasks (line 44) | def submit_tasks():

FILE: examples/mem_scheduler/show_redis_status.py
  function fetch_status (line 25) | def fetch_status(
  function print_diff (line 36) | def print_diff(prev: dict[str, dict[str, int]], curr: dict[str, dict[str...
  function main (line 60) | def main(interval_sec: float = 5.0, stream_key_prefix: str | None = None...

FILE: examples/mem_scheduler/try_schedule_modules.py
  function init_task (line 24) | def init_task():
  class ScheduleModulesRunner (line 99) | class ScheduleModulesRunner(DirectSearchMemoriesAnalyzer):
    method __init__ (line 100) | def __init__(self):
    method start_conversation (line 103) | def start_conversation(self, user_id="test_user", mem_cube_id="test_cu...
    method add_msgs (line 116) | def add_msgs(

FILE: scripts/check_dependencies.py
  function extract_top_level_modules (line 12) | def extract_top_level_modules(tree: ast.Module) -> set[str]:
  function check_importable (line 27) | def check_importable(modules: set[str], filename: str) -> list[str]:
  function main (line 48) | def main():

FILE: src/memos/api/client.py
  class MemOSClient (line 31) | class MemOSClient:
    method __init__ (line 34) | def __init__(
    method _validate_required_params (line 62) | def _validate_required_params(self, **params):
    method get_message (line 68) | def get_message(
    method add_message (line 102) | def add_message(
    method search_memory (line 150) | def search_memory(
    method get_memory (line 197) | def get_memory(
    method create_knowledgebase (line 226) | def create_knowledgebase(
    method delete_knowledgebase (line 258) | def delete_knowledgebase(
    method add_knowledgebase_file_json (line 286) | def add_knowledgebase_file_json(
    method add_knowledgebase_file_form (line 315) | def add_knowledgebase_file_form(
    method delete_knowledgebase_file (line 364) | def delete_knowledgebase_file(
    method get_knowledgebase_file (line 392) | def get_knowledgebase_file(
    method get_task_status (line 420) | def get_task_status(self, task_id: str) -> MemOSGetTaskStatusResponse ...
    method add_feedback (line 446) | def add_feedback(
    method delete_memory (line 488) | def delete_memory(
    method chat (line 515) | def chat(

FILE: src/memos/api/config.py
  function _update_env_from_dict (line 31) | def _update_env_from_dict(data: dict[str, Any]) -> None:
  function get_config_json (line 61) | def get_config_json(name: str, default: Any | None = None) -> Any:
  function get_config_value (line 73) | def get_config_value(path: str, default: Any | None = None) -> Any:
  class NacosConfigManager (line 96) | class NacosConfigManager:
    method _sign (line 108) | def _sign(cls, secret_key: str, data: str) -> str:
    method _parse_value (line 114) | def _parse_value(value: str) -> Any:
    method parse_properties (line 146) | def parse_properties(content: str) -> dict[str, Any]:
    method start_config_watch (line 173) | def start_config_watch(cls):
    method start_watch_if_enabled (line 179) | def start_watch_if_enabled(cls) -> None:
    method init (line 198) | def init(cls) -> None:
  class APIConfig (line 260) | class APIConfig:
    method get_openai_config (line 264) | def get_openai_config() -> dict[str, Any]:
    method qwen_config (line 278) | def qwen_config() -> dict[str, Any]:
    method vllm_config (line 288) | def vllm_config() -> dict[str, Any]:
    method get_activation_config (line 301) | def get_activation_config() -> dict[str, Any]:
    method get_memreader_config (line 323) | def get_memreader_config() -> dict[str, Any]:
    method get_memreader_general_llm_config (line 359) | def get_memreader_general_llm_config() -> dict[str, Any]:
    method get_image_parser_llm_config (line 397) | def get_image_parser_llm_config() -> dict[str, Any]:
    method get_preference_extractor_llm_config (line 429) | def get_preference_extractor_llm_config() -> dict[str, Any]:
    method get_activation_vllm_config (line 460) | def get_activation_vllm_config() -> dict[str, Any]:
    method get_preference_memory_config (line 474) | def get_preference_memory_config() -> dict[str, Any]:
    method get_reranker_config (line 493) | def get_reranker_config() -> dict[str, Any]:
    method get_feedback_reranker_config (line 519) | def get_feedback_reranker_config() -> dict[str, Any]:
    method get_embedder_config (line 547) | def get_embedder_config() -> dict[str, Any]:
    method get_reader_config (line 586) | def get_reader_config() -> dict[str, Any]:
    method get_oss_config (line 599) | def get_oss_config() -> dict[str, Any] | None:
    method get_internet_config (line 628) | def get_internet_config() -> dict[str, Any]:
    method get_nli_config (line 674) | def get_nli_config() -> dict[str, Any]:
    method get_neo4j_community_config (line 681) | def get_neo4j_community_config(user_id: str | None = None) -> dict[str...
    method get_neo4j_config (line 710) | def get_neo4j_config(user_id: str | None = None) -> dict[str, Any]:
    method get_noshared_neo4j_config (line 718) | def get_noshared_neo4j_config(user_id) -> dict[str, Any]:
    method get_neo4j_shared_config (line 731) | def get_neo4j_shared_config(user_id: str | None = None) -> dict[str, A...
    method get_nebular_config (line 745) | def get_nebular_config(user_id: str | None = None) -> dict[str, Any]:
    method get_milvus_config (line 759) | def get_milvus_config():
    method get_polardb_config (line 773) | def get_polardb_config(user_id: str | None = None) -> dict[str, Any]:
    method get_postgres_config (line 810) | def get_postgres_config(user_id: str | None = None) -> dict[str, Any]:
    method get_mysql_config (line 834) | def get_mysql_config() -> dict[str, Any]:
    method get_scheduler_config (line 846) | def get_scheduler_config() -> dict[str, Any]:
    method is_scheduler_enabled (line 876) | def is_scheduler_enabled() -> bool:
    method is_default_cube_config_enabled (line 881) | def is_default_cube_config_enabled() -> bool:
    method is_dingding_bot_enabled (line 886) | def is_dingding_bot_enabled() -> bool:
    method get_dingding_bot_config (line 891) | def get_dingding_bot_config() -> dict[str, Any] | None:
    method get_product_default_config (line 914) | def get_product_default_config() -> dict[str, Any]:
    method get_start_default_config (line 997) | def get_start_default_config() -> dict[str, Any]:
    method create_user_config (line 1033) | def create_user_config(user_name: str, user_id: str) -> tuple["MOSConf...
    method get_default_cube_config (line 1177) | def get_default_cube_config() -> "GeneralMemCubeConfig | None":

FILE: src/memos/api/context/dependencies.py
  function get_g_object (line 12) | def get_g_object() -> G:
  function get_current_g (line 25) | def get_current_g() -> G | None:
  function require_g (line 35) | def require_g() -> G:

FILE: src/memos/api/exceptions.py
  class APIExceptionHandler (line 11) | class APIExceptionHandler:
    method validation_error_handler (line 15) | async def validation_error_handler(request: Request, exc: RequestValid...
    method value_error_handler (line 29) | async def value_error_handler(request: Request, exc: ValueError):
    method global_exception_handler (line 38) | async def global_exception_handler(request: Request, exc: Exception):
    method http_error_handler (line 47) | async def http_error_handler(request: Request, exc: HTTPException):

FILE: src/memos/api/handlers/add_handler.py
  class AddHandler (line 21) | class AddHandler(BaseHandler):
    method __init__ (line 28) | def __init__(self, dependencies: HandlerDependencies):
    method handle_add_memories (line 40) | def handle_add_memories(self, add_req: APIADDRequest) -> MemoryResponse:
    method _resolve_cube_ids (line 116) | def _resolve_cube_ids(self, add_req: APIADDRequest) -> list[str]:
    method _build_cube_view (line 128) | def _build_cube_view(self, add_req: APIADDRequest) -> MemCubeView:

FILE: src/memos/api/handlers/base_handler.py
  class HandlerDependencies (line 18) | class HandlerDependencies:
    method __init__ (line 26) | def __init__(
    method from_init_server (line 79) | def from_init_server(cls, components: dict[str, Any]):
  class BaseHandler (line 98) | class BaseHandler:
    method __init__ (line 106) | def __init__(self, dependencies: HandlerDependencies):
    method llm (line 117) | def llm(self):
    method naive_mem_cube (line 122) | def naive_mem_cube(self):
    method mem_reader (line 127) | def mem_reader(self):
    method mem_scheduler (line 132) | def mem_scheduler(self) -> OptimizedScheduler:
    method searcher (line 137) | def searcher(self) -> AdvancedSearcher:
    method embedder (line 142) | def embedder(self):
    method reranker (line 147) | def reranker(self):
    method graph_db (line 152) | def graph_db(self):
    method vector_db (line 157) | def vector_db(self):
    method mos_server (line 162) | def mos_server(self):
    method deepsearch_agent (line 167) | def deepsearch_agent(self):
    method feedback_server (line 172) | def feedback_server(self):
    method _validate_dependencies (line 176) | def _validate_dependencies(self, *required_deps: str) -> None:

FILE: src/memos/api/handlers/chat_handler.py
  class ChatHandler (line 51) | class ChatHandler(BaseHandler):
    method __init__ (line 59) | def __init__(
    method handle_chat_complete (line 102) | def handle_chat_complete(self, chat_req: APIChatCompleteRequest) -> di...
    method handle_chat_stream (line 237) | def handle_chat_stream(self, chat_req: ChatRequest) -> StreamingResponse:
    method handle_chat_stream_playground (line 422) | def handle_chat_stream_playground(self, chat_req: ChatPlaygroundReques...
    method handle_chat_stream_for_business_user (line 774) | def handle_chat_stream_for_business_user(
    method _dedup_and_supplement_memories (line 966) | def _dedup_and_supplement_memories(
    method _get_internet_reference (line 989) | def _get_internet_reference(
    method _build_pref_md_string_for_playground (line 1006) | def _build_pref_md_string_for_playground(self, pref_mem_list: list[any...
    method _build_system_prompt (line 1041) | def _build_system_prompt(
    method _build_enhance_system_prompt (line 1072) | def _build_enhance_system_prompt(
    method _format_mem_block (line 1110) | def _format_mem_block(
    method _filter_memories_by_threshold (line 1159) | def _filter_memories_by_threshold(
    method _get_further_suggestion (line 1214) | def _get_further_suggestion(
    method _extract_references_from_response (line 1233) | def _extract_references_from_response(self, response: str) -> tuple[st...
    method _extract_struct_data_from_history (line 1257) | def _extract_struct_data_from_history(self, chat_data: list[dict]) -> ...
    method _send_message_to_scheduler (line 1290) | def _send_message_to_scheduler(
    method _add_conversation_to_memory (line 1319) | async def _add_conversation_to_memory(
    method _post_chat_processing (line 1357) | async def _post_chat_processing(
    method _start_post_chat_processing (line 1457) | def _start_post_chat_processing(
    method _start_add_to_memory (line 1552) | def _start_add_to_memory(

FILE: src/memos/api/handlers/component_init.py
  function _get_default_memory_size (line 57) | def _get_default_memory_size(cube_config: Any) -> dict[str, int]:
  function _init_chat_llms (line 77) | def _init_chat_llms(chat_llm_configs: list[dict]) -> dict[str, Any]:
  function init_server (line 109) | def init_server() -> dict[str, Any]:

FILE: src/memos/api/handlers/config_builders.py
  function build_graph_db_config (line 29) | def build_graph_db_config(user_id: str = "default") -> dict[str, Any]:
  function build_vec_db_config (line 57) | def build_vec_db_config() -> dict[str, Any]:
  function build_llm_config (line 72) | def build_llm_config() -> dict[str, Any]:
  function build_chat_llm_config (line 87) | def build_chat_llm_config() -> list[dict[str, Any]]:
  function build_embedder_config (line 114) | def build_embedder_config() -> dict[str, Any]:
  function build_mem_reader_config (line 124) | def build_mem_reader_config() -> dict[str, Any]:
  function build_reranker_config (line 136) | def build_reranker_config() -> dict[str, Any]:
  function build_feedback_reranker_config (line 146) | def build_feedback_reranker_config() -> dict[str, Any]:
  function build_internet_retriever_config (line 156) | def build_internet_retriever_config() -> dict[str, Any]:
  function build_pref_extractor_config (line 166) | def build_pref_extractor_config() -> dict[str, Any]:
  function build_pref_adder_config (line 176) | def build_pref_adder_config() -> dict[str, Any]:
  function build_pref_retriever_config (line 186) | def build_pref_retriever_config() -> dict[str, Any]:
  function build_nli_client_config (line 196) | def build_nli_client_config() -> dict[str, Any]:
  function build_general_llm_config (line 206) | def build_general_llm_config() -> dict[str, Any]:
  function build_image_parser_llm_config (line 219) | def build_image_parser_llm_config() -> dict[str, Any]:

FILE: src/memos/api/handlers/feedback_handler.py
  class FeedbackHandler (line 16) | class FeedbackHandler(BaseHandler):
    method __init__ (line 23) | def __init__(self, dependencies: HandlerDependencies):
    method handle_feedback_memories (line 33) | def handle_feedback_memories(self, feedback_req: APIFeedbackRequest) -...
    method _resolve_cube_ids (line 54) | def _resolve_cube_ids(self, feedback_req: APIFeedbackRequest) -> list[...
    method _build_cube_view (line 63) | def _build_cube_view(self, feedback_req: APIFeedbackRequest) -> MemCub...

FILE: src/memos/api/handlers/formatters_handler.py
  function to_iter (line 17) | def to_iter(running: Any) -> list[Any]:
  function format_memory_item (line 36) | def format_memory_item(
  function post_process_textual_mem (line 68) | def post_process_textual_mem(
  function separate_knowledge_and_conversation_mem (line 134) | def separate_knowledge_and_conversation_mem(memories: list[dict[str, Any...
  function rerank_knowledge_mem (line 160) | def rerank_knowledge_mem(

FILE: src/memos/api/handlers/memory_handler.py
  function handle_get_all_memories (line 31) | def handle_get_all_memories(
  function handle_get_subgraph (line 98) | def handle_get_subgraph(
  function handle_get_memory (line 163) | def handle_get_memory(memory_id: str, naive_mem_cube: NaiveMemCube) -> G...
  function handle_get_memory_by_ids (line 194) | def handle_get_memory_by_ids(
  function handle_get_memories (line 218) | def handle_get_memories(
  function handle_delete_memories (line 317) | def handle_delete_memories(delete_mem_req: DeleteMemoryRequest, naive_me...
  function handle_get_memories_dashboard (line 403) | def handle_get_memories_dashboard(

FILE: src/memos/api/handlers/scheduler_handler.py
  function handle_scheduler_allstatus (line 38) | def handle_scheduler_allstatus(
  function handle_scheduler_status (line 190) | def handle_scheduler_status(
  function handle_task_queue_status (line 249) | def handle_task_queue_status(
  function handle_scheduler_wait (line 343) | def handle_scheduler_wait(
  function handle_scheduler_wait_stream (line 416) | def handle_scheduler_wait_stream(

FILE: src/memos/api/handlers/search_handler.py
  class SearchHandler (line 28) | class SearchHandler(BaseHandler):
    method __init__ (line 35) | def __init__(self, dependencies: HandlerDependencies):
    method handle_search_memories (line 47) | def handle_search_memories(self, search_req: APISearchRequest) -> Sear...
    method _apply_relativity_threshold (line 104) | def _apply_relativity_threshold(results: dict[str, Any], relativity: f...
    method _dedup_text_memories (line 137) | def _dedup_text_memories(self, results: dict[str, Any], target_top_k: ...
    method _mmr_dedup_text_memories (line 181) | def _mmr_dedup_text_memories(
    method _is_unrelated (line 412) | def _is_unrelated(
    method _max_similarity (line 421) | def _max_similarity(
    method _extract_embeddings (line 428) | def _extract_embeddings(self, memories: list[dict[str, Any]]) -> list[...
    method _strip_embeddings (line 457) | def _strip_embeddings(results: dict[str, Any]) -> None:
    method _dice_similarity (line 467) | def _dice_similarity(text1: str, text2: str) -> float:
    method _bigram_similarity (line 491) | def _bigram_similarity(text1: str, text2: str) -> float:
    method _tfidf_similarity (line 518) | def _tfidf_similarity(text1: str, text2: str) -> float:
    method _is_text_highly_similar_optimized (line 564) | def _is_text_highly_similar_optimized(
    method _dice_similarity (line 629) | def _dice_similarity(text1: str, text2: str) -> float:
    method _bigram_similarity (line 653) | def _bigram_similarity(text1: str, text2: str) -> float:
    method _tfidf_similarity (line 680) | def _tfidf_similarity(text1: str, text2: str) -> float:
    method _is_text_highly_similar_optimized (line 726) | def _is_text_highly_similar_optimized(
    method _resolve_cube_ids (line 790) | def _resolve_cube_ids(self, search_req: APISearchRequest) -> list[str]:
    method _build_cube_view (line 802) | def _build_cube_view(self, search_req: APISearchRequest, searcher=None...

FILE: src/memos/api/handlers/suggestion_handler.py
  function _get_further_suggestion (line 26) | def _get_further_suggestion(
  function handle_get_suggestion_queries (line 62) | def handle_get_suggestion_queries(

FILE: src/memos/api/mcp_serve.py
  function load_default_config (line 18) | def load_default_config(user_id="default_user"):
  class MOSMCPServer (line 125) | class MOSMCPServer:
    method __init__ (line 128) | def __init__(self, mos_instance: MOS | None = None):
    method _setup_tools (line 139) | def _setup_tools(self):
  function _run_mcp (line 563) | def _run_mcp(self, transport: str = "stdio", **kwargs):

FILE: src/memos/api/middleware/auth.py
  function _get_auth_pool (line 34) | def _get_auth_pool():
  function hash_api_key (line 60) | def hash_api_key(key: str) -> str:
  function validate_key_format (line 65) | def validate_key_format(key: str) -> bool:
  function get_key_prefix (line 79) | def get_key_prefix(key: str) -> str:
  function lookup_api_key (line 84) | async def lookup_api_key(key_hash: str) -> dict[str, Any] | None:
  function is_internal_request (line 144) | def is_internal_request(request: Request) -> bool:
  function verify_api_key (line 157) | async def verify_api_key(
  function require_scope (line 240) | def require_scope(required_scope: str):

FILE: src/memos/api/middleware/rate_limit.py
  function _get_redis (line 36) | def _get_redis():
  function _get_client_key (line 54) | def _get_client_key(request: Request) -> str:
  function _check_rate_limit_redis (line 77) | def _check_rate_limit_redis(key: str) -> tuple[bool, int, int]:
  function _check_rate_limit_memory (line 122) | def _check_rate_limit_memory(key: str) -> tuple[bool, int, int]:
  class RateLimitMiddleware (line 153) | class RateLimitMiddleware(BaseHTTPMiddleware):
    method dispatch (line 168) | async def dispatch(self, request: Request, call_next: Callable) -> Res...

FILE: src/memos/api/middleware/request_context.py
  function extract_trace_id_from_headers (line 21) | def extract_trace_id_from_headers(request: Request) -> str | None:
  class RequestContextMiddleware (line 29) | class RequestContextMiddleware(BaseHTTPMiddleware):
    method __init__ (line 39) | def __init__(self, app, source: str | None = None):
    method dispatch (line 50) | async def dispatch(self, request: Request, call_next: Callable) -> Res...

FILE: src/memos/api/product_models.py
  class BaseRequest (line 16) | class BaseRequest(BaseModel):
  class BaseResponse (line 20) | class BaseResponse(BaseModel, Generic[T]):
  class UserRegisterRequest (line 29) | class UserRegisterRequest(BaseRequest):
  class GetMemoryPlaygroundRequest (line 40) | class GetMemoryPlaygroundRequest(BaseRequest):
  class Message (line 53) | class Message(BaseModel):
  class MemoryCreate (line 58) | class MemoryCreate(BaseRequest):
  class MemCubeRegister (line 66) | class MemCubeRegister(BaseRequest):
  class ChatRequest (line 71) | class ChatRequest(BaseRequest):
    method _convert_deprecated_fields (line 144) | def _convert_deprecated_fields(self):
  class ChatPlaygroundRequest (line 174) | class ChatPlaygroundRequest(ChatRequest):
  class ChatBusinessRequest (line 182) | class ChatBusinessRequest(ChatRequest):
  class ChatCompleteRequest (line 189) | class ChatCompleteRequest(BaseRequest):
  class UserCreate (line 216) | class UserCreate(BaseRequest):
  class CubeShare (line 222) | class CubeShare(BaseRequest):
  class SimpleResponse (line 227) | class SimpleResponse(BaseResponse[None]):
  class UserRegisterResponse (line 231) | class UserRegisterResponse(BaseResponse[dict]):
  class MemoryResponse (line 235) | class MemoryResponse(BaseResponse[list]):
  class SuggestionResponse (line 239) | class SuggestionResponse(BaseResponse[list]):
  class AddStatusResponse (line 245) | class AddStatusResponse(BaseResponse[dict]):
  class ConfigResponse (line 249) | class ConfigResponse(BaseResponse[None]):
  class SearchResponse (line 253) | class SearchResponse(BaseResponse[dict]):
  class ChatResponse (line 257) | class ChatResponse(BaseResponse[str]):
  class GetMemoryResponse (line 261) | class GetMemoryResponse(BaseResponse[dict]):
  class DeleteMemoryResponse (line 265) | class DeleteMemoryResponse(BaseResponse[dict]):
  class UserResponse (line 269) | class UserResponse(BaseResponse[dict]):
  class UserListResponse (line 273) | class UserListResponse(BaseResponse[list]):
  class MemoryCreateRequest (line 277) | class MemoryCreateRequest(BaseRequest):
  class SearchRequest (line 291) | class SearchRequest(BaseRequest):
  class APISearchRequest (line 301) | class APISearchRequest(BaseRequest):
    method _convert_deprecated_fields (line 483) | def _convert_deprecated_fields(self) -> "APISearchRequest":
  class APIADDRequest (line 518) | class APIADDRequest(BaseRequest):
    method _convert_deprecated_fields (line 638) | def _convert_deprecated_fields(self) -> "APIADDRequest":
  class APIFeedbackRequest (line 718) | class APIFeedbackRequest(BaseRequest):
  class APIChatCompleteRequest (line 762) | class APIChatCompleteRequest(BaseRequest):
  class AddStatusRequest (line 822) | class AddStatusRequest(BaseRequest):
  class GetMemoryRequest (line 830) | class GetMemoryRequest(BaseRequest):
  class GetMemoryDashboardRequest (line 848) | class GetMemoryDashboardRequest(GetMemoryRequest):
  class DeleteMemoryRequest (line 854) | class DeleteMemoryRequest(BaseRequest):
  class SuggestionRequest (line 870) | class SuggestionRequest(BaseRequest):
  class MessageDetail (line 882) | class MessageDetail(BaseModel):
  class MemoryDetail (line 888) | class MemoryDetail(BaseModel):
  class FileDetail (line 894) | class FileDetail(BaseModel):
  class GetMessagesData (line 900) | class GetMessagesData(BaseModel):
  class GetCreateKnowledgebaseData (line 908) | class GetCreateKnowledgebaseData(BaseModel):
  class SearchMemoryData (line 914) | class SearchMemoryData(BaseModel):
  class GetKnowledgebaseFileData (line 938) | class GetKnowledgebaseFileData(BaseModel):
  class GetMemoryData (line 946) | class GetMemoryData(BaseModel):
  class AddMessageData (line 957) | class AddMessageData(BaseModel):
  class DeleteMessageData (line 965) | class DeleteMessageData(BaseModel):
  class ChatMessageData (line 971) | class ChatMessageData(BaseModel):
  class GetTaskStatusMessageData (line 977) | class GetTaskStatusMessageData(BaseModel):
  class MemOSGetMessagesResponse (line 986) | class MemOSGetMessagesResponse(BaseModel):
    method messages (line 994) | def messages(self) -> list[MessageDetail]:
  class MemOSSearchResponse (line 999) | class MemOSSearchResponse(BaseModel):
    method memories (line 1007) | def memories(self) -> list[MemoryDetail]:
    method preferences (line 1012) | def preferences(self) -> list[MemoryDetail]:
    method tool_memories (line 1017) | def tool_memories(self) -> list[MemoryDetail]:
  class MemOSDeleteKnowledgebaseResponse (line 1022) | class MemOSDeleteKnowledgebaseResponse(BaseModel):
    method success (line 1030) | def success(self) -> bool:
  class MemOSDeleteMemoryResponse (line 1035) | class MemOSDeleteMemoryResponse(BaseModel):
    method success (line 1043) | def success(self) -> bool:
  class MemOSChatResponse (line 1048) | class MemOSChatResponse(BaseModel):
    method response (line 1056) | def response(self) -> str:
  class MemOSGetTaskStatusResponse (line 1061) | class MemOSGetTaskStatusResponse(BaseModel):
    method messages (line 1069) | def messages(self) -> list[GetTaskStatusMessageData]:
  class MemOSCreateKnowledgebaseResponse (line 1074) | class MemOSCreateKnowledgebaseResponse(BaseModel):
    method knowledgebase_id (line 1082) | def knowledgebase_id(self) -> str:
  class MemOSAddKnowledgebaseFileResponse (line 1087) | class MemOSAddKnowledgebaseFileResponse(BaseModel):
    method memories (line 1095) | def memories(self) -> list[dict[str, Any]]:
  class MemOSGetMemoryResponse (line 1100) | class MemOSGetMemoryResponse(BaseModel):
    method memories (line 1108) | def memories(self) -> list[MemoryDetail]:
    method preferences (line 1113) | def preferences(self) -> list[MessageDetail] | None:
  class MemOSGetKnowledgebaseFileResponse (line 1118) | class MemOSGetKnowledgebaseFileResponse(BaseModel):
    method files (line 1126) | def files(self) -> list[FileDetail]:
  class MemOSAddResponse (line 1131) | class MemOSAddResponse(BaseModel):
    method success (line 1139) | def success(self) -> bool:
    method task_id (line 1144) | def task_id(self) -> str:
    method status (line 1149) | def status(self) -> str:
  class MemOSAddFeedBackResponse (line 1154) | class MemOSAddFeedBackResponse(BaseModel):
    method success (line 1162) | def success(self) -> bool:
    method task_id (line 1167) | def task_id(self) -> str:
    method status (line 1172) | def status(self) -> str:
  class StatusRequest (line 1180) | class StatusRequest(BaseRequest):
  class StatusResponseItem (line 1187) | class StatusResponseItem(BaseModel):
  class StatusResponse (line 1196) | class StatusResponse(BaseResponse[list[StatusResponseItem]]):
  class TaskQueueData (line 1202) | class TaskQueueData(BaseModel):
  class TaskQueueResponse (line 1224) | class TaskQueueResponse(BaseResponse[TaskQueueData]):
  class TaskSummary (line 1230) | class TaskSummary(BaseModel):
  class AllStatusResponseData (line 1244) | class AllStatusResponseData(BaseModel):
  class AllStatusResponse (line 1255) | class AllStatusResponse(BaseResponse[AllStatusResponseData]):
  class GetUserNamesByMemoryIdsRequest (line 1264) | class GetUserNamesByMemoryIdsRequest(BaseRequest):
  class GetUserNamesByMemoryIdsResponse (line 1270) | class GetUserNamesByMemoryIdsResponse(BaseResponse[dict[str, str | None]]):
  class ExistMemCubeIdRequest (line 1274) | class ExistMemCubeIdRequest(BaseRequest):
  class ExistMemCubeIdResponse (line 1280) | class ExistMemCubeIdResponse(BaseResponse[dict[str, bool]]):
  class DeleteMemoryByRecordIdRequest (line 1284) | class DeleteMemoryByRecordIdRequest(BaseRequest):
  class DeleteMemoryByRecordIdResponse (line 1292) | class DeleteMemoryByRecordIdResponse(BaseResponse[dict]):
  class RecoverMemoryByRecordIdRequest (line 1296) | class RecoverMemoryByRecordIdRequest(BaseRequest):
  class RecoverMemoryByRecordIdResponse (line 1303) | class RecoverMemoryByRecordIdResponse(BaseResponse[dict]):

FILE: src/memos/api/routers/admin_router.py
  class CreateKeyRequest (line 31) | class CreateKeyRequest(BaseModel):
  class CreateKeyResponse (line 38) | class CreateKeyResponse(BaseModel):
  class KeyListResponse (line 46) | class KeyListResponse(BaseModel):
  class RevokeKeyRequest (line 51) | class RevokeKeyRequest(BaseModel):
  class SimpleResponse (line 55) | class SimpleResponse(BaseModel):
  function _get_db_connection (line 60) | def _get_db_connection():
  function create_key (line 79) | def create_key(
  function list_keys (line 126) | def list_keys(
  function revoke_key (line 156) | def revoke_key(
  function generate_new_master_key (line 189) | def generate_new_master_key(
  function admin_health (line 219) | def admin_health():

FILE: src/memos/api/routers/product_router.py
  function get_mos_product_instance (line 38) | def get_mos_product_instance():
  function set_config (line 71) | def set_config(config):
  function register_user (line 79) | def register_user(user_req: UserRegisterRequest):
  function get_suggestion_queries (line 122) | def get_suggestion_queries(user_id: str):
  function get_suggestion_queries_post (line 142) | def get_suggestion_queries_post(suggestion_req: SuggestionRequest):
  function get_all_memories (line 162) | def get_all_memories(memory_req: GetMemoryPlaygroundRequest):
  function create_memory (line 189) | def create_memory(memory_req: MemoryCreateRequest):
  function search_memories (line 264) | def search_memories(search_req: SearchRequest):
  function chat (line 289) | def chat(chat_req: ChatRequest):
  function chat_complete (line 334) | def chat_complete(chat_req: ChatCompleteRequest):
  function list_users (line 368) | def list_users():
  function get_user_info (line 380) | async def get_user_info(user_id: str):
  function get_config (line 396) | def get_config(user_id: str):
  function get_user_config (line 406) | def get_user_config(user_id: str):
  function update_user_config (line 430) | def update_user_config(user_id: str, config_data: dict):
  function get_instance_status (line 455) | def get_instance_status():
  function get_active_user_count (line 469) | def get_active_user_count():

FILE: src/memos/api/routers/server_router.py
  function search_memories (line 105) | def search_memories(search_req: APISearchRequest):
  function add_memories (line 121) | def add_memories(add_req: APIADDRequest):
  function scheduler_allstatus (line 140) | def scheduler_allstatus():
  function scheduler_status (line 150) | def scheduler_status(
  function scheduler_task_queue_status (line 167) | def scheduler_task_queue_status(
  function scheduler_wait (line 177) | def scheduler_wait(
  function scheduler_wait_stream (line 192) | def scheduler_wait_stream(
  function chat_complete (line 213) | def chat_complete(chat_req: APIChatCompleteRequest):
  function chat_stream (line 227) | def chat_stream(chat_req: ChatRequest):
  function chat_stream_playground (line 242) | def chat_stream_playground(chat_req: ChatPlaygroundRequest):
  function get_suggestion_queries (line 266) | def get_suggestion_queries(suggestion_req: SuggestionRequest):
  function get_all_memories (line 283) | def get_all_memories(memory_req: GetMemoryPlaygroundRequest):
  function get_memories (line 313) | def get_memories(memory_req: GetMemoryRequest):
  function get_memory_by_id (line 321) | def get_memory_by_id(memory_id: str):
  function get_memory_by_ids (line 329) | def get_memory_by_ids(memory_ids: list[str]):
  function delete_memories (line 339) | def delete_memories(memory_req: DeleteMemoryRequest):
  function feedback_memories (line 351) | def feedback_memories(feedback_req: APIFeedbackRequest):
  function get_user_names_by_memory_ids (line 370) | def get_user_names_by_memory_ids(request: GetUserNamesByMemoryIdsRequest):
  function exist_mem_cube_id (line 386) | def exist_mem_cube_id(request: ExistMemCubeIdRequest):
  function chat_stream_business_user (line 396) | def chat_stream_business_user(chat_req: ChatBusinessRequest):
  function delete_memory_by_record_id (line 411) | def delete_memory_by_record_id(memory_req: DeleteMemoryByRecordIdRequest):
  function recover_memory_by_record_id (line 431) | def recover_memory_by_record_id(memory_req: RecoverMemoryByRecordIdReque...
  function get_memories_dashboard (line 448) | def get_memories_dashboard(memory_req: GetMemoryDashboardRequest):

FILE: src/memos/api/server_api_ext.py
  class SecurityHeadersMiddleware (line 48) | class SecurityHeadersMiddleware(BaseHTTPMiddleware):
    method dispatch (line 51) | async def dispatch(self, request: Request, call_next) -> Response:
  function health_check (line 111) | async def health_check():

FILE: src/memos/api/start_api.py
  function get_mos_instance (line 49) | def get_mos_instance():
  class BaseRequest (line 85) | class BaseRequest(BaseModel):
  class BaseResponse (line 93) | class BaseResponse(BaseModel, Generic[T]):
  class Message (line 103) | class Message(BaseModel):
  class MemoryCreate (line 116) | class MemoryCreate(BaseRequest):
  class SearchRequest (line 137) | class SearchRequest(BaseRequest):
  class MemCubeRegister (line 150) | class MemCubeRegister(BaseRequest):
  class ChatRequest (line 161) | class ChatRequest(BaseRequest):
  class UserCreate (line 169) | class UserCreate(BaseRequest):
  class CubeShare (line 177) | class CubeShare(BaseRequest):
  class SimpleResponse (line 183) | class SimpleResponse(BaseResponse[None]):
  class ConfigResponse (line 187) | class ConfigResponse(BaseResponse[None]):
  class MemoryResponse (line 191) | class MemoryResponse(BaseResponse[dict]):
  class SearchResponse (line 195) | class SearchResponse(BaseResponse[dict]):
  class ChatResponse (line 199) | class ChatResponse(BaseResponse[str]):
  class UserResponse (line 203) | class UserResponse(BaseResponse[dict]):
  class UserListResponse (line 207) | class UserListResponse(BaseResponse[list]):
  function set_config (line 212) | async def set_config(config: MOSConfig):
  function create_user (line 232) | async def create_user(user_create: UserCreate):
  function list_users (line 243) | async def list_users():
  function get_user_info (line 251) | async def get_user_info():
  function register_mem_cube (line 259) | async def register_mem_cube(mem_cube: MemCubeRegister):
  function unregister_mem_cube (line 273) | async def unregister_mem_cube(mem_cube_id: str, user_id: str | None = No...
  function share_cube (line 285) | async def share_cube(cube_id: str, share_request: CubeShare):
  function add_memory (line 296) | async def add_memory(memory_create: MemoryCreate):
  function get_all_memories (line 324) | async def get_all_memories(
  function get_memory (line 337) | async def get_memory(mem_cube_id: str, memory_id: str, user_id: str | No...
  function search_memories (line 345) | async def search_memories(search_req: SearchRequest):
  function update_memory (line 359) | async def update_memory(
  function delete_memory (line 376) | async def delete_memory(mem_cube_id: str, memory_id: str, user_id: str |...
  function delete_all_memories (line 384) | async def delete_all_memories(mem_cube_id: str, user_id: str | None = No...
  function chat (line 392) | async def chat(chat_req: ChatRequest):
  function home (line 402) | async def home():
  function value_error_handler (line 408) | async def value_error_handler(request: Request, exc: ValueError):
  function global_exception_handler (line 417) | async def global_exception_handler(request: Request, exc: Exception):

FILE: src/memos/api/utils/api_keys.py
  class APIKey (line 15) | class APIKey:
  function generate_api_key (line 23) | def generate_api_key() -> APIKey:
  function hash_key (line 43) | def hash_key(key: str) -> str:
  function validate_key_format (line 48) | def validate_key_format(key: str) -> bool:
  function generate_master_key (line 71) | def generate_master_key() -> tuple[str, str]:
  function create_api_key_in_db (line 84) | def create_api_key_in_db(
  function revoke_api_key (line 134) | def revoke_api_key(conn, key_id: str) -> bool:
  function list_api_keys (line 150) | def list_api_keys(conn, user_name: str | None = None) -> list[dict]:

FILE: src/memos/chunkers/base.py
  class Chunk (line 8) | class Chunk:
    method __init__ (line 11) | def __init__(self, text: str, token_count: int, sentences: list[str]):
  class BaseChunker (line 17) | class BaseChunker(ABC):
    method __init__ (line 21) | def __init__(self, config: BaseChunkerConfig):
    method chunk (line 25) | def chunk(self, text: str) -> list[Chunk]:
    method protect_urls (line 28) | def protect_urls(self, text: str) -> tuple[str, dict[str, str]]:
    method restore_urls (line 50) | def restore_urls(self, text: str, url_map: dict[str, str]) -> str:

FILE: src/memos/chunkers/charactertext_chunker.py
  class CharacterTextChunker (line 11) | class CharacterTextChunker(BaseChunker):
    method __init__ (line 19) | def __init__(
    method chunk (line 37) | def chunk(self, text: str, **kwargs) -> list[str] | list[Chunk]:

FILE: src/memos/chunkers/factory.py
  class ChunkerFactory (line 10) | class ChunkerFactory:
    method from_config (line 19) | def from_config(cls, config_factory: ChunkerConfigFactory) -> BaseChun...

FILE: src/memos/chunkers/markdown_chunker.py
  class MarkdownChunker (line 13) | class MarkdownChunker(BaseChunker):
    method __init__ (line 21) | def __init__(
    method chunk (line 51) | def chunk(self, text: str, **kwargs) -> list[str] | list[Chunk]:
    method _detect_malformed_headers (line 78) | def _detect_malformed_headers(self, text: str) -> bool:
    method _fix_header_hierarchy (line 116) | def _fix_header_hierarchy(self, text: str) -> str:

FILE: src/memos/chunkers/sentence_chunker.py
  class SentenceChunker (line 11) | class SentenceChunker(BaseChunker):
    method __init__ (line 19) | def __init__(self, config: SentenceChunkerConfig):
    method chunk (line 44) | def chunk(self, text: str) -> list[str] | list[Chunk]:

FILE: src/memos/chunkers/simple_chunker.py
  class SimpleTextSplitter (line 1) | class SimpleTextSplitter:
    method __init__ (line 4) | def __init__(self, chunk_size: int, chunk_overlap: int):
    method chunk (line 8) | def chunk(self, text: str, **kwargs) -> list[str]:
    method _simple_split_text (line 11) | def _simple_split_text(self, text: str, chunk_size: int, chunk_overlap...

FILE: src/memos/cli.py
  function export_openapi (line 14) | def export_openapi(output: str) -> bool:
  function download_examples (line 30) | def download_examples(dest: str) -> bool:
  function main (line 70) | def main():

FILE: src/memos/configs/base.py
  class BaseConfig (line 15) | class BaseConfig(BaseModel):
    method set_default_schema (line 31) | def set_default_schema(self) -> "BaseConfig":
    method from_json_file (line 44) | def from_json_file(cls, json_path: str) -> Any:
    method to_json_file (line 50) | def to_json_file(self, json_path: str) -> None:
    method from_yaml_file (line 59) | def from_yaml_file(cls, yaml_path: str) -> Any:
    method to_yaml_file (line 65) | def to_yaml_file(self, yaml_path: str) -> None:
    method get (line 81) | def get(self, key, default=None):

FILE: src/memos/configs/chunker.py
  class BaseChunkerConfig (line 8) | class BaseChunkerConfig(BaseConfig):
  class SentenceChunkerConfig (line 20) | class SentenceChunkerConfig(BaseChunkerConfig):
  class MarkdownChunkerConfig (line 24) | class MarkdownChunkerConfig(BaseChunkerConfig):
  class ChunkerConfigFactory (line 37) | class ChunkerConfigFactory(BaseConfig):
    method validate_backend (line 50) | def validate_backend(cls, backend: str) -> str:
    method create_config (line 57) | def create_config(self) -> "ChunkerConfigFactory":

FILE: src/memos/configs/embedder.py
  class BaseEmbedderConfig (line 8) | class BaseEmbedderConfig(BaseConfig):
  class OllamaEmbedderConfig (line 25) | class OllamaEmbedderConfig(BaseEmbedderConfig):
  class ArkEmbedderConfig (line 29) | class ArkEmbedderConfig(BaseEmbedderConfig):
  class SenTranEmbedderConfig (line 41) | class SenTranEmbedderConfig(BaseEmbedderConfig):
  class UniversalAPIEmbedderConfig (line 50) | class UniversalAPIEmbedderConfig(BaseEmbedderConfig):
  class EmbedderConfigFactory (line 80) | class EmbedderConfigFactory(BaseConfig):
    method validate_backend (line 95) | def validate_backend(cls, backend: str) -> str:
    method create_config (line 102) | def create_config(self) -> "EmbedderConfigFactory":

FILE: src/memos/configs/graph_db.py
  class BaseGraphDBConfig (line 9) | class BaseGraphDBConfig(BaseConfig):
  class Neo4jGraphDBConfig (line 17) | class Neo4jGraphDBConfig(BaseGraphDBConfig):
    method validate_config (line 74) | def validate_config(self):
  class Neo4jCommunityGraphDBConfig (line 83) | class Neo4jCommunityGraphDBConfig(Neo4jGraphDBConfig):
    method validate_community (line 98) | def validate_community(self):
  class NebulaGraphDBConfig (line 106) | class NebulaGraphDBConfig(BaseGraphDBConfig):
    method validate_config (line 150) | def validate_config(self):
  class PolarDBGraphDBConfig (line 157) | class PolarDBGraphDBConfig(BaseConfig):
    method validate_config (line 234) | def validate_config(self):
  class PostgresGraphDBConfig (line 241) | class PostgresGraphDBConfig(BaseConfig):
    method validate_config (line 286) | def validate_config(self):
  class GraphDBConfigFactory (line 295) | class GraphDBConfigFactory(BaseModel):
    method validate_backend (line 309) | def validate_backend(cls, backend: str) -> str:
    method instantiate_config (line 315) | def instantiate_config(self):

FILE: src/memos/configs/internet_retriever.py
  class BaseInternetRetrieverConfig (line 12) | class BaseInternetRetrieverConfig(BaseConfig):
  class GoogleCustomSearchConfig (line 21) | class GoogleCustomSearchConfig(BaseInternetRetrieverConfig):
  class BingSearchConfig (line 31) | class BingSearchConfig(BaseInternetRetrieverConfig):
  class XinyuSearchConfig (line 41) | class XinyuSearchConfig(BaseInternetRetrieverConfig):
  class BochaSearchConfig (line 58) | class BochaSearchConfig(BaseInternetRetrieverConfig):
  class InternetRetrieverConfigFactory (line 70) | class InternetRetrieverConfigFactory(BaseConfig):
    method validate_backend (line 89) | def validate_backend(cls, backend: str | None) -> str | None:
    method create_config (line 96) | def create_config(self) -> "InternetRetrieverConfigFactory":

FILE: src/memos/configs/llm.py
  class BaseLLMConfig (line 8) | class BaseLLMConfig(BaseConfig):
  class OpenAILLMConfig (line 25) | class OpenAILLMConfig(BaseLLMConfig):
  class OpenAIResponsesLLMConfig (line 49) | class OpenAIResponsesLLMConfig(BaseLLMConfig):
  class QwenLLMConfig (line 61) | class QwenLLMConfig(OpenAILLMConfig):
  class DeepSeekLLMConfig (line 68) | class DeepSeekLLMConfig(OpenAILLMConfig):
  class AzureLLMConfig (line 75) | class AzureLLMConfig(BaseLLMConfig):
  class AzureResponsesLLMConfig (line 87) | class AzureResponsesLLMConfig(BaseLLMConfig):
  class OllamaLLMConfig (line 99) | class OllamaLLMConfig(BaseLLMConfig):
  class HFLLMConfig (line 110) | class HFLLMConfig(BaseLLMConfig):
  class VLLMLLMConfig (line 121) | class VLLMLLMConfig(BaseLLMConfig):
  class LLMConfigFactory (line 134) | class LLMConfigFactory(BaseConfig):
    method validate_backend (line 154) | def validate_backend(cls, backend: str) -> str:
    method create_config (line 161) | def create_config(self) -> "LLMConfigFactory":

FILE: src/memos/configs/mem_agent.py
  class BaseAgentConfig (line 8) | class BaseAgentConfig(BaseConfig):
  class SimpleAgentConfig (line 15) | class SimpleAgentConfig(BaseAgentConfig):
  class DeepSearchAgentConfig (line 24) | class DeepSearchAgentConfig(BaseAgentConfig):
  class MemAgentConfigFactory (line 31) | class MemAgentConfigFactory(BaseConfig):
    method validate_backend (line 44) | def validate_backend(cls, backend: str) -> str:
    method create_config (line 51) | def create_config(self) -> "MemAgentConfigFactory":

FILE: src/memos/configs/mem_chat.py
  class BaseMemChatConfig (line 12) | class BaseMemChatConfig(BaseConfig):
  class SimpleMemChatConfig (line 29) | class SimpleMemChatConfig(BaseMemChatConfig):
  class MemChatConfigFactory (line 59) | class MemChatConfigFactory(BaseConfig):
    method validate_backend (line 71) | def validate_backend(cls, backend: str) -> str:
    method create_config (line 78) | def create_config(self) -> "MemChatConfigFactory":

FILE: src/memos/configs/mem_cube.py
  class BaseMemCubeConfig (line 16) | class BaseMemCubeConfig(BaseConfig):
  class GeneralMemCubeConfig (line 31) | class GeneralMemCubeConfig(BaseMemCubeConfig):
    method validate_text_mem (line 65) | def validate_text_mem(cls, text_mem: MemoryConfigFactory) -> MemoryCon...
    method validate_act_mem (line 76) | def validate_act_mem(cls, act_mem: MemoryConfigFactory) -> MemoryConfi...
    method validate_para_mem (line 87) | def validate_para_mem(cls, para_mem: MemoryConfigFactory) -> MemoryCon...
    method validate_pref_mem (line 98) | def validate_pref_mem(cls, pref_mem: MemoryConfigFactory) -> MemoryCon...

FILE: src/memos/configs/mem_os.py
  class MOSConfig (line 14) | class MOSConfig(BaseConfig):
  class MemOSConfigFactory (line 75) | class MemOSConfigFactory(BaseConfig):
    method create_config (line 81) | def create_config(self) -> "MemOSConfigFactory":

FILE: src/memos/configs/mem_reader.py
  class BaseMemReaderConfig (line 12) | class BaseMemReaderConfig(BaseConfig):
    method parse_datetime (line 21) | def parse_datetime(cls, value):
  class SimpleStructMemReaderConfig (line 55) | class SimpleStructMemReaderConfig(BaseMemReaderConfig):
  class MultiModalStructMemReaderConfig (line 62) | class MultiModalStructMemReaderConfig(BaseMemReaderConfig):
  class StrategyStructMemReaderConfig (line 81) | class StrategyStructMemReaderConfig(BaseMemReaderConfig):
  class MemReaderConfigFactory (line 87) | class MemReaderConfigFactory(BaseConfig):
    method validate_backend (line 101) | def validate_backend(cls, backend: str) -> str:
    method create_config (line 108) | def create_config(self) -> "MemReaderConfigFactory":

FILE: src/memos/configs/mem_scheduler.py
  class BaseSchedulerConfig (line 29) | class BaseSchedulerConfig(BaseConfig):
  class GeneralSchedulerConfig (line 77) | class GeneralSchedulerConfig(BaseSchedulerConfig):
  class OptimizedSchedulerConfig (line 127) | class OptimizedSchedulerConfig(GeneralSchedulerConfig):
  class SchedulerConfigFactory (line 135) | class SchedulerConfigFactory(BaseConfig):
    method validate_backend (line 149) | def validate_backend(cls, backend: str) -> str:
    method create_config (line 156) | def create_config(self) -> "SchedulerConfigFactory":
  class RabbitMQConfig (line 166) | class RabbitMQConfig(
  class GraphDBAuthConfig (line 193) | class GraphDBAuthConfig(BaseConfig, DictConversionMixin, EnvConfigMixin):
  class OpenAIConfig (line 210) | class OpenAIConfig(BaseConfig, DictConversionMixin, EnvConfigMixin):
  class AuthConfig (line 216) | class AuthConfig(BaseConfig, DictConversionMixin):
    method validate_partial_initialization (line 225) | def validate_partial_initialization(self) -> "AuthConfig":
    method from_local_config (line 267) | def from_local_config(cls, config_path: str | Path | None = None) -> "...
    method from_local_env (line 309) | def from_local_env(cls) -> "AuthConfig":
    method set_openai_config_to_environment (line 374) | def set_openai_config_to_environment(self):
    method default_config_exists (line 385) | def default_config_exists(cls) -> bool:

FILE: src/memos/configs/mem_user.py
  class BaseUserManagerConfig (line 8) | class BaseUserManagerConfig(BaseConfig):
  class SQLiteUserManagerConfig (line 14) | class SQLiteUserManagerConfig(BaseUserManagerConfig):
  class MySQLUserManagerConfig (line 23) | class MySQLUserManagerConfig(BaseUserManagerConfig):
  class RedisUserManagerConfig (line 34) | class RedisUserManagerConfig(BaseUserManagerConfig):
  class UserManagerConfigFactory (line 45) | class UserManagerConfigFactory(BaseModel):
    method validate_backend (line 61) | def validate_backend(cls, backend: str) -> 
Condensed preview — 862 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (8,457K chars).
[
  {
    "path": ".github/CONTRIBUTING",
    "chars": 187,
    "preview": "Please read https://memos-docs.openmem.net/contribution/overview to learn how to contribute to this repository. 🌟\n\n请阅读 h"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "chars": 1818,
    "preview": "name: \"\\U0001F41B Bug Report\"\ndescription: Report a bug to help us improve MemOS | 报告错误以帮助我们改进 MemOS\ntitle: \"fix: \"\nlabe"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 796,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: \"\\U0001F527 GitHub Pull Requests\"\n    url: https://github.com/MemTe"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.yml",
    "chars": 1268,
    "preview": "name: \"\\U0001F680 Feature request\"\ndescription: Submit a request for a new feature | 申请添加新功能\ntitle: \"feat: \"\nlabels: [\"e"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1730,
    "preview": "## Description\n\nPlease include a summary of the change, the problem it solves, the implementation approach, and relevant"
  },
  {
    "path": ".github/workflows/openclaw-plugin-publish.yml",
    "chars": 3050,
    "preview": "name: OpenClaw Plugin — Build Prebuilds & Publish\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        descripti"
  },
  {
    "path": ".github/workflows/python-release.yml",
    "chars": 687,
    "preview": "name: Upload Python Package to PyPI\n\non:\n  release:\n    types: [published]\n\npermissions:\n  contents: read\n\njobs:\n  deplo"
  },
  {
    "path": ".github/workflows/python-tests.yml",
    "chars": 3430,
    "preview": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more inform"
  },
  {
    "path": ".github/workflows/stale.yml",
    "chars": 999,
    "preview": "name: \"Mark stale issues and PRs\"\n\non:\n  schedule:\n    - cron: '0 2 * * *' # Runs every day at 2 AM UTC\n\npermissions:\n  "
  },
  {
    "path": ".gitignore",
    "chars": 4164,
    "preview": "# MemOS home\n.memos/\n\n# Temporary files\ntmp/\n**/tmp_data/\n\n# evaluation data\n*.csv\n*.jsonl\n**settings.json**\nevaluation/"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 1588,
    "preview": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v5.0.0\n    hooks:\n      - id: trailing-whitespa"
  },
  {
    "path": "LICENSE",
    "chars": 11358,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "Makefile",
    "chars": 831,
    "preview": ".PHONY: test test-report test-cov\n\ninstall:\n\tpoetry install --extras all --with dev --with test\n\tpoetry run pre-commit i"
  },
  {
    "path": "README.md",
    "chars": 16338,
    "preview": "<div align=\"center\">\n  <a href=\"https://memos.openmem.net/\">\n    <img src=\"https://statics.memtensor.com.cn/memos/memos-"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/.gitignore",
    "chars": 164,
    "preview": "# Dependencies\nnode_modules\n\n# Environment variables\n.env\n.env.*\n\n# NPM\n.npmrc\n\n# System\n.DS_Store\nThumbs.db\n\n# Logs\nnpm"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/README.md",
    "chars": 7229,
    "preview": "# MemOS Cloud OpenClaw Plugin (Lifecycle)\n\nOfficial plugin maintained by MemTensor.\n\nA minimal OpenClaw lifecycle plugin"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/README_ZH.md",
    "chars": 5499,
    "preview": "# MemOS Cloud OpenClaw Plugin(Lifecycle 插件)\n\n官方维护:MemTensor。\n\n这是一个最小可用的 OpenClaw lifecycle 插件,功能是:\n- **召回记忆**:在每轮对话前从 Me"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/clawdbot.plugin.json",
    "chars": 3975,
    "preview": "{\n  \"id\": \"memos-cloud-openclaw-plugin\",\n  \"name\": \"MemOS Cloud OpenClaw Plugin\",\n  \"description\": \"MemOS Cloud recall +"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/index.js",
    "chars": 15625,
    "preview": "#!/usr/bin/env node\nimport {\n  addMessage,\n  buildConfig,\n  extractResultData,\n  extractText,\n  formatRecallHookResult,\n"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/lib/check-update.js",
    "chars": 9467,
    "preview": "import https from \"https\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { s"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/lib/memos-cloud-api.js",
    "chars": 17472,
    "preview": "import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { se"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/moltbot.plugin.json",
    "chars": 3975,
    "preview": "{\n  \"id\": \"memos-cloud-openclaw-plugin\",\n  \"name\": \"MemOS Cloud OpenClaw Plugin\",\n  \"description\": \"MemOS Cloud recall +"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/openclaw.plugin.json",
    "chars": 3975,
    "preview": "{\n  \"id\": \"memos-cloud-openclaw-plugin\",\n  \"name\": \"MemOS Cloud OpenClaw Plugin\",\n  \"description\": \"MemOS Cloud recall +"
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/package.json",
    "chars": 1311,
    "preview": "{\n  \"name\": \"@memtensor/memos-cloud-openclaw-plugin\",\n  \"version\": \"0.1.9\",\n  \"description\": \"OpenClaw lifecycle plugin "
  },
  {
    "path": "apps/MemOS-Cloud-OpenClaw-Plugin/scripts/sync-version.js",
    "chars": 1438,
    "preview": "import fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(imp"
  },
  {
    "path": "apps/memos-local-openclaw/.gitignore",
    "chars": 303,
    "preview": "node_modules/\ndist/\n*.tsbuildinfo\n.env\n\n# OS files\n.DS_Store\nThumbs.db\n\n# IDE\n.vscode/\n.idea/\n\n# Generated / non-essenti"
  },
  {
    "path": "apps/memos-local-openclaw/README.md",
    "chars": 40400,
    "preview": "# 🧠 MemOS — OpenClaw Memory Plugin\n\n[![npm version](https://img.shields.io/npm/v/@memtensor/memos-local-openclaw-plugin)"
  },
  {
    "path": "apps/memos-local-openclaw/index.ts",
    "chars": 54727,
    "preview": "/**\n * OpenClaw Plugin Entry — memos-local\n *\n * Full-write local memory with hybrid retrieval (RRF + MMR + recency).\n *"
  },
  {
    "path": "apps/memos-local-openclaw/openclaw.plugin.json",
    "chars": 1453,
    "preview": "{\n  \"id\": \"memos-local-openclaw-plugin\",\n  \"name\": \"MemOS Local Memory\",\n  \"description\": \"Full-write local conversation"
  },
  {
    "path": "apps/memos-local-openclaw/package.json",
    "chars": 1540,
    "preview": "{\n  \"name\": \"@memtensor/memos-local-openclaw-plugin\",\n  \"version\": \"1.0.3\",\n  \"description\": \"MemOS Local memory plugin "
  },
  {
    "path": "apps/memos-local-openclaw/plugin-impl.ts",
    "chars": 21260,
    "preview": "/**\n * MemOS Local Plugin Implementation — loaded by index.ts after ensuring deps.\n */\n\nimport type { OpenClawPluginApi "
  },
  {
    "path": "apps/memos-local-openclaw/scripts/mock-skills.ts",
    "chars": 15115,
    "preview": "/**\n * Mock skill data for testing the Skills viewer page.\n * Run: npx tsx scripts/mock-skills.ts\n */\nimport Database fr"
  },
  {
    "path": "apps/memos-local-openclaw/scripts/postinstall.cjs",
    "chars": 17049,
    "preview": "#!/usr/bin/env node\n\"use strict\";\n\nconst { spawnSync } = require(\"child_process\");\nconst path = require(\"path\");\nconst f"
  },
  {
    "path": "apps/memos-local-openclaw/scripts/refresh-skill.ts",
    "chars": 2860,
    "preview": "#!/usr/bin/env npx tsx\n/**\n * Regenerate a skill's SKILL.md from its source task, using updated prompts.\n * Usage: npx t"
  },
  {
    "path": "apps/memos-local-openclaw/scripts/refresh-summaries.ts",
    "chars": 6357,
    "preview": "import Database from \"better-sqlite3\";\nimport * as path from \"path\";\nimport * as os from \"os\";\nimport * as fs from \"fs\";"
  },
  {
    "path": "apps/memos-local-openclaw/scripts/run-accuracy-test.ts",
    "chars": 35355,
    "preview": "#!/usr/bin/env npx tsx\n/**\n * MemOS Accuracy Test — sends data through OpenClaw Gateway (real pipeline).\n *\n * Ingest us"
  },
  {
    "path": "apps/memos-local-openclaw/scripts/seed-test-data.ts",
    "chars": 11557,
    "preview": "/**\n * Seed script: inserts test data for Task-related features into the live database.\n *\n * Usage:  npx tsx scripts/se"
  },
  {
    "path": "apps/memos-local-openclaw/scripts/smoke-test.ts",
    "chars": 8190,
    "preview": "/**\n * Smoke Test — 用真实 API 跑通完整链路\n *\n * 用法:\n *   npx tsx scripts/smoke-test.ts\n *\n * 需要先在 .env 中配置好 EMBEDDING / SUMMARI"
  },
  {
    "path": "apps/memos-local-openclaw/scripts/start-viewer.ts",
    "chars": 2814,
    "preview": "/**\n * Standalone Viewer launcher — starts the Memory Viewer web UI\n * without needing the full OpenClaw plugin lifecycl"
  },
  {
    "path": "apps/memos-local-openclaw/scripts/test-agent-isolation.ts",
    "chars": 9033,
    "preview": "#!/usr/bin/env npx tsx\n/**\n * Multi-agent data isolation test.\n *\n * Writes data with different owner tags via initPlugi"
  },
  {
    "path": "apps/memos-local-openclaw/skill/browserwing-admin/SKILL.md",
    "chars": 16470,
    "preview": "---\nname: browserwing-admin\ndescription: Manage and operate BrowserWing — an intelligent browser automation platform. In"
  },
  {
    "path": "apps/memos-local-openclaw/skill/browserwing-executor/SKILL.md",
    "chars": 15627,
    "preview": "---\nname: browserwing-executor\ndescription: Control browser automation through HTTP API. Supports page navigation, eleme"
  },
  {
    "path": "apps/memos-local-openclaw/skill/memos-memory-guide/SKILL.md",
    "chars": 9153,
    "preview": "---\nname: memos-memory-guide\ndescription: \"Use the MemOS Local memory system to search and use the user's past conversat"
  },
  {
    "path": "apps/memos-local-openclaw/src/capture/index.ts",
    "chars": 9063,
    "preview": "import type { ConversationMessage, Role, Logger } from \"../types\";\n\nconst SKIP_ROLES: Set<Role> = new Set([\"system\"]);\n\n"
  },
  {
    "path": "apps/memos-local-openclaw/src/config.ts",
    "chars": 2677,
    "preview": "import * as path from \"path\";\nimport { DEFAULTS, type MemosLocalConfig, type PluginContext, type Logger } from \"./types\""
  },
  {
    "path": "apps/memos-local-openclaw/src/embedding/index.ts",
    "chars": 2674,
    "preview": "import type { EmbeddingConfig, Logger } from \"../types\";\nimport { embedOpenAI } from \"./providers/openai\";\nimport { embe"
  },
  {
    "path": "apps/memos-local-openclaw/src/embedding/local.ts",
    "chars": 1107,
    "preview": "import type { Logger } from \"../types\";\nimport { DEFAULTS } from \"../types\";\n\nlet extractorPromise: Promise<any> | null "
  },
  {
    "path": "apps/memos-local-openclaw/src/embedding/providers/cohere.ts",
    "chars": 1828,
    "preview": "import type { EmbeddingConfig, Logger } from \"../../types\";\n\nexport async function embedCohere(\n  texts: string[],\n  cfg"
  },
  {
    "path": "apps/memos-local-openclaw/src/embedding/providers/gemini.ts",
    "chars": 1082,
    "preview": "import type { EmbeddingConfig, Logger } from \"../../types\";\n\nexport async function embedGemini(\n  texts: string[],\n  cfg"
  },
  {
    "path": "apps/memos-local-openclaw/src/embedding/providers/mistral.ts",
    "chars": 935,
    "preview": "import type { EmbeddingConfig, Logger } from \"../../types\";\n\nexport async function embedMistral(\n  texts: string[],\n  cf"
  },
  {
    "path": "apps/memos-local-openclaw/src/embedding/providers/openai.ts",
    "chars": 1284,
    "preview": "import type { EmbeddingConfig, Logger } from \"../../types\";\n\nexport async function embedOpenAI(\n  texts: string[],\n  cfg"
  },
  {
    "path": "apps/memos-local-openclaw/src/embedding/providers/voyage.ts",
    "chars": 904,
    "preview": "import type { EmbeddingConfig, Logger } from \"../../types\";\n\nexport async function embedVoyage(\n  texts: string[],\n  cfg"
  },
  {
    "path": "apps/memos-local-openclaw/src/index.ts",
    "chars": 3481,
    "preview": "import { v4 as uuid } from \"uuid\";\nimport { buildContext } from \"./config\";\nimport { ensureSqliteBinding } from \"./stora"
  },
  {
    "path": "apps/memos-local-openclaw/src/ingest/chunker.ts",
    "chars": 5875,
    "preview": "export interface RawChunk {\n  content: string;\n  kind: \"paragraph\";\n}\n\nconst MAX_CHUNK_CHARS = 3000;\nconst MIN_CHUNK_CHA"
  },
  {
    "path": "apps/memos-local-openclaw/src/ingest/dedup.ts",
    "chars": 1884,
    "preview": "import { cosineSimilarity } from \"../storage/vector\";\nimport type { SqliteStore } from \"../storage/sqlite\";\nimport type "
  },
  {
    "path": "apps/memos-local-openclaw/src/ingest/providers/anthropic.ts",
    "chars": 14174,
    "preview": "import type { SummarizerConfig, Logger } from \"../../types\";\n\nconst SYSTEM_PROMPT = `You generate a retrieval-friendly t"
  },
  {
    "path": "apps/memos-local-openclaw/src/ingest/providers/bedrock.ts",
    "chars": 14576,
    "preview": "import type { SummarizerConfig, Logger } from \"../../types\";\n\nconst SYSTEM_PROMPT = `You generate a retrieval-friendly t"
  },
  {
    "path": "apps/memos-local-openclaw/src/ingest/providers/gemini.ts",
    "chars": 14520,
    "preview": "import type { SummarizerConfig, Logger } from \"../../types\";\n\nconst SYSTEM_PROMPT = `You generate a retrieval-friendly t"
  },
  {
    "path": "apps/memos-local-openclaw/src/ingest/providers/index.ts",
    "chars": 16697,
    "preview": "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { SummarizerConfig, SummaryProvider, Logger } from \""
  },
  {
    "path": "apps/memos-local-openclaw/src/ingest/providers/openai.ts",
    "chars": 16847,
    "preview": "import type { SummarizerConfig, Logger } from \"../../types\";\n\nconst SYSTEM_PROMPT = `You generate a retrieval-friendly t"
  },
  {
    "path": "apps/memos-local-openclaw/src/ingest/task-processor.ts",
    "chars": 20196,
    "preview": "import { v4 as uuid } from \"uuid\";\nimport type { SqliteStore } from \"../storage/sqlite\";\nimport type { PluginContext, Ta"
  },
  {
    "path": "apps/memos-local-openclaw/src/ingest/worker.ts",
    "chars": 11288,
    "preview": "import { v4 as uuid } from \"uuid\";\nimport { createHash } from \"crypto\";\nimport type { ConversationMessage, Chunk, Plugin"
  },
  {
    "path": "apps/memos-local-openclaw/src/recall/engine.ts",
    "chars": 10332,
    "preview": "import type { SqliteStore } from \"../storage/sqlite\";\nimport type { Embedder } from \"../embedding\";\nimport type { Plugin"
  },
  {
    "path": "apps/memos-local-openclaw/src/recall/mmr.ts",
    "chars": 1847,
    "preview": "import { cosineSimilarity } from \"../storage/vector\";\nimport type { SqliteStore } from \"../storage/sqlite\";\n\n/**\n * Maxi"
  },
  {
    "path": "apps/memos-local-openclaw/src/recall/recency.ts",
    "chars": 937,
    "preview": "/**\n * Time decay scoring (PRD §5.3)\n *\n * Applies exponential decay based on document age, biasing towards\n * more rece"
  },
  {
    "path": "apps/memos-local-openclaw/src/recall/rrf.ts",
    "chars": 766,
    "preview": "/**\n * Reciprocal Rank Fusion (PRD §5.2)\n *\n * Merges ranked lists from different retrieval sources (FTS, vector)\n * int"
  },
  {
    "path": "apps/memos-local-openclaw/src/shared/llm-call.ts",
    "chars": 7579,
    "preview": "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { SummarizerConfig, SummaryProvider, Logger, PluginC"
  },
  {
    "path": "apps/memos-local-openclaw/src/skill/bundled-memory-guide.ts",
    "chars": 372,
    "preview": "/**\n * Bundled MemOS memory-guide skill content.\n * Reads from skill/memos-memory-guide/SKILL.md at runtime (single sour"
  },
  {
    "path": "apps/memos-local-openclaw/src/skill/evaluator.ts",
    "chars": 7949,
    "preview": "import type { Chunk, Task, Skill, PluginContext } from \"../types\";\nimport { DEFAULTS } from \"../types\";\nimport { buildSk"
  },
  {
    "path": "apps/memos-local-openclaw/src/skill/evolver.ts",
    "chars": 14553,
    "preview": "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { SqliteStore } from \"../storage/sqlite\";\nimport typ"
  },
  {
    "path": "apps/memos-local-openclaw/src/skill/generator.ts",
    "chars": 21442,
    "preview": "import { v4 as uuid } from \"uuid\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { SqliteStore } f"
  },
  {
    "path": "apps/memos-local-openclaw/src/skill/installer.ts",
    "chars": 2027,
    "preview": "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { SqliteStore } from \"../storage/sqlite\";\nimport typ"
  },
  {
    "path": "apps/memos-local-openclaw/src/skill/upgrader.ts",
    "chars": 9601,
    "preview": "import { v4 as uuid } from \"uuid\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { SqliteStore } f"
  },
  {
    "path": "apps/memos-local-openclaw/src/skill/validator.ts",
    "chars": 6765,
    "preview": "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { PluginContext } from \"../types\";\nimport { DEFAULTS"
  },
  {
    "path": "apps/memos-local-openclaw/src/storage/ensure-binding.ts",
    "chars": 2025,
    "preview": "import { existsSync, mkdirSync, copyFileSync } from \"fs\";\nimport { execSync } from \"child_process\";\nimport path from \"pa"
  },
  {
    "path": "apps/memos-local-openclaw/src/storage/sqlite.ts",
    "chars": 62605,
    "preview": "import Database from \"better-sqlite3\";\nimport { createHash } from \"crypto\";\nimport * as fs from \"fs\";\nimport * as path f"
  },
  {
    "path": "apps/memos-local-openclaw/src/storage/vector.ts",
    "chars": 1178,
    "preview": "import type { SqliteStore } from \"./sqlite\";\n\nexport function cosineSimilarity(a: number[], b: number[]): number {\n  if "
  },
  {
    "path": "apps/memos-local-openclaw/src/telemetry.ts",
    "chars": 8166,
    "preview": "/**\n * Telemetry module — anonymous usage analytics via Aliyun ARMS RUM.\n *\n * Privacy-first design:\n * - Enabled by def"
  },
  {
    "path": "apps/memos-local-openclaw/src/tools/index.ts",
    "chars": 172,
    "preview": "export { createMemorySearchTool } from \"./memory-search\";\nexport { createMemoryTimelineTool } from \"./memory-timeline\";\n"
  },
  {
    "path": "apps/memos-local-openclaw/src/tools/memory-get.ts",
    "chars": 2249,
    "preview": "import type { SqliteStore } from \"../storage/sqlite\";\nimport type { ToolDefinition, GetResult, ChunkRef } from \"../types"
  },
  {
    "path": "apps/memos-local-openclaw/src/tools/memory-search.ts",
    "chars": 1675,
    "preview": "import type { RecallEngine } from \"../recall/engine\";\nimport type { ToolDefinition } from \"../types\";\n\nfunction resolveO"
  },
  {
    "path": "apps/memos-local-openclaw/src/tools/memory-timeline.ts",
    "chars": 2769,
    "preview": "import type { SqliteStore } from \"../storage/sqlite\";\nimport type { ToolDefinition, TimelineResult, TimelineEntry, Chunk"
  },
  {
    "path": "apps/memos-local-openclaw/src/types.ts",
    "chars": 7145,
    "preview": "// ─── Role & Message ───\n\nexport type Role = \"user\" | \"assistant\" | \"system\" | \"tool\";\n\nexport interface ConversationMe"
  },
  {
    "path": "apps/memos-local-openclaw/src/update-check.ts",
    "chars": 3343,
    "preview": "/**\n * Channel-aware update check against npm registry dist-tags.\n * - Prerelease users (e.g. 1.0.2-beta.x) compare agai"
  },
  {
    "path": "apps/memos-local-openclaw/src/viewer/html.ts",
    "chars": 275835,
    "preview": "export function viewerHTML(pluginVersion?: string): string {\nconst vBadge = pluginVersion ? `<span class=\"version-badge\""
  },
  {
    "path": "apps/memos-local-openclaw/src/viewer/server.ts",
    "chars": 102481,
    "preview": "import http from \"node:http\";\nimport os from \"node:os\";\nimport crypto from \"node:crypto\";\nimport { execSync, exec } from"
  },
  {
    "path": "apps/memos-local-openclaw/tests/accuracy.test.ts",
    "chars": 24703,
    "preview": "/**\n * Accuracy Test Suite — runs against REAL LLM models and production DB.\n *\n * What it tests:\n *   A. Dedup accuracy"
  },
  {
    "path": "apps/memos-local-openclaw/tests/bench/README.md",
    "chars": 17146,
    "preview": "# MemOS A/B 评测方案\n\n## 1. 评测背景与目标\n\n### 背景\n\n[OpenClaw](https://github.com/nicepkg/openclaw) 原生记忆系统存在以下核心问题:\n\n- **跨会话遗忘** — "
  },
  {
    "path": "apps/memos-local-openclaw/tests/capture.test.ts",
    "chars": 5367,
    "preview": "import { describe, it, expect } from \"vitest\";\nimport { captureMessages } from \"../src/capture\";\nimport type { Logger } "
  },
  {
    "path": "apps/memos-local-openclaw/tests/chunker.test.ts",
    "chars": 2081,
    "preview": "import { describe, it, expect } from \"vitest\";\nimport { chunkText } from \"../src/ingest/chunker\";\n\ndescribe(\"chunkText\","
  },
  {
    "path": "apps/memos-local-openclaw/tests/integration.test.ts",
    "chars": 10303,
    "preview": "import { describe, it, expect, beforeAll, afterAll } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as path from \"pat"
  },
  {
    "path": "apps/memos-local-openclaw/tests/multi-agent.test.ts",
    "chars": 12226,
    "preview": "import { describe, it, expect, beforeEach, afterEach } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as path from \"p"
  },
  {
    "path": "apps/memos-local-openclaw/tests/plugin-impl-access.test.ts",
    "chars": 4432,
    "preview": "import { describe, it, expect, beforeEach, afterEach } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as path from \"p"
  },
  {
    "path": "apps/memos-local-openclaw/tests/policy.test.ts",
    "chars": 11807,
    "preview": "/**\n * Policy test suite — 10 test cases verifying the retrieval strategy:\n *\n *  1. Simple math → NO search needed\n *  "
  },
  {
    "path": "apps/memos-local-openclaw/tests/recall.test.ts",
    "chars": 2542,
    "preview": "import { describe, it, expect } from \"vitest\";\nimport { rrfFuse } from \"../src/recall/rrf\";\nimport { applyRecencyDecay }"
  },
  {
    "path": "apps/memos-local-openclaw/tests/shutdown-lifecycle.test.ts",
    "chars": 3751,
    "preview": "import { describe, it, expect, vi, afterEach } from \"vitest\";\n\nconst noopLog = {\n  debug: () => {},\n  info: () => {},\n  "
  },
  {
    "path": "apps/memos-local-openclaw/tests/storage.test.ts",
    "chars": 6367,
    "preview": "import { describe, it, expect, beforeEach, afterEach } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as path from \"p"
  },
  {
    "path": "apps/memos-local-openclaw/tests/task-processor.test.ts",
    "chars": 19174,
    "preview": "import { describe, it, expect, beforeEach, afterEach, vi } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as path fro"
  },
  {
    "path": "apps/memos-local-openclaw/tests/worker-lifecycle.test.ts",
    "chars": 3238,
    "preview": "import { describe, it, expect, beforeEach, afterEach, vi } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as path fro"
  },
  {
    "path": "apps/memos-local-openclaw/tsconfig.json",
    "chars": 484,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"CommonJS\",\n    \"lib\": [\"ES2022\"],\n    \"outDir\": \"dist\",\n"
  },
  {
    "path": "apps/memos-local-openclaw/vitest.config.ts",
    "chars": 148,
    "preview": "import { defineConfig } from \"vitest/config\";\n\nexport default defineConfig({\n  test: {\n    testTimeout: 180_000,\n    hoo"
  },
  {
    "path": "apps/memos-local-openclaw/www/demo/index.html",
    "chars": 57094,
    "preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,init"
  },
  {
    "path": "apps/memos-local-openclaw/www/docs/index.html",
    "chars": 52235,
    "preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,init"
  },
  {
    "path": "apps/memos-local-openclaw/www/docs/troubleshooting.html",
    "chars": 16968,
    "preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,init"
  },
  {
    "path": "apps/memos-local-openclaw/www/index.html",
    "chars": 81940,
    "preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,init"
  },
  {
    "path": "apps/openwork-memos-integration/.gitignore",
    "chars": 292,
    "preview": "node_modules/\ndist/\nout/\n.env\n.env.local\n\n# OS files\n.DS_Store\nThumbs.db\n\n# Lock files\npnpm-lock.yaml\npackage-lock.json\n"
  },
  {
    "path": "apps/openwork-memos-integration/CLAUDE.md",
    "chars": 6475,
    "preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
  },
  {
    "path": "apps/openwork-memos-integration/CONTRIBUTING.md",
    "chars": 1637,
    "preview": "# Contributing to Openwork\n\nThank you for your interest in contributing to Openwork! This document provides guidelines a"
  },
  {
    "path": "apps/openwork-memos-integration/LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2026 Accomplish Inc\n\nPermission is hereby granted, free of charge, to any person obtaining a "
  },
  {
    "path": "apps/openwork-memos-integration/README.md",
    "chars": 8257,
    "preview": "<p align=\"center\">\n  <img src=\"docs/banner.svg\" alt=\"Openwork - Open source AI desktop agent that automates file managem"
  },
  {
    "path": "apps/openwork-memos-integration/SECURITY.md",
    "chars": 1190,
    "preview": "# Security Policy\n\n## Supported Versions\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 0.1.x   | "
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/.eslintrc.json",
    "chars": 702,
    "preview": "{\n  \"root\": true,\n  \"env\": {\n    \"browser\": true,\n    \"es2021\": true,\n    \"node\": true\n  },\n  \"parser\": \"@typescript-esl"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/appSettings.integration.test.ts",
    "chars": 11098,
    "preview": "/**\n * Integration tests for appSettings store\n * Tests real electron-store interactions with temporary directories\n * @"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/opencode/cli-path.integration.test.ts",
    "chars": 16177,
    "preview": "/**\n * Integration tests for OpenCode CLI path resolution\n *\n * Tests the cli-path module which resolves paths to the Op"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/opencode/config-generator.integration.test.ts",
    "chars": 12133,
    "preview": "/**\n * Integration tests for OpenCode config generator\n *\n * Tests the config-generator module which creates OpenCode co"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/permission-api.integration.test.ts",
    "chars": 4062,
    "preview": "/**\n * Integration tests for Permission API\n *\n * Tests the REAL exported functions from permission-api module:\n * - isF"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/secureStorage.integration.test.ts",
    "chars": 16105,
    "preview": "/**\n * Integration tests for secureStorage module\n * Tests real electron-store interactions with encrypted API key stora"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/store/freshInstallCleanup.integration.test.ts",
    "chars": 8284,
    "preview": "/**\n * Integration tests for Fresh Install Cleanup\n *\n * Tests the REAL checkAndCleanupFreshInstall function:\n * - Retur"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/taskHistory.integration.test.ts",
    "chars": 19706,
    "preview": "/**\n * Integration tests for taskHistory store\n * Tests real electron-store interactions with task persistence\n * @modul"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/utils/bundled-node.integration.test.ts",
    "chars": 14651,
    "preview": "/**\n * Integration tests for Bundled Node.js utilities\n *\n * Tests the bundled-node module which provides paths to bundl"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/main/utils/system-path.integration.test.ts",
    "chars": 16105,
    "preview": "/**\n * Integration tests for System PATH utilities\n *\n * Tests the system-path module which builds extended PATH strings"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/preload/preload.integration.test.ts",
    "chars": 13700,
    "preview": "/**\n * Integration tests for Preload script\n *\n * Tests the REAL preload script by:\n * 1. Mocking electron APIs (externa"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/App.integration.test.tsx",
    "chars": 10822,
    "preview": "/**\n * Integration tests for App component\n * Tests router setup and route rendering\n *\n * NOTE: This test follows React"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/Header.integration.test.tsx",
    "chars": 7735,
    "preview": "/**\n * Integration tests for Header component\n * Tests rendering and navigation elements\n * @module __tests__/integratio"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/SettingsDialog.integration.test.tsx",
    "chars": 30799,
    "preview": "/**\n * Integration tests for SettingsDialog component\n * Tests dialog rendering, API key management, model selection, an"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/Sidebar.integration.test.tsx",
    "chars": 15116,
    "preview": "/**\n * Integration tests for Sidebar component\n * Tests rendering with conversations, conversation selection, and settin"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/StreamingText.integration.test.tsx",
    "chars": 14138,
    "preview": "/**\n * Integration tests for StreamingText component and useStreamingState hook\n * Tests text streaming animation, compl"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/TaskHistory.integration.test.tsx",
    "chars": 21453,
    "preview": "/**\n * Integration tests for TaskHistory component\n * Tests task list rendering, selection, deletion, and history cleari"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/TaskInputBar.integration.test.tsx",
    "chars": 13407,
    "preview": "/**\n * Integration tests for TaskInputBar component\n * Tests component rendering and user interactions with mocked windo"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/components/TaskLauncher.integration.test.tsx",
    "chars": 33457,
    "preview": "/**\n * Integration tests for TaskLauncher and TaskLauncherItem components\n * Tests rendering, filtering, keyboard naviga"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/pages/Execution.integration.test.tsx",
    "chars": 42864,
    "preview": "/**\n * Integration tests for Execution page\n * Tests rendering with active task, message display, and permission dialog\n"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/pages/Home.integration.test.tsx",
    "chars": 18344,
    "preview": "/**\n * Integration tests for Home page\n * Tests initial render, task input integration, and loading state\n * @module __t"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/integration/renderer/taskStore.integration.test.ts",
    "chars": 29431,
    "preview": "/**\n * Integration tests for taskStore (Zustand)\n * Tests store actions with mocked window.accomplish API\n * @module __t"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/main/config.unit.test.ts",
    "chars": 6471,
    "preview": "import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';\n\n// We need to test the module in isolation, s"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/main/ipc/handlers-utils.unit.test.ts",
    "chars": 23374,
    "preview": "/**\n * Unit tests for pure utility functions extracted from handlers.ts\n *\n * Note: The handlers.ts file contains mostly"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/main/ipc/validation.unit.test.ts",
    "chars": 16469,
    "preview": "import { describe, it, expect } from 'vitest';\nimport {\n  validate,\n  normalizeIpcError,\n  taskConfigSchema,\n  permissio"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/main/opencode/stream-parser.unit.test.ts",
    "chars": 18129,
    "preview": "import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';\nimport { StreamParser } from '../../../src/mai"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/setup.ts",
    "chars": 272,
    "preview": "/**\n * Vitest setup file for tests\n * Configures testing-library matchers and global test utilities\n */\n\nimport '@testin"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/unit/main/ipc/handlers.unit.test.ts",
    "chars": 57124,
    "preview": "/**\n * Unit tests for IPC handlers\n *\n * Tests the registration and invocation of IPC handlers for:\n * - Task operations"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/unit/main/opencode/adapter.unit.test.ts",
    "chars": 26740,
    "preview": "/**\n * Unit tests for OpenCode Adapter\n *\n * Tests the adapter module which manages PTY spawning, stream parsing,\n * and"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/__tests__/unit/main/opencode/task-manager.unit.test.ts",
    "chars": 23517,
    "preview": "/**\n * Unit tests for Task Manager\n *\n * Tests the task-manager module which handles task lifecycle, parallel execution,"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/clean_dmg_install.sh",
    "chars": 6269,
    "preview": "#!/bin/bash\n# Clean all files related to DMG/production installations of Accomplish\n# This removes app data, preferences"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/README.md",
    "chars": 6150,
    "preview": "# E2E Test Infrastructure\n\nThis directory contains the E2E test infrastructure for the Openwork desktop app using Playwr"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/config/index.ts",
    "chars": 79,
    "preview": "export { TEST_TIMEOUTS, TEST_SCENARIOS, type TestScenario } from './timeouts';\n"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/config/timeouts.ts",
    "chars": 1548,
    "preview": "/**\n * Centralized timeout constants for E2E tests.\n * Adjust these based on CI environment performance.\n */\nexport cons"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/docker/Dockerfile",
    "chars": 1373,
    "preview": "# Base image with Playwright dependencies pre-installed\nFROM mcr.microsoft.com/playwright:v1.49.1-noble\n\n# Install Xvfb,"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/docker/docker-compose.yml",
    "chars": 584,
    "preview": "services:\n  e2e-tests:\n    build:\n      context: ../../../..\n      dockerfile: apps/desktop/e2e/docker/Dockerfile\n    en"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/fixtures/electron-app.ts",
    "chars": 1992,
    "preview": "import { test as base, _electron as electron, ElectronApplication, Page } from '@playwright/test';\nimport { fileURLToPat"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/fixtures/index.ts",
    "chars": 47,
    "preview": "export { test, expect } from './electron-app';\n"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/pages/execution.page.ts",
    "chars": 2092,
    "preview": "import type { Page } from '@playwright/test';\nimport { TEST_TIMEOUTS } from '../config';\n\nexport class ExecutionPage {\n "
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/pages/home.page.ts",
    "chars": 735,
    "preview": "import type { Page } from '@playwright/test';\n\nexport class HomePage {\n  constructor(private page: Page) {}\n\n  get title"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/pages/index.ts",
    "chars": 138,
    "preview": "export { HomePage } from './home.page';\nexport { ExecutionPage } from './execution.page';\nexport { SettingsPage } from '"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/pages/settings.page.ts",
    "chars": 5806,
    "preview": "import type { Page } from '@playwright/test';\nimport { TEST_TIMEOUTS } from '../config';\n\nexport class SettingsPage {\n  "
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/playwright.config.ts",
    "chars": 1006,
    "preview": "import { defineConfig } from '@playwright/test';\n\nexport default defineConfig({\n  testDir: './specs',\n  outputDir: './te"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/specs/execution.spec.ts",
    "chars": 20309,
    "preview": "import { test, expect } from '../fixtures';\nimport { HomePage, ExecutionPage } from '../pages';\nimport { captureForAI } "
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/specs/home.spec.ts",
    "chars": 6330,
    "preview": "import { test, expect } from '../fixtures';\nimport { HomePage } from '../pages';\nimport { captureForAI } from '../utils'"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/specs/settings-bedrock.spec.ts",
    "chars": 6663,
    "preview": "import { test, expect } from '../fixtures';\nimport { SettingsPage } from '../pages';\nimport { captureForAI } from '../ut"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/specs/settings-providers.spec.ts",
    "chars": 13390,
    "preview": "import { test, expect } from '../fixtures';\nimport { SettingsPage } from '../pages';\nimport { captureForAI } from '../ut"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/specs/settings.spec.ts",
    "chars": 25013,
    "preview": "import { test, expect } from '../fixtures';\nimport { SettingsPage, HomePage, ExecutionPage } from '../pages';\nimport { c"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/specs/task-launch-guard.spec.ts",
    "chars": 9890,
    "preview": "import { test, expect } from '../fixtures';\nimport { SettingsPage, HomePage } from '../pages';\nimport { captureForAI } f"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/utils/index.ts",
    "chars": 71,
    "preview": "export { captureForAI, type ScreenshotMetadata } from './screenshots';\n"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/e2e/utils/screenshots.ts",
    "chars": 3474,
    "preview": "/**\n * Screenshot utilities for AI-powered visual testing.\n * Captures screenshots with metadata for automated evaluatio"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/index.html",
    "chars": 1068,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/package.json",
    "chars": 6414,
    "preview": "{\n  \"name\": \"@accomplish/desktop\",\n  \"version\": \"0.2.3\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"description\": \"Accomp"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/postcss.config.js",
    "chars": 81,
    "preview": "export default {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n};\n"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/resources/entitlements.mac.plist",
    "chars": 604,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/run_local_ui_prod_api.sh",
    "chars": 221,
    "preview": "#!/bin/bash\n# Run desktop app with LOCAL UI (Vite hot reload) + PRODUCTION API\n# UI: localhost:5173 | API: lite.accompli"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/run_local_ui_staging_api.sh",
    "chars": 234,
    "preview": "#!/bin/bash\n# Run desktop app with LOCAL UI (Vite hot reload) + STAGING API\n# UI: localhost:5173 | API: lite-staging.acc"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/run_prod.sh",
    "chars": 473,
    "preview": "#!/bin/bash\n# Run desktop app with PRODUCTION UI + PRODUCTION API\n# UI: lite.accomplish.ai | API: lite.accomplish.ai\n# T"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/run_staging.sh",
    "chars": 493,
    "preview": "#!/bin/bash\n# Run desktop app with STAGING UI + STAGING API\n# UI: lite-staging.accomplish.ai | API: lite-staging.accompl"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/scripts/after-pack.cjs",
    "chars": 9178,
    "preview": "/**\n * Electron-builder afterPack hook to copy architecture-specific Node.js binaries.\n *\n * This hook runs after packin"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/scripts/download-nodejs.cjs",
    "chars": 5858,
    "preview": "/**\n * Download Node.js standalone binaries for bundling with the Electron app.\n *\n * Downloads Node.js v20.18.1 for:\n *"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/scripts/package.cjs",
    "chars": 1733,
    "preview": "#!/usr/bin/env node\n\n/**\n * Custom packaging script for Electron app with pnpm workspaces.\n * Temporarily removes worksp"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/scripts/patch-electron-name.cjs",
    "chars": 1333,
    "preview": "/**\n * Patches the Electron.app Info.plist to show \"Openwork\" instead of \"Electron\"\n * in macOS Cmd+Tab and Dock during "
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/ask-user-question/SKILL.md",
    "chars": 4075,
    "preview": "---\nname: ask-user-question\ndescription: Ask users questions via the UI. Use when you need clarification, user preferenc"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/ask-user-question/package.json",
    "chars": 333,
    "preview": "{\n  \"name\": \"ask-user-question\",\n  \"version\": \"0.0.1\",\n  \"type\": \"module\",\n  \"imports\": {\n    \"@/*\": \"./src/*\"\n  },\n  \"s"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/ask-user-question/src/index.ts",
    "chars": 5741,
    "preview": "#!/usr/bin/env node\n/**\n * AskUserQuestion MCP Server\n *\n * Exposes an `AskUserQuestion` tool that the agent calls to as"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/ask-user-question/tsconfig.json",
    "chars": 248,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"esMo"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/.gitignore",
    "chars": 52,
    "preview": "# Browser profile data\nprofiles/\ntmp/\nnode_modules/\n"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/SKILL.md",
    "chars": 6887,
    "preview": "---\nname: dev-browser\ndescription: Browser automation with persistent page state. Use when users ask to navigate website"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/package.json",
    "chars": 744,
    "preview": "{\n  \"name\": \"dev-browser\",\n  \"version\": \"0.0.1\",\n  \"type\": \"module\",\n  \"imports\": {\n    \"@/*\": \"./src/*\"\n  },\n  \"scripts"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/references/scraping.md",
    "chars": 5302,
    "preview": "# Data Scraping Guide\n\nFor large datasets (followers, posts, search results), **intercept and replay network requests** "
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/scripts/start-relay.ts",
    "chars": 763,
    "preview": "/**\n * Start the CDP relay server for Chrome extension mode\n *\n * Usage: npm run start-extension\n */\n\nimport { serveRela"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/scripts/start-server.ts",
    "chars": 6162,
    "preview": "import { serve } from \"@/index.js\";\nimport { execSync } from \"child_process\";\nimport { mkdirSync, existsSync, unlinkSync"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/server.sh",
    "chars": 645,
    "preview": "#!/bin/bash\n\n# Get the directory where this script is located\nSCRIPT_DIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pw"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/client.ts",
    "chars": 16000,
    "preview": "import { chromium, type Browser, type Page, type ElementHandle } from \"playwright\";\nimport type {\n  GetPageRequest,\n  Ge"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/index.ts",
    "chars": 9993,
    "preview": "import express, { type Express, type Request, type Response } from \"express\";\n// Using rebrowser-playwright (via npm ali"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/relay.ts",
    "chars": 21675,
    "preview": "/**\n * CDP Relay Server for Chrome Extension mode\n *\n * This server acts as a bridge between Playwright clients and a Ch"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/snapshot/__tests__/snapshot.test.ts",
    "chars": 5698,
    "preview": "import { chromium } from \"playwright\";\nimport type { Browser, BrowserContext, Page } from \"playwright\";\nimport { beforeA"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/snapshot/browser-script.ts",
    "chars": 37835,
    "preview": "/**\n * Browser-injectable snapshot script.\n *\n * This module provides the snapshot functionality as a string that can be"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/snapshot/index.ts",
    "chars": 562,
    "preview": "/**\n * ARIA Snapshot module for dev-browser.\n *\n * Provides Playwright-compatible ARIA snapshots with cross-connection r"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/snapshot/inject.ts",
    "chars": 578,
    "preview": "/**\n * Injectable snapshot script for browser context.\n *\n * This module provides the getSnapshotScript function that re"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/src/types.ts",
    "chars": 838,
    "preview": "// API request/response types - shared between client and server\n\nexport interface ServeOptions {\n  port?: number;\n  hea"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/tsconfig.json",
    "chars": 726,
    "preview": "{\n  \"compilerOptions\": {\n    \"lib\": [\n      \"ESNext\"\n    ],\n    \"target\": \"ESNext\",\n    \"module\": \"Preserve\",\n    \"modul"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/dev-browser/vitest.config.ts",
    "chars": 283,
    "preview": "import { defineConfig } from \"vitest/config\";\n\nexport default defineConfig({\n  test: {\n    globals: true,\n    environmen"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/file-permission/package.json",
    "chars": 331,
    "preview": "{\n  \"name\": \"file-permission\",\n  \"version\": \"0.0.1\",\n  \"type\": \"module\",\n  \"imports\": {\n    \"@/*\": \"./src/*\"\n  },\n  \"scr"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/file-permission/src/index.ts",
    "chars": 4451,
    "preview": "#!/usr/bin/env node\n/**\n * File Permission MCP Server\n *\n * Exposes a `request_file_permission` tool that the agent call"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/file-permission/tsconfig.json",
    "chars": 388,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"esMo"
  },
  {
    "path": "apps/openwork-memos-integration/apps/desktop/skills/safe-file-deletion/SKILL.md",
    "chars": 1164,
    "preview": "---\nname: safe-file-deletion\ndescription: Enforces explicit user permission before any file deletion. Activates when you"
  }
]

// ... and 662 more files (download for full content)

About this extraction

This page contains the full source code of the MemTensor/MemOS GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 862 files (7.6 MB), approximately 2.1M tokens, and a symbol index with 5168 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!