om-memory-system 4.30.1 → 4.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/adapters/opencode/conversation.js +22 -17
- package/dist/adapters/opencode/skills-tool.d.ts +3 -0
- package/dist/adapters/opencode/skills-tool.js +31 -0
- package/dist/adapters/pi/capture.js +39 -23
- package/dist/adapters/pi/extension.d.ts +1 -0
- package/dist/adapters/pi/extension.js +3 -0
- package/dist/adapters/pi/skills-tool.d.ts +3 -0
- package/dist/adapters/pi/skills-tool.js +34 -0
- package/dist/cli/index.js +5 -0
- package/dist/cli/skills-command.d.ts +12 -0
- package/dist/cli/skills-command.js +242 -0
- package/dist/cli/skills-record-past.d.ts +5 -0
- package/dist/cli/skills-record-past.js +85 -0
- package/dist/config-template.d.ts +1 -1
- package/dist/config-template.js +12 -0
- package/dist/config.d.ts +64 -48
- package/dist/config.js +28 -5
- package/dist/core/host.d.ts +7 -0
- package/dist/core/profile-backoff.d.ts +16 -8
- package/dist/core/profile-backoff.js +25 -16
- package/dist/core/skill-agent-modes.d.ts +4 -0
- package/dist/core/skill-agent-modes.js +145 -0
- package/dist/core/skill-operation-reply.d.ts +16 -0
- package/dist/core/skill-operation-reply.js +35 -0
- package/dist/core/skill-operations.d.ts +58 -0
- package/dist/core/skill-operations.js +296 -0
- package/dist/core/skills-tool.d.ts +90 -0
- package/dist/core/skills-tool.js +64 -0
- package/dist/core/tool-input.d.ts +7 -0
- package/dist/core/tool-input.js +21 -0
- package/dist/core/tool-outcome.d.ts +13 -0
- package/dist/core/tool-outcome.js +22 -0
- package/dist/core/turn-outputs.d.ts +30 -0
- package/dist/core/turn-outputs.js +22 -0
- package/dist/importer/auto-backfill.js +13 -1
- package/dist/importer/claude-conversation.d.ts +1 -0
- package/dist/importer/claude-conversation.js +44 -10
- package/dist/importer/claude-hook-api.d.ts +0 -8
- package/dist/importer/claude-hook-api.js +36 -65
- package/dist/importer/claude-import.js +3 -1
- package/dist/importer/claude-reader.d.ts +2 -0
- package/dist/importer/claude-reader.js +2 -2
- package/dist/importer/claude-turn-units.d.ts +26 -0
- package/dist/importer/claude-turn-units.js +85 -0
- package/dist/importer/import-args.d.ts +3 -0
- package/dist/importer/import-args.js +8 -2
- package/dist/importer/import-project.d.ts +6 -0
- package/dist/importer/import-project.js +9 -0
- package/dist/importer/import-runs.d.ts +8 -2
- package/dist/importer/import-runs.js +3 -3
- package/dist/importer/import-sessions.d.ts +8 -1
- package/dist/importer/import-sessions.js +17 -4
- package/dist/importer/import-turn-outputs.d.ts +49 -0
- package/dist/importer/import-turn-outputs.js +88 -0
- package/dist/importer/importer.d.ts +5 -2
- package/dist/importer/importer.js +18 -32
- package/dist/importer/opencode-import.d.ts +2 -0
- package/dist/importer/opencode-import.js +6 -3
- package/dist/importer/opencode-reader.js +11 -5
- package/dist/importer/pi-conversation.d.ts +5 -0
- package/dist/importer/pi-conversation.js +33 -9
- package/dist/importer/profile-catch-up.d.ts +1 -1
- package/dist/importer/record-past-turns.d.ts +28 -0
- package/dist/importer/record-past-turns.js +63 -0
- package/dist/importer/run-import.d.ts +4 -0
- package/dist/importer/run-import.js +24 -3
- package/dist/importer/web-import-api.d.ts +14 -0
- package/dist/importer/web-import-api.js +6 -0
- package/dist/importer/web-import-group.d.ts +4 -1
- package/dist/importer/web-import-group.js +26 -6
- package/dist/importer/web-import-jobs.js +6 -2
- package/dist/index.js +6 -1
- package/dist/services/ai/session/ai-session-manager.js +13 -0
- package/dist/services/auto-capture.d.ts +1 -1
- package/dist/services/auto-capture.js +71 -19
- package/dist/services/chat/chat-runtime.d.ts +2 -0
- package/dist/services/chat/chat-runtime.js +3 -2
- package/dist/services/global-config-writer.js +32 -2
- package/dist/services/profile-cleanup-routes.d.ts +2 -2
- package/dist/services/profile-cleanup-routes.js +19 -2
- package/dist/services/settings-snapshot.js +7 -1
- package/dist/services/skill-learning/agent-proposals.d.ts +76 -0
- package/dist/services/skill-learning/agent-proposals.js +180 -0
- package/dist/services/skill-learning/apply.d.ts +50 -0
- package/dist/services/skill-learning/apply.js +247 -0
- package/dist/services/skill-learning/auto-review.d.ts +40 -0
- package/dist/services/skill-learning/auto-review.js +132 -0
- package/dist/services/skill-learning/draft-turns.d.ts +60 -0
- package/dist/services/skill-learning/draft-turns.js +131 -0
- package/dist/services/skill-learning/drafting.d.ts +73 -0
- package/dist/services/skill-learning/drafting.js +263 -0
- package/dist/services/skill-learning/line-diff.d.ts +6 -0
- package/dist/services/skill-learning/line-diff.js +94 -0
- package/dist/services/skill-learning/operations-deps.d.ts +7 -0
- package/dist/services/skill-learning/operations-deps.js +19 -0
- package/dist/services/skill-learning/proposal-store.d.ts +82 -0
- package/dist/services/skill-learning/proposal-store.js +164 -0
- package/dist/services/skill-learning/ranking.d.ts +36 -0
- package/dist/services/skill-learning/ranking.js +140 -0
- package/dist/services/skill-learning/settings-for.d.ts +11 -0
- package/dist/services/skill-learning/settings-for.js +21 -0
- package/dist/services/skill-learning/skill-config.d.ts +45 -0
- package/dist/services/skill-learning/skill-config.js +70 -0
- package/dist/services/skill-learning/skill-detect.d.ts +29 -0
- package/dist/services/skill-learning/skill-detect.js +130 -0
- package/dist/services/skill-learning/skill-folders.d.ts +24 -0
- package/dist/services/skill-learning/skill-folders.js +42 -0
- package/dist/services/skill-learning/skill-index.d.ts +28 -0
- package/dist/services/skill-learning/skill-index.js +118 -0
- package/dist/services/skill-learning/skill-model.d.ts +10 -0
- package/dist/services/skill-learning/skill-model.js +15 -0
- package/dist/services/skill-learning/store.d.ts +79 -0
- package/dist/services/skill-learning/store.js +245 -0
- package/dist/services/skill-learning/turn-output.d.ts +34 -0
- package/dist/services/skill-learning/turn-output.js +98 -0
- package/dist/services/skill-learning/turn-summaries.d.ts +24 -0
- package/dist/services/skill-learning/turn-summaries.js +64 -0
- package/dist/services/skill-learning/web-review.d.ts +18 -0
- package/dist/services/skill-learning/web-review.js +44 -0
- package/dist/services/skill-routes.d.ts +20 -0
- package/dist/services/skill-routes.js +249 -0
- package/dist/services/turso/connection-manager.js +11 -7
- package/dist/services/turso/table-lease.d.ts +40 -0
- package/dist/services/turso/table-lease.js +77 -0
- package/dist/services/turso/turso-db.d.ts +4 -0
- package/dist/services/turso/turso-db.js +13 -0
- package/dist/services/user-prompt/profile-catch-up-lease.d.ts +3 -22
- package/dist/services/user-prompt/profile-catch-up-lease.js +9 -65
- package/dist/services/web-jobs.d.ts +7 -0
- package/dist/services/web-jobs.js +14 -0
- package/dist/services/web-route-modules.d.ts +2 -2
- package/dist/services/web-route-modules.js +6 -1
- package/dist/services/web-server.js +9 -21
- package/dist/v2/adapter.js +13 -8
- package/dist/web/assets/index-9hu4Z7SV.css +2 -0
- package/dist/web/assets/index-BMt9gPNK.js +129 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-BGDKFzoC.css +0 -2
- package/dist/web/assets/index-CE8tXPA_.js +0 -128
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* src/config.ts until that file reached its size ceiling; the text is
|
|
4
4
|
* unchanged. Shared by config.ts and the global settings writer.
|
|
5
5
|
*/
|
|
6
|
-
export declare const CONFIG_TEMPLATE = "{\n // ============================================\n // omms (Opinionated Modular Memory System) Configuration\n // ============================================\n \n // Storage location for the vector database. Leave unset to use the\n // default ~/.omms/data (a legacy ~/.opencode-mem/data store is migrated\n // there automatically on first start; see docs/omms-migration.md).\n // \"storagePath\": \"~/.omms/data\",\n\n \"userEmailOverride\": \"\",\n \"userNameOverride\": \"\",\n \n // ============================================\n // Embedding Model (for similarity search)\n // ============================================\n // Local = Hugging Face / ONNX via @huggingface/transformers (not Apple MLX).\n // Remote = set embeddingApiUrl (OpenAI-compatible /embeddings); embeddingApiKey is optional.\n \n // Default: Nomic Embed v1 (768 dimensions, 8192 context, multilingual)\n \"embeddingModel\": \"Xenova/nomic-embed-text-v1\",\n\n // Opt-in Nomic task prefixes (search_document: / search_query:). After enabling,\n // re-index existing memories so store and query vectors stay aligned.\n // \"embeddingUseTaskPrefixes\": true,\n \n // Auto-detected dimensions (no need to set manually)\n // \"embeddingDimensions\": 768,\n \n // Other recommended local models:\n // \"embeddingModel\": \"Xenova/jina-embeddings-v2-base-en\", // 768 dims, English-only, 8192 context\n // \"embeddingModel\": \"Xenova/jina-embeddings-v2-small-en\", // 512 dims, faster, 8192 context\n // \"embeddingModel\": \"Xenova/all-MiniLM-L6-v2\", // 384 dims, very fast, 512 context\n // \"embeddingModel\": \"Xenova/all-mpnet-base-v2\", // 768 dims, good quality, 512 context\n \n // Optional: OpenAI-compatible API for embeddings (URL required; key only if the server needs one)\n // \"embeddingApiUrl\": \"https://api.openai.com/v1\",\n // \"embeddingApiKey\": \"env://OPENAI_API_KEY\", // or \"sk-...\" / \"file:///path/to/key\"\n // \"embeddingModel\": \"text-embedding-3-small\", // 1536 dims, auto-detected\n \n // ============================================\n // Web Server Settings\n // ============================================\n \n // Start a background import of past chats on each host's next start.\n \"autoBackfill\": true,\n // \"piBackfillModel\": \"inherit\", // or \"external\", or \"provider/model\"\n // \"opencodeBackfillModel\": \"inherit\", // or \"external\", or \"provider/model\"\n // Directory maps for history recorded in moved or deleted directories.\n // \"importPathMaps\": [{ \"from\": \"~/code/app-feat-x\", \"to\": \"~/code/app\" }],\n // Unresolved directories to hide on the Directory maps page. They stay unimported.\n // \"importIgnoredDirectories\": [\"/private/tmp/scratch\"],\n // Claude Code's folder, when it is not ~/.claude (like CLAUDE_CONFIG_DIR).\n // \"claudeConfigDir\": \"~/.claude-work\",\n\n // Register the web app to start when you log in.\n \"webServerAutoStart\": true,\n // Enable web UI for managing memories (accessible at http://localhost:4747)\n \"webServerEnabled\": true,\n \n // Port for web UI server\n \"webServerPort\": 4747,\n \n // Host address for web UI (use 127.0.0.1 for local only, 0.0.0.0 for network access)\n \"webServerHost\": \"127.0.0.1\",\n\n // Optional HTTP Basic Auth for the web UI. Accepts literal, env://, or file:// secrets.\n // \"webServerAuthPassword\": \"\",\n // \"webServerAuthUsername\": \"\",\n\n // Deprecated: imported once into the API token table, then no longer read.\n // Create API tokens on the Settings page (Keys and access) instead.\n // \"webServerApiToken\": \"env://OMMS_WEB_TOKEN\",\n \n // ============================================\n // Database Settings\n // ============================================\n \n // Maximum vectors per database shard (auto-creates new shard when limit reached)\n \"maxVectorsPerShard\": 50000,\n \n // Automatically delete old memories based on retention period\n \"autoCleanupEnabled\": true,\n \n // Days to keep memories before auto-cleanup (only if autoCleanupEnabled is true)\n \"autoCleanupRetentionDays\": 30,\n \n // Automatically detect and remove duplicate memories\n \"deduplicationEnabled\": true,\n \n // Similarity threshold (0-1) for detecting duplicates (higher = stricter)\n \"deduplicationSimilarityThreshold\": 0.90,\n \n // ============================================\n // Memory Scope Settings\n // ============================================\n\n // Default scope for memory list/search queries\n // \"project\" keeps queries within the current project, \"all-projects\" searches across all project shards\n \"memory\": {\n \"defaultScope\": \"project\"\n },\n\n // ============================================\n // Model for auto-capture and profile learning (same rule in OpenCode and Pi)\n // ============================================\n\n // Which model summarises your work, in this order:\n // 1. The host model below: opencodeProvider/opencodeModel in OpenCode,\n // piProvider/piModel in Pi. Set the model to \"inherit\" to follow\n // whatever model the session is using, or to \"external\" to send\n // every call to the external API below.\n // 2. If no host model is set: the external API (memoryModel/memoryApiUrl/\n // memoryApiKey further down).\n // 3. If neither is set: the session's own model.\n // If the host model fails and the external API is configured, the external\n // API is used instead.\n //\n // Host models go through OpenCode's or Pi's own sign-in (OAuth like Claude\n // Pro/Max, GitHub Copilot, ChatGPT, or your own key there), so no separate API\n // key is needed here.\n //\n // Examples (OpenCode names from 'opencode providers list'; Pi names from its model list):\n // \"opencodeProvider\": \"anthropic\", \"opencodeModel\": \"claude-haiku-4-5-20251001\"\n // \"opencodeProvider\": \"openai\", \"opencodeModel\": \"inherit\"\n // \"piProvider\": \"openai-codex\", \"piModel\": \"gpt-5.6-luna\"\n //\n // \"opencodeProvider\": \"anthropic\",\n // \"opencodeModel\": \"claude-haiku-4-5-20251001\",\n // \"piProvider\": \"openai-codex\",\n // \"piModel\": \"gpt-5.6-luna\",\n\n // ============================================\n // Auto-Capture Settings\n // ============================================\n \n // Auto-capture runs in the background without blocking your main session.\n // Note: Ollama may not support tool calling. Use OpenAI, Anthropic, or Groq for best results.\n \n \"autoCaptureEnabled\": true,\n \n // Provider type: \"openai-chat\" | \"openai-responses\" | \"anthropic\" | \"minimax\" | \"orcarouter\" | \"google-gemini\"\n // Note: \"openai-chat\" is a generic OpenAI API-compatible mode.\n // Any service that follows the OpenAI Chat Completions API can use it via custom \"memoryApiUrl\".\n \"memoryProvider\": \"openai-chat\",\n \n // External API (step 2 above, and the fallback when a host model fails).\n // Uncomment all 3 lines and replace memoryApiKey before use:\n // \"memoryModel\": \"gpt-4o-mini\",\n // \"memoryApiUrl\": \"https://api.openai.com/v1\",\n // \"memoryApiKey\": \"sk-...\",\n\n // API Key Formats:\n // Direct value: \"sk-...\"\n // From file: \"file://~/.config/litellm-key.txt\"\n // From env variable: \"env://LITELLM_API_KEY\"\n \n // Examples for different providers:\n // Any OpenAI-compatible endpoint can use the \"openai-chat\" provider pattern below.\n // Common examples: DeepSeek, Qwen (via Alibaba Cloud ModelStudio),\n // Zhipu GLM (BigModel platform), and Kimi (Moonshot AI platform).\n\n // OpenAI Chat Completion (default, backward compatible):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"gpt-4o-mini\"\n // \"memoryApiUrl\": \"https://api.openai.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n\n // DeepSeek (OpenAI-compatible example):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"deepseek-chat\"\n // \"memoryApiUrl\": \"https://api.deepseek.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n \n // OpenAI Responses API (recommended, with session support):\n // \"memoryProvider\": \"openai-responses\"\n // \"memoryModel\": \"gpt-4o\"\n // \"memoryApiUrl\": \"https://api.openai.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n \n // Anthropic (with session support):\n // \"memoryProvider\": \"anthropic\"\n // \"memoryModel\": \"claude-3-5-haiku-20241022\"\n // \"memoryApiUrl\": \"https://api.anthropic.com/v1\"\n // \"memoryApiKey\": \"sk-ant-...\"\n\n // MiniMax (Anthropic Messages-compatible endpoint, with session support):\n // \"memoryProvider\": \"minimax\"\n // \"memoryModel\": \"MiniMax-M3\"\n // \"memoryApiUrl\": \"https://api.minimax.io\" // global endpoint\n // \"memoryApiKey\": \"<MiniMax API key>\"\n // // China endpoint: \"memoryApiUrl\": \"https://api.minimaxi.com\"\n // // Optional adaptive thinking for MiniMax-M3:\n // \"memoryExtraParams\": { \"thinking\": { \"type\": \"adaptive\" } }\n\n // OrcaRouter (OpenAI-compatible gateway, namespaced model IDs, with session support):\n // \"memoryProvider\": \"orcarouter\"\n // \"memoryApiKey\": \"<OrcaRouter API key>\"\n // // memoryApiUrl and memoryModel are optional \u2014 they default to\n // // https://api.orcarouter.ai/v1 and \"orcarouter/auto\" (a routing alias).\n // // OrcaRouter rejects bare model names, so if you set memoryModel, use a\n // // namespaced ID such as \"openai/gpt-5.5\" or \"deepseek/deepseek-v4-flash\".\n // \"memoryModel\": \"openai/gpt-5.5\"\n\n // Groq (OpenAI-compatible, use openai-chat provider):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"llama-3.3-70b-versatile\"\n // \"memoryApiUrl\": \"https://api.groq.com/openai/v1\"\n // \"memoryApiKey\": \"gsk_...\"\n \n // Maximum iterations for multi-turn AI analysis (for openai-responses, anthropic, and minimax)\n \"autoCaptureMaxIterations\": 5,\n \n // Timeout per iteration in milliseconds (30 seconds default)\n \"autoCaptureIterationTimeout\": 30000,\n\n // Tries for one turn's capture, on Pi and OpenCode, 2 and 4 seconds apart. After the\n // last one fails, a turn the model could not reach goes to the capture retry queue.\n \"autoCaptureMaxRetries\": 3,\n\n // Maximum UTF-8 bytes for the auto-capture markdown context sent to the summary model.\n // Prevents HTTP 400 context overflows on models with ~131K token windows (e.g. Groq Llama).\n // Rough guide: tokens \u2248 bytes / 4 for mixed code/prose.\n \"autoCaptureMaxContextBytes\": 131072,\n \n // Days to keep AI session history before cleanup\n \"aiSessionRetentionDays\": 7,\n\n // Capture diagnostics: every capture attempt always writes one metadata line\n // (model, stop reason, sizes, outcome) to ~/.omms/omms.log, with no\n // conversation text. Set captureTrace to true to also write each attempt's\n // prompt and raw reply to ~/.omms/traces/ for debugging. Traces can contain\n // conversation content: <private> text and common API key formats are\n // redacted, files are readable only by you, and they are deleted after\n // captureTraceRetentionDays. Only this global file can turn tracing on.\n // \"captureTrace\": false,\n // \"captureTraceRetentionDays\": 7,\n // \"captureAttemptRetentionDays\": 30,\n\n // Capture retry queue: when a live capture fails because the model cannot be\n // reached (network error, timeout, HTTP 408, 429 or 5xx), OMMS keeps a copy of\n // the turn in ~/.omms/data and tries it again later. The copy has <private>\n // text and common API key formats removed. Turns are deleted when the retry\n // ends or after captureRetryRetentionHours (0 to 720). 0 turns the queue off.\n // Only this global file can set it.\n // \"captureRetryRetentionHours\": 72,\n\n // Temperature for AI API requests (set to false to omit parameter for models that don't support it)\n // Some reasoning models (like o1, o3, gpt-5) don't support temperature parameter\n // Set to false and add \"memoryTemperature\": false in config when using such models\n \"memoryTemperature\": 0.3,\n\n // Extra parameters to include in API request body\n // Useful for local inference servers (e.g. llama-server with --jinja) that support\n // additional parameters like disabling thinking/reasoning mode\n // Example for Qwen3 models: { \"enable_thinking\": false }\n // \"memoryExtraParams\": {},\n\n // Language for auto-capture summaries (default: \"auto\" for auto-detection)\n // Options: \"auto\", \"en\", \"id\", \"zh\", \"ja\", \"es\", \"fr\", \"de\", \"ru\", \"pt\", \"ar\", \"ko\"\n // \"autoCaptureLanguage\": \"auto\",\n\n // ============================================\n // Toast Notifications\n // ============================================\n\n // Show toast when memory is auto-captured\n \"showAutoCaptureToasts\": true,\n\n // Show toast when user profile is updated\n \"showUserProfileToasts\": true,\n\n // Show toast for error messages\n \"showErrorToasts\": true,\n\n // ============================================\n // User Profile System\n // ============================================\n\n // Analyze user prompts every N prompts to build/update your user profile\n // When N uncaptured prompts accumulate, AI will analyze them to identify:\n // - User preferences (code style, communication style, tool preferences)\n // - User patterns (recurring topics, problem domains, technical interests)\n // - User workflows (development habits, sequences, learning style)\n // - Skill level (overall and per-domain assessment)\n \"userProfileAnalysisInterval\": 10,\n\n // Days before inactive items (all types) are eligible for removal\n \"userProfileStaleDays\": 2,\n\n // Days before an inactive workflow is eligible for removal. A workflow seen\n // userProfileMinEvidenceForRetention times or more is kept.\n \"userProfileWorkflowStaleDays\": 30,\n\n // Number of preferences shown in UI\n \"userProfileDisplayPreferences\": 20,\n \n // Number of patterns shown in UI\n \"userProfileDisplayPatterns\": 15,\n \n // Number of workflows shown in UI\n \"userProfileDisplayWorkflows\": 10,\n \n // Number of preferences injected into LLM conversation context\n // Keep this small \u2014 the strongest signals are enough; more dilute LLM attention\n \"userProfileInjectPreferences\": 5,\n \n // Number of patterns injected into LLM conversation context\n \"userProfileInjectPatterns\": 5,\n \n // Number of workflows injected into LLM conversation context\n \"userProfileInjectWorkflows\": 3,\n \n // Days before preference confidence starts to decay (if not reinforced)\n // Preferences that aren't seen again will gradually lose confidence and be removed\n \"userProfileConfidenceDecayDays\": 30,\n \n // Number of profile versions to keep in changelog (for rollback/debugging)\n // Older versions are automatically cleaned up\n \"userProfileChangelogRetentionCount\": 5,\n\n // Minimum evidence count for a preference/pattern to survive confidence decay\n // Items confirmed fewer times are more likely to be pruned when confidence decays\n \"userProfileMinEvidenceForRetention\": 3,\n\n // Merge duplicate or vague profile items every N analysed prompts (about 1 model call per 100 items)\n \"userProfileAutoCleanupEnabled\": true,\n \"userProfileAutoCleanupInterval\": 500,\n\n // Enable LLM validation of existing preferences against recent behavior.\n // When enabled, each analysis round checks if top-5 preferences still match recent prompts.\n // Experimental \u2014 disabled by default.\n \"userProfileValidationEnabled\": false,\n\n // ============================================\n // Search Settings\n // ============================================\n \n // Minimum similarity score (0-1) for memory search results. Leave unset to\n // follow the default for your embedding model: 0.5 for qwen3-embedding:0.6b\n // (the only model measured), 0.6 for every other embedder.\n // \"similarityThreshold\": 0.5,\n\n // Maximum number of memories to return in search results.\n // Manual searches can ask for fewer; prompt retrieval uses this ceiling.\n \"maxMemories\": 10,\n\n // ============================================\n // Memory Context Controls\n // ============================================\n\n // Recent memories added at fresh session start (OpenCode V1 and Claude Code).\n // Pi and OpenCode V2 search the store with each prompt instead.\n \"chatMessage\": {\n \"enabled\": true,\n // Recent memories injected at session start.\n \"maxMemories\": 3,\n \"excludeCurrentSession\": true,\n // When to inject: \"first\" message of a session or \"always\".\n // \"injectOn\": \"first\",\n },\n\n // OpenCode profile-learning input limit, in UTF-8 bytes. Smaller values can\n // omit prompts from that input. Does not limit Pi or Claude Code profile input.\n \"userProfileMaxContextBytes\": 32768,\n\n // Shared budget for automatic memory context (profile text, memories, and\n // formatting) across all hosts, in approximate tokens: ceil(UTF-8 bytes / 4).\n // Covers prompt retrieval, recent-memory injection, and session-memory\n // restoration. Not an exact provider token limit or a spending limit.\n \"retrievalMaxTokens\": 2000,\n\n // ============================================\n // Advanced Settings\n // ============================================\n \n // Inject user profile into AI context (preferences, patterns, workflows)\n \"injectProfile\": true\n}\n";
|
|
6
|
+
export declare const CONFIG_TEMPLATE = "{\n // ============================================\n // omms (Opinionated Modular Memory System) Configuration\n // ============================================\n \n // Storage location for the vector database. Leave unset to use the\n // default ~/.omms/data (a legacy ~/.opencode-mem/data store is migrated\n // there automatically on first start; see docs/omms-migration.md).\n // \"storagePath\": \"~/.omms/data\",\n\n \"userEmailOverride\": \"\",\n \"userNameOverride\": \"\",\n \n // ============================================\n // Embedding Model (for similarity search)\n // ============================================\n // Local = Hugging Face / ONNX via @huggingface/transformers (not Apple MLX).\n // Remote = set embeddingApiUrl (OpenAI-compatible /embeddings); embeddingApiKey is optional.\n \n // Default: Nomic Embed v1 (768 dimensions, 8192 context, multilingual)\n \"embeddingModel\": \"Xenova/nomic-embed-text-v1\",\n\n // Opt-in Nomic task prefixes (search_document: / search_query:). After enabling,\n // re-index existing memories so store and query vectors stay aligned.\n // \"embeddingUseTaskPrefixes\": true,\n \n // Auto-detected dimensions (no need to set manually)\n // \"embeddingDimensions\": 768,\n \n // Other recommended local models:\n // \"embeddingModel\": \"Xenova/jina-embeddings-v2-base-en\", // 768 dims, English-only, 8192 context\n // \"embeddingModel\": \"Xenova/jina-embeddings-v2-small-en\", // 512 dims, faster, 8192 context\n // \"embeddingModel\": \"Xenova/all-MiniLM-L6-v2\", // 384 dims, very fast, 512 context\n // \"embeddingModel\": \"Xenova/all-mpnet-base-v2\", // 768 dims, good quality, 512 context\n \n // Optional: OpenAI-compatible API for embeddings (URL required; key only if the server needs one)\n // \"embeddingApiUrl\": \"https://api.openai.com/v1\",\n // \"embeddingApiKey\": \"env://OPENAI_API_KEY\", // or \"sk-...\" / \"file:///path/to/key\"\n // \"embeddingModel\": \"text-embedding-3-small\", // 1536 dims, auto-detected\n \n // ============================================\n // Web Server Settings\n // ============================================\n \n // Start a background import of past chats on each host's next start.\n \"autoBackfill\": true,\n // \"piBackfillModel\": \"inherit\", // or \"external\", or \"provider/model\"\n // \"opencodeBackfillModel\": \"inherit\", // or \"external\", or \"provider/model\"\n // Directory maps for history recorded in moved or deleted directories.\n // \"importPathMaps\": [{ \"from\": \"~/code/app-feat-x\", \"to\": \"~/code/app\" }],\n // Unresolved directories to hide on the Directory maps page. They stay unimported.\n // \"importIgnoredDirectories\": [\"/private/tmp/scratch\"],\n // Claude Code's folder, when it is not ~/.claude (like CLAUDE_CONFIG_DIR).\n // \"claudeConfigDir\": \"~/.claude-work\",\n\n // Register the web app to start when you log in.\n \"webServerAutoStart\": true,\n // Enable web UI for managing memories (accessible at http://localhost:4747)\n \"webServerEnabled\": true,\n \n // Port for web UI server\n \"webServerPort\": 4747,\n \n // Host address for web UI (use 127.0.0.1 for local only, 0.0.0.0 for network access)\n \"webServerHost\": \"127.0.0.1\",\n\n // Optional HTTP Basic Auth for the web UI. Accepts literal, env://, or file:// secrets.\n // \"webServerAuthPassword\": \"\",\n // \"webServerAuthUsername\": \"\",\n\n // Deprecated: imported once into the API token table, then no longer read.\n // Create API tokens on the Settings page (Keys and access) instead.\n // \"webServerApiToken\": \"env://OMMS_WEB_TOKEN\",\n \n // ============================================\n // Database Settings\n // ============================================\n \n // Maximum vectors per database shard (auto-creates new shard when limit reached)\n \"maxVectorsPerShard\": 50000,\n \n // Automatically delete old memories based on retention period\n \"autoCleanupEnabled\": true,\n \n // Days to keep memories before auto-cleanup (only if autoCleanupEnabled is true)\n \"autoCleanupRetentionDays\": 30,\n \n // Automatically detect and remove duplicate memories\n \"deduplicationEnabled\": true,\n \n // Similarity threshold (0-1) for detecting duplicates (higher = stricter)\n \"deduplicationSimilarityThreshold\": 0.90,\n \n // ============================================\n // Memory Scope Settings\n // ============================================\n\n // Default scope for memory list/search queries\n // \"project\" keeps queries within the current project, \"all-projects\" searches across all project shards\n \"memory\": {\n \"defaultScope\": \"project\"\n },\n\n // ============================================\n // Model for auto-capture and profile learning (same rule in OpenCode and Pi)\n // ============================================\n\n // Which model summarises your work, in this order:\n // 1. The host model below: opencodeProvider/opencodeModel in OpenCode,\n // piProvider/piModel in Pi. Set the model to \"inherit\" to follow\n // whatever model the session is using, or to \"external\" to send\n // every call to the external API below.\n // 2. If no host model is set: the external API (memoryModel/memoryApiUrl/\n // memoryApiKey further down).\n // 3. If neither is set: the session's own model.\n // If the host model fails and the external API is configured, the external\n // API is used instead.\n //\n // Host models go through OpenCode's or Pi's own sign-in (OAuth like Claude\n // Pro/Max, GitHub Copilot, ChatGPT, or your own key there), so no separate API\n // key is needed here.\n //\n // Examples (OpenCode names from 'opencode providers list'; Pi names from its model list):\n // \"opencodeProvider\": \"anthropic\", \"opencodeModel\": \"claude-haiku-4-5-20251001\"\n // \"opencodeProvider\": \"openai\", \"opencodeModel\": \"inherit\"\n // \"piProvider\": \"openai-codex\", \"piModel\": \"gpt-5.6-luna\"\n //\n // \"opencodeProvider\": \"anthropic\",\n // \"opencodeModel\": \"claude-haiku-4-5-20251001\",\n // \"piProvider\": \"openai-codex\",\n // \"piModel\": \"gpt-5.6-luna\",\n\n // ============================================\n // Auto-Capture Settings\n // ============================================\n \n // Auto-capture runs in the background without blocking your main session.\n // Note: Ollama may not support tool calling. Use OpenAI, Anthropic, or Groq for best results.\n \n \"autoCaptureEnabled\": true,\n \n // Provider type: \"openai-chat\" | \"openai-responses\" | \"anthropic\" | \"minimax\" | \"orcarouter\" | \"google-gemini\"\n // Note: \"openai-chat\" is a generic OpenAI API-compatible mode.\n // Any service that follows the OpenAI Chat Completions API can use it via custom \"memoryApiUrl\".\n \"memoryProvider\": \"openai-chat\",\n \n // External API (step 2 above, and the fallback when a host model fails).\n // Uncomment all 3 lines and replace memoryApiKey before use:\n // \"memoryModel\": \"gpt-4o-mini\",\n // \"memoryApiUrl\": \"https://api.openai.com/v1\",\n // \"memoryApiKey\": \"sk-...\",\n\n // API Key Formats:\n // Direct value: \"sk-...\"\n // From file: \"file://~/.config/litellm-key.txt\"\n // From env variable: \"env://LITELLM_API_KEY\"\n \n // Examples for different providers:\n // Any OpenAI-compatible endpoint can use the \"openai-chat\" provider pattern below.\n // Common examples: DeepSeek, Qwen (via Alibaba Cloud ModelStudio),\n // Zhipu GLM (BigModel platform), and Kimi (Moonshot AI platform).\n\n // OpenAI Chat Completion (default, backward compatible):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"gpt-4o-mini\"\n // \"memoryApiUrl\": \"https://api.openai.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n\n // DeepSeek (OpenAI-compatible example):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"deepseek-chat\"\n // \"memoryApiUrl\": \"https://api.deepseek.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n \n // OpenAI Responses API (recommended, with session support):\n // \"memoryProvider\": \"openai-responses\"\n // \"memoryModel\": \"gpt-4o\"\n // \"memoryApiUrl\": \"https://api.openai.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n \n // Anthropic (with session support):\n // \"memoryProvider\": \"anthropic\"\n // \"memoryModel\": \"claude-3-5-haiku-20241022\"\n // \"memoryApiUrl\": \"https://api.anthropic.com/v1\"\n // \"memoryApiKey\": \"sk-ant-...\"\n\n // MiniMax (Anthropic Messages-compatible endpoint, with session support):\n // \"memoryProvider\": \"minimax\"\n // \"memoryModel\": \"MiniMax-M3\"\n // \"memoryApiUrl\": \"https://api.minimax.io\" // global endpoint\n // \"memoryApiKey\": \"<MiniMax API key>\"\n // // China endpoint: \"memoryApiUrl\": \"https://api.minimaxi.com\"\n // // Optional adaptive thinking for MiniMax-M3:\n // \"memoryExtraParams\": { \"thinking\": { \"type\": \"adaptive\" } }\n\n // OrcaRouter (OpenAI-compatible gateway, namespaced model IDs, with session support):\n // \"memoryProvider\": \"orcarouter\"\n // \"memoryApiKey\": \"<OrcaRouter API key>\"\n // // memoryApiUrl and memoryModel are optional \u2014 they default to\n // // https://api.orcarouter.ai/v1 and \"orcarouter/auto\" (a routing alias).\n // // OrcaRouter rejects bare model names, so if you set memoryModel, use a\n // // namespaced ID such as \"openai/gpt-5.5\" or \"deepseek/deepseek-v4-flash\".\n // \"memoryModel\": \"openai/gpt-5.5\"\n\n // Groq (OpenAI-compatible, use openai-chat provider):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"llama-3.3-70b-versatile\"\n // \"memoryApiUrl\": \"https://api.groq.com/openai/v1\"\n // \"memoryApiKey\": \"gsk_...\"\n \n // Maximum iterations for multi-turn AI analysis (for openai-responses, anthropic, and minimax)\n \"autoCaptureMaxIterations\": 5,\n \n // Timeout per iteration in milliseconds (30 seconds default)\n \"autoCaptureIterationTimeout\": 30000,\n\n // Tries for one turn's capture, on Pi and OpenCode, 2 and 4 seconds apart. After the\n // last one fails, a turn the model could not reach goes to the capture retry queue.\n \"autoCaptureMaxRetries\": 3,\n\n // Maximum UTF-8 bytes for the auto-capture markdown context sent to the summary model.\n // Prevents HTTP 400 context overflows on models with ~131K token windows (e.g. Groq Llama).\n // Rough guide: tokens \u2248 bytes / 4 for mixed code/prose.\n \"autoCaptureMaxContextBytes\": 131072,\n \n // Days to keep AI session history before cleanup\n \"aiSessionRetentionDays\": 7,\n\n // Capture diagnostics: every capture attempt always writes one metadata line\n // (model, stop reason, sizes, outcome) to ~/.omms/omms.log, with no\n // conversation text. Set captureTrace to true to also write each attempt's\n // prompt and raw reply to ~/.omms/traces/ for debugging. Traces can contain\n // conversation content: <private> text and common API key formats are\n // redacted, files are readable only by you, and they are deleted after\n // captureTraceRetentionDays. Only this global file can turn tracing on.\n // \"captureTrace\": false,\n // \"captureTraceRetentionDays\": 7,\n // \"captureAttemptRetentionDays\": 30,\n\n // Skill learning: record each full turn (prompt, replies, tool calls with\n // their outcome, and the skills used) in ~/.omms/data/skills.db, so OMMS can\n // rank your skills and draft better versions. Off by default. A project config\n // can turn it off but never on. No model call is made to record a turn.\n // \"skillLearning\": false,\n // Remove <private> text and common API key formats before a turn is stored.\n // Global config only. Turning this off stores text as written.\n // \"skillLearningRedact\": true,\n // Folders OMMS reads skills from. Absolute or starting with ~/. Global config\n // only. Unset: ~/.agents/skills and the Claude Code folder's skills folder.\n // \"skillFolders\": [\"~/.agents/skills\"],\n\n // Capture retry queue: when a live capture fails because the model cannot be\n // reached (network error, timeout, HTTP 408, 429 or 5xx), OMMS keeps a copy of\n // the turn in ~/.omms/data and tries it again later. The copy has <private>\n // text and common API key formats removed. Turns are deleted when the retry\n // ends or after captureRetryRetentionHours (0 to 720). 0 turns the queue off.\n // Only this global file can set it.\n // \"captureRetryRetentionHours\": 72,\n\n // Temperature for AI API requests (set to false to omit parameter for models that don't support it)\n // Some reasoning models (like o1, o3, gpt-5) don't support temperature parameter\n // Set to false and add \"memoryTemperature\": false in config when using such models\n \"memoryTemperature\": 0.3,\n\n // Extra parameters to include in API request body\n // Useful for local inference servers (e.g. llama-server with --jinja) that support\n // additional parameters like disabling thinking/reasoning mode\n // Example for Qwen3 models: { \"enable_thinking\": false }\n // \"memoryExtraParams\": {},\n\n // Language for auto-capture summaries (default: \"auto\" for auto-detection)\n // Options: \"auto\", \"en\", \"id\", \"zh\", \"ja\", \"es\", \"fr\", \"de\", \"ru\", \"pt\", \"ar\", \"ko\"\n // \"autoCaptureLanguage\": \"auto\",\n\n // ============================================\n // Toast Notifications\n // ============================================\n\n // Show toast when memory is auto-captured\n \"showAutoCaptureToasts\": true,\n\n // Show toast when user profile is updated\n \"showUserProfileToasts\": true,\n\n // Show toast for error messages\n \"showErrorToasts\": true,\n\n // ============================================\n // User Profile System\n // ============================================\n\n // Analyze user prompts every N prompts to build/update your user profile\n // When N uncaptured prompts accumulate, AI will analyze them to identify:\n // - User preferences (code style, communication style, tool preferences)\n // - User patterns (recurring topics, problem domains, technical interests)\n // - User workflows (development habits, sequences, learning style)\n // - Skill level (overall and per-domain assessment)\n \"userProfileAnalysisInterval\": 10,\n\n // Days before inactive items (all types) are eligible for removal\n \"userProfileStaleDays\": 2,\n\n // Days before an inactive workflow is eligible for removal. A workflow seen\n // userProfileMinEvidenceForRetention times or more is kept.\n \"userProfileWorkflowStaleDays\": 30,\n\n // Number of preferences shown in UI\n \"userProfileDisplayPreferences\": 20,\n \n // Number of patterns shown in UI\n \"userProfileDisplayPatterns\": 15,\n \n // Number of workflows shown in UI\n \"userProfileDisplayWorkflows\": 10,\n \n // Number of preferences injected into LLM conversation context\n // Keep this small \u2014 the strongest signals are enough; more dilute LLM attention\n \"userProfileInjectPreferences\": 5,\n \n // Number of patterns injected into LLM conversation context\n \"userProfileInjectPatterns\": 5,\n \n // Number of workflows injected into LLM conversation context\n \"userProfileInjectWorkflows\": 3,\n \n // Days before preference confidence starts to decay (if not reinforced)\n // Preferences that aren't seen again will gradually lose confidence and be removed\n \"userProfileConfidenceDecayDays\": 30,\n \n // Number of profile versions to keep in changelog (for rollback/debugging)\n // Older versions are automatically cleaned up\n \"userProfileChangelogRetentionCount\": 5,\n\n // Minimum evidence count for a preference/pattern to survive confidence decay\n // Items confirmed fewer times are more likely to be pruned when confidence decays\n \"userProfileMinEvidenceForRetention\": 3,\n\n // Merge duplicate or vague profile items every N analysed prompts (about 1 model call per 100 items)\n \"userProfileAutoCleanupEnabled\": true,\n \"userProfileAutoCleanupInterval\": 500,\n\n // Enable LLM validation of existing preferences against recent behavior.\n // When enabled, each analysis round checks if top-5 preferences still match recent prompts.\n // Experimental \u2014 disabled by default.\n \"userProfileValidationEnabled\": false,\n\n // ============================================\n // Search Settings\n // ============================================\n \n // Minimum similarity score (0-1) for memory search results. Leave unset to\n // follow the default for your embedding model: 0.5 for qwen3-embedding:0.6b\n // (the only model measured), 0.6 for every other embedder.\n // \"similarityThreshold\": 0.5,\n\n // Maximum number of memories to return in search results.\n // Manual searches can ask for fewer; prompt retrieval uses this ceiling.\n \"maxMemories\": 10,\n\n // ============================================\n // Memory Context Controls\n // ============================================\n\n // Recent memories added at fresh session start (OpenCode V1 and Claude Code).\n // Pi and OpenCode V2 search the store with each prompt instead.\n \"chatMessage\": {\n \"enabled\": true,\n // Recent memories injected at session start.\n \"maxMemories\": 3,\n \"excludeCurrentSession\": true,\n // When to inject: \"first\" message of a session or \"always\".\n // \"injectOn\": \"first\",\n },\n\n // OpenCode profile-learning input limit, in UTF-8 bytes. Smaller values can\n // omit prompts from that input. Does not limit Pi or Claude Code profile input.\n \"userProfileMaxContextBytes\": 32768,\n\n // Shared budget for automatic memory context (profile text, memories, and\n // formatting) across all hosts, in approximate tokens: ceil(UTF-8 bytes / 4).\n // Covers prompt retrieval, recent-memory injection, and session-memory\n // restoration. Not an exact provider token limit or a spending limit.\n \"retrievalMaxTokens\": 2000,\n\n // ============================================\n // Advanced Settings\n // ============================================\n \n // Inject user profile into AI context (preferences, patterns, workflows)\n \"injectProfile\": true\n}\n";
|
package/dist/config-template.js
CHANGED
|
@@ -242,6 +242,18 @@ export const CONFIG_TEMPLATE = `{
|
|
|
242
242
|
// "captureTraceRetentionDays": 7,
|
|
243
243
|
// "captureAttemptRetentionDays": 30,
|
|
244
244
|
|
|
245
|
+
// Skill learning: record each full turn (prompt, replies, tool calls with
|
|
246
|
+
// their outcome, and the skills used) in ~/.omms/data/skills.db, so OMMS can
|
|
247
|
+
// rank your skills and draft better versions. Off by default. A project config
|
|
248
|
+
// can turn it off but never on. No model call is made to record a turn.
|
|
249
|
+
// "skillLearning": false,
|
|
250
|
+
// Remove <private> text and common API key formats before a turn is stored.
|
|
251
|
+
// Global config only. Turning this off stores text as written.
|
|
252
|
+
// "skillLearningRedact": true,
|
|
253
|
+
// Folders OMMS reads skills from. Absolute or starting with ~/. Global config
|
|
254
|
+
// only. Unset: ~/.agents/skills and the Claude Code folder's skills folder.
|
|
255
|
+
// "skillFolders": ["~/.agents/skills"],
|
|
256
|
+
|
|
245
257
|
// Capture retry queue: when a live capture fails because the model cannot be
|
|
246
258
|
// reached (network error, timeout, HTTP 408, 429 or 5xx), OMMS keeps a copy of
|
|
247
259
|
// the turn in ~/.omms/data and tries it again later. The copy has <private>
|
package/dist/config.d.ts
CHANGED
|
@@ -57,6 +57,12 @@ interface OmmsConfig {
|
|
|
57
57
|
captureAttemptRetentionDays?: number;
|
|
58
58
|
/** Hours to keep failed live captures for retry. 0 turns the queue off. Global config only. */
|
|
59
59
|
captureRetryRetentionHours?: number;
|
|
60
|
+
/** Record full turns into skills.db (ADR-036). A project can only turn it off. */
|
|
61
|
+
skillLearning?: boolean;
|
|
62
|
+
/** Remove `<private>` text and secrets from recorded turns. Default true. Global config only. */
|
|
63
|
+
skillLearningRedact?: boolean;
|
|
64
|
+
/** Folders OMMS reads skills from. Absolute or `~/`. Unset: the shared and Claude Code skill folders. Global config only. */
|
|
65
|
+
skillFolders?: string[];
|
|
60
66
|
autoBackfill?: boolean;
|
|
61
67
|
opencodeBackfillModel?: string;
|
|
62
68
|
piBackfillModel?: string;
|
|
@@ -125,49 +131,6 @@ export declare function normalizeAutoCaptureMaxContextBytes(value: number): numb
|
|
|
125
131
|
export declare function normalizeCaptureRetryRetentionHours(value: unknown): number;
|
|
126
132
|
export declare function normalizeAutoCleanupRetentionDays(value: number): number;
|
|
127
133
|
declare function buildConfig(fileConfig: OmmsConfig): {
|
|
128
|
-
storagePath: string;
|
|
129
|
-
userEmailOverride: string | undefined;
|
|
130
|
-
userNameOverride: string | undefined;
|
|
131
|
-
embeddingModel: string;
|
|
132
|
-
embeddingDimensions: number;
|
|
133
|
-
embeddingUseTaskPrefixes: boolean;
|
|
134
|
-
embeddingApiUrl: string | undefined;
|
|
135
|
-
embeddingApiKey: string | undefined;
|
|
136
|
-
similarityThreshold: number;
|
|
137
|
-
rerankerEnabled: boolean;
|
|
138
|
-
rerankerApiUrl: string | undefined;
|
|
139
|
-
rerankerModel: string | undefined;
|
|
140
|
-
rerankerApiKey: string | undefined;
|
|
141
|
-
rerankerTimeoutMs: number;
|
|
142
|
-
keywordBoost: number;
|
|
143
|
-
keywordMode: KeywordMode;
|
|
144
|
-
maxMemories: number;
|
|
145
|
-
retrievalMaxTokens: number;
|
|
146
|
-
maxProfileItems: number;
|
|
147
|
-
injectProfile: boolean;
|
|
148
|
-
containerTagPrefix: string;
|
|
149
|
-
autoCaptureEnabled: boolean;
|
|
150
|
-
autoCaptureMaxIterations: number;
|
|
151
|
-
autoCaptureIterationTimeout: number;
|
|
152
|
-
autoCaptureMaxRetries: number;
|
|
153
|
-
autoCaptureMaxContextBytes: number;
|
|
154
|
-
autoCaptureLanguage: string | undefined;
|
|
155
|
-
memoryProvider: "openai-chat" | "openai-responses" | "anthropic" | "minimax" | "orcarouter" | "google-gemini";
|
|
156
|
-
memoryModel: string | undefined;
|
|
157
|
-
memoryApiUrl: string | undefined;
|
|
158
|
-
memoryApiKey: string | undefined;
|
|
159
|
-
memoryTemperature: number | false | undefined;
|
|
160
|
-
memoryExtraParams: Record<string, unknown> | undefined;
|
|
161
|
-
opencodeProvider: string | undefined;
|
|
162
|
-
opencodeModel: string | undefined;
|
|
163
|
-
piProvider: string | undefined;
|
|
164
|
-
piModel: string | undefined;
|
|
165
|
-
autoCaptureProviderStatus: import("./config.js").AutoCaptureProviderStatus;
|
|
166
|
-
aiSessionRetentionDays: number;
|
|
167
|
-
captureTrace: boolean;
|
|
168
|
-
captureTraceRetentionDays: number;
|
|
169
|
-
captureAttemptRetentionDays: number;
|
|
170
|
-
captureRetryRetentionHours: number;
|
|
171
134
|
autoBackfill: boolean;
|
|
172
135
|
opencodeBackfillModel: string;
|
|
173
136
|
piBackfillModel: string;
|
|
@@ -225,12 +188,12 @@ declare function buildConfig(fileConfig: OmmsConfig): {
|
|
|
225
188
|
maxAgeDays: number | undefined;
|
|
226
189
|
injectOn: "first" | "always";
|
|
227
190
|
};
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
export declare function validateGlobalConfig(value: unknown): void;
|
|
232
|
-
export declare let CONFIG: {
|
|
191
|
+
skillLearning: boolean;
|
|
192
|
+
skillLearningRedact: boolean;
|
|
193
|
+
skillFolders: string[] | undefined;
|
|
233
194
|
storagePath: string;
|
|
195
|
+
/** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
|
|
196
|
+
skillStoragePath: string | undefined;
|
|
234
197
|
userEmailOverride: string | undefined;
|
|
235
198
|
userNameOverride: string | undefined;
|
|
236
199
|
embeddingModel: string;
|
|
@@ -273,6 +236,11 @@ export declare let CONFIG: {
|
|
|
273
236
|
captureTraceRetentionDays: number;
|
|
274
237
|
captureAttemptRetentionDays: number;
|
|
275
238
|
captureRetryRetentionHours: number;
|
|
239
|
+
};
|
|
240
|
+
export declare function getGlobalConfigSourcePath(): string | undefined;
|
|
241
|
+
export declare function getGlobalConfigWritePath(): string;
|
|
242
|
+
export declare function validateGlobalConfig(value: unknown): void;
|
|
243
|
+
export declare let CONFIG: {
|
|
276
244
|
autoBackfill: boolean;
|
|
277
245
|
opencodeBackfillModel: string;
|
|
278
246
|
piBackfillModel: string;
|
|
@@ -330,6 +298,54 @@ export declare let CONFIG: {
|
|
|
330
298
|
maxAgeDays: number | undefined;
|
|
331
299
|
injectOn: "first" | "always";
|
|
332
300
|
};
|
|
301
|
+
skillLearning: boolean;
|
|
302
|
+
skillLearningRedact: boolean;
|
|
303
|
+
skillFolders: string[] | undefined;
|
|
304
|
+
storagePath: string;
|
|
305
|
+
/** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
|
|
306
|
+
skillStoragePath: string | undefined;
|
|
307
|
+
userEmailOverride: string | undefined;
|
|
308
|
+
userNameOverride: string | undefined;
|
|
309
|
+
embeddingModel: string;
|
|
310
|
+
embeddingDimensions: number;
|
|
311
|
+
embeddingUseTaskPrefixes: boolean;
|
|
312
|
+
embeddingApiUrl: string | undefined;
|
|
313
|
+
embeddingApiKey: string | undefined;
|
|
314
|
+
similarityThreshold: number;
|
|
315
|
+
rerankerEnabled: boolean;
|
|
316
|
+
rerankerApiUrl: string | undefined;
|
|
317
|
+
rerankerModel: string | undefined;
|
|
318
|
+
rerankerApiKey: string | undefined;
|
|
319
|
+
rerankerTimeoutMs: number;
|
|
320
|
+
keywordBoost: number;
|
|
321
|
+
keywordMode: KeywordMode;
|
|
322
|
+
maxMemories: number;
|
|
323
|
+
retrievalMaxTokens: number;
|
|
324
|
+
maxProfileItems: number;
|
|
325
|
+
injectProfile: boolean;
|
|
326
|
+
containerTagPrefix: string;
|
|
327
|
+
autoCaptureEnabled: boolean;
|
|
328
|
+
autoCaptureMaxIterations: number;
|
|
329
|
+
autoCaptureIterationTimeout: number;
|
|
330
|
+
autoCaptureMaxRetries: number;
|
|
331
|
+
autoCaptureMaxContextBytes: number;
|
|
332
|
+
autoCaptureLanguage: string | undefined;
|
|
333
|
+
memoryProvider: "openai-chat" | "openai-responses" | "anthropic" | "minimax" | "orcarouter" | "google-gemini";
|
|
334
|
+
memoryModel: string | undefined;
|
|
335
|
+
memoryApiUrl: string | undefined;
|
|
336
|
+
memoryApiKey: string | undefined;
|
|
337
|
+
memoryTemperature: number | false | undefined;
|
|
338
|
+
memoryExtraParams: Record<string, unknown> | undefined;
|
|
339
|
+
opencodeProvider: string | undefined;
|
|
340
|
+
opencodeModel: string | undefined;
|
|
341
|
+
piProvider: string | undefined;
|
|
342
|
+
piModel: string | undefined;
|
|
343
|
+
autoCaptureProviderStatus: import("./config.js").AutoCaptureProviderStatus;
|
|
344
|
+
aiSessionRetentionDays: number;
|
|
345
|
+
captureTrace: boolean;
|
|
346
|
+
captureTraceRetentionDays: number;
|
|
347
|
+
captureAttemptRetentionDays: number;
|
|
348
|
+
captureRetryRetentionHours: number;
|
|
333
349
|
};
|
|
334
350
|
type RuntimeConfig = ReturnType<typeof buildConfig>;
|
|
335
351
|
export { getAutoCaptureProviderStatus, type AutoCaptureProviderStatus, } from "./services/ai/live-model-choice.js";
|
package/dist/config.js
CHANGED
|
@@ -9,6 +9,7 @@ import { isPlaceholderApiKey } from "./services/ai/api-key-placeholder.js";
|
|
|
9
9
|
import { getAutoCaptureProviderStatus } from "./services/ai/live-model-choice.js";
|
|
10
10
|
import { parseBackfillModel } from "./importer/backfill-model.js";
|
|
11
11
|
import { parseIgnoredDirectories, parseImportPathMaps } from "./importer/import-path-maps.js";
|
|
12
|
+
import { normaliseSkillConfig, projectSkillOverride, } from "./services/skill-learning/skill-config.js";
|
|
12
13
|
import { checkedMemoryLimit, MEMORY_LIMITS } from "./utils/memory-limits.js";
|
|
13
14
|
import { defaultSimilarityThreshold } from "./utils/threshold-default.js";
|
|
14
15
|
import { RERANKER_TIMEOUT_DEFAULT_MS } from "./services/request-timeouts.js";
|
|
@@ -62,6 +63,8 @@ const DEFAULTS = {
|
|
|
62
63
|
captureTraceRetentionDays: 7,
|
|
63
64
|
captureAttemptRetentionDays: 30,
|
|
64
65
|
captureRetryRetentionHours: 72,
|
|
66
|
+
skillLearning: false,
|
|
67
|
+
skillLearningRedact: true,
|
|
65
68
|
autoBackfill: true,
|
|
66
69
|
opencodeBackfillModel: "inherit",
|
|
67
70
|
piBackfillModel: "inherit",
|
|
@@ -126,6 +129,14 @@ function expandPath(path) {
|
|
|
126
129
|
}
|
|
127
130
|
return path;
|
|
128
131
|
}
|
|
132
|
+
function storagePathFrom(fileConfig) {
|
|
133
|
+
return fileConfig.storagePath ? expandPath(fileConfig.storagePath) : resolveDefaultStoragePath();
|
|
134
|
+
}
|
|
135
|
+
/** Keeps skills.db in the global storage path when a project config moves storagePath (ADR-036). */
|
|
136
|
+
function withSkillStorage(config, global) {
|
|
137
|
+
const path = storagePathFrom(global);
|
|
138
|
+
return { ...config, skillStoragePath: path === config.storagePath ? undefined : path };
|
|
139
|
+
}
|
|
129
140
|
/**
|
|
130
141
|
* Load the first config file that exists, in priority order. A file that
|
|
131
142
|
* exists but cannot be read or parsed is not skipped: falling through would let
|
|
@@ -304,9 +315,9 @@ function buildConfig(fileConfig) {
|
|
|
304
315
|
throw new Error(`Invalid userProfileAutoCleanupInterval config: ${userProfileAutoCleanupInterval}`);
|
|
305
316
|
}
|
|
306
317
|
return {
|
|
307
|
-
storagePath: fileConfig
|
|
308
|
-
|
|
309
|
-
|
|
318
|
+
storagePath: storagePathFrom(fileConfig),
|
|
319
|
+
/** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
|
|
320
|
+
skillStoragePath: undefined,
|
|
310
321
|
userEmailOverride: fileConfig.userEmailOverride,
|
|
311
322
|
userNameOverride: fileConfig.userNameOverride,
|
|
312
323
|
embeddingModel: fileConfig.embeddingModel ?? DEFAULTS.embeddingModel,
|
|
@@ -359,6 +370,7 @@ function buildConfig(fileConfig) {
|
|
|
359
370
|
captureTraceRetentionDays: Math.max(1, Math.floor(fileConfig.captureTraceRetentionDays ?? DEFAULTS.captureTraceRetentionDays)),
|
|
360
371
|
captureAttemptRetentionDays: normalizeAutoCleanupRetentionDays(fileConfig.captureAttemptRetentionDays ?? DEFAULTS.captureAttemptRetentionDays),
|
|
361
372
|
captureRetryRetentionHours: normalizeCaptureRetryRetentionHours(fileConfig.captureRetryRetentionHours),
|
|
373
|
+
...normaliseSkillConfig(fileConfig),
|
|
362
374
|
autoBackfill: fileConfig.autoBackfill ?? DEFAULTS.autoBackfill,
|
|
363
375
|
opencodeBackfillModel: fileConfig.opencodeBackfillModel ?? DEFAULTS.opencodeBackfillModel,
|
|
364
376
|
piBackfillModel: fileConfig.piBackfillModel ?? DEFAULTS.piBackfillModel,
|
|
@@ -442,6 +454,7 @@ export function validateGlobalConfig(value) {
|
|
|
442
454
|
}
|
|
443
455
|
const _globalFileConfig = loadConfigFromPaths(CONFIG_FILES);
|
|
444
456
|
let lastFileConfig = _globalFileConfig;
|
|
457
|
+
let lastGlobalFileConfig = _globalFileConfig;
|
|
445
458
|
export let CONFIG = buildConfig(_globalFileConfig);
|
|
446
459
|
export { getAutoCaptureProviderStatus, } from "./services/ai/live-model-choice.js";
|
|
447
460
|
export { isPlaceholderApiKey };
|
|
@@ -521,6 +534,15 @@ export function initConfig(directory, options = {}) {
|
|
|
521
534
|
delete projectOverrides.captureTraceRetentionDays;
|
|
522
535
|
// The queue holds conversation text, so only the global file sets how long.
|
|
523
536
|
delete projectOverrides.captureRetryRetentionHours;
|
|
537
|
+
// Skill learning stores full turns: a project may only turn it off.
|
|
538
|
+
const skillRule = projectSkillOverride(projectConfig);
|
|
539
|
+
if (skillRule.ignoredTurnOn) {
|
|
540
|
+
log("Project config cannot turn on skillLearning; the value was ignored", { directory });
|
|
541
|
+
}
|
|
542
|
+
delete projectOverrides.skillLearning;
|
|
543
|
+
delete projectOverrides.skillLearningRedact;
|
|
544
|
+
delete projectOverrides.skillFolders;
|
|
545
|
+
Object.assign(projectOverrides, skillRule.override);
|
|
524
546
|
delete projectOverrides.autoBackfill;
|
|
525
547
|
delete projectOverrides.opencodeBackfillModel;
|
|
526
548
|
delete projectOverrides.piBackfillModel;
|
|
@@ -539,8 +561,9 @@ export function initConfig(directory, options = {}) {
|
|
|
539
561
|
delete projectOverrides.webServerAutoStart;
|
|
540
562
|
delete projectOverrides.webServerEnabled;
|
|
541
563
|
const merged = { ...globalConfig, ...projectOverrides };
|
|
542
|
-
const nextConfig = buildConfig(merged);
|
|
564
|
+
const nextConfig = withSkillStorage(buildConfig(merged), globalConfig);
|
|
543
565
|
lastFileConfig = merged;
|
|
566
|
+
lastGlobalFileConfig = globalConfig;
|
|
544
567
|
CONFIG = nextConfig;
|
|
545
568
|
lastConfigDirectory = directory;
|
|
546
569
|
lastConfigSignature = configSignature(directory);
|
|
@@ -551,7 +574,7 @@ export function initConfig(directory, options = {}) {
|
|
|
551
574
|
* ~/.omms/data when the migration marker says it succeeded.
|
|
552
575
|
*/
|
|
553
576
|
function refreshStorageResolution() {
|
|
554
|
-
CONFIG = buildConfig(lastFileConfig);
|
|
577
|
+
CONFIG = withSkillStorage(buildConfig(lastFileConfig), lastGlobalFileConfig);
|
|
555
578
|
}
|
|
556
579
|
/**
|
|
557
580
|
* initConfig plus the one-time legacy store migration (decision D13).
|
package/dist/core/host.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { MemoryHost, MemorySourceType } from "../types/index.js";
|
|
2
|
+
export type CaptureToolOutcome = "ok" | "error" | "unknown";
|
|
2
3
|
export interface CaptureToolCall {
|
|
3
4
|
name: string;
|
|
4
5
|
input: string;
|
|
6
|
+
/** Set by the host readers for skill learning. Memory capture ignores it. */
|
|
7
|
+
outcome?: CaptureToolOutcome;
|
|
8
|
+
/** Error text the host reported, at most 2,000 characters. Memory capture ignores it. */
|
|
9
|
+
error?: string;
|
|
10
|
+
/** Full path of a read-type call, for skill detection. Memory capture ignores it. */
|
|
11
|
+
path?: string;
|
|
5
12
|
}
|
|
6
13
|
export interface CaptureConversation {
|
|
7
14
|
textResponses: string[];
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { type ProfileFailureCode } from "./profile-failure.js";
|
|
2
2
|
/** After a failed profile pass, the process waits this long before the next one. */
|
|
3
3
|
export declare const PROFILE_FAILURE_WAIT_MS: number;
|
|
4
|
-
|
|
5
|
-
* An in-process gate: a failing batch is not retried, and paid for, after
|
|
6
|
-
* every turn. Capture never checks it. A restart clears it.
|
|
7
|
-
*/
|
|
8
|
-
export declare const profileBackoff: {
|
|
4
|
+
export interface Backoff {
|
|
9
5
|
canRun(now?: number): boolean;
|
|
10
6
|
recordSuccess(): void;
|
|
11
7
|
recordFailure(now?: number): void;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* An in-process gate: a failing batch is not retried, and paid for, after
|
|
11
|
+
* every turn. Capture never checks it. A restart clears it. Each instance
|
|
12
|
+
* keeps its own wait, so a failure in one never pauses another.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createBackoff(waitMs: number): Backoff;
|
|
15
|
+
export declare const profileBackoff: Backoff;
|
|
16
|
+
/** After a failed skill draft, automatic review waits 10 minutes. Profile learning keeps running. */
|
|
17
|
+
export declare const skillDraftBackoff: Backoff;
|
|
18
|
+
/**
|
|
19
|
+
* Log one metadata record for a failed pass and start the wait on `backoff`.
|
|
20
|
+
* `host` is null for skill drafting, which runs for every host.
|
|
21
|
+
*/
|
|
22
|
+
export declare function recordProfileFailure(host: "opencode" | "pi" | "claude-code" | null, message: string, error: unknown, now?: number, backoff?: Backoff): ProfileFailureCode;
|
|
15
23
|
/** One catch-up run at a time, and no live pass in this process while it runs. */
|
|
16
24
|
export declare const profileCatchUpLock: {
|
|
17
25
|
isActive: () => boolean;
|
|
@@ -2,26 +2,35 @@ import { log } from "../services/logger.js";
|
|
|
2
2
|
import { profileFailureCode } from "./profile-failure.js";
|
|
3
3
|
/** After a failed profile pass, the process waits this long before the next one. */
|
|
4
4
|
export const PROFILE_FAILURE_WAIT_MS = 10 * 60 * 1000;
|
|
5
|
-
let lastFailureAt = null;
|
|
6
5
|
/**
|
|
7
6
|
* An in-process gate: a failing batch is not retried, and paid for, after
|
|
8
|
-
* every turn. Capture never checks it. A restart clears it.
|
|
7
|
+
* every turn. Capture never checks it. A restart clears it. Each instance
|
|
8
|
+
* keeps its own wait, so a failure in one never pauses another.
|
|
9
9
|
*/
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
export function createBackoff(waitMs) {
|
|
11
|
+
let lastFailureAt = null;
|
|
12
|
+
return {
|
|
13
|
+
canRun(now = Date.now()) {
|
|
14
|
+
return lastFailureAt === null || now - lastFailureAt >= waitMs;
|
|
15
|
+
},
|
|
16
|
+
recordSuccess() {
|
|
17
|
+
lastFailureAt = null;
|
|
18
|
+
},
|
|
19
|
+
recordFailure(now = Date.now()) {
|
|
20
|
+
lastFailureAt = now;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export const profileBackoff = createBackoff(PROFILE_FAILURE_WAIT_MS);
|
|
25
|
+
/** After a failed skill draft, automatic review waits 10 minutes. Profile learning keeps running. */
|
|
26
|
+
export const skillDraftBackoff = createBackoff(10 * 60 * 1000);
|
|
27
|
+
/**
|
|
28
|
+
* Log one metadata record for a failed pass and start the wait on `backoff`.
|
|
29
|
+
* `host` is null for skill drafting, which runs for every host.
|
|
30
|
+
*/
|
|
31
|
+
export function recordProfileFailure(host, message, error, now = Date.now(), backoff = profileBackoff) {
|
|
23
32
|
const reason = profileFailureCode(error);
|
|
24
|
-
|
|
33
|
+
backoff.recordFailure(now);
|
|
25
34
|
log(message, { host, reason }, "error");
|
|
26
35
|
return reason;
|
|
27
36
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SkillOperationArgs, SkillOperationResult, SkillOperationsDeps } from "./skill-operations.js";
|
|
2
|
+
export declare function evidence(args: SkillOperationArgs, deps: SkillOperationsDeps, now: number): Promise<SkillOperationResult>;
|
|
3
|
+
export declare function propose(args: SkillOperationArgs, deps: SkillOperationsDeps, now: number): Promise<SkillOperationResult>;
|
|
4
|
+
export declare function undo(args: SkillOperationArgs, deps: SkillOperationsDeps, now: number): Promise<SkillOperationResult>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { DEFAULT_EVIDENCE_TURNS, MAX_EVIDENCE_TURNS, proposeImprovement, proposeNewSkill, skillEvidence, } from "../services/skill-learning/agent-proposals.js";
|
|
2
|
+
import { undoProposal } from "../services/skill-learning/apply.js";
|
|
3
|
+
import { getProposal } from "../services/skill-learning/proposal-store.js";
|
|
4
|
+
import { rankSkills } from "../services/skill-learning/ranking.js";
|
|
5
|
+
import { getSkillIndex } from "../services/skill-learning/skill-index.js";
|
|
6
|
+
import { reply, resolveSkillPath, watchedTarget } from "./skill-operation-reply.js";
|
|
7
|
+
/**
|
|
8
|
+
* The agent fixes a skill without the external API (design §8): `evidence`
|
|
9
|
+
* gives the file and its turns, `propose` saves the agent's revised file as a
|
|
10
|
+
* pending proposal, and `undo` reverts an applied one. No mode calls a model.
|
|
11
|
+
*/
|
|
12
|
+
const NEXT_STEP = "Write the fixed SKILL.md, then call propose with skill, content and summary. Show the user the diff. Call apply only when the user asks.";
|
|
13
|
+
function isReply(value) {
|
|
14
|
+
return "code" in value;
|
|
15
|
+
}
|
|
16
|
+
function parseLimit(value) {
|
|
17
|
+
if (value === undefined || value === "")
|
|
18
|
+
return DEFAULT_EVIDENCE_TURNS;
|
|
19
|
+
const limit = Number(value);
|
|
20
|
+
return Number.isInteger(limit) && limit >= 1 && limit <= MAX_EVIDENCE_TURNS ? limit : null;
|
|
21
|
+
}
|
|
22
|
+
/** Turn ids from numbers, `T12` labels, or a comma-separated list. */
|
|
23
|
+
function parseTurnIds(value) {
|
|
24
|
+
const items = typeof value === "string" ? value.split(",") : (value ?? []);
|
|
25
|
+
const ids = [];
|
|
26
|
+
for (const item of items) {
|
|
27
|
+
const match = /^\s*T?(\d+)\s*$/i.exec(String(item));
|
|
28
|
+
if (match)
|
|
29
|
+
ids.push(Number(match[1]));
|
|
30
|
+
}
|
|
31
|
+
return ids;
|
|
32
|
+
}
|
|
33
|
+
function turnText(turn) {
|
|
34
|
+
const parts = [`[T${turn.id}] ${new Date(turn.createdAt).toISOString()} ${turn.host}`];
|
|
35
|
+
if (turn.failedAfterUse)
|
|
36
|
+
parts.push("A tool call failed after the skill loaded.");
|
|
37
|
+
parts.push(`Prompt: ${turn.prompt}`);
|
|
38
|
+
if (turn.tools.length > 0) {
|
|
39
|
+
parts.push("Tool calls:");
|
|
40
|
+
for (const t of turn.tools) {
|
|
41
|
+
parts.push(`- ${t.name}: ${t.outcome}${t.error ? ` (${t.error})` : ""}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
parts.push(...turn.replies.map((r, i) => `Reply ${i + 1}: ${r}`));
|
|
45
|
+
if (turn.cut)
|
|
46
|
+
parts.push("(cut)");
|
|
47
|
+
return parts.join("\n");
|
|
48
|
+
}
|
|
49
|
+
export async function evidence(args, deps, now) {
|
|
50
|
+
const skill = args.skill?.trim();
|
|
51
|
+
if (!skill)
|
|
52
|
+
return reply("evidence", false, "invalid-args", "Give skill: a name or a path.");
|
|
53
|
+
const limit = parseLimit(args.limit);
|
|
54
|
+
if (limit === null) {
|
|
55
|
+
return reply("evidence", false, "invalid-args", `limit must be 1 to ${MAX_EVIDENCE_TURNS}.`);
|
|
56
|
+
}
|
|
57
|
+
const index = getSkillIndex(deps.folders, now);
|
|
58
|
+
const found = resolveSkillPath("evidence", skill, index);
|
|
59
|
+
if (isReply(found))
|
|
60
|
+
return found;
|
|
61
|
+
const result = await skillEvidence(found.path, limit, { store: deps.store, index, now });
|
|
62
|
+
if (!result.ok)
|
|
63
|
+
return reply("evidence", false, result.code, result.message);
|
|
64
|
+
const ranked = (await rankSkills(deps.store, index, now)).find((r) => r.path === found.path);
|
|
65
|
+
const numbers = {
|
|
66
|
+
uses: ranked?.uses ?? 0,
|
|
67
|
+
failedTurns: ranked?.failedTurns ?? 0,
|
|
68
|
+
attention: ranked?.attention ?? 0,
|
|
69
|
+
};
|
|
70
|
+
const text = [
|
|
71
|
+
`Skill: ${result.ref.name}`,
|
|
72
|
+
`File: ${found.path}`,
|
|
73
|
+
`Last 30 days: ${numbers.uses} uses, ${numbers.failedTurns} failed turns, attention ${numbers.attention.toFixed(2)}`,
|
|
74
|
+
`Current SKILL.md:\n<<<\n${result.content}\n>>>`,
|
|
75
|
+
"Recorded turns, failed first:",
|
|
76
|
+
result.turns.map(turnText).join("\n\n") || "(none)",
|
|
77
|
+
NEXT_STEP,
|
|
78
|
+
].join("\n\n");
|
|
79
|
+
return reply("evidence", true, "ok", text, {
|
|
80
|
+
name: result.ref.name,
|
|
81
|
+
path: found.path,
|
|
82
|
+
content: result.content,
|
|
83
|
+
...numbers,
|
|
84
|
+
turns: result.turns,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function proposeReply(result) {
|
|
88
|
+
if (!result.ok)
|
|
89
|
+
return reply("propose", false, result.code, result.message);
|
|
90
|
+
if (result.code === "no-change")
|
|
91
|
+
return reply("propose", true, "no-change", result.message);
|
|
92
|
+
const p = result.proposal;
|
|
93
|
+
return reply("propose", true, "proposed", `Saved pending proposal ${p.id} for ${p.skillName}. Show the user the diff below. Call apply only when the user asks.\n\n${p.diff ?? ""}`, { proposal: p });
|
|
94
|
+
}
|
|
95
|
+
export async function propose(args, deps, now) {
|
|
96
|
+
if (typeof args.content !== "string" || args.content.trim() === "") {
|
|
97
|
+
return reply("propose", false, "invalid-args", "Give content: the full new SKILL.md.");
|
|
98
|
+
}
|
|
99
|
+
const input = {
|
|
100
|
+
content: args.content,
|
|
101
|
+
summary: args.summary ?? "",
|
|
102
|
+
evidenceTurnIds: parseTurnIds(args.evidence),
|
|
103
|
+
};
|
|
104
|
+
const index = getSkillIndex(deps.folders, now);
|
|
105
|
+
const agentDeps = { store: deps.store, index, now };
|
|
106
|
+
if (args.idea) {
|
|
107
|
+
const name = args.idea.name?.trim();
|
|
108
|
+
if (!name)
|
|
109
|
+
return reply("propose", false, "invalid-args", "An idea needs a name.");
|
|
110
|
+
const target = watchedTarget("propose", deps.folders, args.folder);
|
|
111
|
+
if (isReply(target))
|
|
112
|
+
return target;
|
|
113
|
+
await deps.store.init();
|
|
114
|
+
return proposeReply(await proposeNewSkill(name, target.folder, input, agentDeps));
|
|
115
|
+
}
|
|
116
|
+
const skill = args.skill?.trim();
|
|
117
|
+
if (!skill)
|
|
118
|
+
return reply("propose", false, "invalid-args", "Give skill, or idea and folder.");
|
|
119
|
+
const found = resolveSkillPath("propose", skill, index);
|
|
120
|
+
if (isReply(found))
|
|
121
|
+
return found;
|
|
122
|
+
await deps.store.init();
|
|
123
|
+
return proposeReply(await proposeImprovement(found.path, input, agentDeps));
|
|
124
|
+
}
|
|
125
|
+
export async function undo(args, deps, now) {
|
|
126
|
+
if (!args.id)
|
|
127
|
+
return reply("undo", false, "invalid-args", "Give the proposal id.");
|
|
128
|
+
if (!(await getProposal(deps.store, args.id))) {
|
|
129
|
+
return reply("undo", false, "not-found", `No proposal ${args.id}.`);
|
|
130
|
+
}
|
|
131
|
+
await deps.store.init();
|
|
132
|
+
const result = await undoProposal(args.id, {
|
|
133
|
+
store: deps.store,
|
|
134
|
+
folders: deps.folders,
|
|
135
|
+
now: () => now,
|
|
136
|
+
});
|
|
137
|
+
if (!result.ok) {
|
|
138
|
+
const data = result.savedContent === undefined ? undefined : { savedContent: result.savedContent };
|
|
139
|
+
return reply("undo", false, result.code, result.message, data);
|
|
140
|
+
}
|
|
141
|
+
return reply("undo", true, "undone", `Undid proposal ${args.id} and restored ${result.path}.`, {
|
|
142
|
+
proposal: result.proposal,
|
|
143
|
+
path: result.path,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SkillIndex } from "../services/skill-learning/skill-index.js";
|
|
2
|
+
import type { SkillOperationResult } from "./skill-operations.js";
|
|
3
|
+
/** Helpers that `skill-operations.ts` and `skill-agent-modes.ts` share. */
|
|
4
|
+
export declare function reply(mode: string, success: boolean, code: string, text: string, data?: Record<string, unknown>): SkillOperationResult;
|
|
5
|
+
export declare function expandHome(path: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* The `SKILL.md` path for a skill name or path, or the reply that explains
|
|
8
|
+
* why there is none: no skill of that name, or more than one.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveSkillPath(mode: string, skill: string, index: SkillIndex): {
|
|
11
|
+
path: string;
|
|
12
|
+
} | SkillOperationResult;
|
|
13
|
+
/** `folder` when it is one of the watched folders, or the reply that lists them. */
|
|
14
|
+
export declare function watchedTarget(mode: string, folders: readonly string[], folder: string | undefined): {
|
|
15
|
+
folder: string;
|
|
16
|
+
} | SkillOperationResult;
|