comisai 1.0.10 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +1 -4
- package/node_modules/@comis/agent/package.json +1 -1
- package/node_modules/@comis/channels/package.json +1 -1
- package/node_modules/@comis/cli/dist/wizard/steps/06-channels.js +88 -11
- package/node_modules/@comis/cli/dist/wizard/steps/10-write-config.js +4 -0
- package/node_modules/@comis/cli/dist/wizard/types.d.ts +1 -0
- package/node_modules/@comis/cli/package.json +1 -1
- package/node_modules/@comis/core/package.json +1 -1
- package/node_modules/@comis/daemon/package.json +1 -1
- package/node_modules/@comis/gateway/package.json +1 -1
- package/node_modules/@comis/infra/package.json +1 -1
- package/node_modules/@comis/memory/package.json +1 -1
- package/node_modules/@comis/scheduler/package.json +1 -1
- package/node_modules/@comis/shared/package.json +1 -1
- package/node_modules/@comis/skills/package.json +1 -1
- package/package.json +18 -18
- package/node_modules/@comis/core/dist/approval/approval-gate.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/approval/approval-gate.test.js +0 -1003
- package/node_modules/@comis/core/dist/bootstrap.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/bootstrap.test.js +0 -150
- package/node_modules/@comis/core/dist/config/backup.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/backup.test.js +0 -160
- package/node_modules/@comis/core/dist/config/env-substitution.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/env-substitution.test.js +0 -259
- package/node_modules/@comis/core/dist/config/field-metadata.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/field-metadata.test.js +0 -72
- package/node_modules/@comis/core/dist/config/git-manager.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/git-manager.test.js +0 -1042
- package/node_modules/@comis/core/dist/config/immutable-keys.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/immutable-keys.test.js +0 -283
- package/node_modules/@comis/core/dist/config/include-resolver.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/include-resolver.test.js +0 -292
- package/node_modules/@comis/core/dist/config/layered.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/layered.test.js +0 -211
- package/node_modules/@comis/core/dist/config/loader.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/loader.test.js +0 -300
- package/node_modules/@comis/core/dist/config/migrate.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/migrate.test.js +0 -244
- package/node_modules/@comis/core/dist/config/partial-validator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/partial-validator.test.js +0 -98
- package/node_modules/@comis/core/dist/config/schema-agent-model.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-agent-model.test.js +0 -279
- package/node_modules/@comis/core/dist/config/schema-agent-session.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-agent-session.test.js +0 -242
- package/node_modules/@comis/core/dist/config/schema-agent.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-agent.test.js +0 -645
- package/node_modules/@comis/core/dist/config/schema-channel.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-channel.test.js +0 -341
- package/node_modules/@comis/core/dist/config/schema-coalescer.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-coalescer.test.js +0 -51
- package/node_modules/@comis/core/dist/config/schema-context-engine.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-context-engine.test.js +0 -797
- package/node_modules/@comis/core/dist/config/schema-context-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-context-guard.test.js +0 -111
- package/node_modules/@comis/core/dist/config/schema-daemon.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-daemon.test.js +0 -107
- package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-delivery-timing.test.js +0 -51
- package/node_modules/@comis/core/dist/config/schema-documentation.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-documentation.test.js +0 -63
- package/node_modules/@comis/core/dist/config/schema-gateway.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-gateway.test.js +0 -219
- package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-gemini-cache.test.js +0 -41
- package/node_modules/@comis/core/dist/config/schema-integrations.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-integrations.test.js +0 -92
- package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.test.js +0 -61
- package/node_modules/@comis/core/dist/config/schema-misc.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-misc.test.js +0 -394
- package/node_modules/@comis/core/dist/config/schema-observability.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-observability.test.js +0 -76
- package/node_modules/@comis/core/dist/config/schema-providers.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-providers.test.js +0 -294
- package/node_modules/@comis/core/dist/config/schema-queue.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-queue.test.js +0 -234
- package/node_modules/@comis/core/dist/config/schema-response-prefix.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-response-prefix.test.js +0 -36
- package/node_modules/@comis/core/dist/config/schema-security.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-security.test.js +0 -54
- package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-sender-trust-display.test.js +0 -60
- package/node_modules/@comis/core/dist/config/schema-serializer.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-serializer.test.js +0 -73
- package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.test.js +0 -39
- package/node_modules/@comis/core/dist/context/context.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/context/context.test.js +0 -276
- package/node_modules/@comis/core/dist/domain/agent-response.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/agent-response.test.js +0 -159
- package/node_modules/@comis/core/dist/domain/credential-mapping.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/credential-mapping.test.js +0 -135
- package/node_modules/@comis/core/dist/domain/delivery-origin.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/delivery-origin.test.js +0 -68
- package/node_modules/@comis/core/dist/domain/execution-graph.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/execution-graph.test.js +0 -441
- package/node_modules/@comis/core/dist/domain/memory-entry.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/memory-entry.test.js +0 -193
- package/node_modules/@comis/core/dist/domain/normalized-message.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/normalized-message.test.js +0 -255
- package/node_modules/@comis/core/dist/domain/session-key.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/session-key.test.js +0 -291
- package/node_modules/@comis/core/dist/domain/subagent-context-config.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/subagent-context-config.test.js +0 -131
- package/node_modules/@comis/core/dist/domain/subagent-context-types.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/domain/subagent-context-types.test.js +0 -182
- package/node_modules/@comis/core/dist/event-bus/bus.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/bus.test.js +0 -152
- package/node_modules/@comis/core/dist/event-bus/events-agent.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events-agent.test.js +0 -409
- package/node_modules/@comis/core/dist/event-bus/events-channel.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events-channel.test.js +0 -240
- package/node_modules/@comis/core/dist/event-bus/events-infra.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events-infra.test.js +0 -416
- package/node_modules/@comis/core/dist/event-bus/events-messaging.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events-messaging.test.js +0 -433
- package/node_modules/@comis/core/dist/event-bus/events.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/event-bus/events.test.js +0 -93
- package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/hook-runner-global.test.js +0 -29
- package/node_modules/@comis/core/dist/hooks/hook-runner.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/hook-runner.test.js +0 -490
- package/node_modules/@comis/core/dist/hooks/hook-strategies.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/hook-strategies.test.js +0 -209
- package/node_modules/@comis/core/dist/hooks/integration.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/integration.test.js +0 -235
- package/node_modules/@comis/core/dist/hooks/plugin-registry.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/hooks/plugin-registry.test.js +0 -470
- package/node_modules/@comis/core/dist/load-env.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/load-env.test.js +0 -21
- package/node_modules/@comis/core/dist/security/action-classifier.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/action-classifier.test.js +0 -298
- package/node_modules/@comis/core/dist/security/audit-aggregator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/audit-aggregator.test.js +0 -128
- package/node_modules/@comis/core/dist/security/audit.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/audit.test.js +0 -154
- package/node_modules/@comis/core/dist/security/canary-token.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/canary-token.test.js +0 -45
- package/node_modules/@comis/core/dist/security/config-redaction.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/config-redaction.test.js +0 -107
- package/node_modules/@comis/core/dist/security/external-content.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/external-content.test.js +0 -210
- package/node_modules/@comis/core/dist/security/injection-patterns.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/injection-patterns.test.js +0 -213
- package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/injection-rate-limiter.test.js +0 -194
- package/node_modules/@comis/core/dist/security/input-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/input-guard.test.js +0 -215
- package/node_modules/@comis/core/dist/security/input-security-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/input-security-guard.test.js +0 -215
- package/node_modules/@comis/core/dist/security/input-validator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/input-validator.test.js +0 -133
- package/node_modules/@comis/core/dist/security/log-sanitizer.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/log-sanitizer.test.js +0 -260
- package/node_modules/@comis/core/dist/security/memory-write-validator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/memory-write-validator.test.js +0 -62
- package/node_modules/@comis/core/dist/security/output-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/output-guard.test.js +0 -397
- package/node_modules/@comis/core/dist/security/safe-path.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/safe-path.test.js +0 -95
- package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/scoped-secret-manager.test.js +0 -231
- package/node_modules/@comis/core/dist/security/secret-access.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/secret-access.test.js +0 -41
- package/node_modules/@comis/core/dist/security/secret-crypto.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/secret-crypto.test.js +0 -113
- package/node_modules/@comis/core/dist/security/secret-manager.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/secret-manager.test.js +0 -394
- package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/secret-ref-resolver.test.js +0 -268
- package/node_modules/@comis/core/dist/security/secrets-audit.test.d.ts +0 -10
- package/node_modules/@comis/core/dist/security/secrets-audit.test.js +0 -295
- package/node_modules/@comis/core/dist/security/ssrf-guard.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/ssrf-guard.test.js +0 -127
- package/node_modules/@comis/core/dist/security/token-generator.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/security/token-generator.test.js +0 -35
- package/node_modules/@comis/core/dist/tool-metadata.test.d.ts +0 -1
- package/node_modules/@comis/core/dist/tool-metadata.test.js +0 -112
- package/node_modules/@comis/memory/dist/compaction.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/compaction.test.js +0 -298
- package/node_modules/@comis/memory/dist/context-schema.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/context-schema.test.js +0 -246
- package/node_modules/@comis/memory/dist/context-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/context-store.test.js +0 -708
- package/node_modules/@comis/memory/dist/credential-mapping-schema.test.d.ts +0 -7
- package/node_modules/@comis/memory/dist/credential-mapping-schema.test.js +0 -73
- package/node_modules/@comis/memory/dist/credential-mapping-store.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/credential-mapping-store.test.js +0 -340
- package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/delivery-mirror-adapter.test.js +0 -165
- package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/delivery-queue-adapter.test.js +0 -263
- package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-batch-indexer.test.js +0 -202
- package/node_modules/@comis/memory/dist/embedding-cache-lru.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-cache-lru.test.js +0 -241
- package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/embedding-cache-sqlite.test.js +0 -678
- package/node_modules/@comis/memory/dist/embedding-cache.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-cache.test.js +0 -213
- package/node_modules/@comis/memory/dist/embedding-fingerprint.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-fingerprint.test.js +0 -87
- package/node_modules/@comis/memory/dist/embedding-hash.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-hash.test.js +0 -31
- package/node_modules/@comis/memory/dist/embedding-integration.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/embedding-integration.test.js +0 -232
- package/node_modules/@comis/memory/dist/embedding-provider-factory.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-provider-factory.test.js +0 -176
- package/node_modules/@comis/memory/dist/embedding-provider-local.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/embedding-provider-local.test.js +0 -76
- package/node_modules/@comis/memory/dist/embedding-provider-openai.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/embedding-provider-openai.test.js +0 -100
- package/node_modules/@comis/memory/dist/embedding-queue.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/embedding-queue.test.js +0 -236
- package/node_modules/@comis/memory/dist/hybrid-search.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/hybrid-search.test.js +0 -476
- package/node_modules/@comis/memory/dist/identity-link-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/identity-link-store.test.js +0 -88
- package/node_modules/@comis/memory/dist/memory-api.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/memory-api.test.js +0 -495
- package/node_modules/@comis/memory/dist/memory-concurrency.test.d.ts +0 -20
- package/node_modules/@comis/memory/dist/memory-concurrency.test.js +0 -222
- package/node_modules/@comis/memory/dist/named-graph-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/named-graph-store.test.js +0 -227
- package/node_modules/@comis/memory/dist/observability-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/observability-store.test.js +0 -704
- package/node_modules/@comis/memory/dist/row-mapper.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/row-mapper.test.js +0 -409
- package/node_modules/@comis/memory/dist/schema.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/schema.test.js +0 -240
- package/node_modules/@comis/memory/dist/secret-store-schema.test.d.ts +0 -7
- package/node_modules/@comis/memory/dist/secret-store-schema.test.js +0 -90
- package/node_modules/@comis/memory/dist/session-store.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/session-store.test.js +0 -262
- package/node_modules/@comis/memory/dist/setup-secrets.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/setup-secrets.test.js +0 -140
- package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.d.ts +0 -1
- package/node_modules/@comis/memory/dist/sqlite-memory-adapter.test.js +0 -888
- package/node_modules/@comis/memory/dist/sqlite-secret-store.test.d.ts +0 -8
- package/node_modules/@comis/memory/dist/sqlite-secret-store.test.js +0 -245
- package/node_modules/@comis/shared/dist/abort.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/abort.test.js +0 -71
- package/node_modules/@comis/shared/dist/result.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/result.test.js +0 -178
- package/node_modules/@comis/shared/dist/suppress-error.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/suppress-error.test.js +0 -50
- package/node_modules/@comis/shared/dist/timeout.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/timeout.test.js +0 -75
- package/node_modules/@comis/shared/dist/ttl-cache.test.d.ts +0 -1
- package/node_modules/@comis/shared/dist/ttl-cache.test.js +0 -81
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import Database from "better-sqlite3";
|
|
2
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
3
|
-
import { rowToEntry, insertMemoryRow, storeEmbedding, buildFilterClause, countRows, groupCountRows, ALLOWED_TABLES, ALLOWED_GROUP_COLUMNS, } from "./row-mapper.js";
|
|
4
|
-
import { initSchema, isVecAvailable } from "./schema.js";
|
|
5
|
-
const DIMS = 4;
|
|
6
|
-
// ── Test Fixtures ────────────────────────────────────────────────────
|
|
7
|
-
/** Create a complete MemoryRow with all fields populated. */
|
|
8
|
-
function makeRow(overrides) {
|
|
9
|
-
const defaults = {
|
|
10
|
-
id: "row-1",
|
|
11
|
-
tenant_id: "default",
|
|
12
|
-
agent_id: "default",
|
|
13
|
-
user_id: "user-1",
|
|
14
|
-
content: "test content",
|
|
15
|
-
trust_level: "learned",
|
|
16
|
-
memory_type: "semantic",
|
|
17
|
-
source_who: "agent",
|
|
18
|
-
source_channel: "telegram",
|
|
19
|
-
source_session_key: "sess-123",
|
|
20
|
-
tags: '["tag1","tag2"]',
|
|
21
|
-
created_at: 1700000000000,
|
|
22
|
-
updated_at: 1700001000000,
|
|
23
|
-
expires_at: 1700090000000,
|
|
24
|
-
has_embedding: 0,
|
|
25
|
-
};
|
|
26
|
-
return { ...defaults, ...overrides };
|
|
27
|
-
}
|
|
28
|
-
/** Create a minimal valid MemoryEntry. */
|
|
29
|
-
function makeEntry(overrides) {
|
|
30
|
-
return {
|
|
31
|
-
id: overrides?.id ?? crypto.randomUUID(),
|
|
32
|
-
tenantId: overrides?.tenantId ?? "default",
|
|
33
|
-
agentId: overrides?.agentId ?? "default",
|
|
34
|
-
userId: overrides?.userId ?? "user-1",
|
|
35
|
-
content: overrides?.content ?? "test memory content",
|
|
36
|
-
trustLevel: overrides?.trustLevel ?? "learned",
|
|
37
|
-
source: overrides?.source ?? { who: "agent", channel: "telegram" },
|
|
38
|
-
tags: overrides?.tags ?? [],
|
|
39
|
-
createdAt: overrides?.createdAt ?? Date.now(),
|
|
40
|
-
...(overrides?.updatedAt !== undefined ? { updatedAt: overrides.updatedAt } : {}),
|
|
41
|
-
...(overrides?.expiresAt !== undefined ? { expiresAt: overrides.expiresAt } : {}),
|
|
42
|
-
...(overrides?.embedding ? { embedding: overrides.embedding } : {}),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
// ── rowToEntry ───────────────────────────────────────────────────────
|
|
46
|
-
describe("rowToEntry", () => {
|
|
47
|
-
it("converts a complete MemoryRow with all fields populated", () => {
|
|
48
|
-
const row = makeRow();
|
|
49
|
-
const entry = rowToEntry(row);
|
|
50
|
-
expect(entry.id).toBe("row-1");
|
|
51
|
-
expect(entry.tenantId).toBe("default");
|
|
52
|
-
expect(entry.agentId).toBe("default");
|
|
53
|
-
expect(entry.userId).toBe("user-1");
|
|
54
|
-
expect(entry.content).toBe("test content");
|
|
55
|
-
expect(entry.trustLevel).toBe("learned");
|
|
56
|
-
expect(entry.source.who).toBe("agent");
|
|
57
|
-
expect(entry.source.channel).toBe("telegram");
|
|
58
|
-
expect(entry.source.sessionKey).toBe("sess-123");
|
|
59
|
-
expect(entry.tags).toEqual(["tag1", "tag2"]);
|
|
60
|
-
expect(entry.createdAt).toBe(1700000000000);
|
|
61
|
-
expect(entry.updatedAt).toBe(1700001000000);
|
|
62
|
-
expect(entry.expiresAt).toBe(1700090000000);
|
|
63
|
-
});
|
|
64
|
-
it("converts a MemoryRow with null optional fields", () => {
|
|
65
|
-
const row = makeRow({
|
|
66
|
-
source_channel: null,
|
|
67
|
-
source_session_key: null,
|
|
68
|
-
updated_at: null,
|
|
69
|
-
expires_at: null,
|
|
70
|
-
});
|
|
71
|
-
const entry = rowToEntry(row);
|
|
72
|
-
expect(entry.source.channel).toBeUndefined();
|
|
73
|
-
expect(entry.source.sessionKey).toBeUndefined();
|
|
74
|
-
expect(entry.updatedAt).toBeUndefined();
|
|
75
|
-
expect(entry.expiresAt).toBeUndefined();
|
|
76
|
-
});
|
|
77
|
-
it("includes embedding when provided", () => {
|
|
78
|
-
const row = makeRow();
|
|
79
|
-
const embedding = [0.1, 0.2, 0.3, 0.4];
|
|
80
|
-
const entry = rowToEntry(row, embedding);
|
|
81
|
-
expect(entry.embedding).toEqual([0.1, 0.2, 0.3, 0.4]);
|
|
82
|
-
});
|
|
83
|
-
it("omits embedding when not provided", () => {
|
|
84
|
-
const row = makeRow();
|
|
85
|
-
const entry = rowToEntry(row);
|
|
86
|
-
expect(entry.embedding).toBeUndefined();
|
|
87
|
-
});
|
|
88
|
-
it("correctly parses JSON tags", () => {
|
|
89
|
-
const row = makeRow({ tags: '["important","project-x","review"]' });
|
|
90
|
-
const entry = rowToEntry(row);
|
|
91
|
-
expect(entry.tags).toEqual(["important", "project-x", "review"]);
|
|
92
|
-
});
|
|
93
|
-
it("parses empty JSON tags array", () => {
|
|
94
|
-
const row = makeRow({ tags: "[]" });
|
|
95
|
-
const entry = rowToEntry(row);
|
|
96
|
-
expect(entry.tags).toEqual([]);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
// ── insertMemoryRow ──────────────────────────────────────────────────
|
|
100
|
-
describe("insertMemoryRow", () => {
|
|
101
|
-
let db;
|
|
102
|
-
beforeEach(() => {
|
|
103
|
-
db = new Database(":memory:");
|
|
104
|
-
initSchema(db, DIMS);
|
|
105
|
-
});
|
|
106
|
-
afterEach(() => {
|
|
107
|
-
db.close();
|
|
108
|
-
});
|
|
109
|
-
it("inserts a row with all fields", () => {
|
|
110
|
-
const entry = makeEntry({
|
|
111
|
-
id: "mem-1",
|
|
112
|
-
tenantId: "tenant-a",
|
|
113
|
-
agentId: "coder",
|
|
114
|
-
userId: "user-42",
|
|
115
|
-
content: "important fact",
|
|
116
|
-
trustLevel: "system",
|
|
117
|
-
source: { who: "admin", channel: "cli", sessionKey: "sk-1" },
|
|
118
|
-
tags: ["critical"],
|
|
119
|
-
createdAt: 1700000000000,
|
|
120
|
-
updatedAt: 1700001000000,
|
|
121
|
-
expiresAt: 1700090000000,
|
|
122
|
-
});
|
|
123
|
-
insertMemoryRow(db, entry, "semantic");
|
|
124
|
-
const row = db.prepare("SELECT * FROM memories WHERE id = ?").get("mem-1");
|
|
125
|
-
expect(row.id).toBe("mem-1");
|
|
126
|
-
expect(row.tenant_id).toBe("tenant-a");
|
|
127
|
-
expect(row.agent_id).toBe("coder");
|
|
128
|
-
expect(row.user_id).toBe("user-42");
|
|
129
|
-
expect(row.content).toBe("important fact");
|
|
130
|
-
expect(row.trust_level).toBe("system");
|
|
131
|
-
expect(row.memory_type).toBe("semantic");
|
|
132
|
-
expect(row.source_who).toBe("admin");
|
|
133
|
-
expect(row.source_channel).toBe("cli");
|
|
134
|
-
expect(row.source_session_key).toBe("sk-1");
|
|
135
|
-
expect(JSON.parse(row.tags)).toEqual(["critical"]);
|
|
136
|
-
expect(row.created_at).toBe(1700000000000);
|
|
137
|
-
expect(row.updated_at).toBe(1700001000000);
|
|
138
|
-
expect(row.expires_at).toBe(1700090000000);
|
|
139
|
-
expect(row.has_embedding).toBe(0);
|
|
140
|
-
});
|
|
141
|
-
it("defaults agentId to 'default' when undefined", () => {
|
|
142
|
-
const entry = makeEntry({ id: "mem-2" });
|
|
143
|
-
// Force agentId to undefined to test default path
|
|
144
|
-
entry.agentId = undefined;
|
|
145
|
-
insertMemoryRow(db, entry, "working");
|
|
146
|
-
const row = db.prepare("SELECT agent_id FROM memories WHERE id = ?").get("mem-2");
|
|
147
|
-
expect(row.agent_id).toBe("default");
|
|
148
|
-
});
|
|
149
|
-
it("handles null optional fields (source_channel, source_session_key, expires_at)", () => {
|
|
150
|
-
const entry = makeEntry({
|
|
151
|
-
id: "mem-3",
|
|
152
|
-
source: { who: "agent" },
|
|
153
|
-
});
|
|
154
|
-
insertMemoryRow(db, entry, "episodic");
|
|
155
|
-
const row = db.prepare("SELECT * FROM memories WHERE id = ?").get("mem-3");
|
|
156
|
-
expect(row.source_channel).toBeNull();
|
|
157
|
-
expect(row.source_session_key).toBeNull();
|
|
158
|
-
expect(row.expires_at).toBeNull();
|
|
159
|
-
});
|
|
160
|
-
it("stores correct memory_type", () => {
|
|
161
|
-
const entry = makeEntry({ id: "mem-4" });
|
|
162
|
-
insertMemoryRow(db, entry, "procedural");
|
|
163
|
-
const row = db.prepare("SELECT memory_type FROM memories WHERE id = ?").get("mem-4");
|
|
164
|
-
expect(row.memory_type).toBe("procedural");
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
// ── storeEmbedding ───────────────────────────────────────────────────
|
|
168
|
-
describe("storeEmbedding", () => {
|
|
169
|
-
let db;
|
|
170
|
-
beforeEach(() => {
|
|
171
|
-
db = new Database(":memory:");
|
|
172
|
-
initSchema(db, DIMS);
|
|
173
|
-
});
|
|
174
|
-
afterEach(() => {
|
|
175
|
-
db.close();
|
|
176
|
-
});
|
|
177
|
-
it("inserts embedding and sets has_embedding=1", () => {
|
|
178
|
-
if (!isVecAvailable())
|
|
179
|
-
return;
|
|
180
|
-
// First insert a memory row
|
|
181
|
-
const entry = makeEntry({ id: "mem-embed-1" });
|
|
182
|
-
insertMemoryRow(db, entry, "semantic");
|
|
183
|
-
// Store embedding
|
|
184
|
-
storeEmbedding(db, "mem-embed-1", [0.1, 0.2, 0.3, 0.4]);
|
|
185
|
-
// Verify has_embedding flag
|
|
186
|
-
const row = db.prepare("SELECT has_embedding FROM memories WHERE id = ?").get("mem-embed-1");
|
|
187
|
-
expect(row.has_embedding).toBe(1);
|
|
188
|
-
// Verify vec_memories entry exists
|
|
189
|
-
const vecRow = db
|
|
190
|
-
.prepare("SELECT memory_id FROM vec_memories WHERE memory_id = ?")
|
|
191
|
-
.get("mem-embed-1");
|
|
192
|
-
expect(vecRow).toBeDefined();
|
|
193
|
-
expect(vecRow.memory_id).toBe("mem-embed-1");
|
|
194
|
-
});
|
|
195
|
-
it("stores correct float values in vec_memories", () => {
|
|
196
|
-
if (!isVecAvailable())
|
|
197
|
-
return;
|
|
198
|
-
const entry = makeEntry({ id: "mem-embed-2" });
|
|
199
|
-
insertMemoryRow(db, entry, "semantic");
|
|
200
|
-
storeEmbedding(db, "mem-embed-2", [0.5, 0.25, 0.75, 1.0]);
|
|
201
|
-
const vecRow = db
|
|
202
|
-
.prepare("SELECT embedding FROM vec_memories WHERE memory_id = ?")
|
|
203
|
-
.get("mem-embed-2");
|
|
204
|
-
expect(vecRow).toBeDefined();
|
|
205
|
-
const float32 = new Float32Array(vecRow.embedding.buffer, vecRow.embedding.byteOffset, vecRow.embedding.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
|
206
|
-
expect(float32[0]).toBeCloseTo(0.5, 4);
|
|
207
|
-
expect(float32[1]).toBeCloseTo(0.25, 4);
|
|
208
|
-
expect(float32[2]).toBeCloseTo(0.75, 4);
|
|
209
|
-
expect(float32[3]).toBeCloseTo(1.0, 4);
|
|
210
|
-
});
|
|
211
|
-
it("no-ops when sqlite-vec is unavailable", () => {
|
|
212
|
-
// This test verifies the function doesn't throw when vec is unavailable.
|
|
213
|
-
// We can't easily mock isVecAvailable, but we verify it's called
|
|
214
|
-
// by passing through without error. If vec IS available, this test
|
|
215
|
-
// exercises the normal path instead.
|
|
216
|
-
const entry = makeEntry({ id: "mem-embed-3" });
|
|
217
|
-
insertMemoryRow(db, entry, "semantic");
|
|
218
|
-
// Should not throw regardless of vec availability
|
|
219
|
-
expect(() => storeEmbedding(db, "mem-embed-3", [0.1, 0.2, 0.3, 0.4])).not.toThrow();
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
// ── buildFilterClause ────────────────────────────────────────────────
|
|
223
|
-
describe("buildFilterClause", () => {
|
|
224
|
-
it("empty filters produce empty clause", () => {
|
|
225
|
-
const result = buildFilterClause({});
|
|
226
|
-
expect(result.clause).toBe("");
|
|
227
|
-
expect(result.params).toEqual([]);
|
|
228
|
-
});
|
|
229
|
-
it("single filter produces correct WHERE clause", () => {
|
|
230
|
-
const result = buildFilterClause({ tenantId: "t1" });
|
|
231
|
-
expect(result.clause).toBe("WHERE tenant_id = ?");
|
|
232
|
-
expect(result.params).toEqual(["t1"]);
|
|
233
|
-
});
|
|
234
|
-
it("multiple filters combine with AND", () => {
|
|
235
|
-
const result = buildFilterClause({
|
|
236
|
-
memoryType: "semantic",
|
|
237
|
-
trustLevel: "learned",
|
|
238
|
-
tenantId: "t1",
|
|
239
|
-
});
|
|
240
|
-
expect(result.clause).toBe("WHERE memory_type = ? AND trust_level = ? AND tenant_id = ?");
|
|
241
|
-
expect(result.params).toEqual(["semantic", "learned", "t1"]);
|
|
242
|
-
});
|
|
243
|
-
it("params array matches clause placeholders", () => {
|
|
244
|
-
const result = buildFilterClause({
|
|
245
|
-
agentId: "coder",
|
|
246
|
-
createdAfter: 1000,
|
|
247
|
-
createdBefore: 2000,
|
|
248
|
-
});
|
|
249
|
-
const placeholderCount = (result.clause.match(/\?/g) ?? []).length;
|
|
250
|
-
expect(placeholderCount).toBe(result.params.length);
|
|
251
|
-
expect(result.params).toEqual(["coder", 1000, 2000]);
|
|
252
|
-
});
|
|
253
|
-
it("handles all filter types together", () => {
|
|
254
|
-
const result = buildFilterClause({
|
|
255
|
-
memoryType: "episodic",
|
|
256
|
-
trustLevel: "system",
|
|
257
|
-
tenantId: "t1",
|
|
258
|
-
agentId: "bot",
|
|
259
|
-
createdAfter: 100,
|
|
260
|
-
createdBefore: 900,
|
|
261
|
-
olderThan: 800,
|
|
262
|
-
});
|
|
263
|
-
expect(result.clause).toContain("WHERE");
|
|
264
|
-
expect(result.clause).toContain("memory_type = ?");
|
|
265
|
-
expect(result.clause).toContain("trust_level = ?");
|
|
266
|
-
expect(result.clause).toContain("tenant_id = ?");
|
|
267
|
-
expect(result.clause).toContain("agent_id = ?");
|
|
268
|
-
expect(result.clause).toContain("created_at > ?");
|
|
269
|
-
// createdBefore and olderThan both produce "created_at < ?"
|
|
270
|
-
expect((result.clause.match(/created_at < \?/g) ?? []).length).toBe(2);
|
|
271
|
-
expect(result.params).toHaveLength(7);
|
|
272
|
-
});
|
|
273
|
-
it("undefined values are skipped", () => {
|
|
274
|
-
const result = buildFilterClause({
|
|
275
|
-
memoryType: undefined,
|
|
276
|
-
trustLevel: "external",
|
|
277
|
-
tenantId: undefined,
|
|
278
|
-
});
|
|
279
|
-
expect(result.clause).toBe("WHERE trust_level = ?");
|
|
280
|
-
expect(result.params).toEqual(["external"]);
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
// ── countRows ───────────────────────────────────────────────────────
|
|
284
|
-
describe("countRows", () => {
|
|
285
|
-
let db;
|
|
286
|
-
beforeEach(() => {
|
|
287
|
-
db = new Database(":memory:");
|
|
288
|
-
initSchema(db, DIMS);
|
|
289
|
-
});
|
|
290
|
-
afterEach(() => {
|
|
291
|
-
db.close();
|
|
292
|
-
});
|
|
293
|
-
it("returns 0 for an empty table", () => {
|
|
294
|
-
const count = countRows(db, "memories", "", []);
|
|
295
|
-
expect(count).toBe(0);
|
|
296
|
-
});
|
|
297
|
-
it("returns correct count with entries", () => {
|
|
298
|
-
insertMemoryRow(db, makeEntry({ id: "cr-1" }), "semantic");
|
|
299
|
-
insertMemoryRow(db, makeEntry({ id: "cr-2" }), "episodic");
|
|
300
|
-
insertMemoryRow(db, makeEntry({ id: "cr-3" }), "semantic");
|
|
301
|
-
const count = countRows(db, "memories", "", []);
|
|
302
|
-
expect(count).toBe(3);
|
|
303
|
-
});
|
|
304
|
-
it("filters correctly with WHERE clause", () => {
|
|
305
|
-
insertMemoryRow(db, makeEntry({ id: "cr-4", tenantId: "t1" }), "semantic");
|
|
306
|
-
insertMemoryRow(db, makeEntry({ id: "cr-5", tenantId: "t1" }), "episodic");
|
|
307
|
-
insertMemoryRow(db, makeEntry({ id: "cr-6", tenantId: "t2" }), "semantic");
|
|
308
|
-
const count = countRows(db, "memories", "WHERE tenant_id = ?", ["t1"]);
|
|
309
|
-
expect(count).toBe(2);
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
// ── groupCountRows ──────────────────────────────────────────────────
|
|
313
|
-
describe("groupCountRows", () => {
|
|
314
|
-
let db;
|
|
315
|
-
beforeEach(() => {
|
|
316
|
-
db = new Database(":memory:");
|
|
317
|
-
initSchema(db, DIMS);
|
|
318
|
-
});
|
|
319
|
-
afterEach(() => {
|
|
320
|
-
db.close();
|
|
321
|
-
});
|
|
322
|
-
it("returns empty object for an empty table", () => {
|
|
323
|
-
const result = groupCountRows(db, "memories", "memory_type", "", []);
|
|
324
|
-
expect(result).toEqual({});
|
|
325
|
-
});
|
|
326
|
-
it("groups by memory_type correctly", () => {
|
|
327
|
-
insertMemoryRow(db, makeEntry({ id: "gc-1" }), "semantic");
|
|
328
|
-
insertMemoryRow(db, makeEntry({ id: "gc-2" }), "semantic");
|
|
329
|
-
insertMemoryRow(db, makeEntry({ id: "gc-3" }), "episodic");
|
|
330
|
-
insertMemoryRow(db, makeEntry({ id: "gc-4" }), "working");
|
|
331
|
-
const result = groupCountRows(db, "memories", "memory_type", "", []);
|
|
332
|
-
expect(result).toEqual({
|
|
333
|
-
semantic: 2,
|
|
334
|
-
episodic: 1,
|
|
335
|
-
working: 1,
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
it("filters groups with WHERE clause", () => {
|
|
339
|
-
insertMemoryRow(db, makeEntry({ id: "gc-5", tenantId: "t1" }), "semantic");
|
|
340
|
-
insertMemoryRow(db, makeEntry({ id: "gc-6", tenantId: "t1" }), "episodic");
|
|
341
|
-
insertMemoryRow(db, makeEntry({ id: "gc-7", tenantId: "t2" }), "semantic");
|
|
342
|
-
const result = groupCountRows(db, "memories", "memory_type", "WHERE tenant_id = ?", ["t1"]);
|
|
343
|
-
expect(result).toEqual({
|
|
344
|
-
semantic: 1,
|
|
345
|
-
episodic: 1,
|
|
346
|
-
});
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
// ── DATA-03: Whitelist validation for table/column names ───────────
|
|
350
|
-
describe("countRows whitelist validation", () => {
|
|
351
|
-
let db;
|
|
352
|
-
beforeEach(() => {
|
|
353
|
-
db = new Database(":memory:");
|
|
354
|
-
initSchema(db, DIMS);
|
|
355
|
-
});
|
|
356
|
-
afterEach(() => {
|
|
357
|
-
db.close();
|
|
358
|
-
});
|
|
359
|
-
it("accepts 'memories' table", () => {
|
|
360
|
-
expect(() => countRows(db, "memories", "", [])).not.toThrow();
|
|
361
|
-
});
|
|
362
|
-
it("accepts 'sessions' table", () => {
|
|
363
|
-
expect(() => countRows(db, "sessions", "", [])).not.toThrow();
|
|
364
|
-
});
|
|
365
|
-
it("rejects invalid table name", () => {
|
|
366
|
-
expect(() => countRows(db, "evil_table", "", [])).toThrow('countRows: invalid table "evil_table"');
|
|
367
|
-
});
|
|
368
|
-
it("rejects SQL injection in table name", () => {
|
|
369
|
-
expect(() => countRows(db, "memories; DROP TABLE memories;--", "", [])).toThrow("countRows: invalid table");
|
|
370
|
-
});
|
|
371
|
-
});
|
|
372
|
-
describe("groupCountRows whitelist validation", () => {
|
|
373
|
-
let db;
|
|
374
|
-
beforeEach(() => {
|
|
375
|
-
db = new Database(":memory:");
|
|
376
|
-
initSchema(db, DIMS);
|
|
377
|
-
});
|
|
378
|
-
afterEach(() => {
|
|
379
|
-
db.close();
|
|
380
|
-
});
|
|
381
|
-
it("accepts 'memory_type' column", () => {
|
|
382
|
-
expect(() => groupCountRows(db, "memories", "memory_type", "", [])).not.toThrow();
|
|
383
|
-
});
|
|
384
|
-
it("accepts 'trust_level' column", () => {
|
|
385
|
-
expect(() => groupCountRows(db, "memories", "trust_level", "", [])).not.toThrow();
|
|
386
|
-
});
|
|
387
|
-
it("accepts 'agent_id' column", () => {
|
|
388
|
-
expect(() => groupCountRows(db, "memories", "agent_id", "", [])).not.toThrow();
|
|
389
|
-
});
|
|
390
|
-
it("rejects invalid column name", () => {
|
|
391
|
-
expect(() => groupCountRows(db, "memories", "evil_column", "", [])).toThrow('groupCountRows: invalid column "evil_column"');
|
|
392
|
-
});
|
|
393
|
-
it("rejects invalid table name", () => {
|
|
394
|
-
expect(() => groupCountRows(db, "evil_table", "memory_type", "", [])).toThrow('groupCountRows: invalid table "evil_table"');
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
describe("whitelist exports", () => {
|
|
398
|
-
it("ALLOWED_TABLES contains expected values", () => {
|
|
399
|
-
expect(ALLOWED_TABLES.has("memories")).toBe(true);
|
|
400
|
-
expect(ALLOWED_TABLES.has("sessions")).toBe(true);
|
|
401
|
-
expect(ALLOWED_TABLES.size).toBe(2);
|
|
402
|
-
});
|
|
403
|
-
it("ALLOWED_GROUP_COLUMNS contains expected values", () => {
|
|
404
|
-
expect(ALLOWED_GROUP_COLUMNS.has("memory_type")).toBe(true);
|
|
405
|
-
expect(ALLOWED_GROUP_COLUMNS.has("trust_level")).toBe(true);
|
|
406
|
-
expect(ALLOWED_GROUP_COLUMNS.has("agent_id")).toBe(true);
|
|
407
|
-
expect(ALLOWED_GROUP_COLUMNS.size).toBe(3);
|
|
408
|
-
});
|
|
409
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import Database from "better-sqlite3";
|
|
2
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
3
|
-
import { initSchema, isVecAvailable } from "./schema.js";
|
|
4
|
-
describe("initSchema", () => {
|
|
5
|
-
let db;
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
db = new Database(":memory:");
|
|
8
|
-
});
|
|
9
|
-
it("creates the memories table with correct columns", () => {
|
|
10
|
-
initSchema(db, 1536);
|
|
11
|
-
const columns = db.prepare("PRAGMA table_info(memories)").all();
|
|
12
|
-
const colNames = columns.map((c) => c.name);
|
|
13
|
-
expect(colNames).toContain("id");
|
|
14
|
-
expect(colNames).toContain("tenant_id");
|
|
15
|
-
expect(colNames).toContain("user_id");
|
|
16
|
-
expect(colNames).toContain("content");
|
|
17
|
-
expect(colNames).toContain("trust_level");
|
|
18
|
-
expect(colNames).toContain("memory_type");
|
|
19
|
-
expect(colNames).toContain("source_who");
|
|
20
|
-
expect(colNames).toContain("source_channel");
|
|
21
|
-
expect(colNames).toContain("source_session_key");
|
|
22
|
-
expect(colNames).toContain("tags");
|
|
23
|
-
expect(colNames).toContain("created_at");
|
|
24
|
-
expect(colNames).toContain("updated_at");
|
|
25
|
-
expect(colNames).toContain("expires_at");
|
|
26
|
-
expect(colNames).toContain("has_embedding");
|
|
27
|
-
});
|
|
28
|
-
it("creates the sessions table", () => {
|
|
29
|
-
initSchema(db, 1536);
|
|
30
|
-
const columns = db.prepare("PRAGMA table_info(sessions)").all();
|
|
31
|
-
const colNames = columns.map((c) => c.name);
|
|
32
|
-
expect(colNames).toContain("session_key");
|
|
33
|
-
expect(colNames).toContain("tenant_id");
|
|
34
|
-
expect(colNames).toContain("user_id");
|
|
35
|
-
expect(colNames).toContain("channel_id");
|
|
36
|
-
expect(colNames).toContain("messages");
|
|
37
|
-
expect(colNames).toContain("created_at");
|
|
38
|
-
expect(colNames).toContain("updated_at");
|
|
39
|
-
expect(colNames).toContain("metadata");
|
|
40
|
-
});
|
|
41
|
-
it("creates the memory_fts virtual table", () => {
|
|
42
|
-
initSchema(db, 1536);
|
|
43
|
-
const tables = db
|
|
44
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memory_fts'")
|
|
45
|
-
.all();
|
|
46
|
-
expect(tables).toHaveLength(1);
|
|
47
|
-
expect(tables[0].name).toBe("memory_fts");
|
|
48
|
-
});
|
|
49
|
-
it("creates the vec_memories virtual table when vec is available", () => {
|
|
50
|
-
initSchema(db, 1536);
|
|
51
|
-
if (!isVecAvailable()) {
|
|
52
|
-
// Skip on platforms where sqlite-vec cannot load
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
const tables = db
|
|
56
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='vec_memories'")
|
|
57
|
-
.all();
|
|
58
|
-
expect(tables).toHaveLength(1);
|
|
59
|
-
expect(tables[0].name).toBe("vec_memories");
|
|
60
|
-
});
|
|
61
|
-
it("creates all memory indexes", () => {
|
|
62
|
-
initSchema(db, 1536);
|
|
63
|
-
const indexes = db
|
|
64
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_memories_%'")
|
|
65
|
-
.all();
|
|
66
|
-
const indexNames = indexes.map((i) => i.name);
|
|
67
|
-
expect(indexNames).toContain("idx_memories_tenant");
|
|
68
|
-
expect(indexNames).toContain("idx_memories_trust");
|
|
69
|
-
expect(indexNames).toContain("idx_memories_type");
|
|
70
|
-
expect(indexNames).toContain("idx_memories_created");
|
|
71
|
-
expect(indexNames).toContain("idx_memories_expires");
|
|
72
|
-
});
|
|
73
|
-
it("creates all session indexes", () => {
|
|
74
|
-
initSchema(db, 1536);
|
|
75
|
-
const indexes = db
|
|
76
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_sessions_%'")
|
|
77
|
-
.all();
|
|
78
|
-
const indexNames = indexes.map((i) => i.name);
|
|
79
|
-
expect(indexNames).toContain("idx_sessions_tenant");
|
|
80
|
-
expect(indexNames).toContain("idx_sessions_updated");
|
|
81
|
-
});
|
|
82
|
-
it("FTS5 trigger fires on INSERT", () => {
|
|
83
|
-
initSchema(db, 1536);
|
|
84
|
-
db.prepare(`INSERT INTO memories (id, tenant_id, user_id, content, trust_level, memory_type, source_who, tags, created_at)
|
|
85
|
-
VALUES ('m1', 'default', 'u1', 'the quick brown fox', 'learned', 'semantic', 'agent', '[]', 1000)`).run();
|
|
86
|
-
const ftsRows = db
|
|
87
|
-
.prepare("SELECT * FROM memory_fts WHERE memory_fts MATCH 'fox'")
|
|
88
|
-
.all();
|
|
89
|
-
expect(ftsRows).toHaveLength(1);
|
|
90
|
-
expect(ftsRows[0].content).toBe("the quick brown fox");
|
|
91
|
-
});
|
|
92
|
-
it("FTS5 trigger fires on DELETE", () => {
|
|
93
|
-
initSchema(db, 1536);
|
|
94
|
-
db.prepare(`INSERT INTO memories (id, tenant_id, user_id, content, trust_level, memory_type, source_who, tags, created_at)
|
|
95
|
-
VALUES ('m1', 'default', 'u1', 'the quick brown fox', 'learned', 'semantic', 'agent', '[]', 1000)`).run();
|
|
96
|
-
// Verify it's in FTS
|
|
97
|
-
let ftsRows = db.prepare("SELECT * FROM memory_fts WHERE memory_fts MATCH 'fox'").all();
|
|
98
|
-
expect(ftsRows).toHaveLength(1);
|
|
99
|
-
// Delete from base table
|
|
100
|
-
db.prepare("DELETE FROM memories WHERE id = 'm1'").run();
|
|
101
|
-
// Verify removed from FTS
|
|
102
|
-
ftsRows = db.prepare("SELECT * FROM memory_fts WHERE memory_fts MATCH 'fox'").all();
|
|
103
|
-
expect(ftsRows).toHaveLength(0);
|
|
104
|
-
});
|
|
105
|
-
it("FTS5 trigger fires on UPDATE of content", () => {
|
|
106
|
-
initSchema(db, 1536);
|
|
107
|
-
db.prepare(`INSERT INTO memories (id, tenant_id, user_id, content, trust_level, memory_type, source_who, tags, created_at)
|
|
108
|
-
VALUES ('m1', 'default', 'u1', 'the quick brown fox', 'learned', 'semantic', 'agent', '[]', 1000)`).run();
|
|
109
|
-
// Update content
|
|
110
|
-
db.prepare("UPDATE memories SET content = 'lazy dog sleeps' WHERE id = 'm1'").run();
|
|
111
|
-
// Old content should not match
|
|
112
|
-
const oldRows = db.prepare("SELECT * FROM memory_fts WHERE memory_fts MATCH 'fox'").all();
|
|
113
|
-
expect(oldRows).toHaveLength(0);
|
|
114
|
-
// New content should match
|
|
115
|
-
const newRows = db
|
|
116
|
-
.prepare("SELECT * FROM memory_fts WHERE memory_fts MATCH 'dog'")
|
|
117
|
-
.all();
|
|
118
|
-
expect(newRows).toHaveLength(1);
|
|
119
|
-
expect(newRows[0].content).toBe("lazy dog sleeps");
|
|
120
|
-
});
|
|
121
|
-
it("trust_level CHECK constraint rejects invalid values", () => {
|
|
122
|
-
initSchema(db, 1536);
|
|
123
|
-
expect(() => {
|
|
124
|
-
db.prepare(`INSERT INTO memories (id, tenant_id, user_id, content, trust_level, memory_type, source_who, tags, created_at)
|
|
125
|
-
VALUES ('m1', 'default', 'u1', 'test', 'invalid', 'semantic', 'agent', '[]', 1000)`).run();
|
|
126
|
-
}).toThrow();
|
|
127
|
-
});
|
|
128
|
-
it("memory_type CHECK constraint rejects invalid values", () => {
|
|
129
|
-
initSchema(db, 1536);
|
|
130
|
-
expect(() => {
|
|
131
|
-
db.prepare(`INSERT INTO memories (id, tenant_id, user_id, content, trust_level, memory_type, source_who, tags, created_at)
|
|
132
|
-
VALUES ('m1', 'default', 'u1', 'test', 'learned', 'invalid_type', 'agent', '[]', 1000)`).run();
|
|
133
|
-
}).toThrow();
|
|
134
|
-
});
|
|
135
|
-
it("isVecAvailable() returns true after successful init", () => {
|
|
136
|
-
initSchema(db, 1536);
|
|
137
|
-
// On platforms with sqlite-vec support, this should be true
|
|
138
|
-
// This test validates the flag is set correctly
|
|
139
|
-
const available = isVecAvailable();
|
|
140
|
-
expect(typeof available).toBe("boolean");
|
|
141
|
-
// On this platform, sqlite-vec should load
|
|
142
|
-
expect(available).toBe(true);
|
|
143
|
-
});
|
|
144
|
-
it("initSchema is idempotent -- calling twice does not error", () => {
|
|
145
|
-
initSchema(db, 1536);
|
|
146
|
-
expect(() => initSchema(db, 1536)).not.toThrow();
|
|
147
|
-
// Tables still exist after second call
|
|
148
|
-
const tables = db
|
|
149
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name IN ('memories', 'sessions')")
|
|
150
|
-
.all();
|
|
151
|
-
expect(tables).toHaveLength(2);
|
|
152
|
-
});
|
|
153
|
-
it("vec_memories dimension matches config", () => {
|
|
154
|
-
initSchema(db, 384);
|
|
155
|
-
if (!isVecAvailable())
|
|
156
|
-
return;
|
|
157
|
-
// Insert a vector with correct dimensions (384)
|
|
158
|
-
const float32 = new Float32Array(384);
|
|
159
|
-
float32[0] = 1.0;
|
|
160
|
-
expect(() => {
|
|
161
|
-
db.prepare("INSERT INTO vec_memories(memory_id, embedding) VALUES (?, ?)").run("test-id", float32);
|
|
162
|
-
}).not.toThrow();
|
|
163
|
-
// Verify the entry exists
|
|
164
|
-
const row = db
|
|
165
|
-
.prepare("SELECT memory_id FROM vec_memories WHERE memory_id = ?")
|
|
166
|
-
.get("test-id");
|
|
167
|
-
expect(row).toBeDefined();
|
|
168
|
-
expect(row.memory_id).toBe("test-id");
|
|
169
|
-
});
|
|
170
|
-
it("vec_memories rejects vectors with wrong dimensions", () => {
|
|
171
|
-
initSchema(db, 384);
|
|
172
|
-
if (!isVecAvailable())
|
|
173
|
-
return;
|
|
174
|
-
// Try to insert a vector with wrong dimensions (1536 instead of 384)
|
|
175
|
-
const wrongFloat32 = new Float32Array(1536);
|
|
176
|
-
wrongFloat32[0] = 1.0;
|
|
177
|
-
expect(() => {
|
|
178
|
-
db.prepare("INSERT INTO vec_memories(memory_id, embedding) VALUES (?, ?)").run("test-wrong", wrongFloat32);
|
|
179
|
-
}).toThrow();
|
|
180
|
-
});
|
|
181
|
-
it("accepts all valid trust_level values", () => {
|
|
182
|
-
initSchema(db, 1536);
|
|
183
|
-
const levels = ["system", "learned", "external"];
|
|
184
|
-
for (const level of levels) {
|
|
185
|
-
expect(() => {
|
|
186
|
-
db.prepare(`INSERT INTO memories (id, tenant_id, user_id, content, trust_level, memory_type, source_who, tags, created_at)
|
|
187
|
-
VALUES (?, 'default', 'u1', 'test', ?, 'semantic', 'agent', '[]', 1000)`).run(`m-${level}`, level);
|
|
188
|
-
}).not.toThrow();
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
it("accepts all valid memory_type values", () => {
|
|
192
|
-
initSchema(db, 1536);
|
|
193
|
-
const types = ["working", "episodic", "semantic", "procedural"];
|
|
194
|
-
for (const type of types) {
|
|
195
|
-
expect(() => {
|
|
196
|
-
db.prepare(`INSERT INTO memories (id, tenant_id, user_id, content, trust_level, memory_type, source_who, tags, created_at)
|
|
197
|
-
VALUES (?, 'default', 'u1', 'test', 'learned', ?, 'agent', '[]', 1000)`).run(`m-${type}`, type);
|
|
198
|
-
}).not.toThrow();
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
// ── DATA-05: initSchema returns per-instance vecAvailable ──────
|
|
202
|
-
describe("initSchema return value", () => {
|
|
203
|
-
it("returns { vecAvailable: boolean }", () => {
|
|
204
|
-
const result = initSchema(db, 1536);
|
|
205
|
-
expect(result).toBeDefined();
|
|
206
|
-
expect(typeof result.vecAvailable).toBe("boolean");
|
|
207
|
-
});
|
|
208
|
-
it("returns vecAvailable: true when sqlite-vec loads", () => {
|
|
209
|
-
const result = initSchema(db, 1536);
|
|
210
|
-
// On this platform, sqlite-vec should load
|
|
211
|
-
expect(result.vecAvailable).toBe(true);
|
|
212
|
-
});
|
|
213
|
-
it("returns consistent value across multiple calls on same db", () => {
|
|
214
|
-
const result1 = initSchema(db, 1536);
|
|
215
|
-
const result2 = initSchema(db, 1536);
|
|
216
|
-
expect(result1.vecAvailable).toBe(result2.vecAvailable);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
// ── DATA-02: embeddingDimensions runtime assertion ──────────────
|
|
220
|
-
describe("embeddingDimensions validation", () => {
|
|
221
|
-
it("throws on embeddingDimensions = 0", () => {
|
|
222
|
-
expect(() => initSchema(db, 0)).toThrow("Invalid embeddingDimensions");
|
|
223
|
-
});
|
|
224
|
-
it("throws on embeddingDimensions = -1", () => {
|
|
225
|
-
expect(() => initSchema(db, -1)).toThrow("Invalid embeddingDimensions");
|
|
226
|
-
});
|
|
227
|
-
it("throws on embeddingDimensions = 1.5", () => {
|
|
228
|
-
expect(() => initSchema(db, 1.5)).toThrow("Invalid embeddingDimensions");
|
|
229
|
-
});
|
|
230
|
-
it("throws on embeddingDimensions = NaN", () => {
|
|
231
|
-
expect(() => initSchema(db, NaN)).toThrow("Invalid embeddingDimensions");
|
|
232
|
-
});
|
|
233
|
-
it("does not throw on valid embeddingDimensions = 1536", () => {
|
|
234
|
-
expect(() => initSchema(db, 1536)).not.toThrow();
|
|
235
|
-
});
|
|
236
|
-
it("throws on embeddingDimensions = Infinity", () => {
|
|
237
|
-
expect(() => initSchema(db, Infinity)).toThrow("Invalid embeddingDimensions");
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
});
|