@vellumai/assistant 0.6.0 → 0.6.2
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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +68 -15
- package/Dockerfile +2 -2
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +42 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/openapi.yaml +539 -4
- package/package.json +5 -1
- package/src/__tests__/anthropic-provider.test.ts +160 -95
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/checker.test.ts +138 -172
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-schema.test.ts +5 -0
- package/src/__tests__/context-overflow-approval.test.ts +5 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
- package/src/__tests__/conversation-agent-loop.test.ts +4 -51
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-wipe.test.ts +2 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +0 -2
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/injection-block.test.ts +24 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/install-skill-routing.test.ts +7 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +101 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
- package/src/__tests__/log-export-workspace.test.ts +257 -100
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-recall-log-store.test.ts +132 -0
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +63 -14
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +0 -2
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +4 -3
- package/src/__tests__/send-endpoint-busy.test.ts +42 -3
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
- package/src/__tests__/skill-memory.test.ts +2 -783
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-memory-cleanup.test.ts +12 -12
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +16 -29
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +4 -27
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/transport-hints-queue.test.ts +77 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/agent/loop.ts +6 -29
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/channels/types.ts +5 -0
- package/src/cli/__tests__/run-assistant-command.ts +56 -0
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +1 -34
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +27 -0
- package/src/cli/commands/oauth/connect.ts +25 -5
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +11 -2
- package/src/cli.ts +1 -120
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +91 -5
- package/src/config/bundled-skills/gmail/SKILL.md +13 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +7 -0
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/env-registry.ts +63 -0
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +85 -3
- package/src/daemon/context-overflow-approval.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +179 -65
- package/src/daemon/conversation-attachments.ts +0 -1
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +30 -8
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +359 -308
- package/src/daemon/conversation-surfaces.ts +65 -0
- package/src/daemon/conversation-tool-setup.ts +44 -17
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +19 -3
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +5 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +70 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +220 -158
- package/src/daemon/message-types/conversations.ts +29 -6
- package/src/daemon/message-types/messages.ts +9 -2
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +18 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +87 -10
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- package/src/daemon/transport-hints.ts +33 -0
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +96 -7
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +33 -32
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +151 -117
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +66 -6
- package/src/memory/conversation-queries.ts +58 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +182 -376
- package/src/memory/embedding-local.ts +1 -1
- package/src/memory/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -17
- package/src/memory/graph/consolidation.ts +38 -4
- package/src/memory/graph/conversation-graph-memory.ts +133 -104
- package/src/memory/graph/extraction-job.ts +9 -4
- package/src/memory/graph/extraction.ts +66 -23
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +29 -15
- package/src/memory/graph/injection.ts +38 -8
- package/src/memory/graph/inspect.ts +12 -3
- package/src/memory/graph/retriever.ts +365 -262
- package/src/memory/graph/store.test.ts +48 -0
- package/src/memory/graph/store.ts +150 -11
- package/src/memory/graph/tool-handlers.ts +84 -209
- package/src/memory/graph/tools.ts +8 -52
- package/src/memory/graph/types.ts +24 -0
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/job-handlers/cleanup.ts +44 -1
- package/src/memory/jobs-store.ts +70 -60
- package/src/memory/jobs-worker.ts +44 -28
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +15 -0
- package/src/memory/task-memory-cleanup.ts +30 -11
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/platform-connection.test.ts +2 -2
- package/src/oauth/seed-providers.ts +1 -0
- package/src/permissions/checker.ts +15 -4
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/prompter.ts +0 -2
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +76 -162
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +30 -9
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/assistant-event-hub.ts +22 -0
- package/src/runtime/auth/route-policy.ts +23 -0
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/http-server.ts +32 -2
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/btw-routes.ts +14 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +185 -0
- package/src/runtime/routes/conversation-management-routes.ts +1 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +270 -44
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +41 -278
- package/src/runtime/routes/memory-item-routes.test.ts +168 -233
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +27 -12
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +45 -18
- package/src/skills/catalog-install.ts +10 -2
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/skills/managed-store.ts +2 -2
- package/src/skills/skill-memory.ts +1 -293
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +4 -4
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/permission-checker.ts +78 -18
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/secret-detection-handler.ts +0 -1
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +16 -1
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +3 -5
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -3
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +85 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -377
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/cli/cli-memory.ts +0 -179
- package/src/util/clipboard.ts +0 -34
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
createEdge,
|
|
8
8
|
createNode,
|
|
9
9
|
createTrigger,
|
|
10
|
+
deduplicateParagraphs,
|
|
10
11
|
deleteEdge,
|
|
11
12
|
deleteNode,
|
|
12
13
|
deleteTrigger,
|
|
@@ -1048,3 +1049,50 @@ describe("updateNode event trigger sync", () => {
|
|
|
1048
1049
|
expect(triggers[0].eventDate).toBe(eventDate);
|
|
1049
1050
|
});
|
|
1050
1051
|
});
|
|
1052
|
+
|
|
1053
|
+
// ---------------------------------------------------------------------------
|
|
1054
|
+
// Paragraph deduplication
|
|
1055
|
+
// ---------------------------------------------------------------------------
|
|
1056
|
+
|
|
1057
|
+
describe("deduplicateParagraphs", () => {
|
|
1058
|
+
test("content with no duplicates passes through unchanged", () => {
|
|
1059
|
+
const input = "First paragraph.\n\nSecond paragraph.\n\nThird paragraph.";
|
|
1060
|
+
expect(deduplicateParagraphs(input)).toBe(input);
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
test("two identical paragraphs separated by \\n\\n collapses to one", () => {
|
|
1064
|
+
const input = "Hello world.\n\nHello world.";
|
|
1065
|
+
expect(deduplicateParagraphs(input)).toBe("Hello world.");
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
test("paragraphs that differ only in trailing whitespace are treated as duplicates", () => {
|
|
1069
|
+
const input = "Hello world. \n\nHello world.";
|
|
1070
|
+
expect(deduplicateParagraphs(input)).toBe("Hello world. ");
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
test("bullet lists with repeated items are deduped", () => {
|
|
1074
|
+
const input = "- item one\n- item two\n- item one\n- item three";
|
|
1075
|
+
expect(deduplicateParagraphs(input)).toBe(
|
|
1076
|
+
"- item one\n- item two\n- item three",
|
|
1077
|
+
);
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
test("empty content returns empty string", () => {
|
|
1081
|
+
expect(deduplicateParagraphs("")).toBe("");
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
test("multiple duplicate paragraphs with different content", () => {
|
|
1085
|
+
const input = "Alpha.\n\nBeta.\n\nAlpha.\n\nGamma.\n\nBeta.";
|
|
1086
|
+
expect(deduplicateParagraphs(input)).toBe("Alpha.\n\nBeta.\n\nGamma.");
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
test("bullet dedup within a paragraph preserves non-bullet lines", () => {
|
|
1090
|
+
const input = "Header:\n- item A\n- item B\n- item A";
|
|
1091
|
+
expect(deduplicateParagraphs(input)).toBe("Header:\n- item A\n- item B");
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
test("paragraphs differing only in internal whitespace are treated as duplicates", () => {
|
|
1095
|
+
const input = "hello world\n\nhello world";
|
|
1096
|
+
expect(deduplicateParagraphs(input)).toBe("hello world");
|
|
1097
|
+
});
|
|
1098
|
+
});
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// Memory Graph — Data access layer
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
-
import { and, eq, inArray, sql } from "drizzle-orm";
|
|
5
|
+
import { and, desc, eq, inArray, sql } from "drizzle-orm";
|
|
6
6
|
import { v4 as uuid } from "uuid";
|
|
7
7
|
|
|
8
8
|
import { getDb } from "../db.js";
|
|
9
9
|
import {
|
|
10
10
|
memoryGraphEdges,
|
|
11
|
+
memoryGraphNodeEdits,
|
|
11
12
|
memoryGraphNodes,
|
|
12
13
|
memoryGraphTriggers,
|
|
13
14
|
} from "../schema.js";
|
|
@@ -115,6 +116,60 @@ function rowToTrigger(
|
|
|
115
116
|
};
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Paragraph deduplication
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Remove repeated paragraphs and bullet items from memory node content.
|
|
125
|
+
* Paragraphs are separated by `\n\n`. Within each paragraph, lines starting
|
|
126
|
+
* with `- ` are treated as bullet items and individually deduplicated.
|
|
127
|
+
*/
|
|
128
|
+
export function deduplicateParagraphs(content: string): string {
|
|
129
|
+
if (!content) return content;
|
|
130
|
+
|
|
131
|
+
const paragraphs = content.split("\n\n");
|
|
132
|
+
const seen = new Set<string>();
|
|
133
|
+
const unique: string[] = [];
|
|
134
|
+
|
|
135
|
+
for (const paragraph of paragraphs) {
|
|
136
|
+
// Deduplicate bullet items within the paragraph
|
|
137
|
+
const lines = paragraph.split("\n");
|
|
138
|
+
const isBulletList = lines.some((l) => l.trimStart().startsWith("- "));
|
|
139
|
+
|
|
140
|
+
let processed: string;
|
|
141
|
+
if (isBulletList) {
|
|
142
|
+
const seenBullets = new Set<string>();
|
|
143
|
+
const uniqueLines: string[] = [];
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
if (line.trimStart().startsWith("- ")) {
|
|
146
|
+
const normalized = line.trim().replace(/\s+/g, " ");
|
|
147
|
+
if (!seenBullets.has(normalized)) {
|
|
148
|
+
seenBullets.add(normalized);
|
|
149
|
+
uniqueLines.push(line);
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
uniqueLines.push(line);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
processed = uniqueLines.join("\n");
|
|
156
|
+
} else {
|
|
157
|
+
processed = paragraph;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const normalized = processed.trim().replace(/\s+/g, " ");
|
|
161
|
+
if (normalized === "") {
|
|
162
|
+
// Preserve empty paragraphs (whitespace-only) as separators
|
|
163
|
+
unique.push(processed);
|
|
164
|
+
} else if (!seen.has(normalized)) {
|
|
165
|
+
seen.add(normalized);
|
|
166
|
+
unique.push(processed);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return unique.join("\n\n");
|
|
171
|
+
}
|
|
172
|
+
|
|
118
173
|
// ---------------------------------------------------------------------------
|
|
119
174
|
// Node CRUD
|
|
120
175
|
// ---------------------------------------------------------------------------
|
|
@@ -122,8 +177,11 @@ function rowToTrigger(
|
|
|
122
177
|
export function createNode(node: NewNode): MemoryNode {
|
|
123
178
|
const db = getDb();
|
|
124
179
|
const id = uuid();
|
|
125
|
-
|
|
126
|
-
|
|
180
|
+
const cleanContent = deduplicateParagraphs(node.content);
|
|
181
|
+
db.insert(memoryGraphNodes)
|
|
182
|
+
.values(nodeToInsertValues({ ...node, content: cleanContent }, id))
|
|
183
|
+
.run();
|
|
184
|
+
return { ...node, content: cleanContent, id };
|
|
127
185
|
}
|
|
128
186
|
|
|
129
187
|
export function getNode(id: string): MemoryNode | null {
|
|
@@ -154,7 +212,8 @@ export function updateNode(
|
|
|
154
212
|
const db = getDb();
|
|
155
213
|
const updates: Record<string, unknown> = {};
|
|
156
214
|
|
|
157
|
-
if (changes.content !== undefined)
|
|
215
|
+
if (changes.content !== undefined)
|
|
216
|
+
updates.content = deduplicateParagraphs(changes.content);
|
|
158
217
|
if (changes.type !== undefined) updates.type = changes.type;
|
|
159
218
|
if (changes.created !== undefined) updates.created = changes.created;
|
|
160
219
|
if (changes.lastAccessed !== undefined)
|
|
@@ -180,7 +239,9 @@ export function updateNode(
|
|
|
180
239
|
if (changes.partOfStory !== undefined)
|
|
181
240
|
updates.partOfStory = changes.partOfStory;
|
|
182
241
|
if (changes.imageRefs !== undefined)
|
|
183
|
-
updates.imageRefs = changes.imageRefs
|
|
242
|
+
updates.imageRefs = changes.imageRefs
|
|
243
|
+
? JSON.stringify(changes.imageRefs)
|
|
244
|
+
: null;
|
|
184
245
|
if (changes.scopeId !== undefined) updates.scopeId = changes.scopeId;
|
|
185
246
|
if (changes.eventDate !== undefined) updates.eventDate = changes.eventDate;
|
|
186
247
|
|
|
@@ -382,9 +443,7 @@ export function createTrigger(trigger: NewTrigger): MemoryTrigger {
|
|
|
382
443
|
|
|
383
444
|
export function deleteTrigger(id: string): void {
|
|
384
445
|
const db = getDb();
|
|
385
|
-
db.delete(memoryGraphTriggers)
|
|
386
|
-
.where(eq(memoryGraphTriggers.id, id))
|
|
387
|
-
.run();
|
|
446
|
+
db.delete(memoryGraphTriggers).where(eq(memoryGraphTriggers.id, id)).run();
|
|
388
447
|
}
|
|
389
448
|
|
|
390
449
|
export function updateTrigger(
|
|
@@ -534,7 +593,13 @@ export function supersedeNode(
|
|
|
534
593
|
/**
|
|
535
594
|
* Apply a MemoryDiff atomically. All operations run in a single transaction.
|
|
536
595
|
*/
|
|
537
|
-
export function applyDiff(
|
|
596
|
+
export function applyDiff(
|
|
597
|
+
diff: MemoryDiff,
|
|
598
|
+
opts?: {
|
|
599
|
+
conversationId?: string;
|
|
600
|
+
source?: "extraction" | "consolidation" | "manual";
|
|
601
|
+
},
|
|
602
|
+
): ApplyDiffResult {
|
|
538
603
|
const db = getDb();
|
|
539
604
|
const result: ApplyDiffResult = {
|
|
540
605
|
nodesCreated: 0,
|
|
@@ -558,7 +623,10 @@ export function applyDiff(diff: MemoryDiff): ApplyDiffResult {
|
|
|
558
623
|
// Create nodes
|
|
559
624
|
for (const node of diff.createNodes) {
|
|
560
625
|
const id = uuid();
|
|
561
|
-
|
|
626
|
+
const cleanContent = deduplicateParagraphs(node.content);
|
|
627
|
+
tx.insert(memoryGraphNodes)
|
|
628
|
+
.values(nodeToInsertValues({ ...node, content: cleanContent }, id))
|
|
629
|
+
.run();
|
|
562
630
|
result.nodesCreated++;
|
|
563
631
|
result.createdNodeIds.push(id);
|
|
564
632
|
}
|
|
@@ -567,7 +635,8 @@ export function applyDiff(diff: MemoryDiff): ApplyDiffResult {
|
|
|
567
635
|
for (const update of diff.updateNodes) {
|
|
568
636
|
const updates: Record<string, unknown> = {};
|
|
569
637
|
const c = update.changes;
|
|
570
|
-
if (c.content !== undefined)
|
|
638
|
+
if (c.content !== undefined)
|
|
639
|
+
updates.content = deduplicateParagraphs(c.content as string);
|
|
571
640
|
if (c.type !== undefined) updates.type = c.type;
|
|
572
641
|
if (c.emotionalCharge !== undefined)
|
|
573
642
|
updates.emotionalCharge = JSON.stringify(c.emotionalCharge);
|
|
@@ -584,6 +653,28 @@ export function applyDiff(diff: MemoryDiff): ApplyDiffResult {
|
|
|
584
653
|
updates.sourceConversations = JSON.stringify(c.sourceConversations);
|
|
585
654
|
if (c.eventDate !== undefined) updates.eventDate = c.eventDate;
|
|
586
655
|
|
|
656
|
+
// Record edit history when content changes
|
|
657
|
+
if (updates.content !== undefined) {
|
|
658
|
+
const current = tx
|
|
659
|
+
.select({ content: memoryGraphNodes.content })
|
|
660
|
+
.from(memoryGraphNodes)
|
|
661
|
+
.where(eq(memoryGraphNodes.id, update.id))
|
|
662
|
+
.get();
|
|
663
|
+
if (current && current.content !== updates.content) {
|
|
664
|
+
tx.insert(memoryGraphNodeEdits)
|
|
665
|
+
.values({
|
|
666
|
+
id: uuid(),
|
|
667
|
+
nodeId: update.id,
|
|
668
|
+
previousContent: current.content,
|
|
669
|
+
newContent: updates.content as string,
|
|
670
|
+
source: opts?.source ?? "extraction",
|
|
671
|
+
conversationId: opts?.conversationId ?? null,
|
|
672
|
+
created: Date.now(),
|
|
673
|
+
})
|
|
674
|
+
.run();
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
587
678
|
if (Object.keys(updates).length > 0) {
|
|
588
679
|
tx.update(memoryGraphNodes)
|
|
589
680
|
.set(updates)
|
|
@@ -697,3 +788,51 @@ export function applyDiff(diff: MemoryDiff): ApplyDiffResult {
|
|
|
697
788
|
|
|
698
789
|
return result;
|
|
699
790
|
}
|
|
791
|
+
|
|
792
|
+
// ---------------------------------------------------------------------------
|
|
793
|
+
// Node edit history
|
|
794
|
+
// ---------------------------------------------------------------------------
|
|
795
|
+
|
|
796
|
+
/** Record a content change to a memory node for edit chain tracking. */
|
|
797
|
+
export function recordNodeEdit(opts: {
|
|
798
|
+
nodeId: string;
|
|
799
|
+
previousContent: string;
|
|
800
|
+
newContent: string;
|
|
801
|
+
source: "extraction" | "consolidation" | "manual";
|
|
802
|
+
conversationId?: string;
|
|
803
|
+
}): void {
|
|
804
|
+
const db = getDb();
|
|
805
|
+
db.insert(memoryGraphNodeEdits)
|
|
806
|
+
.values({
|
|
807
|
+
id: uuid(),
|
|
808
|
+
nodeId: opts.nodeId,
|
|
809
|
+
previousContent: opts.previousContent,
|
|
810
|
+
newContent: opts.newContent,
|
|
811
|
+
source: opts.source,
|
|
812
|
+
conversationId: opts.conversationId ?? null,
|
|
813
|
+
created: Date.now(),
|
|
814
|
+
})
|
|
815
|
+
.run();
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/** Retrieve the edit history for a memory node, newest first. */
|
|
819
|
+
export function getNodeEditHistory(
|
|
820
|
+
nodeId: string,
|
|
821
|
+
limit = 20,
|
|
822
|
+
): Array<{
|
|
823
|
+
id: string;
|
|
824
|
+
previousContent: string;
|
|
825
|
+
newContent: string;
|
|
826
|
+
source: string;
|
|
827
|
+
conversationId: string | null;
|
|
828
|
+
created: number;
|
|
829
|
+
}> {
|
|
830
|
+
const db = getDb();
|
|
831
|
+
return db
|
|
832
|
+
.select()
|
|
833
|
+
.from(memoryGraphNodeEdits)
|
|
834
|
+
.where(eq(memoryGraphNodeEdits.nodeId, nodeId))
|
|
835
|
+
.orderBy(desc(memoryGraphNodeEdits.created))
|
|
836
|
+
.limit(limit)
|
|
837
|
+
.all();
|
|
838
|
+
}
|
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// Memory Graph — Tool handlers for recall and remember
|
|
3
3
|
//
|
|
4
|
-
// These are the implementations behind the recall/remember tool definitions.
|
|
5
4
|
// recall: search the living graph or raw archive
|
|
6
|
-
// remember:
|
|
5
|
+
// remember: save facts to the PKB (buffer.md + daily archive)
|
|
7
6
|
// ---------------------------------------------------------------------------
|
|
8
7
|
|
|
8
|
+
import { appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
9
11
|
import type { AssistantConfig } from "../../config/types.js";
|
|
10
12
|
import { getLogger } from "../../util/logger.js";
|
|
13
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
11
14
|
import { buildExcerpt, buildFtsMatchQuery } from "../conversation-queries.js";
|
|
12
15
|
import { embedWithRetry } from "../embed.js";
|
|
13
16
|
import { generateSparseEmbedding } from "../embedding-backend.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
createNode,
|
|
17
|
-
deleteNode,
|
|
18
|
-
getNode,
|
|
19
|
-
getNodesByIds,
|
|
20
|
-
updateNode,
|
|
21
|
-
} from "./store.js";
|
|
22
|
-
import type {
|
|
23
|
-
DecayCurve,
|
|
24
|
-
EmotionalCharge,
|
|
25
|
-
Fidelity,
|
|
26
|
-
MemoryType,
|
|
27
|
-
NewNode,
|
|
28
|
-
SourceType,
|
|
29
|
-
} from "./types.js";
|
|
17
|
+
import { searchGraphNodes } from "./graph-search.js";
|
|
18
|
+
import { getNodesByIds } from "./store.js";
|
|
30
19
|
|
|
31
20
|
const log = getLogger("graph-tool-handlers");
|
|
32
21
|
|
|
@@ -95,6 +84,17 @@ async function handleMemoryRecall(
|
|
|
95
84
|
// Generate sparse embedding for hybrid search (dense + sparse with RRF fusion)
|
|
96
85
|
const sparseVector = generateSparseEmbedding(input.query);
|
|
97
86
|
|
|
87
|
+
// Build date range filter for Qdrant-level filtering
|
|
88
|
+
const dateRange: { afterMs?: number; beforeMs?: number } = {};
|
|
89
|
+
if (input.filters?.after) {
|
|
90
|
+
const afterMs = new Date(input.filters.after).getTime();
|
|
91
|
+
if (!isNaN(afterMs)) dateRange.afterMs = afterMs;
|
|
92
|
+
}
|
|
93
|
+
if (input.filters?.before) {
|
|
94
|
+
const beforeMs = new Date(input.filters.before).getTime();
|
|
95
|
+
if (!isNaN(beforeMs)) dateRange.beforeMs = beforeMs;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
98
|
// Search graph nodes
|
|
99
99
|
const limit = Math.max(1, Math.min(input.num_results ?? 20, 50));
|
|
100
100
|
const searchResults = await searchGraphNodes(
|
|
@@ -102,6 +102,9 @@ async function handleMemoryRecall(
|
|
|
102
102
|
limit,
|
|
103
103
|
[scopeId],
|
|
104
104
|
sparseVector,
|
|
105
|
+
dateRange.afterMs != null || dateRange.beforeMs != null
|
|
106
|
+
? dateRange
|
|
107
|
+
: undefined,
|
|
105
108
|
);
|
|
106
109
|
if (searchResults.length === 0) {
|
|
107
110
|
return { results: [], mode: "memory", query: input.query };
|
|
@@ -121,16 +124,6 @@ async function handleMemoryRecall(
|
|
|
121
124
|
if (!input.filters.types.includes(node.type)) return [];
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
// Date filters
|
|
125
|
-
if (input.filters?.after) {
|
|
126
|
-
const afterMs = new Date(input.filters.after).getTime();
|
|
127
|
-
if (!isNaN(afterMs) && node.created < afterMs) return [];
|
|
128
|
-
}
|
|
129
|
-
if (input.filters?.before) {
|
|
130
|
-
const beforeMs = new Date(input.filters.before).getTime();
|
|
131
|
-
if (!isNaN(beforeMs) && node.created > beforeMs) return [];
|
|
132
|
-
}
|
|
133
|
-
|
|
134
127
|
return [
|
|
135
128
|
{
|
|
136
129
|
id: node.id,
|
|
@@ -157,7 +150,28 @@ async function handleArchiveRecall(
|
|
|
157
150
|
|
|
158
151
|
try {
|
|
159
152
|
const limit = Math.max(1, Math.min(input.num_results ?? 20, 50));
|
|
160
|
-
const ftsMatch = buildFtsMatchQuery(input.query.trim()
|
|
153
|
+
const ftsMatch = buildFtsMatchQuery(input.query.trim(), {
|
|
154
|
+
allowFts5Syntax: true,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const afterMs = input.filters?.after
|
|
158
|
+
? new Date(input.filters.after).getTime()
|
|
159
|
+
: NaN;
|
|
160
|
+
const beforeMs = input.filters?.before
|
|
161
|
+
? new Date(input.filters.before).getTime()
|
|
162
|
+
: NaN;
|
|
163
|
+
const dateConditions: string[] = [];
|
|
164
|
+
const dateParams: number[] = [];
|
|
165
|
+
if (!isNaN(afterMs)) {
|
|
166
|
+
dateConditions.push("m.created_at >= ?");
|
|
167
|
+
dateParams.push(afterMs);
|
|
168
|
+
}
|
|
169
|
+
if (!isNaN(beforeMs)) {
|
|
170
|
+
dateConditions.push("m.created_at <= ?");
|
|
171
|
+
dateParams.push(beforeMs);
|
|
172
|
+
}
|
|
173
|
+
const dateClause =
|
|
174
|
+
dateConditions.length > 0 ? " AND " + dateConditions.join(" AND ") : "";
|
|
161
175
|
|
|
162
176
|
type ArchiveRow = {
|
|
163
177
|
id: string;
|
|
@@ -177,11 +191,12 @@ async function handleArchiveRecall(
|
|
|
177
191
|
JOIN messages m ON m.id = fts.message_id
|
|
178
192
|
JOIN conversations c ON c.id = m.conversation_id
|
|
179
193
|
WHERE messages_fts MATCH ?
|
|
180
|
-
AND c.memory_scope_id =
|
|
194
|
+
AND c.memory_scope_id = ?${dateClause}
|
|
181
195
|
ORDER BY rank
|
|
182
196
|
LIMIT ?`,
|
|
183
197
|
ftsMatch,
|
|
184
198
|
scopeId,
|
|
199
|
+
...dateParams,
|
|
185
200
|
limit,
|
|
186
201
|
);
|
|
187
202
|
} else if (!input.query.trim()) {
|
|
@@ -199,11 +214,12 @@ async function handleArchiveRecall(
|
|
|
199
214
|
`SELECT m.id, m.content, m.role, m.created_at, c.id as conversation_id
|
|
200
215
|
FROM messages m
|
|
201
216
|
JOIN conversations c ON c.id = m.conversation_id
|
|
202
|
-
WHERE m.content LIKE ? ESCAPE '\\' AND c.memory_scope_id =
|
|
217
|
+
WHERE m.content LIKE ? ESCAPE '\\' AND c.memory_scope_id = ?${dateClause}
|
|
203
218
|
ORDER BY m.created_at DESC
|
|
204
219
|
LIMIT ?`,
|
|
205
220
|
likePattern,
|
|
206
221
|
scopeId,
|
|
222
|
+
...dateParams,
|
|
207
223
|
limit,
|
|
208
224
|
);
|
|
209
225
|
}
|
|
@@ -228,199 +244,58 @@ async function handleArchiveRecall(
|
|
|
228
244
|
}
|
|
229
245
|
|
|
230
246
|
// ---------------------------------------------------------------------------
|
|
231
|
-
// remember handler
|
|
247
|
+
// remember handler — writes to PKB buffer + daily archive
|
|
232
248
|
// ---------------------------------------------------------------------------
|
|
233
249
|
|
|
234
250
|
export interface RememberInput {
|
|
235
|
-
|
|
236
|
-
memory_id?: string;
|
|
237
|
-
content?: string;
|
|
238
|
-
type?: string;
|
|
239
|
-
significance?: number;
|
|
240
|
-
emotional_charge?: {
|
|
241
|
-
valence?: number;
|
|
242
|
-
intensity?: number;
|
|
243
|
-
};
|
|
251
|
+
content: string;
|
|
244
252
|
}
|
|
245
253
|
|
|
246
254
|
export interface RememberResult {
|
|
247
255
|
success: boolean;
|
|
248
|
-
op: string;
|
|
249
|
-
memory_id?: string;
|
|
250
256
|
message: string;
|
|
251
257
|
}
|
|
252
258
|
|
|
253
|
-
const VALID_TYPES = new Set<MemoryType>([
|
|
254
|
-
"episodic",
|
|
255
|
-
"semantic",
|
|
256
|
-
"procedural",
|
|
257
|
-
"emotional",
|
|
258
|
-
"prospective",
|
|
259
|
-
"behavioral",
|
|
260
|
-
"narrative",
|
|
261
|
-
"shared",
|
|
262
|
-
]);
|
|
263
|
-
|
|
264
259
|
export function handleRemember(
|
|
265
260
|
input: RememberInput,
|
|
266
|
-
|
|
267
|
-
|
|
261
|
+
_conversationId: string,
|
|
262
|
+
_scopeId: string,
|
|
268
263
|
): RememberResult {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
return handleSave(input, conversationId, scopeId);
|
|
272
|
-
case "update":
|
|
273
|
-
return handleUpdate(input);
|
|
274
|
-
case "delete":
|
|
275
|
-
return handleDelete(input);
|
|
276
|
-
default:
|
|
277
|
-
return {
|
|
278
|
-
success: false,
|
|
279
|
-
op: input.op,
|
|
280
|
-
message: `Unknown operation: ${input.op}`,
|
|
281
|
-
};
|
|
264
|
+
if (!input.content || input.content.trim().length === 0) {
|
|
265
|
+
return { success: false, message: "content is required" };
|
|
282
266
|
}
|
|
283
|
-
}
|
|
284
267
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
268
|
+
const workspaceDir = getWorkspaceDir();
|
|
269
|
+
const pkbDir = join(workspaceDir, "pkb");
|
|
270
|
+
const archiveDir = join(pkbDir, "archive");
|
|
271
|
+
|
|
272
|
+
// Ensure directories exist
|
|
273
|
+
mkdirSync(pkbDir, { recursive: true });
|
|
274
|
+
mkdirSync(archiveDir, { recursive: true });
|
|
275
|
+
|
|
276
|
+
// Build timestamped entry
|
|
277
|
+
const now = new Date();
|
|
278
|
+
const month = now.toLocaleString("en-US", { month: "short" });
|
|
279
|
+
const day = now.getDate();
|
|
280
|
+
const hours = now.getHours();
|
|
281
|
+
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
282
|
+
const ampm = hours >= 12 ? "PM" : "AM";
|
|
283
|
+
const displayHour = hours % 12 || 12;
|
|
284
|
+
const entry = `- [${month} ${day}, ${displayHour}:${minutes} ${ampm}] ${input.content.trim()}\n`;
|
|
285
|
+
|
|
286
|
+
// Append to buffer.md
|
|
287
|
+
const bufferPath = join(pkbDir, "buffer.md");
|
|
288
|
+
appendFileSync(bufferPath, entry, "utf-8");
|
|
289
|
+
|
|
290
|
+
// Append to daily archive
|
|
291
|
+
const yyyy = now.getFullYear();
|
|
292
|
+
const mm = String(now.getMonth() + 1).padStart(2, "0");
|
|
293
|
+
const dd = String(now.getDate()).padStart(2, "0");
|
|
294
|
+
const archivePath = join(archiveDir, `${yyyy}-${mm}-${dd}.md`);
|
|
295
|
+
if (!existsSync(archivePath)) {
|
|
296
|
+
appendFileSync(archivePath, `# ${month} ${day}, ${yyyy}\n\n`, "utf-8");
|
|
303
297
|
}
|
|
298
|
+
appendFileSync(archivePath, entry, "utf-8");
|
|
304
299
|
|
|
305
|
-
|
|
306
|
-
const emotionalCharge: EmotionalCharge = {
|
|
307
|
-
valence: clamp(input.emotional_charge?.valence ?? 0, -1, 1),
|
|
308
|
-
intensity: clamp(input.emotional_charge?.intensity ?? 0, 0, 1),
|
|
309
|
-
decayCurve: "linear" as DecayCurve,
|
|
310
|
-
decayRate: 0.05,
|
|
311
|
-
originalIntensity: clamp(input.emotional_charge?.intensity ?? 0, 0, 1),
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const node: NewNode = {
|
|
315
|
-
content: input.content,
|
|
316
|
-
type: input.type as MemoryType,
|
|
317
|
-
created: now,
|
|
318
|
-
lastAccessed: now,
|
|
319
|
-
lastConsolidated: now,
|
|
320
|
-
eventDate: null,
|
|
321
|
-
emotionalCharge,
|
|
322
|
-
fidelity: "vivid" as Fidelity,
|
|
323
|
-
confidence: 0.95, // Explicitly saved = high confidence
|
|
324
|
-
significance: clamp(input.significance ?? 0.5, 0, 1),
|
|
325
|
-
stability: 14,
|
|
326
|
-
reinforcementCount: 0,
|
|
327
|
-
lastReinforced: now,
|
|
328
|
-
sourceConversations: [conversationId],
|
|
329
|
-
sourceType: "direct" as SourceType,
|
|
330
|
-
narrativeRole: null,
|
|
331
|
-
partOfStory: null,
|
|
332
|
-
imageRefs: null,
|
|
333
|
-
scopeId,
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
const created = createNode(node);
|
|
337
|
-
|
|
338
|
-
// Enqueue embedding job immediately
|
|
339
|
-
enqueueGraphNodeEmbed(created.id);
|
|
340
|
-
|
|
341
|
-
return {
|
|
342
|
-
success: true,
|
|
343
|
-
op: "save",
|
|
344
|
-
memory_id: created.id,
|
|
345
|
-
message: "Memory saved.",
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function handleUpdate(input: RememberInput): RememberResult {
|
|
350
|
-
if (!input.memory_id) {
|
|
351
|
-
return {
|
|
352
|
-
success: false,
|
|
353
|
-
op: "update",
|
|
354
|
-
message: "memory_id is required for update",
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const existing = getNode(input.memory_id);
|
|
359
|
-
if (!existing) {
|
|
360
|
-
return {
|
|
361
|
-
success: false,
|
|
362
|
-
op: "update",
|
|
363
|
-
memory_id: input.memory_id,
|
|
364
|
-
message: "Memory not found",
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const changes: Record<string, unknown> = {};
|
|
369
|
-
if (input.content) changes.content = input.content;
|
|
370
|
-
if (input.significance != null)
|
|
371
|
-
changes.significance = clamp(input.significance, 0, 1);
|
|
372
|
-
if (input.type && VALID_TYPES.has(input.type as MemoryType))
|
|
373
|
-
changes.type = input.type;
|
|
374
|
-
|
|
375
|
-
if (Object.keys(changes).length > 0) {
|
|
376
|
-
updateNode(input.memory_id, changes);
|
|
377
|
-
// Re-embed if content changed
|
|
378
|
-
if (input.content) {
|
|
379
|
-
enqueueGraphNodeEmbed(input.memory_id);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return {
|
|
384
|
-
success: true,
|
|
385
|
-
op: "update",
|
|
386
|
-
memory_id: input.memory_id,
|
|
387
|
-
message: "Memory updated.",
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
function handleDelete(input: RememberInput): RememberResult {
|
|
392
|
-
if (!input.memory_id) {
|
|
393
|
-
return {
|
|
394
|
-
success: false,
|
|
395
|
-
op: "delete",
|
|
396
|
-
message: "memory_id is required for delete",
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
const existing = getNode(input.memory_id);
|
|
401
|
-
if (!existing) {
|
|
402
|
-
return {
|
|
403
|
-
success: false,
|
|
404
|
-
op: "delete",
|
|
405
|
-
memory_id: input.memory_id,
|
|
406
|
-
message: "Memory not found",
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
deleteNode(input.memory_id);
|
|
411
|
-
|
|
412
|
-
return {
|
|
413
|
-
success: true,
|
|
414
|
-
op: "delete",
|
|
415
|
-
memory_id: input.memory_id,
|
|
416
|
-
message: "Memory deleted.",
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// ---------------------------------------------------------------------------
|
|
421
|
-
// Helpers
|
|
422
|
-
// ---------------------------------------------------------------------------
|
|
423
|
-
|
|
424
|
-
function clamp(v: number, min: number, max: number): number {
|
|
425
|
-
return Math.max(min, Math.min(max, v));
|
|
300
|
+
return { success: true, message: "Saved to knowledge base." };
|
|
426
301
|
}
|