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,298 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { createCompactionService } from "./compaction.js";
|
|
3
|
-
import { createSessionStore } from "./session-store.js";
|
|
4
|
-
import { SqliteMemoryAdapter } from "./sqlite-memory-adapter.js";
|
|
5
|
-
// ── Helpers ────────────────────────────────────────────────────────────
|
|
6
|
-
const testConfig = {
|
|
7
|
-
dbPath: ":memory:",
|
|
8
|
-
walMode: false,
|
|
9
|
-
embeddingModel: "test-model",
|
|
10
|
-
embeddingDimensions: 4,
|
|
11
|
-
compaction: { enabled: false, threshold: 1000, targetSize: 500 },
|
|
12
|
-
retention: { maxAgeDays: 0, maxEntries: 0 },
|
|
13
|
-
};
|
|
14
|
-
const testKey = {
|
|
15
|
-
tenantId: "default",
|
|
16
|
-
userId: "user-1",
|
|
17
|
-
channelId: "telegram",
|
|
18
|
-
};
|
|
19
|
-
const testKey2 = {
|
|
20
|
-
tenantId: "default",
|
|
21
|
-
userId: "user-2",
|
|
22
|
-
channelId: "discord",
|
|
23
|
-
};
|
|
24
|
-
/** Simple summarizer that just concatenates messages and returns fixed facts. */
|
|
25
|
-
const mockSummarizer = async (messages) => ({
|
|
26
|
-
summary: `Summary of ${messages.length} messages`,
|
|
27
|
-
facts: ["fact-1", "fact-2"],
|
|
28
|
-
});
|
|
29
|
-
/** Summarizer that returns no facts. */
|
|
30
|
-
const noFactsSummarizer = async (messages) => ({
|
|
31
|
-
summary: `Summary of ${messages.length} messages`,
|
|
32
|
-
facts: [],
|
|
33
|
-
});
|
|
34
|
-
/** Summarizer that tracks how many times it was called. */
|
|
35
|
-
function trackingSummarizer() {
|
|
36
|
-
const fn = async (messages) => {
|
|
37
|
-
fn.callCount++;
|
|
38
|
-
return {
|
|
39
|
-
summary: `Summary of ${messages.length} messages`,
|
|
40
|
-
facts: ["tracked-fact"],
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
fn.callCount = 0;
|
|
44
|
-
return fn;
|
|
45
|
-
}
|
|
46
|
-
// ── Test Suite ─────────────────────────────────────────────────────────
|
|
47
|
-
describe("createCompactionService", () => {
|
|
48
|
-
let db;
|
|
49
|
-
let adapter;
|
|
50
|
-
let sessionStore;
|
|
51
|
-
let compactionService;
|
|
52
|
-
beforeEach(() => {
|
|
53
|
-
// Use the adapter's internal DB for everything (single :memory: connection)
|
|
54
|
-
adapter = new SqliteMemoryAdapter(testConfig);
|
|
55
|
-
db = adapter.getDb();
|
|
56
|
-
sessionStore = createSessionStore(db);
|
|
57
|
-
compactionService = createCompactionService(db, sessionStore, adapter, mockSummarizer);
|
|
58
|
-
});
|
|
59
|
-
afterEach(() => {
|
|
60
|
-
adapter.close();
|
|
61
|
-
});
|
|
62
|
-
// ── Stale session detection ──────────────────────────────────────
|
|
63
|
-
it("compacts sessions idle for longer than minIdleMs", async () => {
|
|
64
|
-
// Save a session and manually set its updatedAt to the past
|
|
65
|
-
sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
|
|
66
|
-
// Set updatedAt to 5 hours ago
|
|
67
|
-
const fiveHoursAgo = Date.now() - 5 * 60 * 60 * 1000;
|
|
68
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(fiveHoursAgo, "default:user-1:telegram");
|
|
69
|
-
const result = await compactionService.compact({ minIdleMs: 4 * 60 * 60 * 1000 });
|
|
70
|
-
expect(result.sessionsCompacted).toBe(1);
|
|
71
|
-
expect(result.compactedKeys).toContain("default:user-1:telegram");
|
|
72
|
-
});
|
|
73
|
-
it("does NOT compact active sessions (within idle threshold)", async () => {
|
|
74
|
-
// Save a session that was recently updated (within minIdleMs)
|
|
75
|
-
sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
|
|
76
|
-
const result = await compactionService.compact({ minIdleMs: 4 * 60 * 60 * 1000 });
|
|
77
|
-
expect(result.sessionsCompacted).toBe(0);
|
|
78
|
-
expect(result.compactedKeys).toHaveLength(0);
|
|
79
|
-
});
|
|
80
|
-
it("skips sessions with no messages", async () => {
|
|
81
|
-
sessionStore.save(testKey, []);
|
|
82
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
83
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
84
|
-
const result = await compactionService.compact({ minIdleMs: 1000 });
|
|
85
|
-
expect(result.sessionsCompacted).toBe(0);
|
|
86
|
-
});
|
|
87
|
-
// ── Episodic memory creation ─────────────────────────────────────
|
|
88
|
-
it("creates episodic memory from stale session summary", async () => {
|
|
89
|
-
sessionStore.save(testKey, [
|
|
90
|
-
{ role: "user", content: "hello" },
|
|
91
|
-
{ role: "assistant", content: "hi" },
|
|
92
|
-
]);
|
|
93
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
94
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
95
|
-
const result = await compactionService.compact({ minIdleMs: 1000 });
|
|
96
|
-
expect(result.episodicCreated).toBe(1);
|
|
97
|
-
// Verify the episodic memory is in the database
|
|
98
|
-
const episodic = db
|
|
99
|
-
.prepare("SELECT * FROM memories WHERE memory_type = 'episodic'")
|
|
100
|
-
.all();
|
|
101
|
-
expect(episodic).toHaveLength(1);
|
|
102
|
-
expect(episodic[0].content).toBe("Summary of 2 messages");
|
|
103
|
-
expect(episodic[0].source_who).toBe("compaction");
|
|
104
|
-
});
|
|
105
|
-
// ── Semantic fact extraction ─────────────────────────────────────
|
|
106
|
-
it("creates semantic memories from extracted facts", async () => {
|
|
107
|
-
sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
|
|
108
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
109
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
110
|
-
const result = await compactionService.compact({ minIdleMs: 1000 });
|
|
111
|
-
expect(result.factsExtracted).toBe(2);
|
|
112
|
-
const facts = db
|
|
113
|
-
.prepare("SELECT content FROM memories WHERE memory_type = 'semantic' AND tags LIKE '%fact%'")
|
|
114
|
-
.all();
|
|
115
|
-
expect(facts).toHaveLength(2);
|
|
116
|
-
expect(facts.map((f) => f.content)).toContain("fact-1");
|
|
117
|
-
expect(facts.map((f) => f.content)).toContain("fact-2");
|
|
118
|
-
});
|
|
119
|
-
it("handles summarizer returning no facts", async () => {
|
|
120
|
-
const service = createCompactionService(db, sessionStore, adapter, noFactsSummarizer);
|
|
121
|
-
sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
|
|
122
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
123
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
124
|
-
const result = await service.compact({ minIdleMs: 1000 });
|
|
125
|
-
expect(result.episodicCreated).toBe(1);
|
|
126
|
-
expect(result.factsExtracted).toBe(0);
|
|
127
|
-
});
|
|
128
|
-
// ── Archive creation ─────────────────────────────────────────────
|
|
129
|
-
it("archives original messages before deletion", async () => {
|
|
130
|
-
const messages = [
|
|
131
|
-
{ role: "user", content: "hello" },
|
|
132
|
-
{ role: "assistant", content: "hi there" },
|
|
133
|
-
];
|
|
134
|
-
sessionStore.save(testKey, messages);
|
|
135
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
136
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
137
|
-
await compactionService.compact({ minIdleMs: 1000 });
|
|
138
|
-
const archives = db.prepare("SELECT * FROM archives").all();
|
|
139
|
-
expect(archives).toHaveLength(1);
|
|
140
|
-
expect(archives[0].session_key).toBe("default:user-1:telegram");
|
|
141
|
-
expect(JSON.parse(archives[0].messages)).toEqual(messages);
|
|
142
|
-
expect(archives[0].expires_at).toBeGreaterThan(archives[0].archived_at);
|
|
143
|
-
});
|
|
144
|
-
it("archives respect configurable retention period", async () => {
|
|
145
|
-
sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
|
|
146
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
147
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
148
|
-
const customRetention = 3 * 24 * 60 * 60 * 1000; // 3 days
|
|
149
|
-
await compactionService.compact({
|
|
150
|
-
minIdleMs: 1000,
|
|
151
|
-
archiveRetentionMs: customRetention,
|
|
152
|
-
});
|
|
153
|
-
const archive = db.prepare("SELECT * FROM archives").get();
|
|
154
|
-
const actualRetention = archive.expires_at - archive.archived_at;
|
|
155
|
-
expect(actualRetention).toBe(customRetention);
|
|
156
|
-
});
|
|
157
|
-
// ── Session deletion ─────────────────────────────────────────────
|
|
158
|
-
it("deletes the session after compaction", async () => {
|
|
159
|
-
sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
|
|
160
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
161
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
162
|
-
await compactionService.compact({ minIdleMs: 1000 });
|
|
163
|
-
const session = sessionStore.load(testKey);
|
|
164
|
-
expect(session).toBeUndefined();
|
|
165
|
-
});
|
|
166
|
-
// ── Multiple sessions ────────────────────────────────────────────
|
|
167
|
-
it("compacts multiple stale sessions in one run", async () => {
|
|
168
|
-
sessionStore.save(testKey, [{ role: "user", content: "hello from user 1" }]);
|
|
169
|
-
sessionStore.save(testKey2, [{ role: "user", content: "hello from user 2" }]);
|
|
170
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
171
|
-
db.prepare("UPDATE sessions SET updated_at = ?").run(longAgo);
|
|
172
|
-
const result = await compactionService.compact({ minIdleMs: 1000 });
|
|
173
|
-
expect(result.sessionsCompacted).toBe(2);
|
|
174
|
-
expect(result.episodicCreated).toBe(2);
|
|
175
|
-
expect(result.factsExtracted).toBe(4); // 2 facts per session
|
|
176
|
-
});
|
|
177
|
-
it("only compacts stale sessions, leaves active ones alone", async () => {
|
|
178
|
-
sessionStore.save(testKey, [{ role: "user", content: "stale" }]);
|
|
179
|
-
sessionStore.save(testKey2, [{ role: "user", content: "active" }]);
|
|
180
|
-
// Only make testKey stale
|
|
181
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
182
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
183
|
-
const result = await compactionService.compact({ minIdleMs: 1000 });
|
|
184
|
-
expect(result.sessionsCompacted).toBe(1);
|
|
185
|
-
expect(result.compactedKeys).toContain("default:user-1:telegram");
|
|
186
|
-
// Active session should still exist
|
|
187
|
-
const activeSession = sessionStore.load(testKey2);
|
|
188
|
-
expect(activeSession).toBeDefined();
|
|
189
|
-
});
|
|
190
|
-
// ── Pluggable summarizer ─────────────────────────────────────────
|
|
191
|
-
it("calls the pluggable summarizer with session messages", async () => {
|
|
192
|
-
const tracker = trackingSummarizer();
|
|
193
|
-
const service = createCompactionService(db, sessionStore, adapter, tracker);
|
|
194
|
-
const messages = [
|
|
195
|
-
{ role: "user", content: "hello" },
|
|
196
|
-
{ role: "assistant", content: "hi" },
|
|
197
|
-
{ role: "user", content: "bye" },
|
|
198
|
-
];
|
|
199
|
-
sessionStore.save(testKey, messages);
|
|
200
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
201
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
202
|
-
await service.compact({ minIdleMs: 1000 });
|
|
203
|
-
expect(tracker.callCount).toBe(1);
|
|
204
|
-
});
|
|
205
|
-
// ── Tenant scoping ──────────────────────────────────────────────
|
|
206
|
-
it("respects tenantId option for scoped compaction", async () => {
|
|
207
|
-
const key1 = { tenantId: "tenant-a", userId: "u1", channelId: "ch1" };
|
|
208
|
-
const key2 = { tenantId: "tenant-b", userId: "u2", channelId: "ch2" };
|
|
209
|
-
sessionStore.save(key1, [{ role: "user", content: "hello" }]);
|
|
210
|
-
sessionStore.save(key2, [{ role: "user", content: "world" }]);
|
|
211
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
212
|
-
db.prepare("UPDATE sessions SET updated_at = ?").run(longAgo);
|
|
213
|
-
const result = await compactionService.compact({
|
|
214
|
-
minIdleMs: 1000,
|
|
215
|
-
tenantId: "tenant-a",
|
|
216
|
-
});
|
|
217
|
-
expect(result.sessionsCompacted).toBe(1);
|
|
218
|
-
expect(result.compactedKeys[0]).toContain("tenant-a");
|
|
219
|
-
// tenant-b session should still exist
|
|
220
|
-
const tenantBSession = sessionStore.load(key2);
|
|
221
|
-
expect(tenantBSession).toBeDefined();
|
|
222
|
-
});
|
|
223
|
-
// ── purgeArchives ────────────────────────────────────────────────
|
|
224
|
-
it("purgeArchives removes expired archives", async () => {
|
|
225
|
-
sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
|
|
226
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
227
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
228
|
-
// Compact with very short retention so archive expires immediately
|
|
229
|
-
await compactionService.compact({
|
|
230
|
-
minIdleMs: 1000,
|
|
231
|
-
archiveRetentionMs: 1, // 1ms retention
|
|
232
|
-
});
|
|
233
|
-
// Verify archive exists
|
|
234
|
-
const beforePurge = db.prepare("SELECT COUNT(*) as cnt FROM archives").get();
|
|
235
|
-
expect(beforePurge.cnt).toBe(1);
|
|
236
|
-
// Wait a tick to ensure expiry
|
|
237
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
238
|
-
const purged = compactionService.purgeArchives();
|
|
239
|
-
expect(purged).toBe(1);
|
|
240
|
-
const afterPurge = db.prepare("SELECT COUNT(*) as cnt FROM archives").get();
|
|
241
|
-
expect(afterPurge.cnt).toBe(0);
|
|
242
|
-
});
|
|
243
|
-
it("purgeArchives does not remove non-expired archives", async () => {
|
|
244
|
-
sessionStore.save(testKey, [{ role: "user", content: "hello" }]);
|
|
245
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
246
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
247
|
-
// Compact with long retention
|
|
248
|
-
await compactionService.compact({
|
|
249
|
-
minIdleMs: 1000,
|
|
250
|
-
archiveRetentionMs: 7 * 24 * 60 * 60 * 1000, // 7 days
|
|
251
|
-
});
|
|
252
|
-
const purged = compactionService.purgeArchives();
|
|
253
|
-
expect(purged).toBe(0);
|
|
254
|
-
const archiveCount = db.prepare("SELECT COUNT(*) as cnt FROM archives").get();
|
|
255
|
-
expect(archiveCount.cnt).toBe(1);
|
|
256
|
-
});
|
|
257
|
-
it("purgeArchives returns 0 when no archives exist", () => {
|
|
258
|
-
const purged = compactionService.purgeArchives();
|
|
259
|
-
expect(purged).toBe(0);
|
|
260
|
-
});
|
|
261
|
-
// ── Empty session edge case ────────────────────────────────────
|
|
262
|
-
it("handles compaction with empty sessions (0 messages) without calling summarizer", async () => {
|
|
263
|
-
// Create a tracking summarizer that should never be called
|
|
264
|
-
const tracker = trackingSummarizer();
|
|
265
|
-
const service = createCompactionService(db, sessionStore, adapter, tracker);
|
|
266
|
-
// Save a session with 0 messages and make it stale
|
|
267
|
-
sessionStore.save(testKey, []);
|
|
268
|
-
const longAgo = Date.now() - 10 * 60 * 60 * 1000;
|
|
269
|
-
db.prepare("UPDATE sessions SET updated_at = ? WHERE session_key = ?").run(longAgo, "default:user-1:telegram");
|
|
270
|
-
const result = await service.compact({ minIdleMs: 1000 });
|
|
271
|
-
// Verify compaction completed successfully (no crash)
|
|
272
|
-
expect(result.sessionsCompacted).toBe(0);
|
|
273
|
-
expect(result.episodicCreated).toBe(0);
|
|
274
|
-
expect(result.factsExtracted).toBe(0);
|
|
275
|
-
expect(result.compactedKeys).toHaveLength(0);
|
|
276
|
-
// Summarizer should never have been called for an empty session
|
|
277
|
-
expect(tracker.callCount).toBe(0);
|
|
278
|
-
// No episodic or semantic memories should have been created
|
|
279
|
-
const episodic = db
|
|
280
|
-
.prepare("SELECT * FROM memories WHERE memory_type = 'episodic'")
|
|
281
|
-
.all();
|
|
282
|
-
expect(episodic).toHaveLength(0);
|
|
283
|
-
const semantic = db
|
|
284
|
-
.prepare("SELECT * FROM memories WHERE memory_type = 'semantic' AND tags LIKE '%fact%'")
|
|
285
|
-
.all();
|
|
286
|
-
expect(semantic).toHaveLength(0);
|
|
287
|
-
});
|
|
288
|
-
// ── Return value ─────────────────────────────────────────────────
|
|
289
|
-
it("returns empty result when no sessions are stale", async () => {
|
|
290
|
-
const result = await compactionService.compact({ minIdleMs: 1000 });
|
|
291
|
-
expect(result).toEqual({
|
|
292
|
-
sessionsCompacted: 0,
|
|
293
|
-
episodicCreated: 0,
|
|
294
|
-
factsExtracted: 0,
|
|
295
|
-
compactedKeys: [],
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import Database from "better-sqlite3";
|
|
3
|
-
import { initContextSchema } from "./context-schema.js";
|
|
4
|
-
/** Open an in-memory database with foreign keys enabled and schema initialized. */
|
|
5
|
-
function createTestDb() {
|
|
6
|
-
const db = new Database(":memory:");
|
|
7
|
-
db.pragma("foreign_keys = ON");
|
|
8
|
-
initContextSchema(db);
|
|
9
|
-
return db;
|
|
10
|
-
}
|
|
11
|
-
/** Insert a minimal conversation row and return its id. */
|
|
12
|
-
function insertConversation(db, id = "conv-1") {
|
|
13
|
-
db.prepare(`INSERT INTO ctx_conversations (conversation_id, tenant_id, agent_id, session_key)
|
|
14
|
-
VALUES (?, 'tenant-1', 'agent-1', ?)`).run(id, `session-${id}`);
|
|
15
|
-
return id;
|
|
16
|
-
}
|
|
17
|
-
/** Insert a minimal message row and return its message_id. */
|
|
18
|
-
function insertMessage(db, conversationId, seq, role = "user", content = "hello") {
|
|
19
|
-
const info = db
|
|
20
|
-
.prepare(`INSERT INTO ctx_messages (conversation_id, seq, role, content, content_hash, token_count)
|
|
21
|
-
VALUES (?, ?, ?, ?, 'hash-' || ?, 1)`)
|
|
22
|
-
.run(conversationId, seq, role, content, seq);
|
|
23
|
-
return Number(info.lastInsertRowid);
|
|
24
|
-
}
|
|
25
|
-
/** Insert a minimal summary row and return its summary_id. */
|
|
26
|
-
function insertSummary(db, summaryId, conversationId, kind = "leaf") {
|
|
27
|
-
db.prepare(`INSERT INTO ctx_summaries (summary_id, conversation_id, kind, content, token_count)
|
|
28
|
-
VALUES (?, ?, ?, 'summary content', 10)`).run(summaryId, conversationId, kind);
|
|
29
|
-
return summaryId;
|
|
30
|
-
}
|
|
31
|
-
describe("initContextSchema", () => {
|
|
32
|
-
let db;
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
db = createTestDb();
|
|
35
|
-
});
|
|
36
|
-
// --- Group 1: table creation ---
|
|
37
|
-
describe("table creation", () => {
|
|
38
|
-
it("creates all 9 regular ctx_ tables", () => {
|
|
39
|
-
// FTS5 creates shadow tables (e.g. ctx_messages_fts_content, _data, etc.)
|
|
40
|
-
// Filter them out by excluding names containing '_fts_' and the FTS5 virtual tables themselves.
|
|
41
|
-
const tables = db
|
|
42
|
-
.prepare(`SELECT name FROM sqlite_master
|
|
43
|
-
WHERE type='table' AND name LIKE 'ctx_%'
|
|
44
|
-
AND name NOT LIKE '%_fts%'
|
|
45
|
-
ORDER BY name`)
|
|
46
|
-
.all();
|
|
47
|
-
const names = tables.map((r) => r.name);
|
|
48
|
-
expect(names).toEqual([
|
|
49
|
-
"ctx_context_items",
|
|
50
|
-
"ctx_conversations",
|
|
51
|
-
"ctx_expansion_grants",
|
|
52
|
-
"ctx_large_files",
|
|
53
|
-
"ctx_message_parts",
|
|
54
|
-
"ctx_messages",
|
|
55
|
-
"ctx_summaries",
|
|
56
|
-
"ctx_summary_messages",
|
|
57
|
-
"ctx_summary_parents",
|
|
58
|
-
]);
|
|
59
|
-
});
|
|
60
|
-
it("creates 2 FTS5 virtual tables", () => {
|
|
61
|
-
const fts = db
|
|
62
|
-
.prepare(`SELECT name FROM sqlite_master
|
|
63
|
-
WHERE sql LIKE '%fts5%' AND name LIKE 'ctx_%'
|
|
64
|
-
ORDER BY name`)
|
|
65
|
-
.all();
|
|
66
|
-
const names = fts.map((r) => r.name);
|
|
67
|
-
expect(names).toEqual(["ctx_messages_fts", "ctx_summaries_fts"]);
|
|
68
|
-
});
|
|
69
|
-
it("creates all expected indexes", () => {
|
|
70
|
-
const indexes = db
|
|
71
|
-
.prepare(`SELECT name FROM sqlite_master
|
|
72
|
-
WHERE type='index' AND name LIKE 'ctx_%'
|
|
73
|
-
ORDER BY name`)
|
|
74
|
-
.all();
|
|
75
|
-
const names = indexes.map((r) => r.name);
|
|
76
|
-
expect(names).toContain("ctx_messages_conv_seq");
|
|
77
|
-
expect(names).toContain("ctx_messages_hash");
|
|
78
|
-
expect(names).toContain("ctx_summaries_conv");
|
|
79
|
-
expect(names).toContain("ctx_summaries_depth");
|
|
80
|
-
expect(names).toContain("ctx_large_files_conv");
|
|
81
|
-
expect(names).toContain("ctx_large_files_hash");
|
|
82
|
-
expect(names).toContain("ctx_grants_session");
|
|
83
|
-
expect(names).toContain("ctx_grants_expires");
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
// --- Group 2: idempotency ---
|
|
87
|
-
describe("idempotency", () => {
|
|
88
|
-
it("calling initContextSchema twice produces no errors", () => {
|
|
89
|
-
// db already had initContextSchema called in createTestDb
|
|
90
|
-
expect(() => initContextSchema(db)).not.toThrow();
|
|
91
|
-
// Verify tables still exist and are usable
|
|
92
|
-
const convId = insertConversation(db);
|
|
93
|
-
insertMessage(db, convId, 1);
|
|
94
|
-
const count = db
|
|
95
|
-
.prepare("SELECT COUNT(*) as c FROM ctx_messages")
|
|
96
|
-
.get();
|
|
97
|
-
expect(count.c).toBe(1);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
// --- Group 3: constraints ---
|
|
101
|
-
describe("constraints", () => {
|
|
102
|
-
it("ctx_messages role CHECK constraint rejects invalid roles", () => {
|
|
103
|
-
const convId = insertConversation(db);
|
|
104
|
-
expect(() => {
|
|
105
|
-
db.prepare(`INSERT INTO ctx_messages (conversation_id, seq, role, content, content_hash, token_count)
|
|
106
|
-
VALUES (?, 1, 'invalid', 'test', 'hash', 1)`).run(convId);
|
|
107
|
-
}).toThrow();
|
|
108
|
-
});
|
|
109
|
-
it("ctx_messages role CHECK allows valid roles", () => {
|
|
110
|
-
const convId = insertConversation(db);
|
|
111
|
-
for (const [i, role] of ["system", "user", "assistant", "tool"].entries()) {
|
|
112
|
-
expect(() => {
|
|
113
|
-
db.prepare(`INSERT INTO ctx_messages (conversation_id, seq, role, content, content_hash, token_count)
|
|
114
|
-
VALUES (?, ?, ?, 'content', 'hash-' || ?, 1)`).run(convId, i + 1, role, i);
|
|
115
|
-
}).not.toThrow();
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
it("ctx_messages UNIQUE(conversation_id, seq) prevents duplicates", () => {
|
|
119
|
-
const convId = insertConversation(db);
|
|
120
|
-
insertMessage(db, convId, 1);
|
|
121
|
-
expect(() => insertMessage(db, convId, 1)).toThrow();
|
|
122
|
-
});
|
|
123
|
-
it("ctx_summaries kind CHECK constraint rejects invalid kinds", () => {
|
|
124
|
-
const convId = insertConversation(db);
|
|
125
|
-
expect(() => {
|
|
126
|
-
db.prepare(`INSERT INTO ctx_summaries (summary_id, conversation_id, kind, content, token_count)
|
|
127
|
-
VALUES ('s1', ?, 'invalid', 'content', 10)`).run(convId);
|
|
128
|
-
}).toThrow();
|
|
129
|
-
});
|
|
130
|
-
it("ctx_context_items CHECK enforces item_type exclusivity", () => {
|
|
131
|
-
const convId = insertConversation(db);
|
|
132
|
-
const msgId = insertMessage(db, convId, 1);
|
|
133
|
-
const sumId = insertSummary(db, "sum-1", convId);
|
|
134
|
-
// item_type='message' with summary_id set -- should fail
|
|
135
|
-
expect(() => {
|
|
136
|
-
db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
|
|
137
|
-
VALUES (?, 1, 'message', ?, ?)`).run(convId, msgId, sumId);
|
|
138
|
-
}).toThrow();
|
|
139
|
-
// item_type='summary' with message_id set -- should fail
|
|
140
|
-
expect(() => {
|
|
141
|
-
db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
|
|
142
|
-
VALUES (?, 2, 'summary', ?, ?)`).run(convId, msgId, sumId);
|
|
143
|
-
}).toThrow();
|
|
144
|
-
// Valid: message item with only message_id
|
|
145
|
-
expect(() => {
|
|
146
|
-
db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
|
|
147
|
-
VALUES (?, 1, 'message', ?, NULL)`).run(convId, msgId);
|
|
148
|
-
}).not.toThrow();
|
|
149
|
-
// Valid: summary item with only summary_id
|
|
150
|
-
expect(() => {
|
|
151
|
-
db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
|
|
152
|
-
VALUES (?, 2, 'summary', NULL, ?)`).run(convId, sumId);
|
|
153
|
-
}).not.toThrow();
|
|
154
|
-
});
|
|
155
|
-
it("ctx_messages AUTOINCREMENT prevents rowid reuse", () => {
|
|
156
|
-
const convId = insertConversation(db);
|
|
157
|
-
insertMessage(db, convId, 1); // message_id = 1
|
|
158
|
-
insertMessage(db, convId, 2); // message_id = 2
|
|
159
|
-
const thirdId = insertMessage(db, convId, 3); // message_id = 3
|
|
160
|
-
expect(thirdId).toBe(3);
|
|
161
|
-
// Delete the last message
|
|
162
|
-
db.prepare("DELETE FROM ctx_messages WHERE message_id = ?").run(thirdId);
|
|
163
|
-
// Insert a new message -- must get id=4, not 3 (AUTOINCREMENT)
|
|
164
|
-
const fourthId = insertMessage(db, convId, 3, "user", "new content");
|
|
165
|
-
expect(fourthId).toBe(4);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
// --- Group 4: foreign keys and cascades ---
|
|
169
|
-
describe("foreign keys and cascades", () => {
|
|
170
|
-
it("deleting a conversation cascades to messages", () => {
|
|
171
|
-
const convId = insertConversation(db);
|
|
172
|
-
insertMessage(db, convId, 1);
|
|
173
|
-
insertMessage(db, convId, 2);
|
|
174
|
-
db.prepare("DELETE FROM ctx_conversations WHERE conversation_id = ?").run(convId);
|
|
175
|
-
const count = db
|
|
176
|
-
.prepare("SELECT COUNT(*) as c FROM ctx_messages")
|
|
177
|
-
.get();
|
|
178
|
-
expect(count.c).toBe(0);
|
|
179
|
-
});
|
|
180
|
-
it("deleting a conversation cascades to summaries and context_items", () => {
|
|
181
|
-
const convId = insertConversation(db);
|
|
182
|
-
const msgId = insertMessage(db, convId, 1);
|
|
183
|
-
insertSummary(db, "sum-1", convId);
|
|
184
|
-
db.prepare(`INSERT INTO ctx_context_items (conversation_id, ordinal, item_type, message_id, summary_id)
|
|
185
|
-
VALUES (?, 1, 'message', ?, NULL)`).run(convId, msgId);
|
|
186
|
-
db.prepare("DELETE FROM ctx_conversations WHERE conversation_id = ?").run(convId);
|
|
187
|
-
const sumCount = db
|
|
188
|
-
.prepare("SELECT COUNT(*) as c FROM ctx_summaries")
|
|
189
|
-
.get();
|
|
190
|
-
const itemCount = db
|
|
191
|
-
.prepare("SELECT COUNT(*) as c FROM ctx_context_items")
|
|
192
|
-
.get();
|
|
193
|
-
expect(sumCount.c).toBe(0);
|
|
194
|
-
expect(itemCount.c).toBe(0);
|
|
195
|
-
});
|
|
196
|
-
it("cannot delete a message referenced by ctx_summary_messages (RESTRICT)", () => {
|
|
197
|
-
const convId = insertConversation(db);
|
|
198
|
-
const msgId = insertMessage(db, convId, 1);
|
|
199
|
-
insertSummary(db, "sum-1", convId);
|
|
200
|
-
db.prepare(`INSERT INTO ctx_summary_messages (summary_id, message_id, ordinal)
|
|
201
|
-
VALUES ('sum-1', ?, 0)`).run(msgId);
|
|
202
|
-
expect(() => {
|
|
203
|
-
db.prepare("DELETE FROM ctx_messages WHERE message_id = ?").run(msgId);
|
|
204
|
-
}).toThrow();
|
|
205
|
-
});
|
|
206
|
-
it("cannot delete a parent summary referenced by ctx_summary_parents (RESTRICT)", () => {
|
|
207
|
-
const convId = insertConversation(db);
|
|
208
|
-
const parentId = insertSummary(db, "sum-parent", convId, "leaf");
|
|
209
|
-
const childId = insertSummary(db, "sum-child", convId, "condensed");
|
|
210
|
-
db.prepare(`INSERT INTO ctx_summary_parents (summary_id, parent_summary_id, ordinal)
|
|
211
|
-
VALUES (?, ?, 0)`).run(childId, parentId);
|
|
212
|
-
expect(() => {
|
|
213
|
-
db.prepare("DELETE FROM ctx_summaries WHERE summary_id = ?").run(parentId);
|
|
214
|
-
}).toThrow();
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
// --- Group 5: FTS5 standalone tables ---
|
|
218
|
-
describe("FTS5 standalone tables", () => {
|
|
219
|
-
it("can insert and query ctx_messages_fts", () => {
|
|
220
|
-
db.prepare("INSERT INTO ctx_messages_fts(rowid, content) VALUES (1, 'the quick brown fox')").run();
|
|
221
|
-
const results = db
|
|
222
|
-
.prepare("SELECT rowid, content FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'brown'")
|
|
223
|
-
.all();
|
|
224
|
-
expect(results).toHaveLength(1);
|
|
225
|
-
expect(results[0].rowid).toBe(1);
|
|
226
|
-
expect(results[0].content).toBe("the quick brown fox");
|
|
227
|
-
});
|
|
228
|
-
it("can insert and query ctx_summaries_fts", () => {
|
|
229
|
-
db.prepare("INSERT INTO ctx_summaries_fts(summary_id, content) VALUES ('sum-1', 'summary of important events')").run();
|
|
230
|
-
const results = db
|
|
231
|
-
.prepare("SELECT summary_id, content FROM ctx_summaries_fts WHERE content MATCH 'important'")
|
|
232
|
-
.all();
|
|
233
|
-
expect(results).toHaveLength(1);
|
|
234
|
-
expect(results[0].summary_id).toBe("sum-1");
|
|
235
|
-
expect(results[0].content).toBe("summary of important events");
|
|
236
|
-
});
|
|
237
|
-
it("FTS5 uses porter tokenizer", () => {
|
|
238
|
-
// "running" should stem to "run" via porter tokenizer
|
|
239
|
-
db.prepare("INSERT INTO ctx_messages_fts(rowid, content) VALUES (1, 'running quickly')").run();
|
|
240
|
-
const results = db
|
|
241
|
-
.prepare("SELECT rowid FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'run'")
|
|
242
|
-
.all();
|
|
243
|
-
expect(results).toHaveLength(1);
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|