@fraziym/axoniz 0.1.0
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/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/agents/definition.d.ts +15 -0
- package/dist/agents/definition.d.ts.map +1 -0
- package/dist/agents/definition.js +41 -0
- package/dist/agents/definition.js.map +1 -0
- package/dist/agents/index.d.ts +12 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +11 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/registry.d.ts +17 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/registry.js +23 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/agents/specialized.d.ts +31 -0
- package/dist/agents/specialized.d.ts.map +1 -0
- package/dist/agents/specialized.js +75 -0
- package/dist/agents/specialized.js.map +1 -0
- package/dist/awareness/index.d.ts +14 -0
- package/dist/awareness/index.d.ts.map +1 -0
- package/dist/awareness/index.js +22 -0
- package/dist/awareness/index.js.map +1 -0
- package/dist/awareness/service.d.ts +180 -0
- package/dist/awareness/service.d.ts.map +1 -0
- package/dist/awareness/service.js +789 -0
- package/dist/awareness/service.js.map +1 -0
- package/dist/cli/entry.d.ts +13 -0
- package/dist/cli/entry.d.ts.map +1 -0
- package/dist/cli/entry.js +35 -0
- package/dist/cli/entry.js.map +1 -0
- package/dist/comms/telegram.d.ts +41 -0
- package/dist/comms/telegram.d.ts.map +1 -0
- package/dist/comms/telegram.js +332 -0
- package/dist/comms/telegram.js.map +1 -0
- package/dist/core/agent.d.ts +252 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +1399 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/auth.d.ts +23 -0
- package/dist/core/auth.d.ts.map +1 -0
- package/dist/core/auth.js +113 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/authority.d.ts +118 -0
- package/dist/core/authority.d.ts.map +1 -0
- package/dist/core/authority.js +402 -0
- package/dist/core/authority.js.map +1 -0
- package/dist/core/backend/async_queue.d.ts +17 -0
- package/dist/core/backend/async_queue.d.ts.map +1 -0
- package/dist/core/backend/async_queue.js +66 -0
- package/dist/core/backend/async_queue.js.map +1 -0
- package/dist/core/backend/index.d.ts +24 -0
- package/dist/core/backend/index.d.ts.map +1 -0
- package/dist/core/backend/index.js +128 -0
- package/dist/core/backend/index.js.map +1 -0
- package/dist/core/backend/llama_server.d.ts +39 -0
- package/dist/core/backend/llama_server.d.ts.map +1 -0
- package/dist/core/backend/llama_server.js +182 -0
- package/dist/core/backend/llama_server.js.map +1 -0
- package/dist/core/backend/llamacpp.d.ts +40 -0
- package/dist/core/backend/llamacpp.d.ts.map +1 -0
- package/dist/core/backend/llamacpp.js +200 -0
- package/dist/core/backend/llamacpp.js.map +1 -0
- package/dist/core/backend/lmstudio.d.ts +53 -0
- package/dist/core/backend/lmstudio.d.ts.map +1 -0
- package/dist/core/backend/lmstudio.js +274 -0
- package/dist/core/backend/lmstudio.js.map +1 -0
- package/dist/core/backend/ollama.d.ts +25 -0
- package/dist/core/backend/ollama.d.ts.map +1 -0
- package/dist/core/backend/ollama.js +86 -0
- package/dist/core/backend/ollama.js.map +1 -0
- package/dist/core/backend/openai.d.ts +27 -0
- package/dist/core/backend/openai.d.ts.map +1 -0
- package/dist/core/backend/openai.js +84 -0
- package/dist/core/backend/openai.js.map +1 -0
- package/dist/core/backend/types.d.ts +73 -0
- package/dist/core/backend/types.d.ts.map +1 -0
- package/dist/core/backend/types.js +137 -0
- package/dist/core/backend/types.js.map +1 -0
- package/dist/core/chat_store.d.ts +77 -0
- package/dist/core/chat_store.d.ts.map +1 -0
- package/dist/core/chat_store.js +332 -0
- package/dist/core/chat_store.js.map +1 -0
- package/dist/core/cli.d.ts +82 -0
- package/dist/core/cli.d.ts.map +1 -0
- package/dist/core/cli.js +602 -0
- package/dist/core/cli.js.map +1 -0
- package/dist/core/config.d.ts +127 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +295 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/debug.d.ts +8 -0
- package/dist/core/debug.d.ts.map +1 -0
- package/dist/core/debug.js +8 -0
- package/dist/core/debug.js.map +1 -0
- package/dist/core/downloader.d.ts +31 -0
- package/dist/core/downloader.d.ts.map +1 -0
- package/dist/core/downloader.js +180 -0
- package/dist/core/downloader.js.map +1 -0
- package/dist/core/errors.d.ts +80 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +134 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/extras.d.ts +160 -0
- package/dist/core/extras.d.ts.map +1 -0
- package/dist/core/extras.js +553 -0
- package/dist/core/extras.js.map +1 -0
- package/dist/core/health.d.ts +27 -0
- package/dist/core/health.d.ts.map +1 -0
- package/dist/core/health.js +96 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/history.d.ts +50 -0
- package/dist/core/history.d.ts.map +1 -0
- package/dist/core/history.js +276 -0
- package/dist/core/history.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/intelligence/ast_index.d.ts +255 -0
- package/dist/core/intelligence/ast_index.d.ts.map +1 -0
- package/dist/core/intelligence/ast_index.js +1369 -0
- package/dist/core/intelligence/ast_index.js.map +1 -0
- package/dist/core/intelligence/confidence.d.ts +113 -0
- package/dist/core/intelligence/confidence.d.ts.map +1 -0
- package/dist/core/intelligence/confidence.js +298 -0
- package/dist/core/intelligence/confidence.js.map +1 -0
- package/dist/core/intelligence/context_compressor.d.ts +80 -0
- package/dist/core/intelligence/context_compressor.d.ts.map +1 -0
- package/dist/core/intelligence/context_compressor.js +245 -0
- package/dist/core/intelligence/context_compressor.js.map +1 -0
- package/dist/core/intelligence/daemon.d.ts +247 -0
- package/dist/core/intelligence/daemon.d.ts.map +1 -0
- package/dist/core/intelligence/daemon.js +842 -0
- package/dist/core/intelligence/daemon.js.map +1 -0
- package/dist/core/intelligence/index.d.ts +60 -0
- package/dist/core/intelligence/index.d.ts.map +1 -0
- package/dist/core/intelligence/index.js +101 -0
- package/dist/core/intelligence/index.js.map +1 -0
- package/dist/core/intelligence/predictor.d.ts +246 -0
- package/dist/core/intelligence/predictor.d.ts.map +1 -0
- package/dist/core/intelligence/predictor.js +590 -0
- package/dist/core/intelligence/predictor.js.map +1 -0
- package/dist/core/intelligence/reflex.d.ts +84 -0
- package/dist/core/intelligence/reflex.d.ts.map +1 -0
- package/dist/core/intelligence/reflex.js +344 -0
- package/dist/core/intelligence/reflex.js.map +1 -0
- package/dist/core/intelligence/self_correction.d.ts +121 -0
- package/dist/core/intelligence/self_correction.d.ts.map +1 -0
- package/dist/core/intelligence/self_correction.js +327 -0
- package/dist/core/intelligence/self_correction.js.map +1 -0
- package/dist/core/intelligence/skill_distiller.d.ts +100 -0
- package/dist/core/intelligence/skill_distiller.d.ts.map +1 -0
- package/dist/core/intelligence/skill_distiller.js +246 -0
- package/dist/core/intelligence/skill_distiller.js.map +1 -0
- package/dist/core/intelligence/swarm.d.ts +330 -0
- package/dist/core/intelligence/swarm.d.ts.map +1 -0
- package/dist/core/intelligence/swarm.js +1064 -0
- package/dist/core/intelligence/swarm.js.map +1 -0
- package/dist/core/intelligence/trajectory.d.ts +107 -0
- package/dist/core/intelligence/trajectory.d.ts.map +1 -0
- package/dist/core/intelligence/trajectory.js +330 -0
- package/dist/core/intelligence/trajectory.js.map +1 -0
- package/dist/core/logger.d.ts +111 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +640 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/loop.d.ts +123 -0
- package/dist/core/loop.d.ts.map +1 -0
- package/dist/core/loop.js +423 -0
- package/dist/core/loop.js.map +1 -0
- package/dist/core/memory.d.ts +81 -0
- package/dist/core/memory.d.ts.map +1 -0
- package/dist/core/memory.js +328 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/metrics.d.ts +29 -0
- package/dist/core/metrics.d.ts.map +1 -0
- package/dist/core/metrics.js +109 -0
- package/dist/core/metrics.js.map +1 -0
- package/dist/core/models.d.ts +51 -0
- package/dist/core/models.d.ts.map +1 -0
- package/dist/core/models.js +146 -0
- package/dist/core/models.js.map +1 -0
- package/dist/core/optimizer.d.ts +34 -0
- package/dist/core/optimizer.d.ts.map +1 -0
- package/dist/core/optimizer.js +131 -0
- package/dist/core/optimizer.js.map +1 -0
- package/dist/core/persona.d.ts +35 -0
- package/dist/core/persona.d.ts.map +1 -0
- package/dist/core/persona.js +126 -0
- package/dist/core/persona.js.map +1 -0
- package/dist/core/rate_limit.d.ts +23 -0
- package/dist/core/rate_limit.d.ts.map +1 -0
- package/dist/core/rate_limit.js +62 -0
- package/dist/core/rate_limit.js.map +1 -0
- package/dist/core/runner.d.ts +63 -0
- package/dist/core/runner.d.ts.map +1 -0
- package/dist/core/runner.js +671 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/core/stream_parser.d.ts +43 -0
- package/dist/core/stream_parser.d.ts.map +1 -0
- package/dist/core/stream_parser.js +140 -0
- package/dist/core/stream_parser.js.map +1 -0
- package/dist/core/tool_schemas.d.ts +26 -0
- package/dist/core/tool_schemas.d.ts.map +1 -0
- package/dist/core/tool_schemas.js +124 -0
- package/dist/core/tool_schemas.js.map +1 -0
- package/dist/core/which.d.ts +2 -0
- package/dist/core/which.d.ts.map +1 -0
- package/dist/core/which.js +35 -0
- package/dist/core/which.js.map +1 -0
- package/dist/goals/index.d.ts +9 -0
- package/dist/goals/index.d.ts.map +1 -0
- package/dist/goals/index.js +17 -0
- package/dist/goals/index.js.map +1 -0
- package/dist/goals/service.d.ts +115 -0
- package/dist/goals/service.d.ts.map +1 -0
- package/dist/goals/service.js +415 -0
- package/dist/goals/service.js.map +1 -0
- package/dist/goals/types.d.ts +47 -0
- package/dist/goals/types.d.ts.map +1 -0
- package/dist/goals/types.js +66 -0
- package/dist/goals/types.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/autonomous_startup.d.ts +51 -0
- package/dist/integrations/autonomous_startup.d.ts.map +1 -0
- package/dist/integrations/autonomous_startup.js +419 -0
- package/dist/integrations/autonomous_startup.js.map +1 -0
- package/dist/integrations/goose.d.ts +105 -0
- package/dist/integrations/goose.d.ts.map +1 -0
- package/dist/integrations/goose.js +408 -0
- package/dist/integrations/goose.js.map +1 -0
- package/dist/integrations/index.d.ts +21 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +17 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/mcp.d.ts +73 -0
- package/dist/integrations/mcp.d.ts.map +1 -0
- package/dist/integrations/mcp.js +307 -0
- package/dist/integrations/mcp.js.map +1 -0
- package/dist/integrations/mempalace.d.ts +18 -0
- package/dist/integrations/mempalace.d.ts.map +1 -0
- package/dist/integrations/mempalace.js +25 -0
- package/dist/integrations/mempalace.js.map +1 -0
- package/dist/integrations/multi_agent.d.ts +82 -0
- package/dist/integrations/multi_agent.d.ts.map +1 -0
- package/dist/integrations/multi_agent.js +280 -0
- package/dist/integrations/multi_agent.js.map +1 -0
- package/dist/integrations/unified_memory.d.ts +415 -0
- package/dist/integrations/unified_memory.d.ts.map +1 -0
- package/dist/integrations/unified_memory.js +1489 -0
- package/dist/integrations/unified_memory.js.map +1 -0
- package/dist/roles/beru.yaml +48 -0
- package/dist/sidecar/client.d.ts +63 -0
- package/dist/sidecar/client.d.ts.map +1 -0
- package/dist/sidecar/client.js +222 -0
- package/dist/sidecar/client.js.map +1 -0
- package/dist/sidecar/index.d.ts +7 -0
- package/dist/sidecar/index.d.ts.map +1 -0
- package/dist/sidecar/index.js +6 -0
- package/dist/sidecar/index.js.map +1 -0
- package/dist/startup.d.ts +22 -0
- package/dist/startup.d.ts.map +1 -0
- package/dist/startup.js +194 -0
- package/dist/startup.js.map +1 -0
- package/dist/tools/_internal.d.ts +90 -0
- package/dist/tools/_internal.d.ts.map +1 -0
- package/dist/tools/_internal.js +375 -0
- package/dist/tools/_internal.js.map +1 -0
- package/dist/tools/_smoke.d.ts +2 -0
- package/dist/tools/_smoke.d.ts.map +1 -0
- package/dist/tools/_smoke.js +203 -0
- package/dist/tools/_smoke.js.map +1 -0
- package/dist/tools/axodex_tools.d.ts +51 -0
- package/dist/tools/axodex_tools.d.ts.map +1 -0
- package/dist/tools/axodex_tools.js +181 -0
- package/dist/tools/axodex_tools.js.map +1 -0
- package/dist/tools/code_tools.d.ts +33 -0
- package/dist/tools/code_tools.d.ts.map +1 -0
- package/dist/tools/code_tools.js +191 -0
- package/dist/tools/code_tools.js.map +1 -0
- package/dist/tools/computer_tools.d.ts +59 -0
- package/dist/tools/computer_tools.d.ts.map +1 -0
- package/dist/tools/computer_tools.js +348 -0
- package/dist/tools/computer_tools.js.map +1 -0
- package/dist/tools/file_tools.d.ts +51 -0
- package/dist/tools/file_tools.d.ts.map +1 -0
- package/dist/tools/file_tools.js +424 -0
- package/dist/tools/file_tools.js.map +1 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +13 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/shell_tools.d.ts +39 -0
- package/dist/tools/shell_tools.d.ts.map +1 -0
- package/dist/tools/shell_tools.js +163 -0
- package/dist/tools/shell_tools.js.map +1 -0
- package/dist/tools/web_tools.d.ts +31 -0
- package/dist/tools/web_tools.d.ts.map +1 -0
- package/dist/tools/web_tools.js +117 -0
- package/dist/tools/web_tools.js.map +1 -0
- package/dist/voice/index.d.ts +41 -0
- package/dist/voice/index.d.ts.map +1 -0
- package/dist/voice/index.js +58 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/light_daemon.d.ts +197 -0
- package/dist/voice/light_daemon.d.ts.map +1 -0
- package/dist/voice/light_daemon.js +746 -0
- package/dist/voice/light_daemon.js.map +1 -0
- package/dist/voice/setup.d.ts +63 -0
- package/dist/voice/setup.d.ts.map +1 -0
- package/dist/voice/setup.js +443 -0
- package/dist/voice/setup.js.map +1 -0
- package/dist/voice/stt.d.ts +148 -0
- package/dist/voice/stt.d.ts.map +1 -0
- package/dist/voice/stt.js +361 -0
- package/dist/voice/stt.js.map +1 -0
- package/dist/voice/tts.d.ts +168 -0
- package/dist/voice/tts.d.ts.map +1 -0
- package/dist/voice/tts.js +698 -0
- package/dist/voice/tts.js.map +1 -0
- package/dist/voice/voice_loop.d.ts +101 -0
- package/dist/voice/voice_loop.d.ts.map +1 -0
- package/dist/voice/voice_loop.js +463 -0
- package/dist/voice/voice_loop.js.map +1 -0
- package/dist/voice/wake_word.d.ts +153 -0
- package/dist/voice/wake_word.d.ts.map +1 -0
- package/dist/voice/wake_word.js +458 -0
- package/dist/voice/wake_word.js.map +1 -0
- package/dist/voice/wav.d.ts +99 -0
- package/dist/voice/wav.d.ts.map +1 -0
- package/dist/voice/wav.js +306 -0
- package/dist/voice/wav.js.map +1 -0
- package/dist/web/broker.d.ts +33 -0
- package/dist/web/broker.d.ts.map +1 -0
- package/dist/web/broker.js +58 -0
- package/dist/web/broker.js.map +1 -0
- package/dist/web/open_browser.d.ts +2 -0
- package/dist/web/open_browser.d.ts.map +1 -0
- package/dist/web/open_browser.js +42 -0
- package/dist/web/open_browser.js.map +1 -0
- package/dist/web/server.d.ts +54 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +831 -0
- package/dist/web/server.js.map +1 -0
- package/dist/web/static/app.js +4121 -0
- package/dist/web/static/index.html +2281 -0
- package/dist/web/static/js/MODULE_SUMMARY.md +124 -0
- package/dist/web/static/js/a11y.js +165 -0
- package/dist/web/static/js/admin.js +2145 -0
- package/dist/web/static/js/assistant.js +475 -0
- package/dist/web/static/js/calendar/reminders.js +114 -0
- package/dist/web/static/js/calendar/utils.js +166 -0
- package/dist/web/static/js/calendar.js +3348 -0
- package/dist/web/static/js/censor.js +356 -0
- package/dist/web/static/js/chat.js +4546 -0
- package/dist/web/static/js/chatRenderer.js +2294 -0
- package/dist/web/static/js/chatStream.js +276 -0
- package/dist/web/static/js/codeRunner.js +398 -0
- package/dist/web/static/js/color/hex.js +14 -0
- package/dist/web/static/js/colorPicker.js +453 -0
- package/dist/web/static/js/compare/icons.js +77 -0
- package/dist/web/static/js/compare/index.js +1482 -0
- package/dist/web/static/js/compare/models.js +104 -0
- package/dist/web/static/js/compare/panes.js +817 -0
- package/dist/web/static/js/compare/probe.js +78 -0
- package/dist/web/static/js/compare/scoreboard.js +223 -0
- package/dist/web/static/js/compare/selector.js +1335 -0
- package/dist/web/static/js/compare/state.js +58 -0
- package/dist/web/static/js/compare/stream.js +695 -0
- package/dist/web/static/js/compare/vote.js +254 -0
- package/dist/web/static/js/cookbook-diagnosis.js +694 -0
- package/dist/web/static/js/cookbook-hwfit.js +1888 -0
- package/dist/web/static/js/cookbook.js +2093 -0
- package/dist/web/static/js/cookbookDownload.js +633 -0
- package/dist/web/static/js/cookbookProgressSignal.js +29 -0
- package/dist/web/static/js/cookbookRunning.js +3506 -0
- package/dist/web/static/js/cookbookServe.js +2128 -0
- package/dist/web/static/js/document.js +9731 -0
- package/dist/web/static/js/documentLibrary.js +3376 -0
- package/dist/web/static/js/dragSort.js +265 -0
- package/dist/web/static/js/editor/ai-inpaint.js +374 -0
- package/dist/web/static/js/editor/ai-models.js +273 -0
- package/dist/web/static/js/editor/ai-rembg.js +230 -0
- package/dist/web/static/js/editor/ai-tool-runner.js +147 -0
- package/dist/web/static/js/editor/ai-tools-misc.js +216 -0
- package/dist/web/static/js/editor/build/controls.js +366 -0
- package/dist/web/static/js/editor/build/popups.js +112 -0
- package/dist/web/static/js/editor/build/right-panel.js +200 -0
- package/dist/web/static/js/editor/build/toolbar.js +73 -0
- package/dist/web/static/js/editor/build/topbar.js +131 -0
- package/dist/web/static/js/editor/build/transform-popup.js +109 -0
- package/dist/web/static/js/editor/canvas-coords.js +21 -0
- package/dist/web/static/js/editor/canvas-events.js +197 -0
- package/dist/web/static/js/editor/canvas-transforms.js +132 -0
- package/dist/web/static/js/editor/checkerboard.js +24 -0
- package/dist/web/static/js/editor/clipboard-and-drop.js +133 -0
- package/dist/web/static/js/editor/composite-helpers.js +83 -0
- package/dist/web/static/js/editor/filters/blur.js +118 -0
- package/dist/web/static/js/editor/filters/edge-feather.js +70 -0
- package/dist/web/static/js/editor/fx/adj-popup.js +677 -0
- package/dist/web/static/js/editor/fx/filter-string.js +38 -0
- package/dist/web/static/js/editor/fx/histogram.js +67 -0
- package/dist/web/static/js/editor/fx/pixel-pass.js +249 -0
- package/dist/web/static/js/editor/harmonize-masks.js +116 -0
- package/dist/web/static/js/editor/history-panel.js +176 -0
- package/dist/web/static/js/editor/keyboard-shortcuts.js +271 -0
- package/dist/web/static/js/editor/layer-helpers.js +131 -0
- package/dist/web/static/js/editor/layer-panel.js +600 -0
- package/dist/web/static/js/editor/mask-utils.js +91 -0
- package/dist/web/static/js/editor/shortcuts-popover.js +149 -0
- package/dist/web/static/js/editor/slider-ux.js +191 -0
- package/dist/web/static/js/editor/snap.js +107 -0
- package/dist/web/static/js/editor/state.js +257 -0
- package/dist/web/static/js/editor/stroke-pipeline.js +162 -0
- package/dist/web/static/js/editor/stroke-tool-sliders.js +64 -0
- package/dist/web/static/js/editor/tools/clone.js +81 -0
- package/dist/web/static/js/editor/tools/crop.js +137 -0
- package/dist/web/static/js/editor/tools/flood-fill.js +72 -0
- package/dist/web/static/js/editor/tools/lasso-mask.js +171 -0
- package/dist/web/static/js/editor/tools/lasso.js +65 -0
- package/dist/web/static/js/editor/tools/move.js +79 -0
- package/dist/web/static/js/editor/tools/stroke.js +123 -0
- package/dist/web/static/js/editor/tools/transform-drag.js +174 -0
- package/dist/web/static/js/editor/tools/transform-handles.js +271 -0
- package/dist/web/static/js/editor/tools/transform-session.js +381 -0
- package/dist/web/static/js/editor/tools/wand.js +46 -0
- package/dist/web/static/js/editor/wire-import.js +148 -0
- package/dist/web/static/js/editor/wire-inpaint-controls.js +164 -0
- package/dist/web/static/js/editor/wire-merge-buttons.js +103 -0
- package/dist/web/static/js/editor/wire-selection-controls.js +170 -0
- package/dist/web/static/js/editor/wire-topbar-menus.js +174 -0
- package/dist/web/static/js/editor/wire-topbar-overflow.js +49 -0
- package/dist/web/static/js/editor/wire-topbar.js +188 -0
- package/dist/web/static/js/emailInbox.js +1245 -0
- package/dist/web/static/js/emailLibrary/replyRecipients.js +27 -0
- package/dist/web/static/js/emailLibrary/signatureFold.js +339 -0
- package/dist/web/static/js/emailLibrary/state.js +34 -0
- package/dist/web/static/js/emailLibrary/utils.js +202 -0
- package/dist/web/static/js/emailLibrary.js +5217 -0
- package/dist/web/static/js/emojiPicker.js +311 -0
- package/dist/web/static/js/escMenuStack.js +102 -0
- package/dist/web/static/js/fileHandler.js +295 -0
- package/dist/web/static/js/gallery.js +2835 -0
- package/dist/web/static/js/galleryEditor.js +3798 -0
- package/dist/web/static/js/group.js +955 -0
- package/dist/web/static/js/init.js +405 -0
- package/dist/web/static/js/keyboard-shortcuts.js +292 -0
- package/dist/web/static/js/langIcons.js +187 -0
- package/dist/web/static/js/markdown/tableRow.js +19 -0
- package/dist/web/static/js/markdown.js +806 -0
- package/dist/web/static/js/memory.js +1418 -0
- package/dist/web/static/js/modalManager.js +1552 -0
- package/dist/web/static/js/modalSnap.js +782 -0
- package/dist/web/static/js/model/matchKey.js +19 -0
- package/dist/web/static/js/modelPicker.js +695 -0
- package/dist/web/static/js/modelSort.js +33 -0
- package/dist/web/static/js/models.js +642 -0
- package/dist/web/static/js/notes.js +5054 -0
- package/dist/web/static/js/package.json +1 -0
- package/dist/web/static/js/platform.js +47 -0
- package/dist/web/static/js/presets.js +1110 -0
- package/dist/web/static/js/providers.js +93 -0
- package/dist/web/static/js/rag.js +177 -0
- package/dist/web/static/js/research/jobs.js +335 -0
- package/dist/web/static/js/research/panel.js +1233 -0
- package/dist/web/static/js/researchSynapse.js +225 -0
- package/dist/web/static/js/search-chat.js +201 -0
- package/dist/web/static/js/search.js +52 -0
- package/dist/web/static/js/section-management.js +260 -0
- package/dist/web/static/js/sessions.js +3108 -0
- package/dist/web/static/js/settings.js +4991 -0
- package/dist/web/static/js/sidebar-layout.js +573 -0
- package/dist/web/static/js/signature.js +504 -0
- package/dist/web/static/js/skills.js +1908 -0
- package/dist/web/static/js/slashAutocomplete.js +267 -0
- package/dist/web/static/js/slashCommands.js +6099 -0
- package/dist/web/static/js/spinner.js +406 -0
- package/dist/web/static/js/storage.js +123 -0
- package/dist/web/static/js/tasks.js +2699 -0
- package/dist/web/static/js/theme.js +2086 -0
- package/dist/web/static/js/tileManager.js +386 -0
- package/dist/web/static/js/tourAutoplay.js +133 -0
- package/dist/web/static/js/tourHints.js +179 -0
- package/dist/web/static/js/tts-ai.js +521 -0
- package/dist/web/static/js/ui.js +1288 -0
- package/dist/web/static/js/util/ordinal.js +13 -0
- package/dist/web/static/js/voiceRecorder.js +283 -0
- package/dist/web/static/js/windowDrag.js +326 -0
- package/dist/web/static/js/windowResize.js +233 -0
- package/dist/web/static/landing.html +824 -0
- package/dist/web/static/lib/docx.umd.min.js +20116 -0
- package/dist/web/static/lib/highlight.min.js +1213 -0
- package/dist/web/static/lib/html2pdf.bundle.min.js +3 -0
- package/dist/web/static/lib/mammoth.browser.min.js +21 -0
- package/dist/web/static/lib/qrcode.min.js +8 -0
- package/dist/web/static/lib/xlsx.full.min.js +24 -0
- package/dist/web/static/login.html +582 -0
- package/dist/web/static/manifest.json +15 -0
- package/dist/web/static/style.css +35733 -0
- package/dist/web/static/sw.js +144 -0
- package/dist/workflows/engine.d.ts +162 -0
- package/dist/workflows/engine.d.ts.map +1 -0
- package/dist/workflows/engine.js +625 -0
- package/dist/workflows/engine.js.map +1 -0
- package/dist/workflows/index.d.ts +8 -0
- package/dist/workflows/index.d.ts.map +1 -0
- package/dist/workflows/index.js +14 -0
- package/dist/workflows/index.js.map +1 -0
- package/package.json +112 -0
|
@@ -0,0 +1,1489 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* axoniz/integrations/unified_memory.ts
|
|
3
|
+
* ======================================
|
|
4
|
+
* Merges Hermes MemoryManager + MemPalace palace + axoniz SemanticMemory
|
|
5
|
+
* into one object used directly by axoniz.core.agent.Agent.
|
|
6
|
+
*
|
|
7
|
+
* Port of axoniz/integrations/unified_memory.py
|
|
8
|
+
*
|
|
9
|
+
* ── STORAGE (port decision) ──────────────────────────────────────────────────
|
|
10
|
+
* The Python palace is a ChromaDB vector store provided by the `mempalace`
|
|
11
|
+
* package. Neither `mempalace` nor `chromadb` has an installable npm equivalent
|
|
12
|
+
* on this machine (the chromadb npm install repeatedly failed against the
|
|
13
|
+
* degraded registry), so the palace is backed by a **local SQLite store using
|
|
14
|
+
* Node's built-in `node:sqlite` (`DatabaseSync`)** — zero dependencies, fully
|
|
15
|
+
* local. Verified working on Node v24.21.0 (including recursive CTEs and FTS5).
|
|
16
|
+
*
|
|
17
|
+
* Because there are no embeddings, "semantic" palace search is performed with
|
|
18
|
+
* the already-ported TF-IDF scorer from `src/core/memory.ts`
|
|
19
|
+
* (`tokenize` + `tfidfScore` over the stored documents). This is a genuine
|
|
20
|
+
* capability downgrade vs. ChromaDB embeddings:
|
|
21
|
+
* - ranking is lexical (TF-IDF cosine), not semantic — paraphrases that share
|
|
22
|
+
* no vocabulary are not retrieved, and drawers with zero vocabulary overlap
|
|
23
|
+
* are dropped entirely (a lexical ranker cannot order them);
|
|
24
|
+
* - there is no ANN index, so search is a linear scan over the filtered
|
|
25
|
+
* drawer set (fine for the local-first scale this targets).
|
|
26
|
+
*
|
|
27
|
+
* The seam for a real embedding backend is `registerEmbeddingBackend()`: pass an
|
|
28
|
+
* object with `name` + `embed(texts)` (sync OR async, e.g. a local
|
|
29
|
+
* node-llama-cpp embedding model) and `PalaceLayer.search()` /
|
|
30
|
+
* `PalaceLayer.searchAsync()` will rank by cosine similarity over those vectors
|
|
31
|
+
* instead of TF-IDF. `VectorBackend` keeps the ChromaDB-shaped surface
|
|
32
|
+
* (add/search/delete/status/get), so a Chroma-backed implementation can be
|
|
33
|
+
* dropped in via the `PalaceLayer` constructor's second argument.
|
|
34
|
+
*
|
|
35
|
+
* ── Other degradations ──────────────────────────────────────────────────────
|
|
36
|
+
* - `mempalace.palace_graph` (traverse / find_tunnels / graph_stats) is not in
|
|
37
|
+
* the Python reference tree, so it is re-implemented over the drawer
|
|
38
|
+
* wing/room metadata (see PalaceLayer.traverse_graph for the graph model).
|
|
39
|
+
* - `mempalace.knowledge_graph.KnowledgeGraph` is replaced by
|
|
40
|
+
* `SqliteKnowledgeGraph`, which is the Python `_MinimalKG` fallback plus
|
|
41
|
+
* real `as_of` temporal filtering (the fallback ignored `as_of`, the original
|
|
42
|
+
* mempalace class honoured it). Return shapes are unchanged.
|
|
43
|
+
* - `chromadb`-specific status strings became backend-neutral.
|
|
44
|
+
* - `threading.Thread(daemon=True)` in `prefetch`/`sync_turn` became
|
|
45
|
+
* fire-and-forget async tasks (both are now awaitable and never reject).
|
|
46
|
+
* - `threading.Lock` in `_prefetch_lock` is dropped: the JS event loop is
|
|
47
|
+
* single-threaded and the field is a plain string assignment.
|
|
48
|
+
*/
|
|
49
|
+
import fs from "node:fs";
|
|
50
|
+
import path from "node:path";
|
|
51
|
+
import { createHash } from "node:crypto";
|
|
52
|
+
import { DatabaseSync } from "node:sqlite";
|
|
53
|
+
import { AXONIZ_HOME, MEMORY_PATH } from "../core/config.js";
|
|
54
|
+
import { SemanticMemory, tokenize, tfidfScore } from "../core/memory.js";
|
|
55
|
+
import { toolArgs, optStr, optNum } from "../core/extras.js";
|
|
56
|
+
import { getLogger } from "../core/logger.js";
|
|
57
|
+
const logger = getLogger();
|
|
58
|
+
/* ── Paths shared across sub-systems ────────────────────────────────────────── */
|
|
59
|
+
export const PALACE_PATH = path.join(AXONIZ_HOME, "palace");
|
|
60
|
+
export const KG_PATH = path.join(AXONIZ_HOME, "knowledge_graph.sqlite3");
|
|
61
|
+
export const WAL_DIR = path.join(AXONIZ_HOME, "wal");
|
|
62
|
+
/** SQLite file holding the palace drawers (replaces the ChromaDB directory). */
|
|
63
|
+
export const PALACE_DB_PATH = path.join(PALACE_PATH, "palace.sqlite3");
|
|
64
|
+
export const COLLECTION_NAME = "mempalace_drawers";
|
|
65
|
+
for (const dir of [PALACE_PATH, WAL_DIR]) {
|
|
66
|
+
try {
|
|
67
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* best effort, matching Python's makedirs(exist_ok=True) */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/* ── context fencing (from hermes memory_manager.py) ────────────────────────── */
|
|
74
|
+
const _FENCE_TAG_RE = /<\/?\s*memory-context\s*>/gi;
|
|
75
|
+
export function _sanitize_context(text) {
|
|
76
|
+
return text.replace(_FENCE_TAG_RE, "");
|
|
77
|
+
}
|
|
78
|
+
/** Wrap recalled memory in a fenced block — prevents the model treating it as user input. */
|
|
79
|
+
export function _build_memory_context_block(raw) {
|
|
80
|
+
if (!raw || !raw.trim())
|
|
81
|
+
return "";
|
|
82
|
+
const clean = _sanitize_context(raw);
|
|
83
|
+
return ("<memory-context>\n" +
|
|
84
|
+
"[System note: recalled memory context — NOT new user input. Treat as background.]\n\n" +
|
|
85
|
+
`${clean}\n` +
|
|
86
|
+
"</memory-context>");
|
|
87
|
+
}
|
|
88
|
+
let _embeddingBackend = null;
|
|
89
|
+
/**
|
|
90
|
+
* Register (or clear, with `null`) the embedding backend used for palace
|
|
91
|
+
* ranking. When none is registered the palace falls back to TF-IDF.
|
|
92
|
+
*/
|
|
93
|
+
export function registerEmbeddingBackend(backend) {
|
|
94
|
+
_embeddingBackend = backend;
|
|
95
|
+
}
|
|
96
|
+
export function getEmbeddingBackend() {
|
|
97
|
+
return _embeddingBackend;
|
|
98
|
+
}
|
|
99
|
+
/** Cosine similarity (0 when either vector is empty/zero-length). */
|
|
100
|
+
export function cosineSimilarity(a, b) {
|
|
101
|
+
const n = Math.min(a.length, b.length);
|
|
102
|
+
let dot = 0;
|
|
103
|
+
let na = 0;
|
|
104
|
+
let nb = 0;
|
|
105
|
+
for (let i = 0; i < n; i++) {
|
|
106
|
+
dot += a[i] * b[i];
|
|
107
|
+
na += a[i] * a[i];
|
|
108
|
+
nb += b[i] * b[i];
|
|
109
|
+
}
|
|
110
|
+
if (na === 0 || nb === 0)
|
|
111
|
+
return 0;
|
|
112
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
113
|
+
}
|
|
114
|
+
/* ── SQLite implementation (the default, dependency-free backend) ──────────── */
|
|
115
|
+
function placeholders(n) {
|
|
116
|
+
return new Array(n).fill("?").join(",");
|
|
117
|
+
}
|
|
118
|
+
function buildIdf(docs) {
|
|
119
|
+
const N = Math.max(docs.length, 1);
|
|
120
|
+
const df = new Map();
|
|
121
|
+
for (const tokens of docs) {
|
|
122
|
+
for (const t of new Set(tokens))
|
|
123
|
+
df.set(t, (df.get(t) ?? 0) + 1);
|
|
124
|
+
}
|
|
125
|
+
const idf = new Map();
|
|
126
|
+
for (const [t, c] of df)
|
|
127
|
+
idf.set(t, Math.log(N / (1 + c)));
|
|
128
|
+
return idf;
|
|
129
|
+
}
|
|
130
|
+
/** TF-IDF weight map matching `tfidfScore`'s tf = count/total convention. */
|
|
131
|
+
function tfidfVector(tokens, idf) {
|
|
132
|
+
const vec = new Map();
|
|
133
|
+
if (tokens.length === 0)
|
|
134
|
+
return vec;
|
|
135
|
+
const tf = new Map();
|
|
136
|
+
for (const t of tokens)
|
|
137
|
+
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
138
|
+
for (const [t, c] of tf)
|
|
139
|
+
vec.set(t, (c / tokens.length) * (idf.get(t) ?? Math.log(10)));
|
|
140
|
+
return vec;
|
|
141
|
+
}
|
|
142
|
+
function cosineMaps(a, b) {
|
|
143
|
+
if (a.size === 0 || b.size === 0)
|
|
144
|
+
return 0;
|
|
145
|
+
const [small, large] = a.size <= b.size ? [a, b] : [b, a];
|
|
146
|
+
let dot = 0;
|
|
147
|
+
for (const [t, w] of small) {
|
|
148
|
+
const other = large.get(t);
|
|
149
|
+
if (other !== undefined)
|
|
150
|
+
dot += w * other;
|
|
151
|
+
}
|
|
152
|
+
let na = 0;
|
|
153
|
+
for (const w of a.values())
|
|
154
|
+
na += w * w;
|
|
155
|
+
let nb = 0;
|
|
156
|
+
for (const w of b.values())
|
|
157
|
+
nb += w * w;
|
|
158
|
+
if (na === 0 || nb === 0)
|
|
159
|
+
return 0;
|
|
160
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
161
|
+
}
|
|
162
|
+
export function round3(x) {
|
|
163
|
+
return Math.round(x * 1000) / 1000;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Drawer store on `node:sqlite`. Documents + wing/room metadata live in a plain
|
|
167
|
+
* table; similarity is TF-IDF (see the module header) unless an embedding
|
|
168
|
+
* backend is registered.
|
|
169
|
+
*/
|
|
170
|
+
export class SqliteVectorBackend {
|
|
171
|
+
name = "sqlite-tfidf";
|
|
172
|
+
dbPath;
|
|
173
|
+
db = null;
|
|
174
|
+
openError = null;
|
|
175
|
+
constructor(dbPath = PALACE_DB_PATH) {
|
|
176
|
+
this.dbPath = dbPath;
|
|
177
|
+
try {
|
|
178
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
179
|
+
const db = new DatabaseSync(dbPath);
|
|
180
|
+
db.exec("PRAGMA journal_mode=WAL");
|
|
181
|
+
db.exec(`CREATE TABLE IF NOT EXISTS drawers (
|
|
182
|
+
id TEXT PRIMARY KEY,
|
|
183
|
+
document TEXT NOT NULL,
|
|
184
|
+
wing TEXT,
|
|
185
|
+
room TEXT,
|
|
186
|
+
added_by TEXT,
|
|
187
|
+
filed_at TEXT
|
|
188
|
+
)`);
|
|
189
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_drawers_wing ON drawers(wing)");
|
|
190
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_drawers_room ON drawers(room)");
|
|
191
|
+
this.db = db;
|
|
192
|
+
}
|
|
193
|
+
catch (e) {
|
|
194
|
+
this.openError = e instanceof Error ? e.message : String(e);
|
|
195
|
+
this.db = null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
get available() {
|
|
199
|
+
return this.db !== null;
|
|
200
|
+
}
|
|
201
|
+
rowToRecord(row) {
|
|
202
|
+
return {
|
|
203
|
+
id: String(row["id"] ?? ""),
|
|
204
|
+
document: String(row["document"] ?? ""),
|
|
205
|
+
metadata: {
|
|
206
|
+
wing: String(row["wing"] ?? "unknown"),
|
|
207
|
+
room: String(row["room"] ?? "unknown"),
|
|
208
|
+
added_by: String(row["added_by"] ?? ""),
|
|
209
|
+
filed_at: String(row["filed_at"] ?? ""),
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
add(records) {
|
|
214
|
+
const db = this.db;
|
|
215
|
+
if (!db || records.length === 0)
|
|
216
|
+
return;
|
|
217
|
+
const stmt = db.prepare(`INSERT INTO drawers (id, document, wing, room, added_by, filed_at)
|
|
218
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
219
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
220
|
+
document = excluded.document,
|
|
221
|
+
wing = excluded.wing,
|
|
222
|
+
room = excluded.room,
|
|
223
|
+
added_by = excluded.added_by,
|
|
224
|
+
filed_at = excluded.filed_at`);
|
|
225
|
+
for (const rec of records) {
|
|
226
|
+
stmt.run(rec.id, rec.document, String(rec.metadata["wing"] ?? "unknown"), String(rec.metadata["room"] ?? "unknown"), String(rec.metadata["added_by"] ?? ""), String(rec.metadata["filed_at"] ?? ""));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
get(ids, filter) {
|
|
230
|
+
const db = this.db;
|
|
231
|
+
if (!db)
|
|
232
|
+
return [];
|
|
233
|
+
const where = [];
|
|
234
|
+
const params = [];
|
|
235
|
+
if (ids && ids.length > 0) {
|
|
236
|
+
where.push(`id IN (${placeholders(ids.length)})`);
|
|
237
|
+
params.push(...ids);
|
|
238
|
+
}
|
|
239
|
+
if (filter?.wing) {
|
|
240
|
+
where.push("wing = ?");
|
|
241
|
+
params.push(filter.wing);
|
|
242
|
+
}
|
|
243
|
+
if (filter?.room) {
|
|
244
|
+
where.push("room = ?");
|
|
245
|
+
params.push(filter.room);
|
|
246
|
+
}
|
|
247
|
+
const sql = `SELECT id, document, wing, room, added_by, filed_at FROM drawers${where.length > 0 ? ` WHERE ${where.join(" AND ")}` : ""}`;
|
|
248
|
+
const rows = db.prepare(sql).all(...params);
|
|
249
|
+
return rows.map((r) => this.rowToRecord(r));
|
|
250
|
+
}
|
|
251
|
+
delete(ids) {
|
|
252
|
+
const db = this.db;
|
|
253
|
+
if (!db || ids.length === 0)
|
|
254
|
+
return 0;
|
|
255
|
+
const stmt = db.prepare(`DELETE FROM drawers WHERE id IN (${placeholders(ids.length)})`);
|
|
256
|
+
const res = stmt.run(...ids);
|
|
257
|
+
return Number(res.changes ?? 0);
|
|
258
|
+
}
|
|
259
|
+
count(filter) {
|
|
260
|
+
const db = this.db;
|
|
261
|
+
if (!db)
|
|
262
|
+
return 0;
|
|
263
|
+
const where = [];
|
|
264
|
+
const params = [];
|
|
265
|
+
if (filter?.wing) {
|
|
266
|
+
where.push("wing = ?");
|
|
267
|
+
params.push(filter.wing);
|
|
268
|
+
}
|
|
269
|
+
if (filter?.room) {
|
|
270
|
+
where.push("room = ?");
|
|
271
|
+
params.push(filter.room);
|
|
272
|
+
}
|
|
273
|
+
const sql = `SELECT COUNT(*) AS n FROM drawers${where.length > 0 ? ` WHERE ${where.join(" AND ")}` : ""}`;
|
|
274
|
+
const row = db.prepare(sql).get(...params);
|
|
275
|
+
return Number(row?.["n"] ?? 0);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* TF-IDF ranked search over the filtered drawer set.
|
|
279
|
+
* `similarity` is TF-IDF cosine in [0,1]; `distance` is 1 - similarity.
|
|
280
|
+
*
|
|
281
|
+
* Lexical ranker caveat: a drawer that shares no vocabulary with the query
|
|
282
|
+
* scores 0 and is dropped (a lexical ranker cannot order it, and returning it
|
|
283
|
+
* would put unrelated text in the context block). ChromaDB embeddings would
|
|
284
|
+
* still return such drawers at a high distance.
|
|
285
|
+
*/
|
|
286
|
+
search(query, options = {}) {
|
|
287
|
+
const limit = options.limit ?? 5;
|
|
288
|
+
const maxDistance = options.maxDistance ?? 1.5;
|
|
289
|
+
const candidates = this.get(null, { wing: options.wing ?? null, room: options.room ?? null });
|
|
290
|
+
if (candidates.length === 0)
|
|
291
|
+
return [];
|
|
292
|
+
const qTokens = tokenize(query);
|
|
293
|
+
const docTokens = candidates.map((c) => tokenize(c.document));
|
|
294
|
+
const idf = buildIdf(docTokens);
|
|
295
|
+
const qVec = tfidfVector(qTokens, idf);
|
|
296
|
+
const hits = [];
|
|
297
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
298
|
+
const tokens = docTokens[i];
|
|
299
|
+
const score = tfidfScore(qTokens, tokens, idf);
|
|
300
|
+
if (score <= 0)
|
|
301
|
+
continue; // no shared vocabulary — not retrievable lexically
|
|
302
|
+
const similarity = round3(cosineMaps(qVec, tfidfVector(tokens, idf)));
|
|
303
|
+
const distance = round3(1 - similarity);
|
|
304
|
+
if (distance > maxDistance)
|
|
305
|
+
continue;
|
|
306
|
+
hits.push({
|
|
307
|
+
id: candidates[i].id,
|
|
308
|
+
document: candidates[i].document,
|
|
309
|
+
metadata: candidates[i].metadata,
|
|
310
|
+
distance,
|
|
311
|
+
similarity,
|
|
312
|
+
score,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
hits.sort((a, b) => b.score - a.score || b.similarity - a.similarity);
|
|
316
|
+
return hits.slice(0, limit);
|
|
317
|
+
}
|
|
318
|
+
status() {
|
|
319
|
+
if (!this.db) {
|
|
320
|
+
return { backend: this.name, total: 0, available: false, error: this.openError ?? "SQLite palace unavailable" };
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
return { backend: this.name, total: this.count(), available: true };
|
|
324
|
+
}
|
|
325
|
+
catch (e) {
|
|
326
|
+
return { backend: this.name, total: 0, available: false, error: errMsg(e) };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
close() {
|
|
330
|
+
try {
|
|
331
|
+
this.db?.close();
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
/* ignore */
|
|
335
|
+
}
|
|
336
|
+
this.db = null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function errMsg(e) {
|
|
340
|
+
return e instanceof Error ? e.message : String(e);
|
|
341
|
+
}
|
|
342
|
+
export class PalaceLayer {
|
|
343
|
+
palace_path;
|
|
344
|
+
backend;
|
|
345
|
+
_available = null;
|
|
346
|
+
_wal;
|
|
347
|
+
/** ChromaDB-shaped compat shim — `agent.marshal_scan()` calls `_db.get()`. */
|
|
348
|
+
_db;
|
|
349
|
+
constructor(palacePath = PALACE_PATH, backend) {
|
|
350
|
+
this.palace_path = palacePath;
|
|
351
|
+
this.backend = backend ?? new SqliteVectorBackend(path.join(palacePath, "palace.sqlite3"));
|
|
352
|
+
this._wal = path.join(WAL_DIR, "palace_writes.jsonl");
|
|
353
|
+
try {
|
|
354
|
+
if (!fs.existsSync(this._wal)) {
|
|
355
|
+
fs.writeFileSync(this._wal, "", { encoding: "utf-8", mode: 0o600 });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
/* best effort */
|
|
360
|
+
}
|
|
361
|
+
this._db = {
|
|
362
|
+
get: (ids) => {
|
|
363
|
+
const records = this.backend.get(ids ?? null);
|
|
364
|
+
return {
|
|
365
|
+
ids: records.map((r) => r.id),
|
|
366
|
+
documents: records.map((r) => r.document),
|
|
367
|
+
metadatas: records.map((r) => r.metadata),
|
|
368
|
+
};
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
is_available() {
|
|
373
|
+
if (this._available === null) {
|
|
374
|
+
this._available = this.backend.status().available;
|
|
375
|
+
if (!this._available) {
|
|
376
|
+
logger.warning("[Palace] local SQLite palace backend unavailable — palace disabled. " +
|
|
377
|
+
"Check write access to " +
|
|
378
|
+
this.palace_path);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return this._available;
|
|
382
|
+
}
|
|
383
|
+
/** Backend-neutral reason string used by search()/store() when disabled. */
|
|
384
|
+
_unavailableReason() {
|
|
385
|
+
return this.backend.status().error ?? "Palace backend unavailable";
|
|
386
|
+
}
|
|
387
|
+
/** Compat shim for the removed ChromaDB collection handle. */
|
|
388
|
+
_get_col(create = true) {
|
|
389
|
+
void create;
|
|
390
|
+
if (!this.is_available())
|
|
391
|
+
return null;
|
|
392
|
+
return this.backend;
|
|
393
|
+
}
|
|
394
|
+
_wal_log(op, info) {
|
|
395
|
+
const entry = { ts: new Date().toISOString(), op, ...info };
|
|
396
|
+
try {
|
|
397
|
+
fs.appendFileSync(this._wal, `${JSON.stringify(entry)}\n`, "utf-8");
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
/* best effort */
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
search(query, wingOrOptions, room, limit = 5, maxDistance = 1.5) {
|
|
404
|
+
let wing = null;
|
|
405
|
+
let rm = room ?? null;
|
|
406
|
+
let lim = limit;
|
|
407
|
+
let maxD = maxDistance;
|
|
408
|
+
if (wingOrOptions !== null && typeof wingOrOptions === "object") {
|
|
409
|
+
const o = wingOrOptions;
|
|
410
|
+
wing = o.wing ?? null;
|
|
411
|
+
rm = o.room ?? null;
|
|
412
|
+
lim = o.limit ?? o.top_k ?? 5;
|
|
413
|
+
maxD = o.max_distance ?? o.maxDistance ?? 1.5;
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
wing = wingOrOptions ?? null;
|
|
417
|
+
}
|
|
418
|
+
if (!this.is_available()) {
|
|
419
|
+
return { results: [], error: this._unavailableReason() };
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
const hits = this.backend.search(query, { wing, room: rm, limit: lim, maxDistance: maxD });
|
|
423
|
+
return {
|
|
424
|
+
results: hits.map((h) => ({
|
|
425
|
+
id: h.id,
|
|
426
|
+
text: h.document,
|
|
427
|
+
wing: String(h.metadata["wing"] ?? "unknown"),
|
|
428
|
+
room: String(h.metadata["room"] ?? "unknown"),
|
|
429
|
+
distance: h.distance,
|
|
430
|
+
similarity: h.similarity,
|
|
431
|
+
metadata: h.metadata,
|
|
432
|
+
})),
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
catch (e) {
|
|
436
|
+
return { results: [], error: errMsg(e) };
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Additive async search: uses the registered embedding backend (awaiting it)
|
|
441
|
+
* when present, otherwise behaves exactly like `search()`.
|
|
442
|
+
*/
|
|
443
|
+
async searchAsync(query, options = {}) {
|
|
444
|
+
const backend = getEmbeddingBackend();
|
|
445
|
+
if (!backend || !this.is_available()) {
|
|
446
|
+
return this.search(query, options);
|
|
447
|
+
}
|
|
448
|
+
try {
|
|
449
|
+
const lim = options.limit ?? options.top_k ?? 5;
|
|
450
|
+
const maxD = options.max_distance ?? options.maxDistance ?? 1.5;
|
|
451
|
+
const candidates = this.backend.get(null, { wing: options.wing ?? null, room: options.room ?? null });
|
|
452
|
+
if (candidates.length === 0)
|
|
453
|
+
return { results: [] };
|
|
454
|
+
const [qVec] = await Promise.resolve(backend.embed([query]));
|
|
455
|
+
const docVecs = await Promise.resolve(backend.embed(candidates.map((c) => c.document)));
|
|
456
|
+
const sim = backend.similarity ?? cosineSimilarity;
|
|
457
|
+
const results = [];
|
|
458
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
459
|
+
const similarity = round3(sim(qVec ?? [], docVecs[i] ?? []));
|
|
460
|
+
const distance = round3(1 - similarity);
|
|
461
|
+
if (distance > maxD)
|
|
462
|
+
continue;
|
|
463
|
+
results.push({
|
|
464
|
+
id: candidates[i].id,
|
|
465
|
+
text: candidates[i].document,
|
|
466
|
+
wing: String(candidates[i].metadata["wing"] ?? "unknown"),
|
|
467
|
+
room: String(candidates[i].metadata["room"] ?? "unknown"),
|
|
468
|
+
distance,
|
|
469
|
+
similarity,
|
|
470
|
+
metadata: candidates[i].metadata,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
results.sort((a, b) => b.similarity - a.similarity);
|
|
474
|
+
return { results: results.slice(0, lim) };
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
return { results: [], error: errMsg(e) };
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
status() {
|
|
481
|
+
const st = this.backend.status();
|
|
482
|
+
if (!st.available) {
|
|
483
|
+
return { error: st.error ?? "Palace backend unavailable", total_drawers: 0, wings: {} };
|
|
484
|
+
}
|
|
485
|
+
try {
|
|
486
|
+
const count = st.total;
|
|
487
|
+
const wings = {};
|
|
488
|
+
if (count > 0) {
|
|
489
|
+
for (const rec of this.backend.get()) {
|
|
490
|
+
const w = String(rec.metadata["wing"] ?? "unknown");
|
|
491
|
+
wings[w] = (wings[w] ?? 0) + 1;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return {
|
|
495
|
+
total_drawers: count,
|
|
496
|
+
wings,
|
|
497
|
+
palace_path: this.palace_path,
|
|
498
|
+
backend: this.backend.name,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
catch (e) {
|
|
502
|
+
return { error: errMsg(e), total_drawers: 0, wings: {} };
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
list_wings() {
|
|
506
|
+
const st = this.status();
|
|
507
|
+
return { wings: st.wings ?? {} };
|
|
508
|
+
}
|
|
509
|
+
list_rooms(wing) {
|
|
510
|
+
if (!this.is_available()) {
|
|
511
|
+
return { wing: wing ?? "all", rooms: {} };
|
|
512
|
+
}
|
|
513
|
+
try {
|
|
514
|
+
const records = this.backend.get(null, { wing: wing ?? null });
|
|
515
|
+
const rooms = {};
|
|
516
|
+
for (const rec of records) {
|
|
517
|
+
const r = String(rec.metadata["room"] ?? "unknown");
|
|
518
|
+
rooms[r] = (rooms[r] ?? 0) + 1;
|
|
519
|
+
}
|
|
520
|
+
return { wing: wing ?? "all", rooms };
|
|
521
|
+
}
|
|
522
|
+
catch (e) {
|
|
523
|
+
return { rooms: {}, error: errMsg(e) };
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
get_context() {
|
|
527
|
+
const st = this.status();
|
|
528
|
+
if (st.error !== undefined && st.total_drawers === 0) {
|
|
529
|
+
return "";
|
|
530
|
+
}
|
|
531
|
+
const wingStr = Object.entries(st.wings ?? {})
|
|
532
|
+
.slice(0, 6)
|
|
533
|
+
.map(([w, c]) => `${w}(${c})`)
|
|
534
|
+
.join(", ");
|
|
535
|
+
return `Palace: ${st.total_drawers} drawers | Wings: ${wingStr}`;
|
|
536
|
+
}
|
|
537
|
+
/* ── Graph helpers ──────────────────────────────────────────────────────── */
|
|
538
|
+
_roomCounts() {
|
|
539
|
+
const byWing = new Map();
|
|
540
|
+
const byRoom = new Map();
|
|
541
|
+
for (const rec of this.backend.get()) {
|
|
542
|
+
const w = String(rec.metadata["wing"] ?? "unknown");
|
|
543
|
+
const r = String(rec.metadata["room"] ?? "unknown");
|
|
544
|
+
if (!byWing.has(w))
|
|
545
|
+
byWing.set(w, new Map());
|
|
546
|
+
byWing.get(w).set(r, (byWing.get(w).get(r) ?? 0) + 1);
|
|
547
|
+
if (!byRoom.has(r))
|
|
548
|
+
byRoom.set(r, new Map());
|
|
549
|
+
byRoom.get(r).set(w, (byRoom.get(r).get(w) ?? 0) + 1);
|
|
550
|
+
}
|
|
551
|
+
return { byWing, byRoom };
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Walk the palace graph from a room.
|
|
555
|
+
*
|
|
556
|
+
* Graph model (re-implementation — `mempalace.palace_graph` is not part of the
|
|
557
|
+
* Python reference tree): a node is a (wing, room) pair. Two nodes are linked
|
|
558
|
+
* when they live in the same wing ("wing" edges), and a room that exists in
|
|
559
|
+
* more than one wing links those copies ("tunnel" edges). BFS is capped at
|
|
560
|
+
* `max_hops` hops and at `MAX_TRAVERSE_NODES` visited nodes.
|
|
561
|
+
*/
|
|
562
|
+
traverse_graph(startRoom, maxHops = 2) {
|
|
563
|
+
if (!this.is_available()) {
|
|
564
|
+
return { error: "Palace unavailable" };
|
|
565
|
+
}
|
|
566
|
+
try {
|
|
567
|
+
const { byWing, byRoom } = this._roomCounts();
|
|
568
|
+
const MAX_TRAVERSE_NODES = 200;
|
|
569
|
+
const key = (w, r) => `${w}\u0000${r}`;
|
|
570
|
+
const visited = new Map();
|
|
571
|
+
const edges = [];
|
|
572
|
+
const startWings = [...(byRoom.get(startRoom)?.keys() ?? [])];
|
|
573
|
+
if (startWings.length === 0) {
|
|
574
|
+
return { start_room: startRoom, max_hops: maxHops, nodes: [], edges: [], error: "room not found" };
|
|
575
|
+
}
|
|
576
|
+
let frontier = [];
|
|
577
|
+
for (const w of startWings) {
|
|
578
|
+
const k = key(w, startRoom);
|
|
579
|
+
visited.set(k, { wing: w, room: startRoom, depth: 0, drawers: byWing.get(w)?.get(startRoom) ?? 0 });
|
|
580
|
+
frontier.push([w, startRoom]);
|
|
581
|
+
}
|
|
582
|
+
for (let depth = 1; depth <= maxHops && frontier.length > 0; depth++) {
|
|
583
|
+
const next = [];
|
|
584
|
+
for (const [w, r] of frontier) {
|
|
585
|
+
// Tunnel: same room name in another wing.
|
|
586
|
+
for (const w2 of byRoom.get(r)?.keys() ?? []) {
|
|
587
|
+
if (w2 === w)
|
|
588
|
+
continue;
|
|
589
|
+
const k = key(w2, r);
|
|
590
|
+
if (!visited.has(k)) {
|
|
591
|
+
if (visited.size >= MAX_TRAVERSE_NODES)
|
|
592
|
+
break;
|
|
593
|
+
visited.set(k, { wing: w2, room: r, depth, drawers: byWing.get(w2)?.get(r) ?? 0 });
|
|
594
|
+
next.push([w2, r]);
|
|
595
|
+
}
|
|
596
|
+
edges.push({ from: [w, r], to: [w2, r], kind: "tunnel" });
|
|
597
|
+
}
|
|
598
|
+
// Wing: sibling rooms in the same wing.
|
|
599
|
+
for (const r2 of byWing.get(w)?.keys() ?? []) {
|
|
600
|
+
if (r2 === r)
|
|
601
|
+
continue;
|
|
602
|
+
const k = key(w, r2);
|
|
603
|
+
if (!visited.has(k)) {
|
|
604
|
+
if (visited.size >= MAX_TRAVERSE_NODES)
|
|
605
|
+
break;
|
|
606
|
+
visited.set(k, { wing: w, room: r2, depth, drawers: byWing.get(w)?.get(r2) ?? 0 });
|
|
607
|
+
next.push([w, r2]);
|
|
608
|
+
}
|
|
609
|
+
edges.push({ from: [w, r], to: [w, r2], kind: "wing" });
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
frontier = next;
|
|
613
|
+
}
|
|
614
|
+
const nodes = [...visited.values()].sort((a, b) => a.depth - b.depth || a.wing.localeCompare(b.wing) || a.room.localeCompare(b.room));
|
|
615
|
+
return { start_room: startRoom, max_hops: maxHops, nodes, edges };
|
|
616
|
+
}
|
|
617
|
+
catch (e) {
|
|
618
|
+
return { error: errMsg(e) };
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
/** Rooms that bridge two wings (or every room present in >1 wing). */
|
|
622
|
+
find_tunnels(wingA, wingB) {
|
|
623
|
+
if (!this.is_available()) {
|
|
624
|
+
return { error: "Palace unavailable" };
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
const { byRoom } = this._roomCounts();
|
|
628
|
+
const tunnels = [];
|
|
629
|
+
for (const [room, wings] of byRoom) {
|
|
630
|
+
if (wingA && !wings.has(wingA))
|
|
631
|
+
continue;
|
|
632
|
+
if (wingB && !wings.has(wingB))
|
|
633
|
+
continue;
|
|
634
|
+
if (wings.size < 2)
|
|
635
|
+
continue;
|
|
636
|
+
let drawers = 0;
|
|
637
|
+
for (const c of wings.values())
|
|
638
|
+
drawers += c;
|
|
639
|
+
tunnels.push({ room, wings: [...wings.keys()], wings_count: wings.size, drawers });
|
|
640
|
+
}
|
|
641
|
+
tunnels.sort((a, b) => b.wings_count - a.wings_count || b.drawers - a.drawers || a.room.localeCompare(b.room));
|
|
642
|
+
return {
|
|
643
|
+
wing_a: wingA ?? null,
|
|
644
|
+
wing_b: wingB ?? null,
|
|
645
|
+
tunnels: tunnels.slice(0, 50),
|
|
646
|
+
count: tunnels.length,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
catch (e) {
|
|
650
|
+
return { error: errMsg(e) };
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
/** Palace graph overview: rooms, wings, intra-wing edges, tunnels. */
|
|
654
|
+
graph_stats() {
|
|
655
|
+
if (!this.is_available()) {
|
|
656
|
+
return { error: "Palace unavailable" };
|
|
657
|
+
}
|
|
658
|
+
try {
|
|
659
|
+
const { byWing, byRoom } = this._roomCounts();
|
|
660
|
+
let edges = 0;
|
|
661
|
+
for (const rooms of byWing.values()) {
|
|
662
|
+
const n = rooms.size;
|
|
663
|
+
if (n > 1)
|
|
664
|
+
edges += (n * (n - 1)) / 2;
|
|
665
|
+
}
|
|
666
|
+
let tunnels = 0;
|
|
667
|
+
for (const wings of byRoom.values()) {
|
|
668
|
+
if (wings.size >= 2)
|
|
669
|
+
tunnels += 1;
|
|
670
|
+
}
|
|
671
|
+
return {
|
|
672
|
+
rooms: byRoom.size,
|
|
673
|
+
wings: byWing.size,
|
|
674
|
+
nodes: [...byWing.values()].reduce((acc, m) => acc + m.size, 0),
|
|
675
|
+
edges,
|
|
676
|
+
tunnels,
|
|
677
|
+
total_drawers: this.backend.count(),
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
catch (e) {
|
|
681
|
+
return { error: errMsg(e) };
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Duplicate check. ChromaDB reported `similarity = 1 - distance`; the SQLite
|
|
686
|
+
* backend reports TF-IDF cosine directly, so `similarity` is comparable —
|
|
687
|
+
* but lexical rather than semantic (see the module header).
|
|
688
|
+
*/
|
|
689
|
+
check_duplicate(content, threshold = 0.9) {
|
|
690
|
+
if (!this.is_available()) {
|
|
691
|
+
return { is_duplicate: false };
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
const hits = this.backend.search(content, { limit: 3 });
|
|
695
|
+
const dups = [];
|
|
696
|
+
for (const h of hits) {
|
|
697
|
+
const sim = round3(h.similarity);
|
|
698
|
+
if (sim >= threshold) {
|
|
699
|
+
dups.push({ id: h.id, similarity: sim, content: h.document.slice(0, 200) });
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return { is_duplicate: dups.length > 0, matches: dups };
|
|
703
|
+
}
|
|
704
|
+
catch (e) {
|
|
705
|
+
return { is_duplicate: false, error: errMsg(e) };
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
store(wingOrOptions, room, content, addedBy = "axoniz") {
|
|
709
|
+
let wing;
|
|
710
|
+
let rm;
|
|
711
|
+
let body;
|
|
712
|
+
let by = addedBy;
|
|
713
|
+
if (wingOrOptions !== null && typeof wingOrOptions === "object") {
|
|
714
|
+
wing = wingOrOptions.wing;
|
|
715
|
+
rm = wingOrOptions.room;
|
|
716
|
+
body = wingOrOptions.content;
|
|
717
|
+
by = wingOrOptions.added_by ?? wingOrOptions.addedBy ?? addedBy;
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
wing = wingOrOptions;
|
|
721
|
+
rm = room ?? "";
|
|
722
|
+
body = content ?? "";
|
|
723
|
+
}
|
|
724
|
+
if (!this.is_available()) {
|
|
725
|
+
return { success: false, error: this._unavailableReason() };
|
|
726
|
+
}
|
|
727
|
+
const w = wing.toLowerCase().replace(/[^a-z0-9_-]/g, "-").slice(0, 40);
|
|
728
|
+
const r = rm.toLowerCase().replace(/[^a-z0-9_-]/g, "-").slice(0, 40);
|
|
729
|
+
const digest = createHash("sha256").update(`${w}${r}${body.slice(0, 100)}`).digest("hex").slice(0, 20);
|
|
730
|
+
const drawerId = `drawer_${w}_${r}_${digest}`;
|
|
731
|
+
this._wal_log("store", { wing: w, room: r, id: drawerId, added_by: by });
|
|
732
|
+
try {
|
|
733
|
+
if (this.backend.get([drawerId]).length > 0) {
|
|
734
|
+
return { success: true, reason: "already_exists", id: drawerId };
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
catch {
|
|
738
|
+
/* ignore lookup failure and try the upsert, as Python does */
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
this.backend.add([
|
|
742
|
+
{
|
|
743
|
+
id: drawerId,
|
|
744
|
+
document: body,
|
|
745
|
+
metadata: { wing: w, room: r, added_by: by, filed_at: new Date().toISOString() },
|
|
746
|
+
},
|
|
747
|
+
]);
|
|
748
|
+
return { success: true, id: drawerId, wing: w, room: r };
|
|
749
|
+
}
|
|
750
|
+
catch (e) {
|
|
751
|
+
return { success: false, error: errMsg(e) };
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
delete(drawerId) {
|
|
755
|
+
if (!this.is_available()) {
|
|
756
|
+
return { success: false, error: "Palace unavailable" };
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
this._wal_log("delete", { id: drawerId });
|
|
760
|
+
this.backend.delete([drawerId]);
|
|
761
|
+
return { success: true, id: drawerId };
|
|
762
|
+
}
|
|
763
|
+
catch (e) {
|
|
764
|
+
return { success: false, error: errMsg(e) };
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
/** camelCase aliases (additive). */
|
|
768
|
+
isAvailable() {
|
|
769
|
+
return this.is_available();
|
|
770
|
+
}
|
|
771
|
+
listWings() {
|
|
772
|
+
return this.list_wings();
|
|
773
|
+
}
|
|
774
|
+
listRooms(wing) {
|
|
775
|
+
return this.list_rooms(wing);
|
|
776
|
+
}
|
|
777
|
+
getContext() {
|
|
778
|
+
return this.get_context();
|
|
779
|
+
}
|
|
780
|
+
traverseGraph(startRoom, maxHops = 2) {
|
|
781
|
+
return this.traverse_graph(startRoom, maxHops);
|
|
782
|
+
}
|
|
783
|
+
findTunnels(wingA, wingB) {
|
|
784
|
+
return this.find_tunnels(wingA, wingB);
|
|
785
|
+
}
|
|
786
|
+
graphStats() {
|
|
787
|
+
return this.graph_stats();
|
|
788
|
+
}
|
|
789
|
+
checkDuplicate(content, threshold = 0.9) {
|
|
790
|
+
return this.check_duplicate(content, threshold);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* SQLite temporal knowledge graph — this is the Python `_MinimalKG` schema and
|
|
795
|
+
* SQL, promoted to the only implementation because the mempalace
|
|
796
|
+
* `KnowledgeGraph` source is not in the reference tree.
|
|
797
|
+
* `as_of` filtering is honoured (documented deviation from `_MinimalKG`, which
|
|
798
|
+
* accepted but ignored it; the real mempalace class honoured it).
|
|
799
|
+
*/
|
|
800
|
+
export class SqliteKnowledgeGraph {
|
|
801
|
+
db_path;
|
|
802
|
+
_conn;
|
|
803
|
+
constructor(dbPath = KG_PATH) {
|
|
804
|
+
this.db_path = dbPath;
|
|
805
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
806
|
+
this._conn = new DatabaseSync(dbPath);
|
|
807
|
+
this._conn.exec("PRAGMA journal_mode=WAL");
|
|
808
|
+
this._conn.exec(`CREATE TABLE IF NOT EXISTS triples (
|
|
809
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
810
|
+
subject TEXT, predicate TEXT, object TEXT,
|
|
811
|
+
valid_from TEXT, valid_until TEXT, source TEXT,
|
|
812
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
813
|
+
)`);
|
|
814
|
+
this._conn.exec("CREATE INDEX IF NOT EXISTS idx_s ON triples(subject)");
|
|
815
|
+
}
|
|
816
|
+
add_triple(subject, predicate, obj, validFrom = null, sourceCloset = null) {
|
|
817
|
+
const res = this._conn
|
|
818
|
+
.prepare("INSERT INTO triples(subject,predicate,object,valid_from,source) VALUES(?,?,?,?,?)")
|
|
819
|
+
.run(subject, predicate, obj, validFrom, sourceCloset);
|
|
820
|
+
return String(res.lastInsertRowid);
|
|
821
|
+
}
|
|
822
|
+
query_entity(name, asOf = null, direction = "both") {
|
|
823
|
+
const rows = [];
|
|
824
|
+
const temporal = asOf
|
|
825
|
+
? " AND (valid_from IS NULL OR valid_from <= ?) AND (valid_until IS NULL OR valid_until > ?)"
|
|
826
|
+
: "";
|
|
827
|
+
const sqlFor = (column) => `SELECT subject, predicate, object, valid_from, valid_until,
|
|
828
|
+
CASE WHEN valid_until IS NULL THEN 1 ELSE 0 END AS current
|
|
829
|
+
FROM triples WHERE ${column} = ?${temporal}`;
|
|
830
|
+
if (direction === "outgoing" || direction === "both") {
|
|
831
|
+
const params = [name];
|
|
832
|
+
if (asOf)
|
|
833
|
+
params.push(asOf, asOf);
|
|
834
|
+
rows.push(...this._conn.prepare(sqlFor("subject")).all(...params).map(normalizeTriple));
|
|
835
|
+
}
|
|
836
|
+
if (direction === "incoming" || direction === "both") {
|
|
837
|
+
const params = [name];
|
|
838
|
+
if (asOf)
|
|
839
|
+
params.push(asOf, asOf);
|
|
840
|
+
rows.push(...this._conn.prepare(sqlFor("object")).all(...params).map(normalizeTriple));
|
|
841
|
+
}
|
|
842
|
+
return rows;
|
|
843
|
+
}
|
|
844
|
+
invalidate(subject, predicate, obj, ended = null) {
|
|
845
|
+
const end = ended ?? formatDate(new Date());
|
|
846
|
+
this._conn
|
|
847
|
+
.prepare("UPDATE triples SET valid_until=? WHERE subject=? AND predicate=? AND object=? AND valid_until IS NULL")
|
|
848
|
+
.run(end, subject, predicate, obj);
|
|
849
|
+
}
|
|
850
|
+
timeline(entityName = null) {
|
|
851
|
+
const sql = "SELECT subject,predicate,object,valid_from,valid_until FROM triples" +
|
|
852
|
+
(entityName ? " WHERE subject=? OR object=?" : "") +
|
|
853
|
+
" ORDER BY created_at";
|
|
854
|
+
const rows = (entityName
|
|
855
|
+
? this._conn.prepare(sql).all(entityName, entityName)
|
|
856
|
+
: this._conn.prepare(sql).all());
|
|
857
|
+
return rows.map((r) => ({
|
|
858
|
+
subject: String(r["subject"] ?? ""),
|
|
859
|
+
predicate: String(r["predicate"] ?? ""),
|
|
860
|
+
object: String(r["object"] ?? ""),
|
|
861
|
+
valid_from: r["valid_from"] === null || r["valid_from"] === undefined ? null : String(r["valid_from"]),
|
|
862
|
+
valid_to: r["valid_until"] === null || r["valid_until"] === undefined ? null : String(r["valid_until"]),
|
|
863
|
+
}));
|
|
864
|
+
}
|
|
865
|
+
stats() {
|
|
866
|
+
const total = Number(this._conn.prepare("SELECT COUNT(*) AS n FROM triples").get()["n"] ?? 0);
|
|
867
|
+
const active = Number(this._conn.prepare("SELECT COUNT(*) AS n FROM triples WHERE valid_until IS NULL").get()["n"] ?? 0);
|
|
868
|
+
return { total_triples: total, active_triples: active };
|
|
869
|
+
}
|
|
870
|
+
close() {
|
|
871
|
+
try {
|
|
872
|
+
this._conn.close();
|
|
873
|
+
}
|
|
874
|
+
catch {
|
|
875
|
+
/* ignore */
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
function normalizeTriple(row) {
|
|
880
|
+
const r = row;
|
|
881
|
+
return {
|
|
882
|
+
subject: String(r["subject"] ?? ""),
|
|
883
|
+
predicate: String(r["predicate"] ?? ""),
|
|
884
|
+
object: String(r["object"] ?? ""),
|
|
885
|
+
valid_from: r["valid_from"] === null || r["valid_from"] === undefined ? null : String(r["valid_from"]),
|
|
886
|
+
valid_to: r["valid_until"] === null || r["valid_until"] === undefined ? null : String(r["valid_until"]),
|
|
887
|
+
current: Number(r["current"] ?? 0),
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
/** `datetime.now().strftime("%Y-%m-%d")` in local time. */
|
|
891
|
+
export function formatDate(d) {
|
|
892
|
+
const p = (x) => String(x).padStart(2, "0");
|
|
893
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Wraps the temporal knowledge graph stored at AXONIZ_HOME.
|
|
897
|
+
* Keeps the Python method names/return shapes (`add`, `query`, `invalidate`,
|
|
898
|
+
* `timeline`, `stats`); the tuple-style keyword arguments are also accepted as
|
|
899
|
+
* an options object.
|
|
900
|
+
*/
|
|
901
|
+
export class KnowledgeGraphLayer {
|
|
902
|
+
_kg_path;
|
|
903
|
+
_kg = null;
|
|
904
|
+
constructor(kgPath = KG_PATH) {
|
|
905
|
+
this._kg_path = kgPath;
|
|
906
|
+
}
|
|
907
|
+
_get() {
|
|
908
|
+
if (this._kg === null) {
|
|
909
|
+
this._kg = new SqliteKnowledgeGraph(this._kg_path);
|
|
910
|
+
}
|
|
911
|
+
return this._kg;
|
|
912
|
+
}
|
|
913
|
+
add(subject, predicate, obj, validFromOrOptions, source) {
|
|
914
|
+
let validFrom = null;
|
|
915
|
+
let src = null;
|
|
916
|
+
if (validFromOrOptions !== null && typeof validFromOrOptions === "object") {
|
|
917
|
+
validFrom = validFromOrOptions.valid_from ?? null;
|
|
918
|
+
src = validFromOrOptions.source ?? null;
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
validFrom = validFromOrOptions ?? null;
|
|
922
|
+
src = source ?? null;
|
|
923
|
+
}
|
|
924
|
+
try {
|
|
925
|
+
return this._get().add_triple(subject, predicate, obj, validFrom, src);
|
|
926
|
+
}
|
|
927
|
+
catch (e) {
|
|
928
|
+
return `[KG error] ${errMsg(e)}`;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
query(entity, asOfOrOptions, direction = "both") {
|
|
932
|
+
let asOf = null;
|
|
933
|
+
let dir = direction;
|
|
934
|
+
if (asOfOrOptions !== null && typeof asOfOrOptions === "object") {
|
|
935
|
+
asOf = asOfOrOptions.as_of ?? null;
|
|
936
|
+
dir = asOfOrOptions.direction ?? "both";
|
|
937
|
+
}
|
|
938
|
+
else {
|
|
939
|
+
asOf = asOfOrOptions ?? null;
|
|
940
|
+
}
|
|
941
|
+
try {
|
|
942
|
+
return this._get().query_entity(entity, asOf, dir);
|
|
943
|
+
}
|
|
944
|
+
catch (e) {
|
|
945
|
+
return [{ error: errMsg(e) }];
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
invalidate(subject, predicate, obj, ended) {
|
|
949
|
+
const end = ended !== null && typeof ended === "object" ? (ended.ended ?? null) : (ended ?? null);
|
|
950
|
+
try {
|
|
951
|
+
this._get().invalidate(subject, predicate, obj, end);
|
|
952
|
+
}
|
|
953
|
+
catch {
|
|
954
|
+
/* Python swallows invalidate errors */
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
timeline(entity) {
|
|
958
|
+
const name = entity !== null && typeof entity === "object" ? (entity.entity ?? null) : (entity ?? null);
|
|
959
|
+
try {
|
|
960
|
+
return this._get().timeline(name);
|
|
961
|
+
}
|
|
962
|
+
catch (e) {
|
|
963
|
+
return [{ error: errMsg(e) }];
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
stats() {
|
|
967
|
+
try {
|
|
968
|
+
return this._get().stats();
|
|
969
|
+
}
|
|
970
|
+
catch (e) {
|
|
971
|
+
return { error: errMsg(e) };
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
close() {
|
|
975
|
+
try {
|
|
976
|
+
this._kg?.close();
|
|
977
|
+
}
|
|
978
|
+
catch {
|
|
979
|
+
/* ignore */
|
|
980
|
+
}
|
|
981
|
+
this._kg = null;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
/* ════════════════════════════════════════════════════════════════════════════
|
|
985
|
+
* AgentDiary — AAAK diary stored in the palace
|
|
986
|
+
* ════════════════════════════════════════════════════════════════════════════ */
|
|
987
|
+
/**
|
|
988
|
+
* The agent's personal journal, stored in the palace.
|
|
989
|
+
* Each agent gets wing_<name>/diary, with AAAK-compressed entries.
|
|
990
|
+
*/
|
|
991
|
+
export class AgentDiary {
|
|
992
|
+
static AAAK_SPEC = "AAAK: compressed memory dialect. " +
|
|
993
|
+
"ENTITIES: 3-letter codes. EMOTIONS: *warm* *fierce* *raw* *bloom*. " +
|
|
994
|
+
"STRUCTURE: SESSION:DATE|TOPIC:tag|entry. ★ to ★★★★★. " +
|
|
995
|
+
"HALLS: hall_facts hall_events hall_decisions hall_diary. " +
|
|
996
|
+
"WINGS: wing_user wing_agent wing_code wing_project. " +
|
|
997
|
+
"ROOMS: hyphenated-slugs.";
|
|
998
|
+
palace;
|
|
999
|
+
agent_name;
|
|
1000
|
+
_wing;
|
|
1001
|
+
_room = "diary";
|
|
1002
|
+
constructor(palace, agentName = "axoniz-zero") {
|
|
1003
|
+
this.palace = palace;
|
|
1004
|
+
this.agent_name = agentName.toLowerCase().replace(/[^a-z0-9_]/g, "_");
|
|
1005
|
+
this._wing = `wing_${this.agent_name}`;
|
|
1006
|
+
}
|
|
1007
|
+
write(entry, topic = "general") {
|
|
1008
|
+
const now = formatDate(new Date());
|
|
1009
|
+
const content = `SESSION:${now}|TOPIC:${topic}|${entry}`;
|
|
1010
|
+
return this.palace.store(this._wing, this._room, content, this.agent_name);
|
|
1011
|
+
}
|
|
1012
|
+
read(lastN = 10) {
|
|
1013
|
+
return this.palace.search("SESSION TOPIC diary", this._wing, this._room, lastN);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Drop-in replacement for `axoniz.core.memory.SemanticMemory` used by Agent.
|
|
1018
|
+
*
|
|
1019
|
+
* Combines:
|
|
1020
|
+
* 1. SemanticMemory — TF-IDF, always-on, no deps (from src/core/memory.ts)
|
|
1021
|
+
* 2. PalaceLayer — local SQLite palace (wings/rooms/drawers)
|
|
1022
|
+
* 3. KnowledgeGraphLayer — temporal KG (SQLite)
|
|
1023
|
+
* 4. AgentDiary — AAAK journal stored in the palace
|
|
1024
|
+
*
|
|
1025
|
+
* Hermes MemoryManager patterns used:
|
|
1026
|
+
* - context fencing (_build_memory_context_block)
|
|
1027
|
+
* - prefetch/sync_turn lifecycle
|
|
1028
|
+
* - single provider limit enforced (palace replaces mempalace_bridge)
|
|
1029
|
+
*/
|
|
1030
|
+
export class UnifiedMemory {
|
|
1031
|
+
builtin;
|
|
1032
|
+
palace;
|
|
1033
|
+
kg;
|
|
1034
|
+
diary;
|
|
1035
|
+
_prefetch_cache = "";
|
|
1036
|
+
constructor(config = {}) {
|
|
1037
|
+
const agentName = String(config["agent_name"] ?? "axoniz-zero");
|
|
1038
|
+
const palacePath = String(config["palace_path"] ?? PALACE_PATH);
|
|
1039
|
+
const kgPath = String(config["kg_path"] ?? KG_PATH);
|
|
1040
|
+
// 1. Always-on local memory (TF-IDF, zero deps)
|
|
1041
|
+
this.builtin = new SemanticMemory(MEMORY_PATH);
|
|
1042
|
+
// 2. Palace (local SQLite backend; embeddings pluggable)
|
|
1043
|
+
this.palace = new PalaceLayer(palacePath);
|
|
1044
|
+
// 3. Knowledge graph
|
|
1045
|
+
this.kg = new KnowledgeGraphLayer(kgPath);
|
|
1046
|
+
// 4. Diary
|
|
1047
|
+
this.diary = new AgentDiary(this.palace, agentName);
|
|
1048
|
+
// Hermes: prefetch cache for the next turn
|
|
1049
|
+
logger.info(`[UnifiedMemory] ready | palace=${palacePath} | kg=${kgPath} | palace_enabled=${this.palace.is_available()}`);
|
|
1050
|
+
}
|
|
1051
|
+
/* ── SemanticMemory drop-in interface ───────────────────────────────────── */
|
|
1052
|
+
save(key, value, tags, importance = 0.5) {
|
|
1053
|
+
const result = this.builtin.save(key, value, tags ?? undefined, importance);
|
|
1054
|
+
if (this.palace.is_available()) {
|
|
1055
|
+
const wing = tags && tags.length > 0 ? tags[0] : "general";
|
|
1056
|
+
this.palace.store(wing, "memory", `${key}: ${value}`, "axoniz");
|
|
1057
|
+
}
|
|
1058
|
+
return result;
|
|
1059
|
+
}
|
|
1060
|
+
get(key) {
|
|
1061
|
+
const local = this.builtin.get(key);
|
|
1062
|
+
if (!local.includes("not found in memory.")) {
|
|
1063
|
+
return local;
|
|
1064
|
+
}
|
|
1065
|
+
if (this.palace.is_available()) {
|
|
1066
|
+
const r = this.palace.search(key, null, null, 1);
|
|
1067
|
+
const items = r.results;
|
|
1068
|
+
if (items.length > 0) {
|
|
1069
|
+
return items[0].text;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return local;
|
|
1073
|
+
}
|
|
1074
|
+
delete(key) {
|
|
1075
|
+
return this.builtin.delete(key);
|
|
1076
|
+
}
|
|
1077
|
+
clear() {
|
|
1078
|
+
this.builtin.clear();
|
|
1079
|
+
}
|
|
1080
|
+
all() {
|
|
1081
|
+
return this.builtin.all();
|
|
1082
|
+
}
|
|
1083
|
+
all_rich() {
|
|
1084
|
+
return this.builtin.allRich();
|
|
1085
|
+
}
|
|
1086
|
+
allRich() {
|
|
1087
|
+
return this.all_rich();
|
|
1088
|
+
}
|
|
1089
|
+
list_keys() {
|
|
1090
|
+
const local = this.builtin.listKeys();
|
|
1091
|
+
if (this.palace.is_available()) {
|
|
1092
|
+
const wings = this.palace.list_wings().wings;
|
|
1093
|
+
const entries = Object.entries(wings);
|
|
1094
|
+
if (entries.length > 0) {
|
|
1095
|
+
const wingStr = entries.map(([w, c]) => `${w}(${c})`).join(", ");
|
|
1096
|
+
return `${local}\n\n[Palace wings]: ${wingStr}`;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
return local;
|
|
1100
|
+
}
|
|
1101
|
+
listKeys() {
|
|
1102
|
+
return this.list_keys();
|
|
1103
|
+
}
|
|
1104
|
+
search(query, topK = 5) {
|
|
1105
|
+
return this.builtin.search(query, topK);
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Build the context block injected into the system prompt.
|
|
1109
|
+
* Hermes pattern: fenced block, prefetch from the palace.
|
|
1110
|
+
*/
|
|
1111
|
+
get_relevant_context(query, maxTokens = 800) {
|
|
1112
|
+
const parts = [];
|
|
1113
|
+
// Prefetch cache from the previous turn (Hermes pattern)
|
|
1114
|
+
const cached = this._prefetch_cache;
|
|
1115
|
+
if (cached) {
|
|
1116
|
+
parts.push(cached);
|
|
1117
|
+
}
|
|
1118
|
+
else if (this.palace.is_available()) {
|
|
1119
|
+
// Live fetch when there is no cache yet
|
|
1120
|
+
const ctx = this.palace.get_context();
|
|
1121
|
+
if (ctx) {
|
|
1122
|
+
parts.push(ctx);
|
|
1123
|
+
}
|
|
1124
|
+
const r = this.palace.search(query, null, null, 3);
|
|
1125
|
+
const hits = r.results;
|
|
1126
|
+
if (hits.length > 0) {
|
|
1127
|
+
const lines = ["[PALACE SEARCH]"];
|
|
1128
|
+
for (const h of hits) {
|
|
1129
|
+
lines.push(` [${h.wing}/${h.room}] ${h.text.slice(0, 200)}`);
|
|
1130
|
+
}
|
|
1131
|
+
parts.push(lines.join("\n"));
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
// Always include TF-IDF local memory too
|
|
1135
|
+
const localCtx = this.builtin.getRelevantContext(query, Math.floor(maxTokens / 2));
|
|
1136
|
+
if (localCtx) {
|
|
1137
|
+
parts.push(localCtx);
|
|
1138
|
+
}
|
|
1139
|
+
if (parts.length === 0) {
|
|
1140
|
+
return "";
|
|
1141
|
+
}
|
|
1142
|
+
return _build_memory_context_block(parts.join("\n\n"));
|
|
1143
|
+
}
|
|
1144
|
+
getRelevantContext(query, maxTokens = 800) {
|
|
1145
|
+
return this.get_relevant_context(query, maxTokens);
|
|
1146
|
+
}
|
|
1147
|
+
auto_extract_and_save(text, source = "conversation") {
|
|
1148
|
+
this.builtin.autoExtractAndSave(text, source);
|
|
1149
|
+
if (this.palace.is_available() && text.length > 50) {
|
|
1150
|
+
this.palace.store("wing_axoniz", "conversations", text.slice(0, 600), "auto");
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
autoExtractAndSave(text, source = "conversation") {
|
|
1154
|
+
this.auto_extract_and_save(text, source);
|
|
1155
|
+
}
|
|
1156
|
+
/* ── Hermes lifecycle hooks ─────────────────────────────────────────────── */
|
|
1157
|
+
/**
|
|
1158
|
+
* Called before each agent turn — refreshes the cached palace context.
|
|
1159
|
+
* Python used a daemon thread; this is a fire-and-forget async task that is
|
|
1160
|
+
* also awaitable and never rejects.
|
|
1161
|
+
*/
|
|
1162
|
+
async prefetch(query) {
|
|
1163
|
+
if (!this.palace.is_available()) {
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
try {
|
|
1167
|
+
const parts = [];
|
|
1168
|
+
const ctx = this.palace.get_context();
|
|
1169
|
+
if (ctx) {
|
|
1170
|
+
parts.push(ctx);
|
|
1171
|
+
}
|
|
1172
|
+
const r = this.palace.search(query, null, null, 4);
|
|
1173
|
+
const hits = r.results;
|
|
1174
|
+
if (hits.length > 0) {
|
|
1175
|
+
const lines = ["[PALACE]"];
|
|
1176
|
+
for (const h of hits) {
|
|
1177
|
+
lines.push(` [${h.wing}/${h.room}] ${h.text.slice(0, 150)}`);
|
|
1178
|
+
}
|
|
1179
|
+
parts.push(lines.join("\n"));
|
|
1180
|
+
}
|
|
1181
|
+
this._prefetch_cache = parts.join("\n\n");
|
|
1182
|
+
}
|
|
1183
|
+
catch (e) {
|
|
1184
|
+
logger.debug(`[UnifiedMemory] prefetch failed: ${errMsg(e)}`);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
/** Called after each turn — saves the conversation to the palace. */
|
|
1188
|
+
async sync_turn(userContent, assistantContent) {
|
|
1189
|
+
if (!this.palace.is_available()) {
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
try {
|
|
1193
|
+
this.palace.store("wing_axoniz", "conversation-log", `USR: ${userContent.slice(0, 300)}\nAGT: ${assistantContent.slice(0, 300)}`, "sync");
|
|
1194
|
+
}
|
|
1195
|
+
catch (e) {
|
|
1196
|
+
logger.debug(`[UnifiedMemory] sync_turn failed: ${errMsg(e)}`);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
syncTurn(userContent, assistantContent) {
|
|
1200
|
+
return this.sync_turn(userContent, assistantContent);
|
|
1201
|
+
}
|
|
1202
|
+
/* ── Tool map — injected into Agent._tool_map ───────────────────────────── */
|
|
1203
|
+
/**
|
|
1204
|
+
* Callable tools to merge into the agent tool map.
|
|
1205
|
+
* Overrides memory_save/get/list with the unified versions and adds
|
|
1206
|
+
* palace/kg/diary tools. Key names match the Python exactly, and each callable
|
|
1207
|
+
* accepts the Python kwargs object the agent dispatches (`fn(args)`) as well
|
|
1208
|
+
* as plain positional arguments.
|
|
1209
|
+
*/
|
|
1210
|
+
as_tool_map() {
|
|
1211
|
+
return {
|
|
1212
|
+
// ── Override existing axoniz memory tools ──
|
|
1213
|
+
memory_save: (...args) => {
|
|
1214
|
+
const a = toolArgs(args, ["key", "value", "tags", "importance"]);
|
|
1215
|
+
return this.save(String(a["key"] ?? ""), String(a["value"] ?? ""), a["tags"], optNum(a["importance"]) ?? 0.5);
|
|
1216
|
+
},
|
|
1217
|
+
memory_get: (...args) => {
|
|
1218
|
+
const a = toolArgs(args, ["key"]);
|
|
1219
|
+
return this.get(String(a["key"] ?? ""));
|
|
1220
|
+
},
|
|
1221
|
+
memory_list: () => this.list_keys(),
|
|
1222
|
+
// ── Palace read tools ──
|
|
1223
|
+
palace_search: (...args) => {
|
|
1224
|
+
const a = toolArgs(args, ["query", "wing", "room", "limit"]);
|
|
1225
|
+
return this._t_palace_search(String(a["query"] ?? ""), optStr(a["wing"]), optStr(a["room"]), optNum(a["limit"]));
|
|
1226
|
+
},
|
|
1227
|
+
palace_context: () => this._t_palace_context(),
|
|
1228
|
+
palace_status: () => this._t_palace_status(),
|
|
1229
|
+
palace_wings: () => this._t_palace_wings(),
|
|
1230
|
+
palace_rooms: (...args) => {
|
|
1231
|
+
const a = toolArgs(args, ["wing"]);
|
|
1232
|
+
return this._t_palace_rooms(optStr(a["wing"]));
|
|
1233
|
+
},
|
|
1234
|
+
palace_check_dup: (...args) => {
|
|
1235
|
+
const a = toolArgs(args, ["content", "threshold"]);
|
|
1236
|
+
return this._t_palace_check_dup(String(a["content"] ?? ""), optNum(a["threshold"]) ?? 0.9);
|
|
1237
|
+
},
|
|
1238
|
+
palace_graph_traverse: (...args) => {
|
|
1239
|
+
const a = toolArgs(args, ["start_room", "max_hops"]);
|
|
1240
|
+
return this._t_palace_traverse(String(a["start_room"] ?? ""), optNum(a["max_hops"]) ?? 2);
|
|
1241
|
+
},
|
|
1242
|
+
palace_find_tunnels: (...args) => {
|
|
1243
|
+
const a = toolArgs(args, ["wing_a", "wing_b"]);
|
|
1244
|
+
return this._t_palace_tunnels(optStr(a["wing_a"]), optStr(a["wing_b"]));
|
|
1245
|
+
},
|
|
1246
|
+
palace_graph_stats: () => this._t_palace_graph_stats(),
|
|
1247
|
+
// ── Palace write tools ──
|
|
1248
|
+
palace_store: (...args) => {
|
|
1249
|
+
const a = toolArgs(args, ["wing", "room", "content"]);
|
|
1250
|
+
return this._t_palace_store(String(a["wing"] ?? ""), String(a["room"] ?? ""), String(a["content"] ?? ""));
|
|
1251
|
+
},
|
|
1252
|
+
palace_delete: (...args) => {
|
|
1253
|
+
const a = toolArgs(args, ["drawer_id"]);
|
|
1254
|
+
return this._t_palace_delete(String(a["drawer_id"] ?? ""));
|
|
1255
|
+
},
|
|
1256
|
+
// ── Knowledge graph ──
|
|
1257
|
+
kg_add: (...args) => {
|
|
1258
|
+
const a = toolArgs(args, ["subject", "predicate", "obj", "valid_from", "source"]);
|
|
1259
|
+
return this._t_kg_add(String(a["subject"] ?? ""), String(a["predicate"] ?? ""), String(a["obj"] ?? ""), optStr(a["valid_from"]), optStr(a["source"]));
|
|
1260
|
+
},
|
|
1261
|
+
kg_query: (...args) => {
|
|
1262
|
+
const a = toolArgs(args, ["entity", "as_of", "direction"]);
|
|
1263
|
+
return this._t_kg_query(String(a["entity"] ?? ""), optStr(a["as_of"]), optStr(a["direction"]) ?? "both");
|
|
1264
|
+
},
|
|
1265
|
+
kg_invalidate: (...args) => {
|
|
1266
|
+
const a = toolArgs(args, ["subject", "predicate", "obj", "ended"]);
|
|
1267
|
+
return this._t_kg_invalidate(String(a["subject"] ?? ""), String(a["predicate"] ?? ""), String(a["obj"] ?? ""), optStr(a["ended"]));
|
|
1268
|
+
},
|
|
1269
|
+
kg_timeline: (...args) => {
|
|
1270
|
+
const a = toolArgs(args, ["entity"]);
|
|
1271
|
+
return this._t_kg_timeline(optStr(a["entity"]));
|
|
1272
|
+
},
|
|
1273
|
+
kg_stats: () => this._t_kg_stats(),
|
|
1274
|
+
// ── Agent diary ──
|
|
1275
|
+
diary_write: (...args) => {
|
|
1276
|
+
const a = toolArgs(args, ["entry", "topic"]);
|
|
1277
|
+
return this._t_diary_write(String(a["entry"] ?? ""), optStr(a["topic"]) ?? "general");
|
|
1278
|
+
},
|
|
1279
|
+
diary_read: (...args) => {
|
|
1280
|
+
const a = toolArgs(args, ["last_n"]);
|
|
1281
|
+
return this._t_diary_read(optNum(a["last_n"]) ?? 10);
|
|
1282
|
+
},
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
asToolMap() {
|
|
1286
|
+
return this.as_tool_map();
|
|
1287
|
+
}
|
|
1288
|
+
/** OpenAI-format schemas for all memory tools — used in TOOL_SCHEMAS. */
|
|
1289
|
+
as_tool_schemas() {
|
|
1290
|
+
return [
|
|
1291
|
+
_fn("palace_search", "Semantic search across the palace. Use before answering questions about past decisions, projects, or context. Returns verbatim stored content.", {
|
|
1292
|
+
query: ["string", "Search query"],
|
|
1293
|
+
wing: ["string", "Wing filter (optional)"],
|
|
1294
|
+
room: ["string", "Room filter (optional)"],
|
|
1295
|
+
limit: ["integer", "Max results (default 5)"],
|
|
1296
|
+
}, ["query"]),
|
|
1297
|
+
_fn("palace_store", "Save verbatim content into the palace. wing=domain (project/person/topic), room=sub-topic.", {
|
|
1298
|
+
wing: ["string", "Domain (e.g. project name)"],
|
|
1299
|
+
room: ["string", "Sub-topic"],
|
|
1300
|
+
content: ["string", "Verbatim content to store"],
|
|
1301
|
+
}, ["wing", "room", "content"]),
|
|
1302
|
+
_fn("palace_context", "Load palace wake-up context: total drawers, wings overview. Call at start of session.", {}, []),
|
|
1303
|
+
_fn("palace_wings", "List all palace wings and drawer counts.", {}, []),
|
|
1304
|
+
_fn("palace_rooms", "List rooms in a wing.", { wing: ["string", "Wing name"] }, []),
|
|
1305
|
+
_fn("palace_check_dup", "Check if content already exists before storing.", {
|
|
1306
|
+
content: ["string", "Content to check"],
|
|
1307
|
+
threshold: ["number", "Similarity threshold 0-1 (default 0.9)"],
|
|
1308
|
+
}, ["content"]),
|
|
1309
|
+
_fn("palace_delete", "Delete a palace drawer by ID.", { drawer_id: ["string", "Drawer ID"] }, ["drawer_id"]),
|
|
1310
|
+
_fn("palace_graph_traverse", "Walk palace graph from a room — find connected ideas across wings.", {
|
|
1311
|
+
start_room: ["string", "Room to start from"],
|
|
1312
|
+
max_hops: ["integer", "Hops to follow (default 2)"],
|
|
1313
|
+
}, ["start_room"]),
|
|
1314
|
+
_fn("palace_find_tunnels", "Find rooms that bridge two wings.", { wing_a: ["string", "First wing"], wing_b: ["string", "Second wing"] }, []),
|
|
1315
|
+
_fn("palace_graph_stats", "Palace graph overview: rooms, tunnels, edges.", {}, []),
|
|
1316
|
+
_fn("kg_add", "Add a temporal fact: subject → predicate → object. E.g. ('Max','loves','chess',valid_from='2025-01-01')", {
|
|
1317
|
+
subject: ["string", "Entity"],
|
|
1318
|
+
predicate: ["string", "Relationship"],
|
|
1319
|
+
obj: ["string", "Connected entity"],
|
|
1320
|
+
valid_from: ["string", "When true (YYYY-MM-DD)"],
|
|
1321
|
+
source: ["string", "Source reference"],
|
|
1322
|
+
}, ["subject", "predicate", "obj"]),
|
|
1323
|
+
_fn("kg_query", "Query knowledge graph for an entity's facts. direction: outgoing/incoming/both.", {
|
|
1324
|
+
entity: ["string", "Entity name"],
|
|
1325
|
+
as_of: ["string", "Date filter YYYY-MM-DD"],
|
|
1326
|
+
direction: ["string", "outgoing/incoming/both"],
|
|
1327
|
+
}, ["entity"]),
|
|
1328
|
+
_fn("kg_invalidate", "Mark a fact as no longer true.", {
|
|
1329
|
+
subject: ["string", ""],
|
|
1330
|
+
predicate: ["string", ""],
|
|
1331
|
+
obj: ["string", ""],
|
|
1332
|
+
ended: ["string", "When it ended YYYY-MM-DD"],
|
|
1333
|
+
}, ["subject", "predicate", "obj"]),
|
|
1334
|
+
_fn("kg_timeline", "Chronological timeline of facts.", { entity: ["string", "Entity name (optional)"] }, []),
|
|
1335
|
+
_fn("kg_stats", "Knowledge graph statistics.", {}, []),
|
|
1336
|
+
_fn("diary_write", "Write a diary entry in AAAK format. Your personal journal across sessions.", { entry: ["string", "AAAK-format diary entry"], topic: ["string", "Topic tag (optional)"] }, ["entry"]),
|
|
1337
|
+
_fn("diary_read", "Read your recent diary entries.", { last_n: ["integer", "Number of entries (default 10)"] }, []),
|
|
1338
|
+
];
|
|
1339
|
+
}
|
|
1340
|
+
asToolSchemas() {
|
|
1341
|
+
return this.as_tool_schemas();
|
|
1342
|
+
}
|
|
1343
|
+
/* ── Tool implementations ───────────────────────────────────────────────── */
|
|
1344
|
+
_t_palace_search(query, wing, room, limit = 5) {
|
|
1345
|
+
const r = this.palace.search(query, wing ?? null, room ?? null, limit);
|
|
1346
|
+
if (r.error !== undefined && r.results.length === 0) {
|
|
1347
|
+
return `[Palace error] ${r.error}`;
|
|
1348
|
+
}
|
|
1349
|
+
const hits = r.results;
|
|
1350
|
+
if (hits.length === 0) {
|
|
1351
|
+
return `No palace results for: ${query}`;
|
|
1352
|
+
}
|
|
1353
|
+
const lines = [`Palace search: '${query}' (${hits.length} results)`];
|
|
1354
|
+
for (const h of hits) {
|
|
1355
|
+
lines.push(` [${h.wing}/${h.room}] sim=${h.similarity} | ${h.text.slice(0, 200)}`);
|
|
1356
|
+
}
|
|
1357
|
+
return lines.join("\n");
|
|
1358
|
+
}
|
|
1359
|
+
_t_palace_store(wing, room, content) {
|
|
1360
|
+
return JSON.stringify(this.palace.store(wing, room, content, "agent"));
|
|
1361
|
+
}
|
|
1362
|
+
_t_palace_context() {
|
|
1363
|
+
const st = this.palace.status();
|
|
1364
|
+
if (st.error !== undefined && st.total_drawers === 0) {
|
|
1365
|
+
return "[Palace empty or unavailable. Local SQLite palace backend is not initialized.]";
|
|
1366
|
+
}
|
|
1367
|
+
const lines = [
|
|
1368
|
+
`Palace status: ${st.total_drawers} drawers`,
|
|
1369
|
+
"Wings: " +
|
|
1370
|
+
Object.entries(st.wings ?? {})
|
|
1371
|
+
.slice(0, 8)
|
|
1372
|
+
.map(([w, c]) => `${w}(${c})`)
|
|
1373
|
+
.join(", "),
|
|
1374
|
+
];
|
|
1375
|
+
return lines.join("\n");
|
|
1376
|
+
}
|
|
1377
|
+
_t_palace_status() {
|
|
1378
|
+
return JSON.stringify(this.palace.status(), null, 2);
|
|
1379
|
+
}
|
|
1380
|
+
_t_palace_wings() {
|
|
1381
|
+
return JSON.stringify(this.palace.list_wings(), null, 2);
|
|
1382
|
+
}
|
|
1383
|
+
_t_palace_rooms(wing) {
|
|
1384
|
+
return JSON.stringify(this.palace.list_rooms(wing ?? null), null, 2);
|
|
1385
|
+
}
|
|
1386
|
+
_t_palace_check_dup(content, threshold = 0.9) {
|
|
1387
|
+
return JSON.stringify(this.palace.check_duplicate(content, threshold), null, 2);
|
|
1388
|
+
}
|
|
1389
|
+
_t_palace_delete(drawerId) {
|
|
1390
|
+
return JSON.stringify(this.palace.delete(drawerId), null, 2);
|
|
1391
|
+
}
|
|
1392
|
+
_t_palace_traverse(startRoom, maxHops = 2) {
|
|
1393
|
+
return JSON.stringify(this.palace.traverse_graph(startRoom, maxHops), null, 2);
|
|
1394
|
+
}
|
|
1395
|
+
_t_palace_tunnels(wingA, wingB) {
|
|
1396
|
+
return JSON.stringify(this.palace.find_tunnels(wingA ?? null, wingB ?? null), null, 2);
|
|
1397
|
+
}
|
|
1398
|
+
_t_palace_graph_stats() {
|
|
1399
|
+
return JSON.stringify(this.palace.graph_stats(), null, 2);
|
|
1400
|
+
}
|
|
1401
|
+
_t_kg_add(subject, predicate, obj, validFrom, source) {
|
|
1402
|
+
const result = this.kg.add(subject, predicate, obj, validFrom ?? null, source ?? null);
|
|
1403
|
+
return `Added: ${subject} → ${predicate} → ${obj} | id=${result}`;
|
|
1404
|
+
}
|
|
1405
|
+
_t_kg_query(entity, asOf, direction = "both") {
|
|
1406
|
+
const facts = this.kg.query(entity, asOf ?? null, direction);
|
|
1407
|
+
if (facts.length === 0) {
|
|
1408
|
+
return `No facts found for: ${entity}`;
|
|
1409
|
+
}
|
|
1410
|
+
const lines = [`KG facts for '${entity}':`];
|
|
1411
|
+
for (const f of facts.slice(0, 20)) {
|
|
1412
|
+
const status = f.current ? "✓" : "✗";
|
|
1413
|
+
lines.push(` ${status} ${f.subject} → ${f.predicate} → ${f.object} [${f.valid_from ?? ""}→${f.valid_to ?? ""}]`);
|
|
1414
|
+
}
|
|
1415
|
+
return lines.join("\n");
|
|
1416
|
+
}
|
|
1417
|
+
_t_kg_invalidate(subject, predicate, obj, ended) {
|
|
1418
|
+
this.kg.invalidate(subject, predicate, obj, ended ?? null);
|
|
1419
|
+
return `Invalidated: ${subject} → ${predicate} → ${obj} (ended: ${ended ?? "today"})`;
|
|
1420
|
+
}
|
|
1421
|
+
_t_kg_timeline(entity) {
|
|
1422
|
+
const facts = this.kg.timeline(entity ?? null);
|
|
1423
|
+
if (facts.length === 0) {
|
|
1424
|
+
return "No timeline entries.";
|
|
1425
|
+
}
|
|
1426
|
+
const lines = [`Timeline${entity ? ` for ${entity}` : ""}:`];
|
|
1427
|
+
for (const f of facts.slice(0, 20)) {
|
|
1428
|
+
lines.push(` ${f["valid_from"] ?? "?"} | ${f["subject"] ?? ""} → ${f["predicate"] ?? ""} → ${f["object"] ?? ""} ` +
|
|
1429
|
+
`${f["valid_to"] ? "(expired)" : ""}`);
|
|
1430
|
+
}
|
|
1431
|
+
return lines.join("\n");
|
|
1432
|
+
}
|
|
1433
|
+
_t_kg_stats() {
|
|
1434
|
+
return JSON.stringify(this.kg.stats(), null, 2);
|
|
1435
|
+
}
|
|
1436
|
+
_t_diary_write(entry, topic = "general") {
|
|
1437
|
+
const r = this.diary.write(entry, topic);
|
|
1438
|
+
return `Diary entry saved: ${r.id ?? JSON.stringify(r)}`;
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* `last_n` may be passed positionally (tool map) or as `{ last_n }` — the
|
|
1442
|
+
* Python CLI calls it as `memory._t_diary_read(last_n=1)`.
|
|
1443
|
+
*/
|
|
1444
|
+
_t_diary_read(lastN = 10) {
|
|
1445
|
+
const n = typeof lastN === "object" && lastN !== null ? (lastN.last_n ?? 10) : lastN;
|
|
1446
|
+
const r = this.diary.read(n);
|
|
1447
|
+
const hits = r.results;
|
|
1448
|
+
if (hits.length === 0) {
|
|
1449
|
+
return "No diary entries yet.";
|
|
1450
|
+
}
|
|
1451
|
+
const lines = ["Recent diary entries:"];
|
|
1452
|
+
for (const h of hits) {
|
|
1453
|
+
lines.push(` ${h.text.slice(0, 300)}`);
|
|
1454
|
+
}
|
|
1455
|
+
return lines.join("\n");
|
|
1456
|
+
}
|
|
1457
|
+
/** Close the underlying SQLite handles (additive; Python had no explicit close). */
|
|
1458
|
+
close() {
|
|
1459
|
+
this.palace.backend.close();
|
|
1460
|
+
this.kg.close();
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
export function _fn(name, description, props, required) {
|
|
1464
|
+
const properties = {};
|
|
1465
|
+
for (const [k, v] of Object.entries(props)) {
|
|
1466
|
+
if (Array.isArray(v)) {
|
|
1467
|
+
const prop = { type: v[0] };
|
|
1468
|
+
if (v[1])
|
|
1469
|
+
prop["description"] = v[1];
|
|
1470
|
+
properties[k] = prop;
|
|
1471
|
+
}
|
|
1472
|
+
else {
|
|
1473
|
+
properties[k] = v;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
return {
|
|
1477
|
+
type: "function",
|
|
1478
|
+
function: {
|
|
1479
|
+
name,
|
|
1480
|
+
description,
|
|
1481
|
+
parameters: {
|
|
1482
|
+
type: "object",
|
|
1483
|
+
properties,
|
|
1484
|
+
required,
|
|
1485
|
+
},
|
|
1486
|
+
},
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
//# sourceMappingURL=unified_memory.js.map
|