@kernel.chat/kbot 3.61.7 → 3.62.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/package.json +9 -122
- package/dist/a2a-client.d.ts.map +0 -1
- package/dist/a2a-client.js.map +0 -1
- package/dist/a2a.d.ts.map +0 -1
- package/dist/a2a.js.map +0 -1
- package/dist/agent-create.d.ts.map +0 -1
- package/dist/agent-create.js.map +0 -1
- package/dist/agent-protocol.d.ts.map +0 -1
- package/dist/agent-protocol.js.map +0 -1
- package/dist/agent-protocol.test.d.ts +0 -2
- package/dist/agent-protocol.test.d.ts.map +0 -1
- package/dist/agent-protocol.test.js +0 -730
- package/dist/agent-protocol.test.js.map +0 -1
- package/dist/agent-teams.d.ts.map +0 -1
- package/dist/agent-teams.js.map +0 -1
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js.map +0 -1
- package/dist/agents/agents.test.d.ts +0 -2
- package/dist/agents/agents.test.d.ts.map +0 -1
- package/dist/agents/agents.test.js +0 -127
- package/dist/agents/agents.test.js.map +0 -1
- package/dist/agents/creative.d.ts.map +0 -1
- package/dist/agents/creative.js.map +0 -1
- package/dist/agents/developer.d.ts.map +0 -1
- package/dist/agents/developer.js.map +0 -1
- package/dist/agents/gamedev.d.ts.map +0 -1
- package/dist/agents/gamedev.js.map +0 -1
- package/dist/agents/playtester.d.ts.map +0 -1
- package/dist/agents/playtester.js.map +0 -1
- package/dist/agents/producer.d.ts.map +0 -1
- package/dist/agents/producer.js.map +0 -1
- package/dist/agents/replit.d.ts.map +0 -1
- package/dist/agents/replit.js.map +0 -1
- package/dist/agents/specialists.d.ts.map +0 -1
- package/dist/agents/specialists.js.map +0 -1
- package/dist/agents/trader.d.ts.map +0 -1
- package/dist/agents/trader.js.map +0 -1
- package/dist/architect.d.ts.map +0 -1
- package/dist/architect.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/auth.test.d.ts +0 -2
- package/dist/auth.test.d.ts.map +0 -1
- package/dist/auth.test.js +0 -87
- package/dist/auth.test.js.map +0 -1
- package/dist/automations.d.ts.map +0 -1
- package/dist/automations.js.map +0 -1
- package/dist/autonomous-contributor.d.ts.map +0 -1
- package/dist/autonomous-contributor.js.map +0 -1
- package/dist/autopoiesis.d.ts.map +0 -1
- package/dist/autopoiesis.js.map +0 -1
- package/dist/behaviour.d.ts.map +0 -1
- package/dist/behaviour.js.map +0 -1
- package/dist/bench.d.ts.map +0 -1
- package/dist/bench.js.map +0 -1
- package/dist/bootstrap.d.ts.map +0 -1
- package/dist/bootstrap.js.map +0 -1
- package/dist/briefing.d.ts.map +0 -1
- package/dist/briefing.js.map +0 -1
- package/dist/build-targets.d.ts.map +0 -1
- package/dist/build-targets.js.map +0 -1
- package/dist/changelog.d.ts.map +0 -1
- package/dist/changelog.js.map +0 -1
- package/dist/channels/kbot-channel.d.ts.map +0 -1
- package/dist/channels/kbot-channel.js.map +0 -1
- package/dist/checkpoint.d.ts.map +0 -1
- package/dist/checkpoint.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cloud-agent.d.ts.map +0 -1
- package/dist/cloud-agent.js.map +0 -1
- package/dist/cloud-sync.d.ts.map +0 -1
- package/dist/cloud-sync.js.map +0 -1
- package/dist/codebase-guardian.d.ts.map +0 -1
- package/dist/codebase-guardian.js.map +0 -1
- package/dist/collective-learning.d.ts.map +0 -1
- package/dist/collective-learning.js.map +0 -1
- package/dist/collective-network.d.ts.map +0 -1
- package/dist/collective-network.js.map +0 -1
- package/dist/collective.d.ts.map +0 -1
- package/dist/collective.js.map +0 -1
- package/dist/community-autopilot.d.ts.map +0 -1
- package/dist/community-autopilot.js.map +0 -1
- package/dist/community-manager.d.ts.map +0 -1
- package/dist/community-manager.js.map +0 -1
- package/dist/compete.d.ts.map +0 -1
- package/dist/compete.js.map +0 -1
- package/dist/completions.d.ts.map +0 -1
- package/dist/completions.js.map +0 -1
- package/dist/confidence.d.ts.map +0 -1
- package/dist/confidence.js.map +0 -1
- package/dist/consultation.d.ts.map +0 -1
- package/dist/consultation.js.map +0 -1
- package/dist/consultation.test.d.ts +0 -2
- package/dist/consultation.test.d.ts.map +0 -1
- package/dist/consultation.test.js +0 -86
- package/dist/consultation.test.js.map +0 -1
- package/dist/context-manager.d.ts.map +0 -1
- package/dist/context-manager.js.map +0 -1
- package/dist/context-manager.test.d.ts +0 -2
- package/dist/context-manager.test.d.ts.map +0 -1
- package/dist/context-manager.test.js +0 -121
- package/dist/context-manager.test.js.map +0 -1
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js.map +0 -1
- package/dist/context.test.d.ts +0 -2
- package/dist/context.test.d.ts.map +0 -1
- package/dist/context.test.js +0 -561
- package/dist/context.test.js.map +0 -1
- package/dist/cross-device-sync.d.ts.map +0 -1
- package/dist/cross-device-sync.js.map +0 -1
- package/dist/daemon.d.ts.map +0 -1
- package/dist/daemon.js.map +0 -1
- package/dist/dashboard.d.ts.map +0 -1
- package/dist/dashboard.js.map +0 -1
- package/dist/decision-journal.d.ts.map +0 -1
- package/dist/decision-journal.js.map +0 -1
- package/dist/digest.d.ts.map +0 -1
- package/dist/digest.js.map +0 -1
- package/dist/discovery.d.ts.map +0 -1
- package/dist/discovery.js.map +0 -1
- package/dist/doctor.d.ts.map +0 -1
- package/dist/doctor.js.map +0 -1
- package/dist/doctor.test.d.ts +0 -2
- package/dist/doctor.test.d.ts.map +0 -1
- package/dist/doctor.test.js +0 -432
- package/dist/doctor.test.js.map +0 -1
- package/dist/dream-mode.d.ts.map +0 -1
- package/dist/dream-mode.js.map +0 -1
- package/dist/email-agent.d.ts.map +0 -1
- package/dist/email-agent.js.map +0 -1
- package/dist/email-service.d.ts.map +0 -1
- package/dist/email-service.js.map +0 -1
- package/dist/embeddings.d.ts.map +0 -1
- package/dist/embeddings.js.map +0 -1
- package/dist/emergent-swarm.d.ts.map +0 -1
- package/dist/emergent-swarm.js.map +0 -1
- package/dist/entropy-context.d.ts.map +0 -1
- package/dist/entropy-context.js.map +0 -1
- package/dist/episodic-memory.d.ts.map +0 -1
- package/dist/episodic-memory.js.map +0 -1
- package/dist/error-correction.d.ts.map +0 -1
- package/dist/error-correction.js.map +0 -1
- package/dist/evolution.d.ts.map +0 -1
- package/dist/evolution.js.map +0 -1
- package/dist/evolution.test.d.ts +0 -2
- package/dist/evolution.test.d.ts.map +0 -1
- package/dist/evolution.test.js +0 -160
- package/dist/evolution.test.js.map +0 -1
- package/dist/export.d.ts.map +0 -1
- package/dist/export.js.map +0 -1
- package/dist/forge-marketplace-server.d.ts.map +0 -1
- package/dist/forge-marketplace-server.js.map +0 -1
- package/dist/forge-marketplace.d.ts.map +0 -1
- package/dist/forge-marketplace.js.map +0 -1
- package/dist/forge-registry.d.ts.map +0 -1
- package/dist/forge-registry.js.map +0 -1
- package/dist/free-energy.d.ts.map +0 -1
- package/dist/free-energy.js.map +0 -1
- package/dist/gitagent-export.d.ts.map +0 -1
- package/dist/gitagent-export.js.map +0 -1
- package/dist/github-release.d.ts.map +0 -1
- package/dist/github-release.js.map +0 -1
- package/dist/godel-limits.d.ts.map +0 -1
- package/dist/godel-limits.js.map +0 -1
- package/dist/graph-memory.d.ts.map +0 -1
- package/dist/graph-memory.js.map +0 -1
- package/dist/graph-memory.test.d.ts +0 -2
- package/dist/graph-memory.test.d.ts.map +0 -1
- package/dist/graph-memory.test.js +0 -946
- package/dist/graph-memory.test.js.map +0 -1
- package/dist/guardrails.d.ts.map +0 -1
- package/dist/guardrails.js.map +0 -1
- package/dist/handoffs.d.ts.map +0 -1
- package/dist/handoffs.js.map +0 -1
- package/dist/hooks-integration.d.ts.map +0 -1
- package/dist/hooks-integration.js.map +0 -1
- package/dist/hooks.d.ts.map +0 -1
- package/dist/hooks.js.map +0 -1
- package/dist/ide/acp-server.d.ts.map +0 -1
- package/dist/ide/acp-server.js.map +0 -1
- package/dist/ide/bridge.d.ts.map +0 -1
- package/dist/ide/bridge.js.map +0 -1
- package/dist/ide/index.d.ts.map +0 -1
- package/dist/ide/index.js.map +0 -1
- package/dist/ide/lsp-bridge.d.ts.map +0 -1
- package/dist/ide/lsp-bridge.js.map +0 -1
- package/dist/ide/mcp-server.d.ts.map +0 -1
- package/dist/ide/mcp-server.js.map +0 -1
- package/dist/imessage-agent.d.ts.map +0 -1
- package/dist/imessage-agent.js.map +0 -1
- package/dist/inference.d.ts.map +0 -1
- package/dist/inference.js.map +0 -1
- package/dist/init-science.d.ts.map +0 -1
- package/dist/init-science.js.map +0 -1
- package/dist/init.d.ts.map +0 -1
- package/dist/init.js.map +0 -1
- package/dist/init.test.d.ts +0 -2
- package/dist/init.test.d.ts.map +0 -1
- package/dist/init.test.js +0 -91
- package/dist/init.test.js.map +0 -1
- package/dist/integrated-information.d.ts.map +0 -1
- package/dist/integrated-information.js.map +0 -1
- package/dist/integrations/ableton-live.d.ts.map +0 -1
- package/dist/integrations/ableton-live.js.map +0 -1
- package/dist/integrations/ableton-m4l.d.ts.map +0 -1
- package/dist/integrations/ableton-m4l.js.map +0 -1
- package/dist/integrations/ableton-osc-installer.d.ts.map +0 -1
- package/dist/integrations/ableton-osc-installer.js.map +0 -1
- package/dist/integrations/ableton-osc.d.ts.map +0 -1
- package/dist/integrations/ableton-osc.js.map +0 -1
- package/dist/integrations/openclaw/plugin.d.ts.map +0 -1
- package/dist/integrations/openclaw/plugin.js.map +0 -1
- package/dist/intentionality.d.ts.map +0 -1
- package/dist/intentionality.js.map +0 -1
- package/dist/interactive-buttons.d.ts.map +0 -1
- package/dist/interactive-buttons.js.map +0 -1
- package/dist/interference.d.ts.map +0 -1
- package/dist/interference.js.map +0 -1
- package/dist/introspection.d.ts.map +0 -1
- package/dist/introspection.js.map +0 -1
- package/dist/kbot-service.d.ts.map +0 -1
- package/dist/kbot-service.js.map +0 -1
- package/dist/knowledge-base.d.ts.map +0 -1
- package/dist/knowledge-base.js.map +0 -1
- package/dist/lab.d.ts.map +0 -1
- package/dist/lab.js.map +0 -1
- package/dist/learned-router.d.ts.map +0 -1
- package/dist/learned-router.js.map +0 -1
- package/dist/learning.d.ts.map +0 -1
- package/dist/learning.js.map +0 -1
- package/dist/learning.test.d.ts +0 -2
- package/dist/learning.test.d.ts.map +0 -1
- package/dist/learning.test.js +0 -106
- package/dist/learning.test.js.map +0 -1
- package/dist/lsp-client.d.ts.map +0 -1
- package/dist/lsp-client.js.map +0 -1
- package/dist/lsp-deep.d.ts.map +0 -1
- package/dist/lsp-deep.js.map +0 -1
- package/dist/machine.d.ts.map +0 -1
- package/dist/machine.js.map +0 -1
- package/dist/marketplace.d.ts.map +0 -1
- package/dist/marketplace.js.map +0 -1
- package/dist/matrix.d.ts.map +0 -1
- package/dist/matrix.js.map +0 -1
- package/dist/mcp-apps.d.ts.map +0 -1
- package/dist/mcp-apps.js.map +0 -1
- package/dist/mcp-plugins.d.ts.map +0 -1
- package/dist/mcp-plugins.js.map +0 -1
- package/dist/memory-hotswap.d.ts.map +0 -1
- package/dist/memory-hotswap.js.map +0 -1
- package/dist/memory-synthesis.d.ts.map +0 -1
- package/dist/memory-synthesis.js.map +0 -1
- package/dist/memory-synthesis.test.d.ts +0 -2
- package/dist/memory-synthesis.test.d.ts.map +0 -1
- package/dist/memory-synthesis.test.js +0 -83
- package/dist/memory-synthesis.test.js.map +0 -1
- package/dist/memory.d.ts.map +0 -1
- package/dist/memory.js.map +0 -1
- package/dist/memory.test.d.ts +0 -2
- package/dist/memory.test.d.ts.map +0 -1
- package/dist/memory.test.js +0 -450
- package/dist/memory.test.js.map +0 -1
- package/dist/meta-agent.d.ts.map +0 -1
- package/dist/meta-agent.js.map +0 -1
- package/dist/migrate.d.ts.map +0 -1
- package/dist/migrate.js.map +0 -1
- package/dist/multi-session.d.ts.map +0 -1
- package/dist/multi-session.js.map +0 -1
- package/dist/multimodal.d.ts.map +0 -1
- package/dist/multimodal.js.map +0 -1
- package/dist/music-learning.d.ts.map +0 -1
- package/dist/music-learning.js.map +0 -1
- package/dist/notifications.d.ts.map +0 -1
- package/dist/notifications.js.map +0 -1
- package/dist/observer.d.ts.map +0 -1
- package/dist/observer.js.map +0 -1
- package/dist/openclaw-connect.d.ts.map +0 -1
- package/dist/openclaw-connect.js.map +0 -1
- package/dist/packages/kbot/src/tools/mcp-client.d.ts.map +0 -1
- package/dist/packages/kbot/src/tools/mcp-client.js.map +0 -1
- package/dist/packages/kbot/src/tools/mcp-discovery.d.ts.map +0 -1
- package/dist/packages/kbot/src/tools/mcp-discovery.js.map +0 -1
- package/dist/packages/kbot/src/tools/memory-harness.d.ts.map +0 -1
- package/dist/packages/kbot/src/tools/memory-harness.js.map +0 -1
- package/dist/pair.d.ts.map +0 -1
- package/dist/pair.js.map +0 -1
- package/dist/pattern-feed.d.ts.map +0 -1
- package/dist/pattern-feed.js.map +0 -1
- package/dist/permissions.d.ts.map +0 -1
- package/dist/permissions.js.map +0 -1
- package/dist/personal-security.d.ts.map +0 -1
- package/dist/personal-security.js.map +0 -1
- package/dist/planner.d.ts.map +0 -1
- package/dist/planner.js.map +0 -1
- package/dist/plugin/index.d.ts.map +0 -1
- package/dist/plugin/index.js.map +0 -1
- package/dist/plugin-sdk.d.ts.map +0 -1
- package/dist/plugin-sdk.js.map +0 -1
- package/dist/plugins.d.ts.map +0 -1
- package/dist/plugins.js.map +0 -1
- package/dist/predictive-processing.d.ts.map +0 -1
- package/dist/predictive-processing.js.map +0 -1
- package/dist/privacy-router.d.ts.map +0 -1
- package/dist/privacy-router.js.map +0 -1
- package/dist/prompt-cache.d.ts.map +0 -1
- package/dist/prompt-cache.js.map +0 -1
- package/dist/prompt-evolution.d.ts.map +0 -1
- package/dist/prompt-evolution.js.map +0 -1
- package/dist/provider-fallback.d.ts.map +0 -1
- package/dist/provider-fallback.js.map +0 -1
- package/dist/quality-diversity.d.ts.map +0 -1
- package/dist/quality-diversity.js.map +0 -1
- package/dist/rate-limiter.d.ts.map +0 -1
- package/dist/rate-limiter.js.map +0 -1
- package/dist/reasoning.d.ts.map +0 -1
- package/dist/reasoning.js.map +0 -1
- package/dist/record.d.ts.map +0 -1
- package/dist/record.js.map +0 -1
- package/dist/reflection.d.ts.map +0 -1
- package/dist/reflection.js.map +0 -1
- package/dist/replit.d.ts.map +0 -1
- package/dist/replit.js.map +0 -1
- package/dist/repo-map.d.ts.map +0 -1
- package/dist/repo-map.js.map +0 -1
- package/dist/sandbox-policy.d.ts.map +0 -1
- package/dist/sandbox-policy.js.map +0 -1
- package/dist/sdk.d.ts.map +0 -1
- package/dist/sdk.js.map +0 -1
- package/dist/seed-knowledge.d.ts.map +0 -1
- package/dist/seed-knowledge.js.map +0 -1
- package/dist/self-defense.d.ts.map +0 -1
- package/dist/self-defense.js.map +0 -1
- package/dist/self-eval.d.ts.map +0 -1
- package/dist/self-eval.js.map +0 -1
- package/dist/serve.d.ts.map +0 -1
- package/dist/serve.js.map +0 -1
- package/dist/sessions.d.ts.map +0 -1
- package/dist/sessions.js.map +0 -1
- package/dist/sessions.test.d.ts +0 -2
- package/dist/sessions.test.d.ts.map +0 -1
- package/dist/sessions.test.js +0 -55
- package/dist/sessions.test.js.map +0 -1
- package/dist/share.d.ts.map +0 -1
- package/dist/share.js.map +0 -1
- package/dist/side-conversation.d.ts.map +0 -1
- package/dist/side-conversation.js.map +0 -1
- package/dist/simulation.d.ts.map +0 -1
- package/dist/simulation.js.map +0 -1
- package/dist/skill-library.d.ts.map +0 -1
- package/dist/skill-library.js.map +0 -1
- package/dist/skill-rating.d.ts.map +0 -1
- package/dist/skill-rating.js.map +0 -1
- package/dist/skill-system.d.ts.map +0 -1
- package/dist/skill-system.js.map +0 -1
- package/dist/skills-loader.d.ts.map +0 -1
- package/dist/skills-loader.js.map +0 -1
- package/dist/spec.d.ts.map +0 -1
- package/dist/spec.js.map +0 -1
- package/dist/strange-loops.d.ts.map +0 -1
- package/dist/strange-loops.js.map +0 -1
- package/dist/streaming.d.ts.map +0 -1
- package/dist/streaming.js.map +0 -1
- package/dist/streaming.test.d.ts +0 -2
- package/dist/streaming.test.d.ts.map +0 -1
- package/dist/streaming.test.js +0 -41
- package/dist/streaming.test.js.map +0 -1
- package/dist/synthesis-engine.d.ts.map +0 -1
- package/dist/synthesis-engine.js.map +0 -1
- package/dist/task-ledger.d.ts.map +0 -1
- package/dist/task-ledger.js.map +0 -1
- package/dist/teach.d.ts.map +0 -1
- package/dist/teach.js.map +0 -1
- package/dist/team.d.ts.map +0 -1
- package/dist/team.js.map +0 -1
- package/dist/telemetry.d.ts.map +0 -1
- package/dist/telemetry.js.map +0 -1
- package/dist/temporal.d.ts.map +0 -1
- package/dist/temporal.js.map +0 -1
- package/dist/terminal-caps.d.ts.map +0 -1
- package/dist/terminal-caps.js.map +0 -1
- package/dist/tool-pipeline.d.ts.map +0 -1
- package/dist/tool-pipeline.js.map +0 -1
- package/dist/tools/ableton-knowledge.d.ts.map +0 -1
- package/dist/tools/ableton-knowledge.js.map +0 -1
- package/dist/tools/ableton.d.ts.map +0 -1
- package/dist/tools/ableton.js.map +0 -1
- package/dist/tools/admin.d.ts.map +0 -1
- package/dist/tools/admin.js.map +0 -1
- package/dist/tools/analytics.d.ts.map +0 -1
- package/dist/tools/analytics.js.map +0 -1
- package/dist/tools/arrangement-engine.d.ts.map +0 -1
- package/dist/tools/arrangement-engine.js.map +0 -1
- package/dist/tools/audit.d.ts.map +0 -1
- package/dist/tools/audit.js.map +0 -1
- package/dist/tools/background.d.ts.map +0 -1
- package/dist/tools/background.js.map +0 -1
- package/dist/tools/bash.d.ts.map +0 -1
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/bash.test.d.ts +0 -2
- package/dist/tools/bash.test.d.ts.map +0 -1
- package/dist/tools/bash.test.js +0 -284
- package/dist/tools/bash.test.js.map +0 -1
- package/dist/tools/bootstrapper.d.ts.map +0 -1
- package/dist/tools/bootstrapper.js.map +0 -1
- package/dist/tools/browser-agent.d.ts.map +0 -1
- package/dist/tools/browser-agent.js.map +0 -1
- package/dist/tools/browser.d.ts.map +0 -1
- package/dist/tools/browser.js.map +0 -1
- package/dist/tools/build-matrix.d.ts.map +0 -1
- package/dist/tools/build-matrix.js.map +0 -1
- package/dist/tools/comfyui-plugin.d.ts.map +0 -1
- package/dist/tools/comfyui-plugin.js.map +0 -1
- package/dist/tools/composio.d.ts.map +0 -1
- package/dist/tools/composio.js.map +0 -1
- package/dist/tools/computer.d.ts.map +0 -1
- package/dist/tools/computer.js.map +0 -1
- package/dist/tools/containers.d.ts.map +0 -1
- package/dist/tools/containers.js.map +0 -1
- package/dist/tools/content-engine.d.ts.map +0 -1
- package/dist/tools/content-engine.js.map +0 -1
- package/dist/tools/contribute.d.ts.map +0 -1
- package/dist/tools/contribute.js.map +0 -1
- package/dist/tools/creative.d.ts.map +0 -1
- package/dist/tools/creative.js.map +0 -1
- package/dist/tools/creative.test.d.ts +0 -2
- package/dist/tools/creative.test.d.ts.map +0 -1
- package/dist/tools/creative.test.js +0 -281
- package/dist/tools/creative.test.js.map +0 -1
- package/dist/tools/ctf.d.ts.map +0 -1
- package/dist/tools/ctf.js.map +0 -1
- package/dist/tools/database.d.ts.map +0 -1
- package/dist/tools/database.js.map +0 -1
- package/dist/tools/db-admin.d.ts.map +0 -1
- package/dist/tools/db-admin.js.map +0 -1
- package/dist/tools/deploy-all.d.ts.map +0 -1
- package/dist/tools/deploy-all.js.map +0 -1
- package/dist/tools/deploy.d.ts.map +0 -1
- package/dist/tools/deploy.js.map +0 -1
- package/dist/tools/dj-set-builder.d.ts.map +0 -1
- package/dist/tools/dj-set-builder.js.map +0 -1
- package/dist/tools/documents.d.ts.map +0 -1
- package/dist/tools/documents.js.map +0 -1
- package/dist/tools/e2b-sandbox.d.ts.map +0 -1
- package/dist/tools/e2b-sandbox.js.map +0 -1
- package/dist/tools/email.d.ts.map +0 -1
- package/dist/tools/email.js.map +0 -1
- package/dist/tools/emergent.d.ts.map +0 -1
- package/dist/tools/emergent.js.map +0 -1
- package/dist/tools/env-manager.d.ts.map +0 -1
- package/dist/tools/env-manager.js.map +0 -1
- package/dist/tools/fetch.d.ts.map +0 -1
- package/dist/tools/fetch.js.map +0 -1
- package/dist/tools/fetch.test.d.ts +0 -2
- package/dist/tools/fetch.test.d.ts.map +0 -1
- package/dist/tools/fetch.test.js +0 -363
- package/dist/tools/fetch.test.js.map +0 -1
- package/dist/tools/files.d.ts.map +0 -1
- package/dist/tools/files.js.map +0 -1
- package/dist/tools/files.test.d.ts +0 -2
- package/dist/tools/files.test.d.ts.map +0 -1
- package/dist/tools/files.test.js +0 -395
- package/dist/tools/files.test.js.map +0 -1
- package/dist/tools/finance.d.ts.map +0 -1
- package/dist/tools/finance.js.map +0 -1
- package/dist/tools/finance.test.d.ts +0 -2
- package/dist/tools/finance.test.d.ts.map +0 -1
- package/dist/tools/finance.test.js +0 -245
- package/dist/tools/finance.test.js.map +0 -1
- package/dist/tools/forge.d.ts.map +0 -1
- package/dist/tools/forge.js.map +0 -1
- package/dist/tools/forge.test.d.ts +0 -2
- package/dist/tools/forge.test.d.ts.map +0 -1
- package/dist/tools/forge.test.js +0 -250
- package/dist/tools/forge.test.js.map +0 -1
- package/dist/tools/gamedev.d.ts.map +0 -1
- package/dist/tools/gamedev.js.map +0 -1
- package/dist/tools/gamedev.test.d.ts +0 -2
- package/dist/tools/gamedev.test.d.ts.map +0 -1
- package/dist/tools/gamedev.test.js +0 -937
- package/dist/tools/gamedev.test.js.map +0 -1
- package/dist/tools/git.d.ts.map +0 -1
- package/dist/tools/git.js.map +0 -1
- package/dist/tools/git.test.d.ts +0 -2
- package/dist/tools/git.test.d.ts.map +0 -1
- package/dist/tools/git.test.js +0 -303
- package/dist/tools/git.test.js.map +0 -1
- package/dist/tools/github.d.ts.map +0 -1
- package/dist/tools/github.js.map +0 -1
- package/dist/tools/hacker-toolkit.d.ts.map +0 -1
- package/dist/tools/hacker-toolkit.js.map +0 -1
- package/dist/tools/hypothesis-engine.d.ts.map +0 -1
- package/dist/tools/hypothesis-engine.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/index.test.d.ts +0 -2
- package/dist/tools/index.test.d.ts.map +0 -1
- package/dist/tools/index.test.js +0 -155
- package/dist/tools/index.test.js.map +0 -1
- package/dist/tools/kbot-local.d.ts.map +0 -1
- package/dist/tools/kbot-local.js.map +0 -1
- package/dist/tools/lab-bio.d.ts.map +0 -1
- package/dist/tools/lab-bio.js.map +0 -1
- package/dist/tools/lab-chem.d.ts.map +0 -1
- package/dist/tools/lab-chem.js.map +0 -1
- package/dist/tools/lab-core.d.ts.map +0 -1
- package/dist/tools/lab-core.js.map +0 -1
- package/dist/tools/lab-data.d.ts.map +0 -1
- package/dist/tools/lab-data.js.map +0 -1
- package/dist/tools/lab-earth.d.ts.map +0 -1
- package/dist/tools/lab-earth.js.map +0 -1
- package/dist/tools/lab-frontier.d.ts.map +0 -1
- package/dist/tools/lab-frontier.js.map +0 -1
- package/dist/tools/lab-health.d.ts.map +0 -1
- package/dist/tools/lab-health.js.map +0 -1
- package/dist/tools/lab-humanities.d.ts.map +0 -1
- package/dist/tools/lab-humanities.js.map +0 -1
- package/dist/tools/lab-math.d.ts.map +0 -1
- package/dist/tools/lab-math.js.map +0 -1
- package/dist/tools/lab-neuro.d.ts.map +0 -1
- package/dist/tools/lab-neuro.js.map +0 -1
- package/dist/tools/lab-physics.d.ts.map +0 -1
- package/dist/tools/lab-physics.js.map +0 -1
- package/dist/tools/lab-social.d.ts.map +0 -1
- package/dist/tools/lab-social.js.map +0 -1
- package/dist/tools/lsp-tools.d.ts.map +0 -1
- package/dist/tools/lsp-tools.js.map +0 -1
- package/dist/tools/machine-tools.d.ts.map +0 -1
- package/dist/tools/machine-tools.js.map +0 -1
- package/dist/tools/magenta-plugin.d.ts.map +0 -1
- package/dist/tools/magenta-plugin.js.map +0 -1
- package/dist/tools/matrix.d.ts.map +0 -1
- package/dist/tools/matrix.js.map +0 -1
- package/dist/tools/mcp-client.d.ts.map +0 -1
- package/dist/tools/mcp-client.js.map +0 -1
- package/dist/tools/mcp-marketplace.d.ts.map +0 -1
- package/dist/tools/mcp-marketplace.js.map +0 -1
- package/dist/tools/memory-tools.d.ts.map +0 -1
- package/dist/tools/memory-tools.js.map +0 -1
- package/dist/tools/monitor.d.ts.map +0 -1
- package/dist/tools/monitor.js.map +0 -1
- package/dist/tools/music-theory.d.ts.map +0 -1
- package/dist/tools/music-theory.js.map +0 -1
- package/dist/tools/notebook.d.ts.map +0 -1
- package/dist/tools/notebook.js.map +0 -1
- package/dist/tools/openclaw.d.ts.map +0 -1
- package/dist/tools/openclaw.js.map +0 -1
- package/dist/tools/parallel.d.ts.map +0 -1
- package/dist/tools/parallel.js.map +0 -1
- package/dist/tools/pentest.d.ts.map +0 -1
- package/dist/tools/pentest.js.map +0 -1
- package/dist/tools/producer-engine.d.ts.map +0 -1
- package/dist/tools/producer-engine.js.map +0 -1
- package/dist/tools/quality.d.ts.map +0 -1
- package/dist/tools/quality.js.map +0 -1
- package/dist/tools/redblue.d.ts.map +0 -1
- package/dist/tools/redblue.js.map +0 -1
- package/dist/tools/research-notebook.d.ts.map +0 -1
- package/dist/tools/research-notebook.js.map +0 -1
- package/dist/tools/research-pipeline.d.ts.map +0 -1
- package/dist/tools/research-pipeline.js.map +0 -1
- package/dist/tools/research.d.ts.map +0 -1
- package/dist/tools/research.js.map +0 -1
- package/dist/tools/sandbox.d.ts.map +0 -1
- package/dist/tools/sandbox.js.map +0 -1
- package/dist/tools/science-graph.d.ts.map +0 -1
- package/dist/tools/science-graph.js.map +0 -1
- package/dist/tools/search.d.ts.map +0 -1
- package/dist/tools/search.js.map +0 -1
- package/dist/tools/search.test.d.ts +0 -2
- package/dist/tools/search.test.d.ts.map +0 -1
- package/dist/tools/search.test.js +0 -311
- package/dist/tools/search.test.js.map +0 -1
- package/dist/tools/security-brain.d.ts.map +0 -1
- package/dist/tools/security-brain.js.map +0 -1
- package/dist/tools/security-hunt.d.ts.map +0 -1
- package/dist/tools/security-hunt.js.map +0 -1
- package/dist/tools/security.d.ts.map +0 -1
- package/dist/tools/security.js.map +0 -1
- package/dist/tools/sentiment.d.ts.map +0 -1
- package/dist/tools/sentiment.js.map +0 -1
- package/dist/tools/serum2-preset.d.ts.map +0 -1
- package/dist/tools/serum2-preset.js.map +0 -1
- package/dist/tools/social.d.ts.map +0 -1
- package/dist/tools/social.js.map +0 -1
- package/dist/tools/sound-designer.d.ts.map +0 -1
- package/dist/tools/sound-designer.js.map +0 -1
- package/dist/tools/stocks.d.ts.map +0 -1
- package/dist/tools/stocks.js.map +0 -1
- package/dist/tools/stocks.test.d.ts +0 -2
- package/dist/tools/stocks.test.d.ts.map +0 -1
- package/dist/tools/stocks.test.js +0 -82
- package/dist/tools/stocks.test.js.map +0 -1
- package/dist/tools/subagent.d.ts.map +0 -1
- package/dist/tools/subagent.js.map +0 -1
- package/dist/tools/tasks.d.ts.map +0 -1
- package/dist/tools/tasks.js.map +0 -1
- package/dist/tools/test-runner.d.ts.map +0 -1
- package/dist/tools/test-runner.js.map +0 -1
- package/dist/tools/training.d.ts.map +0 -1
- package/dist/tools/training.js.map +0 -1
- package/dist/tools/vfx.d.ts.map +0 -1
- package/dist/tools/vfx.js.map +0 -1
- package/dist/tools/visa-payments.d.ts.map +0 -1
- package/dist/tools/visa-payments.js.map +0 -1
- package/dist/tools/wallet.d.ts.map +0 -1
- package/dist/tools/wallet.js.map +0 -1
- package/dist/tools/wallet.test.d.ts +0 -2
- package/dist/tools/wallet.test.d.ts.map +0 -1
- package/dist/tools/wallet.test.js +0 -205
- package/dist/tools/wallet.test.js.map +0 -1
- package/dist/tools/weather.d.ts.map +0 -1
- package/dist/tools/weather.js.map +0 -1
- package/dist/tools/worktree.d.ts.map +0 -1
- package/dist/tools/worktree.js.map +0 -1
- package/dist/tree-planner.d.ts.map +0 -1
- package/dist/tree-planner.js.map +0 -1
- package/dist/tui.d.ts.map +0 -1
- package/dist/tui.js.map +0 -1
- package/dist/tutorial.d.ts.map +0 -1
- package/dist/tutorial.js.map +0 -1
- package/dist/ui-adapter.d.ts.map +0 -1
- package/dist/ui-adapter.js.map +0 -1
- package/dist/ui.d.ts.map +0 -1
- package/dist/ui.js.map +0 -1
- package/dist/updater.d.ts.map +0 -1
- package/dist/updater.js.map +0 -1
- package/dist/user-graph.d.ts.map +0 -1
- package/dist/user-graph.js.map +0 -1
- package/dist/voice-loop.d.ts.map +0 -1
- package/dist/voice-loop.js.map +0 -1
- package/dist/voice-realtime.d.ts.map +0 -1
- package/dist/voice-realtime.js.map +0 -1
- package/dist/voice.d.ts.map +0 -1
- package/dist/voice.js.map +0 -1
- package/dist/watch.d.ts.map +0 -1
- package/dist/watch.js.map +0 -1
- package/dist/workflows.d.ts.map +0 -1
- package/dist/workflows.js.map +0 -1
|
@@ -1,946 +0,0 @@
|
|
|
1
|
-
// Tests for kbot Graph Memory — knowledge graph for entity-relationship reasoning
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
// Mock node:fs before importing the module under test
|
|
4
|
-
vi.mock('node:fs', () => ({
|
|
5
|
-
existsSync: vi.fn(),
|
|
6
|
-
readFileSync: vi.fn(),
|
|
7
|
-
writeFileSync: vi.fn(),
|
|
8
|
-
mkdirSync: vi.fn(),
|
|
9
|
-
readdirSync: vi.fn(),
|
|
10
|
-
}));
|
|
11
|
-
vi.mock('node:os', () => ({
|
|
12
|
-
homedir: vi.fn(() => '/mock-home'),
|
|
13
|
-
}));
|
|
14
|
-
// Mock crypto to produce deterministic IDs for testing
|
|
15
|
-
let idCounter = 0;
|
|
16
|
-
vi.mock('node:crypto', () => ({
|
|
17
|
-
randomBytes: vi.fn(() => {
|
|
18
|
-
const hex = (idCounter++).toString(16).padStart(8, '0');
|
|
19
|
-
return { toString: () => hex };
|
|
20
|
-
}),
|
|
21
|
-
}));
|
|
22
|
-
// Mock registerTool to avoid side effects
|
|
23
|
-
vi.mock('./tools/index.js', () => ({
|
|
24
|
-
registerTool: vi.fn(),
|
|
25
|
-
}));
|
|
26
|
-
import { load, save, addNode, addEdge, findNode, getNeighbors, getSubgraph, shortestPath, queryRelation, decayUnused, prune, toContext, extractEntities, autoConnect, importFromMemory, getStats, getNode, getGraph, } from './graph-memory.js';
|
|
27
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
28
|
-
const mockedExistsSync = vi.mocked(existsSync);
|
|
29
|
-
const mockedReadFileSync = vi.mocked(readFileSync);
|
|
30
|
-
const mockedWriteFileSync = vi.mocked(writeFileSync);
|
|
31
|
-
const mockedMkdirSync = vi.mocked(mkdirSync);
|
|
32
|
-
const mockedReaddirSync = vi.mocked(readdirSync);
|
|
33
|
-
const KBOT_DIR = '/mock-home/.kbot';
|
|
34
|
-
const GRAPH_FILE = '/mock-home/.kbot/graph.json';
|
|
35
|
-
beforeEach(() => {
|
|
36
|
-
vi.clearAllMocks();
|
|
37
|
-
idCounter = 0;
|
|
38
|
-
// Reset graph to empty state via load() with no file
|
|
39
|
-
mockedExistsSync.mockImplementation((p) => {
|
|
40
|
-
if (p === KBOT_DIR)
|
|
41
|
-
return true;
|
|
42
|
-
return false; // graph.json does not exist
|
|
43
|
-
});
|
|
44
|
-
load();
|
|
45
|
-
vi.clearAllMocks();
|
|
46
|
-
idCounter = 100; // Start IDs from 100 for clarity
|
|
47
|
-
});
|
|
48
|
-
// ─── load ────────────────────────────────────────────────────────────────
|
|
49
|
-
describe('load', () => {
|
|
50
|
-
it('creates kbot dir if it does not exist', () => {
|
|
51
|
-
mockedExistsSync.mockReturnValueOnce(false); // dir check
|
|
52
|
-
mockedExistsSync.mockReturnValueOnce(false); // file check
|
|
53
|
-
load();
|
|
54
|
-
expect(mockedMkdirSync).toHaveBeenCalledWith(KBOT_DIR, { recursive: true });
|
|
55
|
-
});
|
|
56
|
-
it('initializes empty graph when graph.json does not exist', () => {
|
|
57
|
-
mockedExistsSync.mockReturnValueOnce(true); // dir exists
|
|
58
|
-
mockedExistsSync.mockReturnValueOnce(false); // file does not exist
|
|
59
|
-
load();
|
|
60
|
-
const graph = getGraph();
|
|
61
|
-
expect(graph.nodes.size).toBe(0);
|
|
62
|
-
expect(graph.edges).toEqual([]);
|
|
63
|
-
});
|
|
64
|
-
it('loads nodes and edges from graph.json', () => {
|
|
65
|
-
const graphData = {
|
|
66
|
-
nodes: [
|
|
67
|
-
['abc', { id: 'abc', type: 'entity', name: 'Test', properties: {}, created: '2026-01-01', lastAccessed: '2026-01-01', accessCount: 1 }],
|
|
68
|
-
],
|
|
69
|
-
edges: [
|
|
70
|
-
{ source: 'abc', target: 'def', relation: 'uses', weight: 0.5, created: '2026-01-01' },
|
|
71
|
-
],
|
|
72
|
-
};
|
|
73
|
-
mockedExistsSync.mockReturnValue(true);
|
|
74
|
-
mockedReadFileSync.mockReturnValue(JSON.stringify(graphData));
|
|
75
|
-
load();
|
|
76
|
-
const graph = getGraph();
|
|
77
|
-
expect(graph.nodes.size).toBe(1);
|
|
78
|
-
expect(graph.nodes.get('abc').name).toBe('Test');
|
|
79
|
-
expect(graph.edges).toHaveLength(1);
|
|
80
|
-
expect(graph.edges[0].relation).toBe('uses');
|
|
81
|
-
});
|
|
82
|
-
it('resets to empty graph when JSON is malformed', () => {
|
|
83
|
-
mockedExistsSync.mockReturnValue(true);
|
|
84
|
-
mockedReadFileSync.mockReturnValue('not-valid-json{{{');
|
|
85
|
-
load();
|
|
86
|
-
const graph = getGraph();
|
|
87
|
-
expect(graph.nodes.size).toBe(0);
|
|
88
|
-
expect(graph.edges).toEqual([]);
|
|
89
|
-
});
|
|
90
|
-
it('handles missing nodes array gracefully', () => {
|
|
91
|
-
mockedExistsSync.mockReturnValue(true);
|
|
92
|
-
mockedReadFileSync.mockReturnValue(JSON.stringify({ edges: [] }));
|
|
93
|
-
load();
|
|
94
|
-
const graph = getGraph();
|
|
95
|
-
expect(graph.nodes.size).toBe(0);
|
|
96
|
-
expect(graph.edges).toEqual([]);
|
|
97
|
-
});
|
|
98
|
-
it('handles missing edges array gracefully', () => {
|
|
99
|
-
mockedExistsSync.mockReturnValue(true);
|
|
100
|
-
mockedReadFileSync.mockReturnValue(JSON.stringify({ nodes: [] }));
|
|
101
|
-
load();
|
|
102
|
-
const graph = getGraph();
|
|
103
|
-
expect(graph.nodes.size).toBe(0);
|
|
104
|
-
expect(graph.edges).toEqual([]);
|
|
105
|
-
});
|
|
106
|
-
it('does not create dir if it already exists', () => {
|
|
107
|
-
mockedExistsSync.mockReturnValueOnce(true); // dir exists
|
|
108
|
-
mockedExistsSync.mockReturnValueOnce(false); // file doesn't exist
|
|
109
|
-
load();
|
|
110
|
-
expect(mockedMkdirSync).not.toHaveBeenCalled();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
// ─── save ────────────────────────────────────────────────────────────────
|
|
114
|
-
describe('save', () => {
|
|
115
|
-
it('writes graph to graph.json as JSON with nodes and edges', () => {
|
|
116
|
-
mockedExistsSync.mockReturnValue(true);
|
|
117
|
-
addNode('entity', 'TestNode');
|
|
118
|
-
save();
|
|
119
|
-
expect(mockedWriteFileSync).toHaveBeenCalledTimes(1);
|
|
120
|
-
const [path, content, encoding] = mockedWriteFileSync.mock.calls[0];
|
|
121
|
-
expect(path).toBe(GRAPH_FILE);
|
|
122
|
-
expect(encoding).toBe('utf-8');
|
|
123
|
-
const parsed = JSON.parse(content);
|
|
124
|
-
expect(parsed.nodes).toHaveLength(1);
|
|
125
|
-
expect(parsed.nodes[0][1].name).toBe('TestNode');
|
|
126
|
-
expect(parsed.edges).toEqual([]);
|
|
127
|
-
});
|
|
128
|
-
it('creates kbot dir if it does not exist', () => {
|
|
129
|
-
mockedExistsSync.mockReturnValueOnce(false);
|
|
130
|
-
save();
|
|
131
|
-
expect(mockedMkdirSync).toHaveBeenCalledWith(KBOT_DIR, { recursive: true });
|
|
132
|
-
});
|
|
133
|
-
it('serializes Map entries as arrays of [id, node] tuples', () => {
|
|
134
|
-
mockedExistsSync.mockReturnValue(true);
|
|
135
|
-
const nodeA = addNode('file', 'auth.ts');
|
|
136
|
-
const nodeB = addNode('person', 'Isaac');
|
|
137
|
-
addEdge(nodeA.id, nodeB.id, 'authored_by');
|
|
138
|
-
save();
|
|
139
|
-
const parsed = JSON.parse(mockedWriteFileSync.mock.calls[0][1]);
|
|
140
|
-
expect(parsed.nodes).toHaveLength(2);
|
|
141
|
-
// Each entry is [id, nodeObject]
|
|
142
|
-
expect(parsed.nodes[0][0]).toBe(nodeA.id);
|
|
143
|
-
expect(parsed.nodes[1][0]).toBe(nodeB.id);
|
|
144
|
-
expect(parsed.edges).toHaveLength(1);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
// ─── addNode ─────────────────────────────────────────────────────────────
|
|
148
|
-
describe('addNode', () => {
|
|
149
|
-
it('creates a new node with correct fields', () => {
|
|
150
|
-
vi.useFakeTimers();
|
|
151
|
-
vi.setSystemTime(new Date('2026-03-29T10:00:00Z'));
|
|
152
|
-
const node = addNode('entity', 'JWT');
|
|
153
|
-
expect(node.type).toBe('entity');
|
|
154
|
-
expect(node.name).toBe('JWT');
|
|
155
|
-
expect(node.id).toBeDefined();
|
|
156
|
-
expect(node.accessCount).toBe(1);
|
|
157
|
-
expect(node.created).toBe('2026-03-29T10:00:00.000Z');
|
|
158
|
-
expect(node.lastAccessed).toBe('2026-03-29T10:00:00.000Z');
|
|
159
|
-
expect(node.properties).toEqual({});
|
|
160
|
-
vi.useRealTimers();
|
|
161
|
-
});
|
|
162
|
-
it('stores properties on the node', () => {
|
|
163
|
-
const node = addNode('file', 'auth.ts', { path: '/src/auth.ts', language: 'typescript' });
|
|
164
|
-
expect(node.properties.path).toBe('/src/auth.ts');
|
|
165
|
-
expect(node.properties.language).toBe('typescript');
|
|
166
|
-
});
|
|
167
|
-
it('adds node to the graph', () => {
|
|
168
|
-
const node = addNode('concept', 'REST API');
|
|
169
|
-
const graph = getGraph();
|
|
170
|
-
expect(graph.nodes.has(node.id)).toBe(true);
|
|
171
|
-
expect(graph.nodes.get(node.id).name).toBe('REST API');
|
|
172
|
-
});
|
|
173
|
-
it('returns existing node for duplicate type+name (case-insensitive)', () => {
|
|
174
|
-
const first = addNode('entity', 'JWT');
|
|
175
|
-
const second = addNode('entity', 'jwt');
|
|
176
|
-
expect(first.id).toBe(second.id);
|
|
177
|
-
expect(getGraph().nodes.size).toBe(1);
|
|
178
|
-
});
|
|
179
|
-
it('increments accessCount on duplicate', () => {
|
|
180
|
-
const first = addNode('entity', 'JWT');
|
|
181
|
-
expect(first.accessCount).toBe(1);
|
|
182
|
-
const second = addNode('entity', 'jwt');
|
|
183
|
-
expect(second.accessCount).toBe(2);
|
|
184
|
-
});
|
|
185
|
-
it('merges properties on duplicate', () => {
|
|
186
|
-
addNode('entity', 'JWT', { algo: 'RS256' });
|
|
187
|
-
const updated = addNode('entity', 'jwt', { issuer: 'kbot' });
|
|
188
|
-
expect(updated.properties.algo).toBe('RS256');
|
|
189
|
-
expect(updated.properties.issuer).toBe('kbot');
|
|
190
|
-
});
|
|
191
|
-
it('allows same name with different type', () => {
|
|
192
|
-
const entity = addNode('entity', 'auth');
|
|
193
|
-
const file = addNode('file', 'auth');
|
|
194
|
-
expect(entity.id).not.toBe(file.id);
|
|
195
|
-
expect(getGraph().nodes.size).toBe(2);
|
|
196
|
-
});
|
|
197
|
-
it('generates unique IDs for different nodes', () => {
|
|
198
|
-
const a = addNode('entity', 'NodeA');
|
|
199
|
-
const b = addNode('entity', 'NodeB');
|
|
200
|
-
expect(a.id).not.toBe(b.id);
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
// ─── addEdge ─────────────────────────────────────────────────────────────
|
|
204
|
-
describe('addEdge', () => {
|
|
205
|
-
it('creates an edge between two existing nodes', () => {
|
|
206
|
-
const nodeA = addNode('file', 'auth.ts');
|
|
207
|
-
const nodeB = addNode('entity', 'JWT');
|
|
208
|
-
const result = addEdge(nodeA.id, nodeB.id, 'uses');
|
|
209
|
-
expect(result).toBe(true);
|
|
210
|
-
expect(getGraph().edges).toHaveLength(1);
|
|
211
|
-
expect(getGraph().edges[0]).toMatchObject({
|
|
212
|
-
source: nodeA.id,
|
|
213
|
-
target: nodeB.id,
|
|
214
|
-
relation: 'uses',
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
it('returns false when source node does not exist', () => {
|
|
218
|
-
const node = addNode('entity', 'Test');
|
|
219
|
-
expect(addEdge('nonexistent', node.id, 'uses')).toBe(false);
|
|
220
|
-
expect(getGraph().edges).toHaveLength(0);
|
|
221
|
-
});
|
|
222
|
-
it('returns false when target node does not exist', () => {
|
|
223
|
-
const node = addNode('entity', 'Test');
|
|
224
|
-
expect(addEdge(node.id, 'nonexistent', 'uses')).toBe(false);
|
|
225
|
-
expect(getGraph().edges).toHaveLength(0);
|
|
226
|
-
});
|
|
227
|
-
it('returns false when both nodes do not exist', () => {
|
|
228
|
-
expect(addEdge('fake1', 'fake2', 'uses')).toBe(false);
|
|
229
|
-
});
|
|
230
|
-
it('uses default weight of 0.5', () => {
|
|
231
|
-
const a = addNode('entity', 'A');
|
|
232
|
-
const b = addNode('entity', 'B');
|
|
233
|
-
addEdge(a.id, b.id, 'relates');
|
|
234
|
-
expect(getGraph().edges[0].weight).toBe(0.5);
|
|
235
|
-
});
|
|
236
|
-
it('accepts custom weight', () => {
|
|
237
|
-
const a = addNode('entity', 'A');
|
|
238
|
-
const b = addNode('entity', 'B');
|
|
239
|
-
addEdge(a.id, b.id, 'relates', 0.9);
|
|
240
|
-
expect(getGraph().edges[0].weight).toBe(0.9);
|
|
241
|
-
});
|
|
242
|
-
it('clamps weight above 1 to 1', () => {
|
|
243
|
-
const a = addNode('entity', 'A');
|
|
244
|
-
const b = addNode('entity', 'B');
|
|
245
|
-
addEdge(a.id, b.id, 'relates', 5.0);
|
|
246
|
-
expect(getGraph().edges[0].weight).toBe(1);
|
|
247
|
-
});
|
|
248
|
-
it('clamps weight below 0 to 0', () => {
|
|
249
|
-
const a = addNode('entity', 'A');
|
|
250
|
-
const b = addNode('entity', 'B');
|
|
251
|
-
addEdge(a.id, b.id, 'relates', -3.0);
|
|
252
|
-
expect(getGraph().edges[0].weight).toBe(0);
|
|
253
|
-
});
|
|
254
|
-
it('strengthens existing edge by 0.1 on duplicate', () => {
|
|
255
|
-
const a = addNode('entity', 'A');
|
|
256
|
-
const b = addNode('entity', 'B');
|
|
257
|
-
addEdge(a.id, b.id, 'uses', 0.5);
|
|
258
|
-
addEdge(a.id, b.id, 'uses', 0.9); // same source/target/relation
|
|
259
|
-
expect(getGraph().edges).toHaveLength(1);
|
|
260
|
-
expect(getGraph().edges[0].weight).toBe(0.6); // 0.5 + 0.1
|
|
261
|
-
});
|
|
262
|
-
it('allows different relation types between same nodes', () => {
|
|
263
|
-
const a = addNode('entity', 'A');
|
|
264
|
-
const b = addNode('entity', 'B');
|
|
265
|
-
addEdge(a.id, b.id, 'uses');
|
|
266
|
-
addEdge(a.id, b.id, 'depends_on');
|
|
267
|
-
expect(getGraph().edges).toHaveLength(2);
|
|
268
|
-
});
|
|
269
|
-
it('sets created timestamp on new edge', () => {
|
|
270
|
-
vi.useFakeTimers();
|
|
271
|
-
vi.setSystemTime(new Date('2026-06-01T12:00:00Z'));
|
|
272
|
-
const a = addNode('entity', 'A');
|
|
273
|
-
const b = addNode('entity', 'B');
|
|
274
|
-
addEdge(a.id, b.id, 'relates');
|
|
275
|
-
expect(getGraph().edges[0].created).toBe('2026-06-01T12:00:00.000Z');
|
|
276
|
-
vi.useRealTimers();
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
// ─── findNode ────────────────────────────────────────────────────────────
|
|
280
|
-
describe('findNode', () => {
|
|
281
|
-
it('returns empty array on empty graph', () => {
|
|
282
|
-
expect(findNode('anything')).toEqual([]);
|
|
283
|
-
});
|
|
284
|
-
it('finds exact match by name', () => {
|
|
285
|
-
addNode('entity', 'JWT');
|
|
286
|
-
const results = findNode('JWT');
|
|
287
|
-
expect(results).toHaveLength(1);
|
|
288
|
-
expect(results[0].name).toBe('JWT');
|
|
289
|
-
});
|
|
290
|
-
it('finds case-insensitive match', () => {
|
|
291
|
-
addNode('entity', 'Authentication');
|
|
292
|
-
const results = findNode('authentication');
|
|
293
|
-
expect(results).toHaveLength(1);
|
|
294
|
-
expect(results[0].name).toBe('Authentication');
|
|
295
|
-
});
|
|
296
|
-
it('finds substring match', () => {
|
|
297
|
-
addNode('file', 'graph-memory.ts');
|
|
298
|
-
const results = findNode('graph');
|
|
299
|
-
expect(results).toHaveLength(1);
|
|
300
|
-
expect(results[0].name).toBe('graph-memory.ts');
|
|
301
|
-
});
|
|
302
|
-
it('returns results sorted by relevance', () => {
|
|
303
|
-
addNode('entity', 'auth');
|
|
304
|
-
addNode('entity', 'authentication');
|
|
305
|
-
addNode('entity', 'authorization');
|
|
306
|
-
const results = findNode('auth');
|
|
307
|
-
// Exact match should come first
|
|
308
|
-
expect(results[0].name).toBe('auth');
|
|
309
|
-
});
|
|
310
|
-
it('matches against property values', () => {
|
|
311
|
-
addNode('file', 'auth.ts', { path: '/src/auth.ts', language: 'typescript' });
|
|
312
|
-
const results = findNode('typescript');
|
|
313
|
-
expect(results.length).toBeGreaterThanOrEqual(1);
|
|
314
|
-
});
|
|
315
|
-
it('returns at most 20 results', () => {
|
|
316
|
-
for (let i = 0; i < 30; i++) {
|
|
317
|
-
addNode('entity', `test-item-${i}`);
|
|
318
|
-
}
|
|
319
|
-
const results = findNode('test');
|
|
320
|
-
expect(results.length).toBeLessThanOrEqual(20);
|
|
321
|
-
});
|
|
322
|
-
it('increments accessCount on found nodes', () => {
|
|
323
|
-
const node = addNode('entity', 'JWT');
|
|
324
|
-
expect(node.accessCount).toBe(1);
|
|
325
|
-
findNode('JWT');
|
|
326
|
-
expect(getNode(node.id).accessCount).toBe(2);
|
|
327
|
-
});
|
|
328
|
-
it('excludes results below similarity threshold', () => {
|
|
329
|
-
addNode('entity', 'alpha');
|
|
330
|
-
// 'zzzzz' should score very low against 'alpha'
|
|
331
|
-
const results = findNode('zzzzz');
|
|
332
|
-
expect(results).toHaveLength(0);
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
// ─── getNeighbors (BFS) ─────────────────────────────────────────────────
|
|
336
|
-
describe('getNeighbors', () => {
|
|
337
|
-
it('returns just the node itself when it has no edges', () => {
|
|
338
|
-
const node = addNode('entity', 'Isolated');
|
|
339
|
-
const result = getNeighbors(node.id);
|
|
340
|
-
expect(result.nodes).toHaveLength(1);
|
|
341
|
-
expect(result.nodes[0].id).toBe(node.id);
|
|
342
|
-
expect(result.edges).toHaveLength(0);
|
|
343
|
-
});
|
|
344
|
-
it('returns direct neighbors at depth 1', () => {
|
|
345
|
-
const a = addNode('entity', 'A');
|
|
346
|
-
const b = addNode('entity', 'B');
|
|
347
|
-
const c = addNode('entity', 'C');
|
|
348
|
-
addEdge(a.id, b.id, 'uses');
|
|
349
|
-
addEdge(a.id, c.id, 'uses');
|
|
350
|
-
const result = getNeighbors(a.id, 1);
|
|
351
|
-
expect(result.nodes).toHaveLength(3); // A, B, C
|
|
352
|
-
expect(result.edges).toHaveLength(2);
|
|
353
|
-
});
|
|
354
|
-
it('traverses outgoing edges', () => {
|
|
355
|
-
const a = addNode('entity', 'A');
|
|
356
|
-
const b = addNode('entity', 'B');
|
|
357
|
-
addEdge(a.id, b.id, 'uses');
|
|
358
|
-
const result = getNeighbors(a.id, 1);
|
|
359
|
-
const nodeNames = result.nodes.map(n => n.name);
|
|
360
|
-
expect(nodeNames).toContain('B');
|
|
361
|
-
});
|
|
362
|
-
it('traverses incoming edges (undirected BFS)', () => {
|
|
363
|
-
const a = addNode('entity', 'A');
|
|
364
|
-
const b = addNode('entity', 'B');
|
|
365
|
-
addEdge(a.id, b.id, 'uses');
|
|
366
|
-
// Query from B — should find A via the reverse direction
|
|
367
|
-
const result = getNeighbors(b.id, 1);
|
|
368
|
-
const nodeNames = result.nodes.map(n => n.name);
|
|
369
|
-
expect(nodeNames).toContain('A');
|
|
370
|
-
});
|
|
371
|
-
it('traverses multiple hops at depth 2', () => {
|
|
372
|
-
const a = addNode('entity', 'A');
|
|
373
|
-
const b = addNode('entity', 'B');
|
|
374
|
-
const c = addNode('entity', 'C');
|
|
375
|
-
addEdge(a.id, b.id, 'uses');
|
|
376
|
-
addEdge(b.id, c.id, 'uses');
|
|
377
|
-
const result = getNeighbors(a.id, 2);
|
|
378
|
-
const nodeNames = result.nodes.map(n => n.name);
|
|
379
|
-
expect(nodeNames).toContain('A');
|
|
380
|
-
expect(nodeNames).toContain('B');
|
|
381
|
-
expect(nodeNames).toContain('C');
|
|
382
|
-
});
|
|
383
|
-
it('does not find nodes beyond the requested depth', () => {
|
|
384
|
-
const a = addNode('entity', 'A');
|
|
385
|
-
const b = addNode('entity', 'B');
|
|
386
|
-
const c = addNode('entity', 'C');
|
|
387
|
-
addEdge(a.id, b.id, 'uses');
|
|
388
|
-
addEdge(b.id, c.id, 'uses');
|
|
389
|
-
// Depth 1 from A should only reach B, not C
|
|
390
|
-
const result = getNeighbors(a.id, 1);
|
|
391
|
-
const nodeNames = result.nodes.map(n => n.name);
|
|
392
|
-
expect(nodeNames).toContain('B');
|
|
393
|
-
expect(nodeNames).not.toContain('C');
|
|
394
|
-
});
|
|
395
|
-
it('clamps depth to minimum of 1', () => {
|
|
396
|
-
const a = addNode('entity', 'A');
|
|
397
|
-
const b = addNode('entity', 'B');
|
|
398
|
-
addEdge(a.id, b.id, 'uses');
|
|
399
|
-
// depth 0 should be clamped to 1
|
|
400
|
-
const result = getNeighbors(a.id, 0);
|
|
401
|
-
expect(result.nodes.length).toBeGreaterThanOrEqual(2);
|
|
402
|
-
});
|
|
403
|
-
it('clamps depth to maximum of 3', () => {
|
|
404
|
-
// Build a chain: A->B->C->D->E
|
|
405
|
-
const nodes = ['A', 'B', 'C', 'D', 'E'].map(n => addNode('entity', n));
|
|
406
|
-
for (let i = 0; i < 4; i++) {
|
|
407
|
-
addEdge(nodes[i].id, nodes[i + 1].id, 'next');
|
|
408
|
-
}
|
|
409
|
-
// Depth 10 should be clamped to 3, so from A we reach A, B, C, D (not E)
|
|
410
|
-
const result = getNeighbors(nodes[0].id, 10);
|
|
411
|
-
const nodeNames = result.nodes.map(n => n.name);
|
|
412
|
-
expect(nodeNames).toContain('D');
|
|
413
|
-
expect(nodeNames).not.toContain('E');
|
|
414
|
-
});
|
|
415
|
-
it('does not revisit already-visited nodes', () => {
|
|
416
|
-
// Cycle: A->B->C->A
|
|
417
|
-
const a = addNode('entity', 'A');
|
|
418
|
-
const b = addNode('entity', 'B');
|
|
419
|
-
const c = addNode('entity', 'C');
|
|
420
|
-
addEdge(a.id, b.id, 'next');
|
|
421
|
-
addEdge(b.id, c.id, 'next');
|
|
422
|
-
addEdge(c.id, a.id, 'next');
|
|
423
|
-
const result = getNeighbors(a.id, 3);
|
|
424
|
-
// Should have exactly 3 unique nodes
|
|
425
|
-
expect(result.nodes).toHaveLength(3);
|
|
426
|
-
});
|
|
427
|
-
});
|
|
428
|
-
// ─── getSubgraph ─────────────────────────────────────────────────────────
|
|
429
|
-
describe('getSubgraph', () => {
|
|
430
|
-
it('returns empty result for empty ID list', () => {
|
|
431
|
-
const result = getSubgraph([]);
|
|
432
|
-
expect(result.nodes).toEqual([]);
|
|
433
|
-
expect(result.edges).toEqual([]);
|
|
434
|
-
});
|
|
435
|
-
it('returns only requested nodes', () => {
|
|
436
|
-
const a = addNode('entity', 'A');
|
|
437
|
-
const b = addNode('entity', 'B');
|
|
438
|
-
addNode('entity', 'C'); // Not requested
|
|
439
|
-
addEdge(a.id, b.id, 'uses');
|
|
440
|
-
const result = getSubgraph([a.id, b.id]);
|
|
441
|
-
expect(result.nodes).toHaveLength(2);
|
|
442
|
-
expect(result.nodes.map(n => n.name).sort()).toEqual(['A', 'B']);
|
|
443
|
-
});
|
|
444
|
-
it('includes only edges between requested nodes', () => {
|
|
445
|
-
const a = addNode('entity', 'A');
|
|
446
|
-
const b = addNode('entity', 'B');
|
|
447
|
-
const c = addNode('entity', 'C');
|
|
448
|
-
addEdge(a.id, b.id, 'uses');
|
|
449
|
-
addEdge(b.id, c.id, 'depends_on');
|
|
450
|
-
const result = getSubgraph([a.id, b.id]);
|
|
451
|
-
expect(result.edges).toHaveLength(1);
|
|
452
|
-
expect(result.edges[0].relation).toBe('uses');
|
|
453
|
-
});
|
|
454
|
-
it('skips nonexistent node IDs', () => {
|
|
455
|
-
const a = addNode('entity', 'A');
|
|
456
|
-
const result = getSubgraph([a.id, 'nonexistent']);
|
|
457
|
-
expect(result.nodes).toHaveLength(1);
|
|
458
|
-
expect(result.nodes[0].id).toBe(a.id);
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
// ─── shortestPath (BFS) ─────────────────────────────────────────────────
|
|
462
|
-
describe('shortestPath', () => {
|
|
463
|
-
it('returns empty for nonexistent source', () => {
|
|
464
|
-
const node = addNode('entity', 'A');
|
|
465
|
-
expect(shortestPath('fake', node.id)).toEqual([]);
|
|
466
|
-
});
|
|
467
|
-
it('returns empty for nonexistent target', () => {
|
|
468
|
-
const node = addNode('entity', 'A');
|
|
469
|
-
expect(shortestPath(node.id, 'fake')).toEqual([]);
|
|
470
|
-
});
|
|
471
|
-
it('returns [id] when source equals target', () => {
|
|
472
|
-
const node = addNode('entity', 'A');
|
|
473
|
-
expect(shortestPath(node.id, node.id)).toEqual([node.id]);
|
|
474
|
-
});
|
|
475
|
-
it('finds direct path between two connected nodes', () => {
|
|
476
|
-
const a = addNode('entity', 'A');
|
|
477
|
-
const b = addNode('entity', 'B');
|
|
478
|
-
addEdge(a.id, b.id, 'uses');
|
|
479
|
-
const path = shortestPath(a.id, b.id);
|
|
480
|
-
expect(path).toEqual([a.id, b.id]);
|
|
481
|
-
});
|
|
482
|
-
it('finds path via intermediate node', () => {
|
|
483
|
-
const a = addNode('entity', 'A');
|
|
484
|
-
const b = addNode('entity', 'B');
|
|
485
|
-
const c = addNode('entity', 'C');
|
|
486
|
-
addEdge(a.id, b.id, 'uses');
|
|
487
|
-
addEdge(b.id, c.id, 'uses');
|
|
488
|
-
const path = shortestPath(a.id, c.id);
|
|
489
|
-
expect(path).toEqual([a.id, b.id, c.id]);
|
|
490
|
-
});
|
|
491
|
-
it('works in reverse direction (undirected)', () => {
|
|
492
|
-
const a = addNode('entity', 'A');
|
|
493
|
-
const b = addNode('entity', 'B');
|
|
494
|
-
addEdge(a.id, b.id, 'uses');
|
|
495
|
-
// Path from B to A should exist via undirected traversal
|
|
496
|
-
const path = shortestPath(b.id, a.id);
|
|
497
|
-
expect(path).toEqual([b.id, a.id]);
|
|
498
|
-
});
|
|
499
|
-
it('returns empty array when no path exists', () => {
|
|
500
|
-
const a = addNode('entity', 'A');
|
|
501
|
-
const b = addNode('entity', 'B');
|
|
502
|
-
// No edges
|
|
503
|
-
expect(shortestPath(a.id, b.id)).toEqual([]);
|
|
504
|
-
});
|
|
505
|
-
it('finds shortest path in graph with multiple routes', () => {
|
|
506
|
-
// A -> B -> D (length 2)
|
|
507
|
-
// A -> C -> D (length 2)
|
|
508
|
-
// BFS should find one of these length-2 paths
|
|
509
|
-
const a = addNode('entity', 'A');
|
|
510
|
-
const b = addNode('entity', 'B');
|
|
511
|
-
const c = addNode('entity', 'C');
|
|
512
|
-
const d = addNode('entity', 'D');
|
|
513
|
-
addEdge(a.id, b.id, 'r1');
|
|
514
|
-
addEdge(b.id, d.id, 'r2');
|
|
515
|
-
addEdge(a.id, c.id, 'r3');
|
|
516
|
-
addEdge(c.id, d.id, 'r4');
|
|
517
|
-
const path = shortestPath(a.id, d.id);
|
|
518
|
-
expect(path).toHaveLength(3); // A -> ? -> D
|
|
519
|
-
expect(path[0]).toBe(a.id);
|
|
520
|
-
expect(path[2]).toBe(d.id);
|
|
521
|
-
});
|
|
522
|
-
it('handles cycles without infinite loop', () => {
|
|
523
|
-
const a = addNode('entity', 'A');
|
|
524
|
-
const b = addNode('entity', 'B');
|
|
525
|
-
const c = addNode('entity', 'C');
|
|
526
|
-
addEdge(a.id, b.id, 'next');
|
|
527
|
-
addEdge(b.id, c.id, 'next');
|
|
528
|
-
addEdge(c.id, a.id, 'next'); // cycle back
|
|
529
|
-
const path = shortestPath(a.id, c.id);
|
|
530
|
-
// Since the graph is treated as undirected, A is adjacent to C via the C->A edge
|
|
531
|
-
// BFS finds the shortest path which is length 2: [A, C]
|
|
532
|
-
expect(path).toHaveLength(2);
|
|
533
|
-
expect(path).toEqual([a.id, c.id]);
|
|
534
|
-
});
|
|
535
|
-
});
|
|
536
|
-
// ─── queryRelation ───────────────────────────────────────────────────────
|
|
537
|
-
describe('queryRelation', () => {
|
|
538
|
-
it('returns empty array when no edges exist', () => {
|
|
539
|
-
expect(queryRelation('uses')).toEqual([]);
|
|
540
|
-
});
|
|
541
|
-
it('finds edges by relation type', () => {
|
|
542
|
-
const a = addNode('entity', 'A');
|
|
543
|
-
const b = addNode('entity', 'B');
|
|
544
|
-
const c = addNode('entity', 'C');
|
|
545
|
-
addEdge(a.id, b.id, 'uses');
|
|
546
|
-
addEdge(a.id, c.id, 'depends_on');
|
|
547
|
-
const results = queryRelation('uses');
|
|
548
|
-
expect(results).toHaveLength(1);
|
|
549
|
-
expect(results[0].source).toBe(a.id);
|
|
550
|
-
expect(results[0].target).toBe(b.id);
|
|
551
|
-
});
|
|
552
|
-
it('is case-insensitive', () => {
|
|
553
|
-
const a = addNode('entity', 'A');
|
|
554
|
-
const b = addNode('entity', 'B');
|
|
555
|
-
addEdge(a.id, b.id, 'USES');
|
|
556
|
-
expect(queryRelation('uses')).toHaveLength(1);
|
|
557
|
-
expect(queryRelation('Uses')).toHaveLength(1);
|
|
558
|
-
});
|
|
559
|
-
it('returns all matching edges', () => {
|
|
560
|
-
const a = addNode('entity', 'A');
|
|
561
|
-
const b = addNode('entity', 'B');
|
|
562
|
-
const c = addNode('entity', 'C');
|
|
563
|
-
addEdge(a.id, b.id, 'related_to');
|
|
564
|
-
addEdge(a.id, c.id, 'related_to');
|
|
565
|
-
expect(queryRelation('related_to')).toHaveLength(2);
|
|
566
|
-
});
|
|
567
|
-
});
|
|
568
|
-
// ─── decayUnused ─────────────────────────────────────────────────────────
|
|
569
|
-
describe('decayUnused', () => {
|
|
570
|
-
it('decays edges of nodes not accessed for more than N days', () => {
|
|
571
|
-
vi.useFakeTimers();
|
|
572
|
-
vi.setSystemTime(new Date('2026-01-01T00:00:00Z'));
|
|
573
|
-
const a = addNode('entity', 'OldNode');
|
|
574
|
-
const b = addNode('entity', 'NewNode');
|
|
575
|
-
addEdge(a.id, b.id, 'uses', 0.8);
|
|
576
|
-
// Jump forward 40 days
|
|
577
|
-
vi.setSystemTime(new Date('2026-02-10T00:00:00Z'));
|
|
578
|
-
// Add a node so 'NewNode' was already accessed on Jan 1
|
|
579
|
-
const result = decayUnused(30);
|
|
580
|
-
expect(result.decayed).toBe(2); // Both were last accessed on Jan 1
|
|
581
|
-
// Edge weight is reduced by 0.1 for each decayed node connected to it.
|
|
582
|
-
// Both OldNode and NewNode were last accessed on Jan 1, so both trigger decay.
|
|
583
|
-
// The edge is connected to both, so weight is reduced twice: 0.8 - 0.1 - 0.1 = 0.6
|
|
584
|
-
expect(getGraph().edges[0].weight).toBeCloseTo(0.6, 5);
|
|
585
|
-
vi.useRealTimers();
|
|
586
|
-
});
|
|
587
|
-
it('does not decay recently accessed nodes', () => {
|
|
588
|
-
vi.useFakeTimers();
|
|
589
|
-
vi.setSystemTime(new Date('2026-03-29T00:00:00Z'));
|
|
590
|
-
const a = addNode('entity', 'Fresh');
|
|
591
|
-
const b = addNode('entity', 'AlsoFresh');
|
|
592
|
-
addEdge(a.id, b.id, 'uses', 0.5);
|
|
593
|
-
const result = decayUnused(30);
|
|
594
|
-
expect(result.decayed).toBe(0);
|
|
595
|
-
expect(getGraph().edges[0].weight).toBe(0.5);
|
|
596
|
-
vi.useRealTimers();
|
|
597
|
-
});
|
|
598
|
-
it('does not reduce edge weight below 0', () => {
|
|
599
|
-
vi.useFakeTimers();
|
|
600
|
-
vi.setSystemTime(new Date('2026-01-01T00:00:00Z'));
|
|
601
|
-
const a = addNode('entity', 'A');
|
|
602
|
-
const b = addNode('entity', 'B');
|
|
603
|
-
addEdge(a.id, b.id, 'uses', 0.05);
|
|
604
|
-
vi.setSystemTime(new Date('2026-06-01T00:00:00Z'));
|
|
605
|
-
decayUnused(1);
|
|
606
|
-
expect(getGraph().edges[0].weight).toBe(0);
|
|
607
|
-
vi.useRealTimers();
|
|
608
|
-
});
|
|
609
|
-
});
|
|
610
|
-
// ─── prune ───────────────────────────────────────────────────────────────
|
|
611
|
-
describe('prune', () => {
|
|
612
|
-
it('removes edges below the weight threshold', () => {
|
|
613
|
-
const a = addNode('entity', 'A');
|
|
614
|
-
const b = addNode('entity', 'B');
|
|
615
|
-
const c = addNode('entity', 'C');
|
|
616
|
-
addEdge(a.id, b.id, 'strong', 0.9);
|
|
617
|
-
addEdge(a.id, c.id, 'weak', 0.1);
|
|
618
|
-
const result = prune(0.5);
|
|
619
|
-
expect(result.removedEdges).toBe(1);
|
|
620
|
-
expect(getGraph().edges).toHaveLength(1);
|
|
621
|
-
expect(getGraph().edges[0].relation).toBe('strong');
|
|
622
|
-
});
|
|
623
|
-
it('removes disconnected low-access nodes', () => {
|
|
624
|
-
addNode('entity', 'Connected');
|
|
625
|
-
const lonely = addNode('entity', 'Lonely');
|
|
626
|
-
const a = addNode('entity', 'A');
|
|
627
|
-
const firstNode = getGraph().nodes.values().next().value;
|
|
628
|
-
addEdge(a.id, firstNode.id, 'uses', 0.8);
|
|
629
|
-
// lonely has accessCount=1, effective weight = 0.1, below threshold 0.5
|
|
630
|
-
const result = prune(0.5);
|
|
631
|
-
expect(result.removedNodes).toBeGreaterThanOrEqual(0);
|
|
632
|
-
// lonely should be removed since it has no edges and low effective weight
|
|
633
|
-
const remaining = Array.from(getGraph().nodes.values()).map(n => n.name);
|
|
634
|
-
expect(remaining).not.toContain('Lonely');
|
|
635
|
-
});
|
|
636
|
-
it('keeps well-connected nodes even with low edge weight', () => {
|
|
637
|
-
const a = addNode('entity', 'WellConnected');
|
|
638
|
-
const b = addNode('entity', 'Partner');
|
|
639
|
-
addEdge(a.id, b.id, 'uses', 0.8);
|
|
640
|
-
prune(0.3);
|
|
641
|
-
expect(getGraph().nodes.has(a.id)).toBe(true);
|
|
642
|
-
expect(getGraph().nodes.has(b.id)).toBe(true);
|
|
643
|
-
});
|
|
644
|
-
it('returns correct counts', () => {
|
|
645
|
-
const a = addNode('entity', 'A');
|
|
646
|
-
const b = addNode('entity', 'B');
|
|
647
|
-
addEdge(a.id, b.id, 'weak', 0.1);
|
|
648
|
-
const result = prune(0.5);
|
|
649
|
-
expect(result.removedEdges).toBe(1);
|
|
650
|
-
// Both A and B become disconnected after edge removal
|
|
651
|
-
// Both have accessCount=1, effective weight=0.1, below 0.5
|
|
652
|
-
expect(result.removedNodes).toBe(2);
|
|
653
|
-
});
|
|
654
|
-
});
|
|
655
|
-
// ─── toContext ────────────────────────────────────────────────────────────
|
|
656
|
-
describe('toContext', () => {
|
|
657
|
-
it('returns "[No graph memory]" for empty graph', () => {
|
|
658
|
-
expect(toContext()).toBe('[No graph memory]');
|
|
659
|
-
});
|
|
660
|
-
it('includes header with node and edge counts', () => {
|
|
661
|
-
addNode('entity', 'JWT');
|
|
662
|
-
const result = toContext();
|
|
663
|
-
expect(result).toContain('[Graph Memory');
|
|
664
|
-
expect(result).toContain('1 nodes');
|
|
665
|
-
expect(result).toContain('0 edges');
|
|
666
|
-
});
|
|
667
|
-
it('formats edges as [type:name] --relation--> [type:name]', () => {
|
|
668
|
-
const a = addNode('file', 'auth.ts');
|
|
669
|
-
const b = addNode('entity', 'JWT');
|
|
670
|
-
addEdge(a.id, b.id, 'uses');
|
|
671
|
-
const result = toContext();
|
|
672
|
-
expect(result).toContain('[file:auth.ts] --uses--> [entity:JWT]');
|
|
673
|
-
});
|
|
674
|
-
it('includes isolated nodes with properties', () => {
|
|
675
|
-
addNode('decision', 'use RS256', { reason: 'security' });
|
|
676
|
-
const result = toContext();
|
|
677
|
-
expect(result).toContain('[decision:use RS256]');
|
|
678
|
-
expect(result).toContain('reason=security');
|
|
679
|
-
});
|
|
680
|
-
it('respects maxTokens budget', () => {
|
|
681
|
-
// Add many nodes and edges
|
|
682
|
-
const nodes = [];
|
|
683
|
-
for (let i = 0; i < 50; i++) {
|
|
684
|
-
nodes.push(addNode('entity', `entity-${i}-${'x'.repeat(50)}`));
|
|
685
|
-
}
|
|
686
|
-
for (let i = 1; i < 50; i++) {
|
|
687
|
-
addEdge(nodes[0].id, nodes[i].id, 'related_to');
|
|
688
|
-
}
|
|
689
|
-
// Very small token budget
|
|
690
|
-
const result = toContext(50);
|
|
691
|
-
// 50 tokens * 4 chars = 200 chars — should be truncated
|
|
692
|
-
// Result should be shorter than full graph
|
|
693
|
-
const fullResult = toContext(100000);
|
|
694
|
-
expect(result.length).toBeLessThan(fullResult.length);
|
|
695
|
-
});
|
|
696
|
-
});
|
|
697
|
-
// ─── extractEntities ─────────────────────────────────────────────────────
|
|
698
|
-
describe('extractEntities', () => {
|
|
699
|
-
it('extracts file paths as file nodes', () => {
|
|
700
|
-
const nodes = extractEntities('Check the file /src/auth.ts', 'I read /src/auth.ts and found issues.');
|
|
701
|
-
const fileNodes = nodes.filter(n => n.type === 'file');
|
|
702
|
-
expect(fileNodes.length).toBeGreaterThanOrEqual(1);
|
|
703
|
-
expect(fileNodes[0].properties.path).toBe('/src/auth.ts');
|
|
704
|
-
});
|
|
705
|
-
it('extracts GitHub issue references as bug nodes', () => {
|
|
706
|
-
const nodes = extractEntities('Fix kbot/kernel#42', 'Working on kbot/kernel#42 now.');
|
|
707
|
-
const bugNodes = nodes.filter(n => n.type === 'bug');
|
|
708
|
-
expect(bugNodes.length).toBeGreaterThanOrEqual(1);
|
|
709
|
-
});
|
|
710
|
-
it('extracts function/class names as entity nodes', () => {
|
|
711
|
-
const nodes = extractEntities('', 'The function processAuth handles authentication and class AuthManager orchestrates it.');
|
|
712
|
-
const entityNodes = nodes.filter(n => n.type === 'entity' && n.properties.kind === 'code-identifier');
|
|
713
|
-
const names = entityNodes.map(n => n.name);
|
|
714
|
-
expect(names).toContain('processAuth');
|
|
715
|
-
expect(names).toContain('AuthManager');
|
|
716
|
-
});
|
|
717
|
-
it('extracts decisions from conversation', () => {
|
|
718
|
-
const nodes = extractEntities("Let's use RS256 for JWT signing", "Good choice. We decided to go with TypeScript for the rewrite.");
|
|
719
|
-
const decisionNodes = nodes.filter(n => n.type === 'decision');
|
|
720
|
-
expect(decisionNodes.length).toBeGreaterThanOrEqual(1);
|
|
721
|
-
});
|
|
722
|
-
it('extracts patterns from conversation', () => {
|
|
723
|
-
const nodes = extractEntities('always validate user input before database calls', 'Yes, and never trust client-side data');
|
|
724
|
-
const patternNodes = nodes.filter(n => n.type === 'pattern');
|
|
725
|
-
expect(patternNodes.length).toBeGreaterThanOrEqual(1);
|
|
726
|
-
});
|
|
727
|
-
it('deduplicates file paths', () => {
|
|
728
|
-
const nodes = extractEntities('Read /src/auth.ts and /src/auth.ts', 'Also checking /src/auth.ts');
|
|
729
|
-
const fileNodes = nodes.filter(n => n.type === 'file' && n.properties.path === '/src/auth.ts');
|
|
730
|
-
// All references should map to the same node (duplicate detection in addNode)
|
|
731
|
-
const uniqueIds = new Set(fileNodes.map(n => n.id));
|
|
732
|
-
expect(uniqueIds.size).toBe(1);
|
|
733
|
-
});
|
|
734
|
-
it('returns empty array when no entities found', () => {
|
|
735
|
-
const nodes = extractEntities('Hello', 'Hi there');
|
|
736
|
-
// Might be empty or have minimal results depending on heuristics
|
|
737
|
-
// At minimum, should not throw
|
|
738
|
-
expect(Array.isArray(nodes)).toBe(true);
|
|
739
|
-
});
|
|
740
|
-
it('skips very short identifiers', () => {
|
|
741
|
-
const nodes = extractEntities('', 'const ab = 1; const validName = 2;');
|
|
742
|
-
// 'ab' is too short (<=2 chars), 'validName' should be captured
|
|
743
|
-
const entityNodes = nodes.filter(n => n.type === 'entity' && n.properties.kind === 'code-identifier');
|
|
744
|
-
const names = entityNodes.map(n => n.name);
|
|
745
|
-
expect(names).not.toContain('ab');
|
|
746
|
-
expect(names).toContain('validName');
|
|
747
|
-
});
|
|
748
|
-
});
|
|
749
|
-
// ─── autoConnect ─────────────────────────────────────────────────────────
|
|
750
|
-
describe('autoConnect', () => {
|
|
751
|
-
it('returns 0 for nonexistent node', () => {
|
|
752
|
-
expect(autoConnect('fake-id')).toBe(0);
|
|
753
|
-
});
|
|
754
|
-
it('connects nodes accessed at the same time', () => {
|
|
755
|
-
vi.useFakeTimers();
|
|
756
|
-
vi.setSystemTime(new Date('2026-03-29T12:00:00Z'));
|
|
757
|
-
const a = addNode('file', 'auth.ts');
|
|
758
|
-
const b = addNode('entity', 'JWT');
|
|
759
|
-
const connected = autoConnect(a.id);
|
|
760
|
-
expect(connected).toBeGreaterThanOrEqual(1);
|
|
761
|
-
expect(getGraph().edges.length).toBeGreaterThanOrEqual(1);
|
|
762
|
-
vi.useRealTimers();
|
|
763
|
-
});
|
|
764
|
-
it('infers relation type based on node types', () => {
|
|
765
|
-
vi.useFakeTimers();
|
|
766
|
-
vi.setSystemTime(new Date('2026-03-29T12:00:00Z'));
|
|
767
|
-
const file = addNode('file', 'auth.ts');
|
|
768
|
-
const entity = addNode('entity', 'JWT');
|
|
769
|
-
autoConnect(file.id);
|
|
770
|
-
const edges = getGraph().edges;
|
|
771
|
-
expect(edges.length).toBeGreaterThanOrEqual(1);
|
|
772
|
-
// file -> entity should be 'contains'
|
|
773
|
-
const relevant = edges.find(e => e.source === file.id && e.target === entity.id);
|
|
774
|
-
if (relevant) {
|
|
775
|
-
expect(relevant.relation).toBe('contains');
|
|
776
|
-
}
|
|
777
|
-
vi.useRealTimers();
|
|
778
|
-
});
|
|
779
|
-
it('does not connect node to itself', () => {
|
|
780
|
-
vi.useFakeTimers();
|
|
781
|
-
vi.setSystemTime(new Date('2026-03-29T12:00:00Z'));
|
|
782
|
-
const a = addNode('entity', 'Solo');
|
|
783
|
-
autoConnect(a.id);
|
|
784
|
-
const selfEdges = getGraph().edges.filter(e => e.source === a.id && e.target === a.id);
|
|
785
|
-
expect(selfEdges).toHaveLength(0);
|
|
786
|
-
vi.useRealTimers();
|
|
787
|
-
});
|
|
788
|
-
});
|
|
789
|
-
// ─── importFromMemory ────────────────────────────────────────────────────
|
|
790
|
-
describe('importFromMemory', () => {
|
|
791
|
-
it('returns zero counts when memory dir does not exist', () => {
|
|
792
|
-
mockedExistsSync.mockReturnValue(false);
|
|
793
|
-
const result = importFromMemory('/mock-home/.kbot/memory');
|
|
794
|
-
expect(result.imported).toBe(0);
|
|
795
|
-
expect(result.edges).toBe(0);
|
|
796
|
-
});
|
|
797
|
-
it('imports JSON files from category directories', () => {
|
|
798
|
-
mockedExistsSync.mockImplementation((p) => {
|
|
799
|
-
const path = String(p);
|
|
800
|
-
if (path === '/mock-home/.kbot/memory')
|
|
801
|
-
return true;
|
|
802
|
-
if (path === '/mock-home/.kbot/memory/fact')
|
|
803
|
-
return true;
|
|
804
|
-
if (path === '/mock-home/.kbot/memory/context.md')
|
|
805
|
-
return false;
|
|
806
|
-
return false;
|
|
807
|
-
});
|
|
808
|
-
mockedReaddirSync.mockReturnValue(['item1.json']);
|
|
809
|
-
mockedReadFileSync.mockReturnValue(JSON.stringify({
|
|
810
|
-
key: 'user-prefers-ts',
|
|
811
|
-
content: 'User prefers TypeScript',
|
|
812
|
-
created_at: '2026-01-01',
|
|
813
|
-
}));
|
|
814
|
-
const result = importFromMemory('/mock-home/.kbot/memory');
|
|
815
|
-
expect(result.imported).toBeGreaterThanOrEqual(1);
|
|
816
|
-
});
|
|
817
|
-
it('skips malformed JSON files without throwing', () => {
|
|
818
|
-
mockedExistsSync.mockImplementation((p) => {
|
|
819
|
-
const path = String(p);
|
|
820
|
-
if (path === '/mock-home/.kbot/memory')
|
|
821
|
-
return true;
|
|
822
|
-
if (path === '/mock-home/.kbot/memory/fact')
|
|
823
|
-
return true;
|
|
824
|
-
if (path === '/mock-home/.kbot/memory/context.md')
|
|
825
|
-
return false;
|
|
826
|
-
return false;
|
|
827
|
-
});
|
|
828
|
-
mockedReaddirSync.mockReturnValue(['bad.json']);
|
|
829
|
-
mockedReadFileSync.mockReturnValue('not json!!!');
|
|
830
|
-
// Should not throw
|
|
831
|
-
const result = importFromMemory('/mock-home/.kbot/memory');
|
|
832
|
-
expect(result.imported).toBe(0);
|
|
833
|
-
});
|
|
834
|
-
it('imports sections from context.md as concept nodes', () => {
|
|
835
|
-
mockedExistsSync.mockImplementation((p) => {
|
|
836
|
-
const path = String(p);
|
|
837
|
-
if (path === '/mock-home/.kbot/memory')
|
|
838
|
-
return true;
|
|
839
|
-
if (path === '/mock-home/.kbot/memory/context.md')
|
|
840
|
-
return true;
|
|
841
|
-
return false;
|
|
842
|
-
});
|
|
843
|
-
mockedReaddirSync.mockReturnValue([]);
|
|
844
|
-
mockedReadFileSync.mockReturnValue('# Memory\n\n## TypeScript Preferences\nUser likes TS.\n\n## Git Workflow\nPrefers rebase.\n');
|
|
845
|
-
const result = importFromMemory('/mock-home/.kbot/memory');
|
|
846
|
-
expect(result.imported).toBeGreaterThanOrEqual(2);
|
|
847
|
-
});
|
|
848
|
-
});
|
|
849
|
-
// ─── getStats ────────────────────────────────────────────────────────────
|
|
850
|
-
describe('getStats', () => {
|
|
851
|
-
it('returns zero counts for empty graph', () => {
|
|
852
|
-
const stats = getStats();
|
|
853
|
-
expect(stats.nodeCount).toBe(0);
|
|
854
|
-
expect(stats.edgeCount).toBe(0);
|
|
855
|
-
expect(stats.nodesByType).toEqual({});
|
|
856
|
-
expect(stats.topNodes).toEqual([]);
|
|
857
|
-
});
|
|
858
|
-
it('counts nodes and edges correctly', () => {
|
|
859
|
-
const a = addNode('entity', 'A');
|
|
860
|
-
const b = addNode('file', 'B');
|
|
861
|
-
addEdge(a.id, b.id, 'uses');
|
|
862
|
-
const stats = getStats();
|
|
863
|
-
expect(stats.nodeCount).toBe(2);
|
|
864
|
-
expect(stats.edgeCount).toBe(1);
|
|
865
|
-
});
|
|
866
|
-
it('groups nodes by type', () => {
|
|
867
|
-
addNode('entity', 'A');
|
|
868
|
-
addNode('entity', 'B');
|
|
869
|
-
addNode('file', 'C');
|
|
870
|
-
addNode('person', 'D');
|
|
871
|
-
const stats = getStats();
|
|
872
|
-
expect(stats.nodesByType.entity).toBe(2);
|
|
873
|
-
expect(stats.nodesByType.file).toBe(1);
|
|
874
|
-
expect(stats.nodesByType.person).toBe(1);
|
|
875
|
-
});
|
|
876
|
-
it('returns top nodes sorted by access count', () => {
|
|
877
|
-
const a = addNode('entity', 'Popular');
|
|
878
|
-
// Access it multiple times via duplicate addNode
|
|
879
|
-
addNode('entity', 'popular'); // accessCount = 2
|
|
880
|
-
addNode('entity', 'popular'); // accessCount = 3
|
|
881
|
-
addNode('entity', 'Unpopular');
|
|
882
|
-
const stats = getStats();
|
|
883
|
-
expect(stats.topNodes[0].name).toBe('Popular');
|
|
884
|
-
expect(stats.topNodes[0].accessCount).toBe(3);
|
|
885
|
-
});
|
|
886
|
-
it('limits topNodes to 10', () => {
|
|
887
|
-
for (let i = 0; i < 15; i++) {
|
|
888
|
-
addNode('entity', `Node-${i}`);
|
|
889
|
-
}
|
|
890
|
-
const stats = getStats();
|
|
891
|
-
expect(stats.topNodes.length).toBeLessThanOrEqual(10);
|
|
892
|
-
});
|
|
893
|
-
});
|
|
894
|
-
// ─── getNode / getGraph ──────────────────────────────────────────────────
|
|
895
|
-
describe('getNode / getGraph', () => {
|
|
896
|
-
it('getNode returns undefined for nonexistent ID', () => {
|
|
897
|
-
expect(getNode('nonexistent')).toBeUndefined();
|
|
898
|
-
});
|
|
899
|
-
it('getNode returns the node for a valid ID', () => {
|
|
900
|
-
const node = addNode('entity', 'Test');
|
|
901
|
-
expect(getNode(node.id)).toBe(node);
|
|
902
|
-
});
|
|
903
|
-
it('getGraph returns the live graph object', () => {
|
|
904
|
-
const graph = getGraph();
|
|
905
|
-
expect(graph).toBeDefined();
|
|
906
|
-
expect(graph.nodes).toBeInstanceOf(Map);
|
|
907
|
-
expect(Array.isArray(graph.edges)).toBe(true);
|
|
908
|
-
});
|
|
909
|
-
});
|
|
910
|
-
// ─── Edge cases ──────────────────────────────────────────────────────────
|
|
911
|
-
describe('edge cases', () => {
|
|
912
|
-
it('handles empty string name for addNode', () => {
|
|
913
|
-
const node = addNode('entity', '');
|
|
914
|
-
expect(node.name).toBe('');
|
|
915
|
-
expect(getGraph().nodes.size).toBe(1);
|
|
916
|
-
});
|
|
917
|
-
it('handles special characters in node names', () => {
|
|
918
|
-
const node = addNode('entity', 'auth.ts (v2) [deprecated]');
|
|
919
|
-
expect(node.name).toBe('auth.ts (v2) [deprecated]');
|
|
920
|
-
});
|
|
921
|
-
it('handles unicode in node names', () => {
|
|
922
|
-
const node = addNode('person', 'Isaac Hernandez');
|
|
923
|
-
const results = findNode('Isaac');
|
|
924
|
-
expect(results).toHaveLength(1);
|
|
925
|
-
});
|
|
926
|
-
it('handles empty properties object', () => {
|
|
927
|
-
const node = addNode('entity', 'Test', {});
|
|
928
|
-
expect(node.properties).toEqual({});
|
|
929
|
-
});
|
|
930
|
-
it('handles empty relation string on edge', () => {
|
|
931
|
-
const a = addNode('entity', 'A');
|
|
932
|
-
const b = addNode('entity', 'B');
|
|
933
|
-
const result = addEdge(a.id, b.id, '');
|
|
934
|
-
expect(result).toBe(true);
|
|
935
|
-
expect(getGraph().edges[0].relation).toBe('');
|
|
936
|
-
});
|
|
937
|
-
it('addEdge between the same node twice (self-loop)', () => {
|
|
938
|
-
const a = addNode('entity', 'A');
|
|
939
|
-
// Source and target are the same
|
|
940
|
-
const result = addEdge(a.id, a.id, 'self-reference');
|
|
941
|
-
expect(result).toBe(true);
|
|
942
|
-
expect(getGraph().edges).toHaveLength(1);
|
|
943
|
-
expect(getGraph().edges[0].source).toBe(getGraph().edges[0].target);
|
|
944
|
-
});
|
|
945
|
-
});
|
|
946
|
-
//# sourceMappingURL=graph-memory.test.js.map
|