@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
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
import { rmSync } from "node:fs";
|
|
2
|
-
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
3
|
-
|
|
4
|
-
import { Command } from "commander";
|
|
5
|
-
import { eq, like } from "drizzle-orm";
|
|
6
|
-
|
|
7
|
-
mock.module("../util/logger.js", () => ({
|
|
8
|
-
getLogger: () =>
|
|
9
|
-
new Proxy({} as Record<string, unknown>, {
|
|
10
|
-
get: () => () => {},
|
|
11
|
-
}),
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
mock.module("../memory/qdrant-client.js", () => ({
|
|
15
|
-
getQdrantClient: () => ({
|
|
16
|
-
searchWithFilter: async () => [],
|
|
17
|
-
hybridSearch: async () => [],
|
|
18
|
-
upsertPoints: async () => {},
|
|
19
|
-
deletePoints: async () => {},
|
|
20
|
-
}),
|
|
21
|
-
initQdrantClient: () => {},
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
// Controllable mock for buildCliProgram
|
|
25
|
-
let mockCommands: { name: string; description: string }[] = [];
|
|
26
|
-
|
|
27
|
-
function makeMockProgram(): Command {
|
|
28
|
-
const program = new Command();
|
|
29
|
-
for (const cmd of mockCommands) {
|
|
30
|
-
program.command(cmd.name).description(cmd.description);
|
|
31
|
-
}
|
|
32
|
-
return program;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
mock.module("../cli/program.js", () => ({
|
|
36
|
-
buildCliProgram: () => makeMockProgram(),
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
import { DEFAULT_CONFIG } from "../config/defaults.js";
|
|
40
|
-
|
|
41
|
-
const TEST_CONFIG = {
|
|
42
|
-
...DEFAULT_CONFIG,
|
|
43
|
-
memory: {
|
|
44
|
-
...DEFAULT_CONFIG.memory,
|
|
45
|
-
enabled: true,
|
|
46
|
-
extraction: {
|
|
47
|
-
...DEFAULT_CONFIG.memory.extraction,
|
|
48
|
-
useLLM: false,
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
mock.module("../config/loader.js", () => ({
|
|
54
|
-
loadConfig: () => TEST_CONFIG,
|
|
55
|
-
getConfig: () => TEST_CONFIG,
|
|
56
|
-
loadRawConfig: () => ({}),
|
|
57
|
-
saveRawConfig: () => {},
|
|
58
|
-
invalidateConfigCache: () => {},
|
|
59
|
-
}));
|
|
60
|
-
|
|
61
|
-
import {
|
|
62
|
-
buildCliCapabilityStatement,
|
|
63
|
-
seedCliCommandMemories,
|
|
64
|
-
upsertCliCapabilityMemory,
|
|
65
|
-
} from "../cli/cli-memory.js";
|
|
66
|
-
import { getDb, initializeDb, resetDb } from "../memory/db.js";
|
|
67
|
-
import { memoryGraphNodes, memoryJobs } from "../memory/schema.js";
|
|
68
|
-
import { ensureDataDir, getDbPath } from "../util/platform.js";
|
|
69
|
-
|
|
70
|
-
ensureDataDir();
|
|
71
|
-
initializeDb();
|
|
72
|
-
|
|
73
|
-
afterAll(() => {
|
|
74
|
-
resetDb();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
function resetTables() {
|
|
78
|
-
const db = getDb();
|
|
79
|
-
db.run("DELETE FROM memory_embeddings");
|
|
80
|
-
db.run("DELETE FROM memory_graph_nodes");
|
|
81
|
-
db.run("DELETE FROM memory_jobs");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ─── buildCliCapabilityStatement ────────────────────────────────────────────
|
|
85
|
-
|
|
86
|
-
describe("buildCliCapabilityStatement", () => {
|
|
87
|
-
test("includes 'assistant' prefix, name, and description", () => {
|
|
88
|
-
const result = buildCliCapabilityStatement("doctor", "Run diagnostic checks");
|
|
89
|
-
expect(result).toContain('"assistant doctor"');
|
|
90
|
-
expect(result).toContain("Run diagnostic checks");
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test("truncates long statements to 500 chars", () => {
|
|
94
|
-
const longDesc = "x".repeat(600);
|
|
95
|
-
const result = buildCliCapabilityStatement("test", longDesc);
|
|
96
|
-
expect(result.length).toBe(500);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// ─── upsertCliCapabilityMemory ──────────────────────────────────────────────
|
|
101
|
-
|
|
102
|
-
describe("upsertCliCapabilityMemory", () => {
|
|
103
|
-
beforeEach(resetTables);
|
|
104
|
-
|
|
105
|
-
test("inserts with correct type, content, confidence, significance", () => {
|
|
106
|
-
upsertCliCapabilityMemory("doctor", "Run diagnostic checks");
|
|
107
|
-
|
|
108
|
-
const db = getDb();
|
|
109
|
-
const items = db.select().from(memoryGraphNodes).all();
|
|
110
|
-
expect(items).toHaveLength(1);
|
|
111
|
-
expect(items[0].type).toBe("procedural");
|
|
112
|
-
expect(items[0].content).toMatch(/^cli:doctor\n/);
|
|
113
|
-
expect(items[0].confidence).toBe(1.0);
|
|
114
|
-
expect(items[0].significance).toBe(0.7);
|
|
115
|
-
expect(items[0].fidelity).toBe("vivid");
|
|
116
|
-
expect(items[0].scopeId).toBe("default");
|
|
117
|
-
|
|
118
|
-
// Should also enqueue an embed_graph_node job
|
|
119
|
-
const jobs = db.select().from(memoryJobs).all();
|
|
120
|
-
expect(jobs).toHaveLength(1);
|
|
121
|
-
expect(jobs[0].type).toBe("embed_graph_node");
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test("is idempotent (same entry only touches lastAccessed)", () => {
|
|
125
|
-
upsertCliCapabilityMemory("doctor", "Run diagnostic checks");
|
|
126
|
-
|
|
127
|
-
const db = getDb();
|
|
128
|
-
const before = db.select().from(memoryGraphNodes).all();
|
|
129
|
-
expect(before).toHaveLength(1);
|
|
130
|
-
const originalLastAccessed = before[0].lastAccessed;
|
|
131
|
-
|
|
132
|
-
// Upsert again
|
|
133
|
-
upsertCliCapabilityMemory("doctor", "Run diagnostic checks");
|
|
134
|
-
|
|
135
|
-
const after = db.select().from(memoryGraphNodes).all();
|
|
136
|
-
expect(after).toHaveLength(1);
|
|
137
|
-
// Same content, so only lastAccessed changes
|
|
138
|
-
expect(after[0].content).toBe(before[0].content);
|
|
139
|
-
expect(after[0].lastAccessed).toBeGreaterThanOrEqual(originalLastAccessed);
|
|
140
|
-
|
|
141
|
-
// Should NOT enqueue a second embed job (only 1 from initial insert)
|
|
142
|
-
const jobs = db.select().from(memoryJobs).all();
|
|
143
|
-
expect(jobs).toHaveLength(1);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
test("updates content when description changes", () => {
|
|
147
|
-
upsertCliCapabilityMemory("doctor", "Original description");
|
|
148
|
-
|
|
149
|
-
const db = getDb();
|
|
150
|
-
const before = db.select().from(memoryGraphNodes).all();
|
|
151
|
-
expect(before).toHaveLength(1);
|
|
152
|
-
expect(before[0].content).toContain("Original description");
|
|
153
|
-
|
|
154
|
-
// Change description
|
|
155
|
-
upsertCliCapabilityMemory("doctor", "Updated description");
|
|
156
|
-
|
|
157
|
-
const after = db.select().from(memoryGraphNodes).all();
|
|
158
|
-
expect(after).toHaveLength(1);
|
|
159
|
-
expect(after[0].content).toContain("Updated description");
|
|
160
|
-
expect(after[0].content).not.toBe(before[0].content);
|
|
161
|
-
|
|
162
|
-
// Should enqueue a second embed job
|
|
163
|
-
const jobs = db.select().from(memoryJobs).all();
|
|
164
|
-
expect(jobs).toHaveLength(2);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test("reactivates soft-deleted items", () => {
|
|
168
|
-
upsertCliCapabilityMemory("doctor", "Run diagnostic checks");
|
|
169
|
-
|
|
170
|
-
const db = getDb();
|
|
171
|
-
// Soft-delete the item
|
|
172
|
-
db.update(memoryGraphNodes)
|
|
173
|
-
.set({ fidelity: "gone" })
|
|
174
|
-
.where(like(memoryGraphNodes.content, "cli:doctor\n%"))
|
|
175
|
-
.run();
|
|
176
|
-
|
|
177
|
-
const deleted = db.select().from(memoryGraphNodes).all();
|
|
178
|
-
expect(deleted[0].fidelity).toBe("gone");
|
|
179
|
-
|
|
180
|
-
// Clear jobs from initial insert
|
|
181
|
-
db.run("DELETE FROM memory_jobs");
|
|
182
|
-
|
|
183
|
-
// Upsert again — should reactivate
|
|
184
|
-
upsertCliCapabilityMemory("doctor", "Run diagnostic checks");
|
|
185
|
-
|
|
186
|
-
const reactivated = db.select().from(memoryGraphNodes).all();
|
|
187
|
-
expect(reactivated).toHaveLength(1);
|
|
188
|
-
expect(reactivated[0].fidelity).toBe("vivid");
|
|
189
|
-
|
|
190
|
-
// Should enqueue embed job for reactivated item
|
|
191
|
-
const jobs = db.select().from(memoryJobs).all();
|
|
192
|
-
expect(jobs).toHaveLength(1);
|
|
193
|
-
expect(jobs[0].type).toBe("embed_graph_node");
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test("does not throw on DB error", () => {
|
|
197
|
-
resetDb();
|
|
198
|
-
const db = getDb();
|
|
199
|
-
db.run("DROP TABLE IF EXISTS memory_graph_nodes");
|
|
200
|
-
|
|
201
|
-
expect(() => {
|
|
202
|
-
upsertCliCapabilityMemory("doctor", "Run diagnostic checks");
|
|
203
|
-
}).not.toThrow();
|
|
204
|
-
|
|
205
|
-
// Restore DB state for subsequent tests.
|
|
206
|
-
resetDb();
|
|
207
|
-
const dbPath = getDbPath();
|
|
208
|
-
for (const ext of ["", "-wal", "-shm"]) {
|
|
209
|
-
rmSync(`${dbPath}${ext}`, { force: true });
|
|
210
|
-
}
|
|
211
|
-
initializeDb();
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// ─── seedCliCommandMemories ─────────────────────────────────────────────────
|
|
216
|
-
|
|
217
|
-
describe("seedCliCommandMemories", () => {
|
|
218
|
-
beforeEach(() => {
|
|
219
|
-
resetTables();
|
|
220
|
-
// Reset mock commands
|
|
221
|
-
mockCommands = [];
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
test("upserts capability memories for all commands", () => {
|
|
225
|
-
mockCommands = [
|
|
226
|
-
{ name: "doctor", description: "Run diagnostic checks" },
|
|
227
|
-
{ name: "config", description: "Manage configuration" },
|
|
228
|
-
{ name: "keys", description: "Manage API keys" },
|
|
229
|
-
];
|
|
230
|
-
|
|
231
|
-
seedCliCommandMemories();
|
|
232
|
-
|
|
233
|
-
const db = getDb();
|
|
234
|
-
const items = db
|
|
235
|
-
.select()
|
|
236
|
-
.from(memoryGraphNodes)
|
|
237
|
-
.where(eq(memoryGraphNodes.type, "procedural"))
|
|
238
|
-
.all();
|
|
239
|
-
expect(items).toHaveLength(3);
|
|
240
|
-
|
|
241
|
-
const contentPrefixes = items.map((i) => i.content.split("\n")[0]).sort();
|
|
242
|
-
expect(contentPrefixes).toEqual([
|
|
243
|
-
"cli:config",
|
|
244
|
-
"cli:doctor",
|
|
245
|
-
"cli:keys",
|
|
246
|
-
]);
|
|
247
|
-
|
|
248
|
-
// All should be vivid
|
|
249
|
-
for (const item of items) {
|
|
250
|
-
expect(item.fidelity).toBe("vivid");
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test("prunes stale capabilities for commands no longer registered", () => {
|
|
255
|
-
// First seed with three commands
|
|
256
|
-
mockCommands = [
|
|
257
|
-
{ name: "doctor", description: "Run diagnostic checks" },
|
|
258
|
-
{ name: "config", description: "Manage configuration" },
|
|
259
|
-
{ name: "keys", description: "Manage API keys" },
|
|
260
|
-
];
|
|
261
|
-
seedCliCommandMemories();
|
|
262
|
-
|
|
263
|
-
const db = getDb();
|
|
264
|
-
const beforeItems = db
|
|
265
|
-
.select()
|
|
266
|
-
.from(memoryGraphNodes)
|
|
267
|
-
.where(eq(memoryGraphNodes.type, "procedural"))
|
|
268
|
-
.all();
|
|
269
|
-
expect(beforeItems).toHaveLength(3);
|
|
270
|
-
expect(beforeItems.every((i) => i.fidelity === "vivid")).toBe(true);
|
|
271
|
-
|
|
272
|
-
// Now seed with only doctor — config and keys should be pruned
|
|
273
|
-
mockCommands = [
|
|
274
|
-
{ name: "doctor", description: "Run diagnostic checks" },
|
|
275
|
-
];
|
|
276
|
-
seedCliCommandMemories();
|
|
277
|
-
|
|
278
|
-
const afterItems = db
|
|
279
|
-
.select()
|
|
280
|
-
.from(memoryGraphNodes)
|
|
281
|
-
.where(eq(memoryGraphNodes.type, "procedural"))
|
|
282
|
-
.all();
|
|
283
|
-
expect(afterItems).toHaveLength(3); // still 3 rows, but 2 are soft-deleted
|
|
284
|
-
|
|
285
|
-
const active = afterItems.filter((i) => i.fidelity === "vivid");
|
|
286
|
-
const deleted = afterItems.filter((i) => i.fidelity === "gone");
|
|
287
|
-
|
|
288
|
-
expect(active).toHaveLength(1);
|
|
289
|
-
expect(active[0].content).toMatch(/^cli:doctor\n/);
|
|
290
|
-
|
|
291
|
-
expect(deleted).toHaveLength(2);
|
|
292
|
-
const deletedPrefixes = deleted.map((i) => i.content.split("\n")[0]).sort();
|
|
293
|
-
expect(deletedPrefixes).toEqual(["cli:config", "cli:keys"]);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
test("handles empty command list without errors", () => {
|
|
297
|
-
// Pre-populate a CLI command so we can verify it gets pruned
|
|
298
|
-
upsertCliCapabilityMemory("old-command", "An old command");
|
|
299
|
-
|
|
300
|
-
const db = getDb();
|
|
301
|
-
const beforeItems = db.select().from(memoryGraphNodes).all();
|
|
302
|
-
expect(beforeItems).toHaveLength(1);
|
|
303
|
-
expect(beforeItems[0].fidelity).toBe("vivid");
|
|
304
|
-
|
|
305
|
-
// Seed with empty commands
|
|
306
|
-
mockCommands = [];
|
|
307
|
-
seedCliCommandMemories();
|
|
308
|
-
|
|
309
|
-
// The existing command should be pruned (soft-deleted)
|
|
310
|
-
const afterItems = db.select().from(memoryGraphNodes).all();
|
|
311
|
-
expect(afterItems).toHaveLength(1);
|
|
312
|
-
expect(afterItems[0].fidelity).toBe("gone");
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
test("does not prune non-cli capability memories", () => {
|
|
316
|
-
// Pre-insert a skill capability memory directly into the DB
|
|
317
|
-
const db = getDb();
|
|
318
|
-
const now = Date.now();
|
|
319
|
-
db.insert(memoryGraphNodes)
|
|
320
|
-
.values({
|
|
321
|
-
id: "skill-test-item",
|
|
322
|
-
type: "procedural",
|
|
323
|
-
content: "skill:test-skill\nThe test skill does things.",
|
|
324
|
-
fidelity: "vivid",
|
|
325
|
-
confidence: 1.0,
|
|
326
|
-
significance: 0.7,
|
|
327
|
-
sourceType: "inferred",
|
|
328
|
-
scopeId: "default",
|
|
329
|
-
created: now,
|
|
330
|
-
lastAccessed: now,
|
|
331
|
-
lastConsolidated: now,
|
|
332
|
-
emotionalCharge: '{"valence":0,"intensity":0.1,"decayCurve":"linear","decayRate":0.05,"originalIntensity":0.1}',
|
|
333
|
-
stability: 14,
|
|
334
|
-
reinforcementCount: 0,
|
|
335
|
-
lastReinforced: now,
|
|
336
|
-
sourceConversations: "[]",
|
|
337
|
-
narrativeRole: null,
|
|
338
|
-
partOfStory: null,
|
|
339
|
-
})
|
|
340
|
-
.run();
|
|
341
|
-
|
|
342
|
-
// Seed with empty commands — CLI pruner runs but should skip skill:* items
|
|
343
|
-
mockCommands = [];
|
|
344
|
-
seedCliCommandMemories();
|
|
345
|
-
|
|
346
|
-
const item = db
|
|
347
|
-
.select()
|
|
348
|
-
.from(memoryGraphNodes)
|
|
349
|
-
.where(like(memoryGraphNodes.content, "skill:test-skill\n%"))
|
|
350
|
-
.get();
|
|
351
|
-
expect(item).toBeDefined();
|
|
352
|
-
expect(item!.fidelity).toBe("vivid");
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
test("does not throw on error", () => {
|
|
356
|
-
mockCommands = [
|
|
357
|
-
{ name: "doctor", description: "Run diagnostic checks" },
|
|
358
|
-
];
|
|
359
|
-
|
|
360
|
-
// Drop memory_graph_nodes to force a DB error during the prune phase
|
|
361
|
-
resetDb();
|
|
362
|
-
const db = getDb();
|
|
363
|
-
db.run("DROP TABLE IF EXISTS memory_graph_nodes");
|
|
364
|
-
|
|
365
|
-
expect(() => {
|
|
366
|
-
seedCliCommandMemories();
|
|
367
|
-
}).not.toThrow();
|
|
368
|
-
|
|
369
|
-
// Restore DB state for subsequent tests.
|
|
370
|
-
resetDb();
|
|
371
|
-
const dbPath = getDbPath();
|
|
372
|
-
for (const ext of ["", "-wal", "-shm"]) {
|
|
373
|
-
rmSync(`${dbPath}${ext}`, { force: true });
|
|
374
|
-
}
|
|
375
|
-
initializeDb();
|
|
376
|
-
});
|
|
377
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
extractLastCodeBlock,
|
|
5
|
-
formatConversationForExport,
|
|
6
|
-
} from "../util/clipboard.js";
|
|
7
|
-
|
|
8
|
-
describe("formatConversationForExport", () => {
|
|
9
|
-
test("formats user and assistant messages", () => {
|
|
10
|
-
const messages = [
|
|
11
|
-
{ role: "user", text: "Hello" },
|
|
12
|
-
{ role: "assistant", text: "Hi there!" },
|
|
13
|
-
];
|
|
14
|
-
expect(formatConversationForExport(messages)).toBe(
|
|
15
|
-
"you> Hello\n\nassistant> Hi there!",
|
|
16
|
-
);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("handles a single message", () => {
|
|
20
|
-
const messages = [{ role: "user", text: "Just me" }];
|
|
21
|
-
expect(formatConversationForExport(messages)).toBe("you> Just me");
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test("handles empty messages array", () => {
|
|
25
|
-
expect(formatConversationForExport([])).toBe("");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test("preserves multiline message text", () => {
|
|
29
|
-
const messages = [{ role: "assistant", text: "Line 1\nLine 2\nLine 3" }];
|
|
30
|
-
expect(formatConversationForExport(messages)).toBe(
|
|
31
|
-
"assistant> Line 1\nLine 2\nLine 3",
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe("extractLastCodeBlock", () => {
|
|
37
|
-
test("extracts a simple code block", () => {
|
|
38
|
-
const text = "```\nhello world\n```";
|
|
39
|
-
expect(extractLastCodeBlock(text)).toBe("hello world");
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test("extracts code block with language tag", () => {
|
|
43
|
-
const text = "```typescript\nconst x = 1;\n```";
|
|
44
|
-
expect(extractLastCodeBlock(text)).toBe("const x = 1;");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("returns the last code block when multiple exist", () => {
|
|
48
|
-
const text = "```\nfirst\n```\nsome text\n```\nsecond\n```";
|
|
49
|
-
expect(extractLastCodeBlock(text)).toBe("second");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("handles empty code blocks", () => {
|
|
53
|
-
const text = "```\n```";
|
|
54
|
-
expect(extractLastCodeBlock(text)).toBe("");
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test("handles empty code blocks with language tag", () => {
|
|
58
|
-
const text = "```python\n```";
|
|
59
|
-
expect(extractLastCodeBlock(text)).toBe("");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("does not match inline backticks as closing fence", () => {
|
|
63
|
-
const text = '```\nconst s = "```"\n```';
|
|
64
|
-
expect(extractLastCodeBlock(text)).toBe('const s = "```"');
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("handles multi-line code blocks", () => {
|
|
68
|
-
const text = "```js\nfunction foo() {\n return 42;\n}\n```";
|
|
69
|
-
expect(extractLastCodeBlock(text)).toBe(
|
|
70
|
-
"function foo() {\n return 42;\n}",
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("returns null when no code blocks exist", () => {
|
|
75
|
-
expect(extractLastCodeBlock("no code here")).toBeNull();
|
|
76
|
-
expect(extractLastCodeBlock("`inline code`")).toBeNull();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test("extracts last block when separated by text", () => {
|
|
80
|
-
const text = "```\nfirst\n```\nSome explanation\n```\nsecond\n```";
|
|
81
|
-
expect(extractLastCodeBlock(text)).toBe("second");
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("handles non-empty block followed by empty block with text between", () => {
|
|
85
|
-
const text = "```\nreal code\n```\nSome text\n```\n```";
|
|
86
|
-
expect(extractLastCodeBlock(text)).toBe("");
|
|
87
|
-
});
|
|
88
|
-
});
|
package/src/cli/cli-memory.ts
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { and, eq, like, sql } from "drizzle-orm";
|
|
2
|
-
import { v4 as uuid } from "uuid";
|
|
3
|
-
|
|
4
|
-
import { getDb } from "../memory/db.js";
|
|
5
|
-
import { enqueueMemoryJob } from "../memory/jobs-store.js";
|
|
6
|
-
import { memoryGraphNodes } from "../memory/schema.js";
|
|
7
|
-
import { getLogger } from "../util/logger.js";
|
|
8
|
-
import { buildCliProgram } from "./program.js";
|
|
9
|
-
|
|
10
|
-
const log = getLogger("cli-memory");
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Build a capability statement for a CLI command.
|
|
14
|
-
* Truncated to 500 chars max (matching the limit used by memory item extraction).
|
|
15
|
-
*/
|
|
16
|
-
export function buildCliCapabilityStatement(
|
|
17
|
-
name: string,
|
|
18
|
-
description: string,
|
|
19
|
-
): string {
|
|
20
|
-
let statement = `The "assistant ${name}" CLI command is available. ${description}.`;
|
|
21
|
-
|
|
22
|
-
// Truncate to 500 chars max
|
|
23
|
-
if (statement.length > 500) {
|
|
24
|
-
statement = statement.slice(0, 500);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return statement;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Default emotional charge for capability graph nodes. */
|
|
31
|
-
const DEFAULT_EMOTIONAL_CHARGE = JSON.stringify({
|
|
32
|
-
valence: 0,
|
|
33
|
-
intensity: 0.1,
|
|
34
|
-
decayCurve: "linear",
|
|
35
|
-
decayRate: 0.05,
|
|
36
|
-
originalIntensity: 0.1,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Upsert a capability memory graph node for a CLI command.
|
|
41
|
-
* Best-effort: errors are logged but never thrown.
|
|
42
|
-
*/
|
|
43
|
-
export function upsertCliCapabilityMemory(
|
|
44
|
-
commandName: string,
|
|
45
|
-
description: string,
|
|
46
|
-
): void {
|
|
47
|
-
try {
|
|
48
|
-
const db = getDb();
|
|
49
|
-
const statement = buildCliCapabilityStatement(commandName, description);
|
|
50
|
-
const content = `cli:${commandName}\n${statement}`;
|
|
51
|
-
const scopeId = "default";
|
|
52
|
-
const now = Date.now();
|
|
53
|
-
|
|
54
|
-
const existing = db
|
|
55
|
-
.select()
|
|
56
|
-
.from(memoryGraphNodes)
|
|
57
|
-
.where(
|
|
58
|
-
and(
|
|
59
|
-
eq(memoryGraphNodes.type, "procedural"),
|
|
60
|
-
like(memoryGraphNodes.content, `cli:${commandName}\n%`),
|
|
61
|
-
eq(memoryGraphNodes.scopeId, scopeId),
|
|
62
|
-
),
|
|
63
|
-
)
|
|
64
|
-
.get();
|
|
65
|
-
|
|
66
|
-
if (existing) {
|
|
67
|
-
if (
|
|
68
|
-
existing.content === content &&
|
|
69
|
-
existing.fidelity !== "gone"
|
|
70
|
-
) {
|
|
71
|
-
// Same content — just touch lastAccessed
|
|
72
|
-
db.update(memoryGraphNodes)
|
|
73
|
-
.set({ lastAccessed: now })
|
|
74
|
-
.where(eq(memoryGraphNodes.id, existing.id))
|
|
75
|
-
.run();
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (existing.fidelity !== "gone") {
|
|
80
|
-
// Content changed — update content
|
|
81
|
-
db.update(memoryGraphNodes)
|
|
82
|
-
.set({
|
|
83
|
-
content,
|
|
84
|
-
lastAccessed: now,
|
|
85
|
-
})
|
|
86
|
-
.where(eq(memoryGraphNodes.id, existing.id))
|
|
87
|
-
.run();
|
|
88
|
-
enqueueMemoryJob("embed_graph_node", { nodeId: existing.id });
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// fidelity === "gone" — reactivate
|
|
93
|
-
db.update(memoryGraphNodes)
|
|
94
|
-
.set({
|
|
95
|
-
fidelity: "vivid",
|
|
96
|
-
content,
|
|
97
|
-
created: now,
|
|
98
|
-
lastAccessed: now,
|
|
99
|
-
})
|
|
100
|
-
.where(eq(memoryGraphNodes.id, existing.id))
|
|
101
|
-
.run();
|
|
102
|
-
enqueueMemoryJob("embed_graph_node", { nodeId: existing.id });
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// No existing — insert new graph node
|
|
107
|
-
const id = uuid();
|
|
108
|
-
db.insert(memoryGraphNodes)
|
|
109
|
-
.values({
|
|
110
|
-
id,
|
|
111
|
-
content,
|
|
112
|
-
type: "procedural",
|
|
113
|
-
created: now,
|
|
114
|
-
lastAccessed: now,
|
|
115
|
-
lastConsolidated: now,
|
|
116
|
-
emotionalCharge: DEFAULT_EMOTIONAL_CHARGE,
|
|
117
|
-
fidelity: "vivid",
|
|
118
|
-
confidence: 1.0,
|
|
119
|
-
significance: 0.7,
|
|
120
|
-
stability: 14,
|
|
121
|
-
reinforcementCount: 0,
|
|
122
|
-
lastReinforced: now,
|
|
123
|
-
sourceConversations: JSON.stringify([]),
|
|
124
|
-
sourceType: "inferred",
|
|
125
|
-
narrativeRole: null,
|
|
126
|
-
partOfStory: null,
|
|
127
|
-
scopeId,
|
|
128
|
-
})
|
|
129
|
-
.run();
|
|
130
|
-
enqueueMemoryJob("embed_graph_node", { nodeId: id });
|
|
131
|
-
} catch (err) {
|
|
132
|
-
log.warn({ err, commandName }, "Failed to upsert CLI capability memory");
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Seed capability memory graph nodes for all CLI commands.
|
|
138
|
-
* Prunes stale entries whose commands are no longer registered.
|
|
139
|
-
* Best-effort: errors are logged but never thrown.
|
|
140
|
-
*/
|
|
141
|
-
export function seedCliCommandMemories(): void {
|
|
142
|
-
try {
|
|
143
|
-
const program = buildCliProgram();
|
|
144
|
-
const commandNames = new Set<string>();
|
|
145
|
-
|
|
146
|
-
for (const cmd of program.commands) {
|
|
147
|
-
commandNames.add(cmd.name());
|
|
148
|
-
upsertCliCapabilityMemory(cmd.name(), cmd.description());
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Prune stale capability memories for commands no longer registered
|
|
152
|
-
const db = getDb();
|
|
153
|
-
const allCapabilities = db
|
|
154
|
-
.select()
|
|
155
|
-
.from(memoryGraphNodes)
|
|
156
|
-
.where(
|
|
157
|
-
and(
|
|
158
|
-
eq(memoryGraphNodes.type, "procedural"),
|
|
159
|
-
eq(memoryGraphNodes.scopeId, "default"),
|
|
160
|
-
sql`${memoryGraphNodes.fidelity} != 'gone'`,
|
|
161
|
-
),
|
|
162
|
-
)
|
|
163
|
-
.all();
|
|
164
|
-
|
|
165
|
-
const now = Date.now();
|
|
166
|
-
for (const item of allCapabilities) {
|
|
167
|
-
if (!item.content.startsWith("cli:")) continue;
|
|
168
|
-
const itemCommandName = item.content.split("\n")[0].replace("cli:", "");
|
|
169
|
-
if (!commandNames.has(itemCommandName)) {
|
|
170
|
-
db.update(memoryGraphNodes)
|
|
171
|
-
.set({ fidelity: "gone", lastAccessed: now })
|
|
172
|
-
.where(eq(memoryGraphNodes.id, item.id))
|
|
173
|
-
.run();
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
} catch (err) {
|
|
177
|
-
log.warn({ err }, "Failed to seed CLI command memories");
|
|
178
|
-
}
|
|
179
|
-
}
|
package/src/util/clipboard.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
import { PlatformError } from "./errors.js";
|
|
4
|
-
import { getClipboardCommand } from "./platform.js";
|
|
5
|
-
|
|
6
|
-
export function copyToClipboard(text: string): void {
|
|
7
|
-
const cmd = getClipboardCommand();
|
|
8
|
-
if (!cmd) {
|
|
9
|
-
throw new PlatformError("Clipboard not supported on this platform");
|
|
10
|
-
}
|
|
11
|
-
execSync(cmd, { input: text, stdio: ["pipe", "ignore", "ignore"] });
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function formatConversationForExport(
|
|
15
|
-
messages: Array<{ role: string; text: string }>,
|
|
16
|
-
): string {
|
|
17
|
-
return messages
|
|
18
|
-
.map((m) => {
|
|
19
|
-
const label = m.role === "user" ? "you" : "assistant";
|
|
20
|
-
return `${label}> ${m.text}`;
|
|
21
|
-
})
|
|
22
|
-
.join("\n\n");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function extractLastCodeBlock(text: string): string | null {
|
|
26
|
-
const re = /```[^\n]*\n((?:[\s\S]*?\n)?)```/g;
|
|
27
|
-
let last: RegExpExecArray | null = null;
|
|
28
|
-
let m: RegExpExecArray | null;
|
|
29
|
-
while ((m = re.exec(text)) != null) {
|
|
30
|
-
last = m;
|
|
31
|
-
}
|
|
32
|
-
if (!last) return null;
|
|
33
|
-
return last[1].trimEnd();
|
|
34
|
-
}
|