comisai 1.0.11 → 1.0.13
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/node_modules/@comis/agent/dist/model/operation-model-defaults.js +1 -0
- package/node_modules/@comis/agent/dist/session/comis-session-manager.js +9 -9
- package/node_modules/@comis/agent/package.json +1 -1
- package/node_modules/@comis/channels/package.json +1 -1
- package/node_modules/@comis/cli/package.json +1 -1
- package/node_modules/@comis/core/dist/event-bus/events-infra.d.ts +2 -0
- package/node_modules/@comis/core/package.json +1 -1
- package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +12 -1
- package/node_modules/@comis/daemon/dist/wiring/setup-gateway.js +1 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-schedulers.js +1 -0
- 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/dist/cron/cron-types.d.ts +6 -0
- package/node_modules/@comis/scheduler/dist/cron/cron-types.js +2 -0
- package/node_modules/@comis/scheduler/package.json +1 -1
- package/node_modules/@comis/shared/package.json +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +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,708 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import Database from "better-sqlite3";
|
|
3
|
-
import { createContextStore } from "./context-store.js";
|
|
4
|
-
let db;
|
|
5
|
-
let store;
|
|
6
|
-
let convCounter = 0;
|
|
7
|
-
/** Create a fresh store for each test. */
|
|
8
|
-
function freshStore() {
|
|
9
|
-
const d = new Database(":memory:");
|
|
10
|
-
d.pragma("foreign_keys = ON");
|
|
11
|
-
const s = createContextStore(d);
|
|
12
|
-
return { db: d, store: s };
|
|
13
|
-
}
|
|
14
|
-
/** Helper: create a test conversation with unique session key. */
|
|
15
|
-
function createTestConversation(s) {
|
|
16
|
-
convCounter++;
|
|
17
|
-
return s.createConversation({
|
|
18
|
-
tenantId: "t1",
|
|
19
|
-
agentId: "a1",
|
|
20
|
-
sessionKey: `sess-${convCounter}`,
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
convCounter = 0;
|
|
25
|
-
const fresh = freshStore();
|
|
26
|
-
db = fresh.db;
|
|
27
|
-
store = fresh.store;
|
|
28
|
-
});
|
|
29
|
-
// =========================================================================
|
|
30
|
-
// Group 1: Conversations
|
|
31
|
-
// =========================================================================
|
|
32
|
-
describe("conversations", () => {
|
|
33
|
-
it("createConversation returns conversation_id with conv_ prefix", () => {
|
|
34
|
-
const id = createTestConversation(store);
|
|
35
|
-
expect(id).toMatch(/^conv_[0-9a-f]{16}$/);
|
|
36
|
-
});
|
|
37
|
-
it("getConversation returns inserted row", () => {
|
|
38
|
-
const id = store.createConversation({
|
|
39
|
-
tenantId: "t1",
|
|
40
|
-
agentId: "a1",
|
|
41
|
-
sessionKey: "sess-get",
|
|
42
|
-
title: "Test Title",
|
|
43
|
-
});
|
|
44
|
-
const row = store.getConversation(id);
|
|
45
|
-
expect(row).toBeDefined();
|
|
46
|
-
expect(row.conversation_id).toBe(id);
|
|
47
|
-
expect(row.tenant_id).toBe("t1");
|
|
48
|
-
expect(row.agent_id).toBe("a1");
|
|
49
|
-
expect(row.session_key).toBe("sess-get");
|
|
50
|
-
expect(row.title).toBe("Test Title");
|
|
51
|
-
expect(row.created_at).toBeTruthy();
|
|
52
|
-
expect(row.updated_at).toBeTruthy();
|
|
53
|
-
});
|
|
54
|
-
it("getConversationBySession finds by tenant+session", () => {
|
|
55
|
-
const id = store.createConversation({
|
|
56
|
-
tenantId: "t1",
|
|
57
|
-
agentId: "a1",
|
|
58
|
-
sessionKey: "sess-find",
|
|
59
|
-
});
|
|
60
|
-
const row = store.getConversationBySession("t1", "sess-find");
|
|
61
|
-
expect(row).toBeDefined();
|
|
62
|
-
expect(row.conversation_id).toBe(id);
|
|
63
|
-
});
|
|
64
|
-
it("getConversationBySession returns undefined for missing", () => {
|
|
65
|
-
const row = store.getConversationBySession("t1", "nonexistent");
|
|
66
|
-
expect(row).toBeUndefined();
|
|
67
|
-
});
|
|
68
|
-
it("touchConversation updates updated_at", () => {
|
|
69
|
-
const id = createTestConversation(store);
|
|
70
|
-
const before = store.getConversation(id).updated_at;
|
|
71
|
-
// Touch should update the timestamp
|
|
72
|
-
store.touchConversation(id);
|
|
73
|
-
const after = store.getConversation(id).updated_at;
|
|
74
|
-
// At minimum, they should both be valid timestamps
|
|
75
|
-
expect(before).toBeTruthy();
|
|
76
|
-
expect(after).toBeTruthy();
|
|
77
|
-
// SQLite datetime('now') has 1-second resolution, so we just verify
|
|
78
|
-
// the field exists and is a valid datetime string
|
|
79
|
-
expect(after).toMatch(/^\d{4}-\d{2}-\d{2}/);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
// =========================================================================
|
|
83
|
-
// Group 2: Messages
|
|
84
|
-
// =========================================================================
|
|
85
|
-
describe("messages", () => {
|
|
86
|
-
it("insertMessage returns auto-incremented message_id", () => {
|
|
87
|
-
const convId = createTestConversation(store);
|
|
88
|
-
const id1 = store.insertMessage({
|
|
89
|
-
conversationId: convId,
|
|
90
|
-
seq: 1,
|
|
91
|
-
role: "user",
|
|
92
|
-
content: "msg1",
|
|
93
|
-
contentHash: "h1",
|
|
94
|
-
tokenCount: 5,
|
|
95
|
-
});
|
|
96
|
-
const id2 = store.insertMessage({
|
|
97
|
-
conversationId: convId,
|
|
98
|
-
seq: 2,
|
|
99
|
-
role: "assistant",
|
|
100
|
-
content: "msg2",
|
|
101
|
-
contentHash: "h2",
|
|
102
|
-
tokenCount: 10,
|
|
103
|
-
});
|
|
104
|
-
const id3 = store.insertMessage({
|
|
105
|
-
conversationId: convId,
|
|
106
|
-
seq: 3,
|
|
107
|
-
role: "user",
|
|
108
|
-
content: "msg3",
|
|
109
|
-
contentHash: "h3",
|
|
110
|
-
tokenCount: 7,
|
|
111
|
-
});
|
|
112
|
-
expect(id1).toBe(1);
|
|
113
|
-
expect(id2).toBe(2);
|
|
114
|
-
expect(id3).toBe(3);
|
|
115
|
-
});
|
|
116
|
-
it("insertMessage creates FTS entry", () => {
|
|
117
|
-
const convId = createTestConversation(store);
|
|
118
|
-
const msgId = store.insertMessage({
|
|
119
|
-
conversationId: convId,
|
|
120
|
-
seq: 1,
|
|
121
|
-
role: "user",
|
|
122
|
-
content: "hello world test phrase",
|
|
123
|
-
contentHash: "hfts",
|
|
124
|
-
tokenCount: 4,
|
|
125
|
-
});
|
|
126
|
-
// Query FTS5 directly
|
|
127
|
-
const results = db
|
|
128
|
-
.prepare("SELECT rowid, content FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'hello'")
|
|
129
|
-
.all();
|
|
130
|
-
expect(results).toHaveLength(1);
|
|
131
|
-
expect(results[0].rowid).toBe(msgId);
|
|
132
|
-
expect(results[0].content).toBe("hello world test phrase");
|
|
133
|
-
});
|
|
134
|
-
it("getMessagesByConversation returns messages in seq order", () => {
|
|
135
|
-
const convId = createTestConversation(store);
|
|
136
|
-
store.insertMessage({ conversationId: convId, seq: 3, role: "user", content: "c", contentHash: "h3", tokenCount: 1 });
|
|
137
|
-
store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "a", contentHash: "h1", tokenCount: 1 });
|
|
138
|
-
store.insertMessage({ conversationId: convId, seq: 2, role: "assistant", content: "b", contentHash: "h2", tokenCount: 1 });
|
|
139
|
-
const msgs = store.getMessagesByConversation(convId);
|
|
140
|
-
expect(msgs).toHaveLength(3);
|
|
141
|
-
expect(msgs[0].seq).toBe(1);
|
|
142
|
-
expect(msgs[1].seq).toBe(2);
|
|
143
|
-
expect(msgs[2].seq).toBe(3);
|
|
144
|
-
});
|
|
145
|
-
it("getMessagesByConversation with afterSeq filters correctly", () => {
|
|
146
|
-
const convId = createTestConversation(store);
|
|
147
|
-
for (let i = 1; i <= 5; i++) {
|
|
148
|
-
store.insertMessage({ conversationId: convId, seq: i, role: "user", content: `m${i}`, contentHash: `h${i}`, tokenCount: 1 });
|
|
149
|
-
}
|
|
150
|
-
const msgs = store.getMessagesByConversation(convId, { afterSeq: 3 });
|
|
151
|
-
expect(msgs).toHaveLength(2);
|
|
152
|
-
expect(msgs[0].seq).toBe(4);
|
|
153
|
-
expect(msgs[1].seq).toBe(5);
|
|
154
|
-
});
|
|
155
|
-
it("getMessagesByConversation with limit caps results", () => {
|
|
156
|
-
const convId = createTestConversation(store);
|
|
157
|
-
for (let i = 1; i <= 10; i++) {
|
|
158
|
-
store.insertMessage({ conversationId: convId, seq: i, role: "user", content: `m${i}`, contentHash: `h${i}`, tokenCount: 1 });
|
|
159
|
-
}
|
|
160
|
-
const msgs = store.getMessagesByConversation(convId, { limit: 3 });
|
|
161
|
-
expect(msgs).toHaveLength(3);
|
|
162
|
-
});
|
|
163
|
-
it("getMessagesByIds batch fetches correctly", () => {
|
|
164
|
-
const convId = createTestConversation(store);
|
|
165
|
-
const ids = [];
|
|
166
|
-
for (let i = 1; i <= 5; i++) {
|
|
167
|
-
ids.push(store.insertMessage({ conversationId: convId, seq: i, role: "user", content: `m${i}`, contentHash: `h${i}`, tokenCount: 1 }));
|
|
168
|
-
}
|
|
169
|
-
const results = store.getMessagesByIds([ids[0], ids[2], ids[4]]);
|
|
170
|
-
expect(results).toHaveLength(3);
|
|
171
|
-
expect(results[0].seq).toBe(1);
|
|
172
|
-
expect(results[1].seq).toBe(3);
|
|
173
|
-
expect(results[2].seq).toBe(5);
|
|
174
|
-
});
|
|
175
|
-
it("getMessagesByIds returns empty for empty input", () => {
|
|
176
|
-
const results = store.getMessagesByIds([]);
|
|
177
|
-
expect(results).toEqual([]);
|
|
178
|
-
});
|
|
179
|
-
it("getMessagesByIds handles large batches", () => {
|
|
180
|
-
const convId = createTestConversation(store);
|
|
181
|
-
// Insert 550 messages to exercise chunking (>500)
|
|
182
|
-
for (let i = 1; i <= 550; i++) {
|
|
183
|
-
store.insertMessage({
|
|
184
|
-
conversationId: convId,
|
|
185
|
-
seq: i,
|
|
186
|
-
role: "user",
|
|
187
|
-
content: `msg-${i}`,
|
|
188
|
-
contentHash: `hash-${i}`,
|
|
189
|
-
tokenCount: 1,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
// Fetch all 550 by IDs
|
|
193
|
-
const allIds = Array.from({ length: 550 }, (_, i) => i + 1);
|
|
194
|
-
const results = store.getMessagesByIds(allIds);
|
|
195
|
-
expect(results).toHaveLength(550);
|
|
196
|
-
});
|
|
197
|
-
it("getMessageByHash finds by content_hash", () => {
|
|
198
|
-
const convId = createTestConversation(store);
|
|
199
|
-
store.insertMessage({
|
|
200
|
-
conversationId: convId,
|
|
201
|
-
seq: 1,
|
|
202
|
-
role: "user",
|
|
203
|
-
content: "unique content",
|
|
204
|
-
contentHash: "unique-hash-123",
|
|
205
|
-
tokenCount: 2,
|
|
206
|
-
});
|
|
207
|
-
const row = store.getMessageByHash(convId, "unique-hash-123");
|
|
208
|
-
expect(row).toBeDefined();
|
|
209
|
-
expect(row.content).toBe("unique content");
|
|
210
|
-
});
|
|
211
|
-
it("getLastMessageSeq returns max seq", () => {
|
|
212
|
-
const convId = createTestConversation(store);
|
|
213
|
-
store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "a", contentHash: "h1", tokenCount: 1 });
|
|
214
|
-
store.insertMessage({ conversationId: convId, seq: 2, role: "assistant", content: "b", contentHash: "h2", tokenCount: 1 });
|
|
215
|
-
store.insertMessage({ conversationId: convId, seq: 3, role: "user", content: "c", contentHash: "h3", tokenCount: 1 });
|
|
216
|
-
expect(store.getLastMessageSeq(convId)).toBe(3);
|
|
217
|
-
});
|
|
218
|
-
it("getLastMessageSeq returns 0 for empty conversation", () => {
|
|
219
|
-
const convId = createTestConversation(store);
|
|
220
|
-
expect(store.getLastMessageSeq(convId)).toBe(0);
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
// =========================================================================
|
|
224
|
-
// Group 3: Message Parts
|
|
225
|
-
// =========================================================================
|
|
226
|
-
describe("message parts", () => {
|
|
227
|
-
it("insertParts and getPartsByMessage round-trips correctly", () => {
|
|
228
|
-
const convId = createTestConversation(store);
|
|
229
|
-
const msgId = store.insertMessage({
|
|
230
|
-
conversationId: convId,
|
|
231
|
-
seq: 1,
|
|
232
|
-
role: "user",
|
|
233
|
-
content: "multipart",
|
|
234
|
-
contentHash: "hmp",
|
|
235
|
-
tokenCount: 1,
|
|
236
|
-
});
|
|
237
|
-
store.insertParts(msgId, [
|
|
238
|
-
{ ordinal: 0, partType: "text", content: "Hello" },
|
|
239
|
-
{ ordinal: 1, partType: "image", metadata: '{"url":"http://..."}' },
|
|
240
|
-
{ ordinal: 2, partType: "text", content: "World" },
|
|
241
|
-
]);
|
|
242
|
-
const parts = store.getPartsByMessage(msgId);
|
|
243
|
-
expect(parts).toHaveLength(3);
|
|
244
|
-
expect(parts[0].ordinal).toBe(0);
|
|
245
|
-
expect(parts[0].part_type).toBe("text");
|
|
246
|
-
expect(parts[0].content).toBe("Hello");
|
|
247
|
-
expect(parts[1].ordinal).toBe(1);
|
|
248
|
-
expect(parts[1].part_type).toBe("image");
|
|
249
|
-
expect(parts[1].metadata).toBe('{"url":"http://..."}');
|
|
250
|
-
expect(parts[2].ordinal).toBe(2);
|
|
251
|
-
expect(parts[2].content).toBe("World");
|
|
252
|
-
});
|
|
253
|
-
it("getPartsByMessages batches across multiple messages", () => {
|
|
254
|
-
const convId = createTestConversation(store);
|
|
255
|
-
const msgIds = [];
|
|
256
|
-
for (let i = 1; i <= 3; i++) {
|
|
257
|
-
const msgId = store.insertMessage({
|
|
258
|
-
conversationId: convId,
|
|
259
|
-
seq: i,
|
|
260
|
-
role: "user",
|
|
261
|
-
content: `msg-${i}`,
|
|
262
|
-
contentHash: `h${i}`,
|
|
263
|
-
tokenCount: 1,
|
|
264
|
-
});
|
|
265
|
-
msgIds.push(msgId);
|
|
266
|
-
store.insertParts(msgId, [
|
|
267
|
-
{ ordinal: 0, partType: "text", content: `part-${i}-a` },
|
|
268
|
-
{ ordinal: 1, partType: "text", content: `part-${i}-b` },
|
|
269
|
-
]);
|
|
270
|
-
}
|
|
271
|
-
const partsMap = store.getPartsByMessages(msgIds);
|
|
272
|
-
expect(partsMap.size).toBe(3);
|
|
273
|
-
for (const msgId of msgIds) {
|
|
274
|
-
const parts = partsMap.get(msgId);
|
|
275
|
-
expect(parts).toHaveLength(2);
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
// =========================================================================
|
|
280
|
-
// Group 4: Summaries
|
|
281
|
-
// =========================================================================
|
|
282
|
-
describe("summaries", () => {
|
|
283
|
-
it("insertSummary stores and FTS-indexes the summary", () => {
|
|
284
|
-
const convId = createTestConversation(store);
|
|
285
|
-
const sumId = store.insertSummary({
|
|
286
|
-
summaryId: "sum-001",
|
|
287
|
-
conversationId: convId,
|
|
288
|
-
kind: "leaf",
|
|
289
|
-
depth: 0,
|
|
290
|
-
content: "Important discussion about architecture",
|
|
291
|
-
tokenCount: 20,
|
|
292
|
-
});
|
|
293
|
-
expect(sumId).toBe("sum-001");
|
|
294
|
-
// Verify stored
|
|
295
|
-
const row = store.getSummary("sum-001");
|
|
296
|
-
expect(row).toBeDefined();
|
|
297
|
-
expect(row.content).toBe("Important discussion about architecture");
|
|
298
|
-
expect(row.kind).toBe("leaf");
|
|
299
|
-
expect(row.depth).toBe(0);
|
|
300
|
-
// Verify FTS indexed
|
|
301
|
-
const fts = db
|
|
302
|
-
.prepare("SELECT summary_id, content FROM ctx_summaries_fts WHERE content MATCH 'architecture'")
|
|
303
|
-
.all();
|
|
304
|
-
expect(fts).toHaveLength(1);
|
|
305
|
-
expect(fts[0].summary_id).toBe("sum-001");
|
|
306
|
-
});
|
|
307
|
-
it("getSummariesByConversation returns all summaries", () => {
|
|
308
|
-
const convId = createTestConversation(store);
|
|
309
|
-
store.insertSummary({ summaryId: "s1", conversationId: convId, kind: "leaf", depth: 0, content: "sum1", tokenCount: 5 });
|
|
310
|
-
store.insertSummary({ summaryId: "s2", conversationId: convId, kind: "leaf", depth: 0, content: "sum2", tokenCount: 5 });
|
|
311
|
-
store.insertSummary({ summaryId: "s3", conversationId: convId, kind: "condensed", depth: 1, content: "sum3", tokenCount: 5 });
|
|
312
|
-
const sums = store.getSummariesByConversation(convId);
|
|
313
|
-
expect(sums).toHaveLength(3);
|
|
314
|
-
});
|
|
315
|
-
it("getSummariesByConversation filters by depth", () => {
|
|
316
|
-
const convId = createTestConversation(store);
|
|
317
|
-
store.insertSummary({ summaryId: "sd0a", conversationId: convId, kind: "leaf", depth: 0, content: "d0a", tokenCount: 5 });
|
|
318
|
-
store.insertSummary({ summaryId: "sd0b", conversationId: convId, kind: "leaf", depth: 0, content: "d0b", tokenCount: 5 });
|
|
319
|
-
store.insertSummary({ summaryId: "sd1a", conversationId: convId, kind: "condensed", depth: 1, content: "d1a", tokenCount: 5 });
|
|
320
|
-
const depth0 = store.getSummariesByConversation(convId, { depth: 0 });
|
|
321
|
-
expect(depth0).toHaveLength(2);
|
|
322
|
-
expect(depth0.every((s) => s.depth === 0)).toBe(true);
|
|
323
|
-
});
|
|
324
|
-
it("updateSummaryCountsDirty sets dirty flag", () => {
|
|
325
|
-
const convId = createTestConversation(store);
|
|
326
|
-
store.insertSummary({ summaryId: "sd", conversationId: convId, kind: "leaf", depth: 0, content: "dirty test", tokenCount: 5 });
|
|
327
|
-
// Initially counts_dirty = 0
|
|
328
|
-
expect(store.getSummary("sd").counts_dirty).toBe(0);
|
|
329
|
-
// Set dirty
|
|
330
|
-
store.updateSummaryCountsDirty(["sd"], true);
|
|
331
|
-
expect(store.getSummary("sd").counts_dirty).toBe(1);
|
|
332
|
-
// Clear dirty
|
|
333
|
-
store.updateSummaryCountsDirty(["sd"], false);
|
|
334
|
-
expect(store.getSummary("sd").counts_dirty).toBe(0);
|
|
335
|
-
});
|
|
336
|
-
it("deleteSummary removes from table and FTS", () => {
|
|
337
|
-
const convId = createTestConversation(store);
|
|
338
|
-
store.insertSummary({ summaryId: "sdel", conversationId: convId, kind: "leaf", depth: 0, content: "delete me please", tokenCount: 5 });
|
|
339
|
-
// Verify it exists
|
|
340
|
-
expect(store.getSummary("sdel")).toBeDefined();
|
|
341
|
-
// Delete
|
|
342
|
-
store.deleteSummary("sdel");
|
|
343
|
-
// Verify gone from table
|
|
344
|
-
expect(store.getSummary("sdel")).toBeUndefined();
|
|
345
|
-
// Verify gone from FTS
|
|
346
|
-
const fts = db
|
|
347
|
-
.prepare("SELECT summary_id FROM ctx_summaries_fts WHERE content MATCH 'delete'")
|
|
348
|
-
.all();
|
|
349
|
-
expect(fts).toHaveLength(0);
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
// =========================================================================
|
|
353
|
-
// Group 5: Summary Links
|
|
354
|
-
// =========================================================================
|
|
355
|
-
describe("summary links", () => {
|
|
356
|
-
it("linkSummaryMessages and getSourceMessageIds round-trip", () => {
|
|
357
|
-
const convId = createTestConversation(store);
|
|
358
|
-
const msgIds = [];
|
|
359
|
-
for (let i = 1; i <= 3; i++) {
|
|
360
|
-
msgIds.push(store.insertMessage({ conversationId: convId, seq: i, role: "user", content: `m${i}`, contentHash: `h${i}`, tokenCount: 1 }));
|
|
361
|
-
}
|
|
362
|
-
store.insertSummary({ summaryId: "slink", conversationId: convId, kind: "leaf", depth: 0, content: "link test", tokenCount: 5 });
|
|
363
|
-
store.linkSummaryMessages("slink", msgIds);
|
|
364
|
-
const result = store.getSourceMessageIds("slink");
|
|
365
|
-
expect(result).toEqual(msgIds);
|
|
366
|
-
});
|
|
367
|
-
it("linkSummaryParents and getParentSummaryIds round-trip", () => {
|
|
368
|
-
const convId = createTestConversation(store);
|
|
369
|
-
store.insertSummary({ summaryId: "parent1", conversationId: convId, kind: "leaf", depth: 0, content: "p1", tokenCount: 5 });
|
|
370
|
-
store.insertSummary({ summaryId: "parent2", conversationId: convId, kind: "leaf", depth: 0, content: "p2", tokenCount: 5 });
|
|
371
|
-
store.insertSummary({ summaryId: "child", conversationId: convId, kind: "condensed", depth: 1, content: "child", tokenCount: 5 });
|
|
372
|
-
store.linkSummaryParents("child", ["parent1", "parent2"]);
|
|
373
|
-
const parents = store.getParentSummaryIds("child");
|
|
374
|
-
expect(parents).toEqual(["parent1", "parent2"]);
|
|
375
|
-
});
|
|
376
|
-
it("getChildSummaryIds returns children of a parent", () => {
|
|
377
|
-
const convId = createTestConversation(store);
|
|
378
|
-
store.insertSummary({ summaryId: "p1", conversationId: convId, kind: "leaf", depth: 0, content: "parent", tokenCount: 5 });
|
|
379
|
-
store.insertSummary({ summaryId: "c1", conversationId: convId, kind: "condensed", depth: 1, content: "child", tokenCount: 5 });
|
|
380
|
-
store.linkSummaryParents("c1", ["p1"]);
|
|
381
|
-
const children = store.getChildSummaryIds("p1");
|
|
382
|
-
expect(children).toEqual(["c1"]);
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
|
-
// =========================================================================
|
|
386
|
-
// Group 6: Context Items
|
|
387
|
-
// =========================================================================
|
|
388
|
-
describe("context items", () => {
|
|
389
|
-
it("replaceContextItems sets and getContextItems retrieves", () => {
|
|
390
|
-
const convId = createTestConversation(store);
|
|
391
|
-
const m1 = store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "a", contentHash: "h1", tokenCount: 1 });
|
|
392
|
-
const m2 = store.insertMessage({ conversationId: convId, seq: 2, role: "assistant", content: "b", contentHash: "h2", tokenCount: 1 });
|
|
393
|
-
store.insertSummary({ summaryId: "si", conversationId: convId, kind: "leaf", depth: 0, content: "sum", tokenCount: 5 });
|
|
394
|
-
store.replaceContextItems(convId, [
|
|
395
|
-
{ ordinal: 0, itemType: "message", messageId: m1 },
|
|
396
|
-
{ ordinal: 1, itemType: "summary", summaryId: "si" },
|
|
397
|
-
{ ordinal: 2, itemType: "message", messageId: m2 },
|
|
398
|
-
]);
|
|
399
|
-
const items = store.getContextItems(convId);
|
|
400
|
-
expect(items).toHaveLength(3);
|
|
401
|
-
expect(items[0].ordinal).toBe(0);
|
|
402
|
-
expect(items[0].item_type).toBe("message");
|
|
403
|
-
expect(items[0].message_id).toBe(m1);
|
|
404
|
-
expect(items[1].ordinal).toBe(1);
|
|
405
|
-
expect(items[1].item_type).toBe("summary");
|
|
406
|
-
expect(items[1].summary_id).toBe("si");
|
|
407
|
-
expect(items[2].ordinal).toBe(2);
|
|
408
|
-
expect(items[2].item_type).toBe("message");
|
|
409
|
-
expect(items[2].message_id).toBe(m2);
|
|
410
|
-
});
|
|
411
|
-
it("replaceContextItems replaces existing items", () => {
|
|
412
|
-
const convId = createTestConversation(store);
|
|
413
|
-
const m1 = store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "a", contentHash: "h1", tokenCount: 1 });
|
|
414
|
-
const m2 = store.insertMessage({ conversationId: convId, seq: 2, role: "user", content: "b", contentHash: "h2", tokenCount: 1 });
|
|
415
|
-
// First set
|
|
416
|
-
store.replaceContextItems(convId, [
|
|
417
|
-
{ ordinal: 0, itemType: "message", messageId: m1 },
|
|
418
|
-
]);
|
|
419
|
-
expect(store.getContextItems(convId)).toHaveLength(1);
|
|
420
|
-
// Replace with different items
|
|
421
|
-
store.replaceContextItems(convId, [
|
|
422
|
-
{ ordinal: 0, itemType: "message", messageId: m2 },
|
|
423
|
-
]);
|
|
424
|
-
const items = store.getContextItems(convId);
|
|
425
|
-
expect(items).toHaveLength(1);
|
|
426
|
-
expect(items[0].message_id).toBe(m2);
|
|
427
|
-
});
|
|
428
|
-
});
|
|
429
|
-
// =========================================================================
|
|
430
|
-
// Group 7: Large Files
|
|
431
|
-
// =========================================================================
|
|
432
|
-
describe("large files", () => {
|
|
433
|
-
it("insertLargeFile and getLargeFile round-trip", () => {
|
|
434
|
-
const convId = createTestConversation(store);
|
|
435
|
-
const fileId = store.insertLargeFile({
|
|
436
|
-
fileId: "file-001",
|
|
437
|
-
conversationId: convId,
|
|
438
|
-
fileName: "document.pdf",
|
|
439
|
-
mimeType: "application/pdf",
|
|
440
|
-
byteSize: 12345,
|
|
441
|
-
contentHash: "filehash123",
|
|
442
|
-
storagePath: "/data/files/file-001.pdf",
|
|
443
|
-
explorationSummary: "A PDF document about testing",
|
|
444
|
-
});
|
|
445
|
-
expect(fileId).toBe("file-001");
|
|
446
|
-
const row = store.getLargeFile("file-001");
|
|
447
|
-
expect(row).toBeDefined();
|
|
448
|
-
expect(row.file_name).toBe("document.pdf");
|
|
449
|
-
expect(row.mime_type).toBe("application/pdf");
|
|
450
|
-
expect(row.byte_size).toBe(12345);
|
|
451
|
-
expect(row.content_hash).toBe("filehash123");
|
|
452
|
-
expect(row.storage_path).toBe("/data/files/file-001.pdf");
|
|
453
|
-
expect(row.exploration_summary).toBe("A PDF document about testing");
|
|
454
|
-
});
|
|
455
|
-
it("getLargeFileByHash finds by conversation+hash", () => {
|
|
456
|
-
const convId = createTestConversation(store);
|
|
457
|
-
store.insertLargeFile({
|
|
458
|
-
fileId: "file-hash-test",
|
|
459
|
-
conversationId: convId,
|
|
460
|
-
contentHash: "unique-file-hash",
|
|
461
|
-
storagePath: "/data/files/test.bin",
|
|
462
|
-
});
|
|
463
|
-
const row = store.getLargeFileByHash(convId, "unique-file-hash");
|
|
464
|
-
expect(row).toBeDefined();
|
|
465
|
-
expect(row.file_id).toBe("file-hash-test");
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
// =========================================================================
|
|
469
|
-
// Group 8: Expansion Grants
|
|
470
|
-
// =========================================================================
|
|
471
|
-
describe("expansion grants", () => {
|
|
472
|
-
it("createGrant and getGrant round-trip", () => {
|
|
473
|
-
const convId = createTestConversation(store);
|
|
474
|
-
const grantId = store.createGrant({
|
|
475
|
-
grantId: "grant-001",
|
|
476
|
-
issuerSession: "sess-1",
|
|
477
|
-
conversationIds: [convId],
|
|
478
|
-
summaryIds: ["sum-a", "sum-b"],
|
|
479
|
-
maxDepth: 5,
|
|
480
|
-
tokenCap: 8000,
|
|
481
|
-
expiresAt: "2099-12-31 23:59:59",
|
|
482
|
-
});
|
|
483
|
-
expect(grantId).toBe("grant-001");
|
|
484
|
-
const row = store.getGrant("grant-001");
|
|
485
|
-
expect(row).toBeDefined();
|
|
486
|
-
expect(row.issuer_session).toBe("sess-1");
|
|
487
|
-
expect(JSON.parse(row.conversation_ids)).toEqual([convId]);
|
|
488
|
-
expect(JSON.parse(row.summary_ids)).toEqual(["sum-a", "sum-b"]);
|
|
489
|
-
expect(row.max_depth).toBe(5);
|
|
490
|
-
expect(row.token_cap).toBe(8000);
|
|
491
|
-
expect(row.tokens_consumed).toBe(0);
|
|
492
|
-
expect(row.revoked).toBe(0);
|
|
493
|
-
});
|
|
494
|
-
it("getActiveGrants filters by session and non-revoked non-expired", () => {
|
|
495
|
-
const convId = createTestConversation(store);
|
|
496
|
-
// Active grant
|
|
497
|
-
store.createGrant({
|
|
498
|
-
grantId: "g-active",
|
|
499
|
-
issuerSession: "sess-x",
|
|
500
|
-
conversationIds: [convId],
|
|
501
|
-
expiresAt: "2099-12-31 23:59:59",
|
|
502
|
-
});
|
|
503
|
-
// Revoked grant
|
|
504
|
-
store.createGrant({
|
|
505
|
-
grantId: "g-revoked",
|
|
506
|
-
issuerSession: "sess-x",
|
|
507
|
-
conversationIds: [convId],
|
|
508
|
-
expiresAt: "2099-12-31 23:59:59",
|
|
509
|
-
});
|
|
510
|
-
store.revokeGrant("g-revoked");
|
|
511
|
-
// Expired grant
|
|
512
|
-
store.createGrant({
|
|
513
|
-
grantId: "g-expired",
|
|
514
|
-
issuerSession: "sess-x",
|
|
515
|
-
conversationIds: [convId],
|
|
516
|
-
expiresAt: "2000-01-01 00:00:00",
|
|
517
|
-
});
|
|
518
|
-
const active = store.getActiveGrants("sess-x");
|
|
519
|
-
expect(active).toHaveLength(1);
|
|
520
|
-
expect(active[0].grant_id).toBe("g-active");
|
|
521
|
-
});
|
|
522
|
-
it("consumeGrantTokens increments tokens_consumed", () => {
|
|
523
|
-
const convId = createTestConversation(store);
|
|
524
|
-
store.createGrant({
|
|
525
|
-
grantId: "g-consume",
|
|
526
|
-
issuerSession: "sess-c",
|
|
527
|
-
conversationIds: [convId],
|
|
528
|
-
expiresAt: "2099-12-31 23:59:59",
|
|
529
|
-
});
|
|
530
|
-
store.consumeGrantTokens("g-consume", 100);
|
|
531
|
-
expect(store.getGrant("g-consume").tokens_consumed).toBe(100);
|
|
532
|
-
store.consumeGrantTokens("g-consume", 200);
|
|
533
|
-
expect(store.getGrant("g-consume").tokens_consumed).toBe(300);
|
|
534
|
-
});
|
|
535
|
-
it("revokeGrant sets revoked flag", () => {
|
|
536
|
-
const convId = createTestConversation(store);
|
|
537
|
-
store.createGrant({
|
|
538
|
-
grantId: "g-revoke",
|
|
539
|
-
issuerSession: "sess-r",
|
|
540
|
-
conversationIds: [convId],
|
|
541
|
-
expiresAt: "2099-12-31 23:59:59",
|
|
542
|
-
});
|
|
543
|
-
store.revokeGrant("g-revoke");
|
|
544
|
-
expect(store.getGrant("g-revoke").revoked).toBe(1);
|
|
545
|
-
});
|
|
546
|
-
it("cleanupExpiredGrants removes expired and revoked", () => {
|
|
547
|
-
const convId = createTestConversation(store);
|
|
548
|
-
// Expired grant
|
|
549
|
-
store.createGrant({
|
|
550
|
-
grantId: "g-exp",
|
|
551
|
-
issuerSession: "sess-cl",
|
|
552
|
-
conversationIds: [convId],
|
|
553
|
-
expiresAt: "2000-01-01 00:00:00",
|
|
554
|
-
});
|
|
555
|
-
// Revoked grant
|
|
556
|
-
store.createGrant({
|
|
557
|
-
grantId: "g-rev",
|
|
558
|
-
issuerSession: "sess-cl",
|
|
559
|
-
conversationIds: [convId],
|
|
560
|
-
expiresAt: "2099-12-31 23:59:59",
|
|
561
|
-
});
|
|
562
|
-
store.revokeGrant("g-rev");
|
|
563
|
-
// Active grant (should survive)
|
|
564
|
-
store.createGrant({
|
|
565
|
-
grantId: "g-keep",
|
|
566
|
-
issuerSession: "sess-cl",
|
|
567
|
-
conversationIds: [convId],
|
|
568
|
-
expiresAt: "2099-12-31 23:59:59",
|
|
569
|
-
});
|
|
570
|
-
const count = store.cleanupExpiredGrants();
|
|
571
|
-
expect(count).toBe(2);
|
|
572
|
-
expect(store.getGrant("g-exp")).toBeUndefined();
|
|
573
|
-
expect(store.getGrant("g-rev")).toBeUndefined();
|
|
574
|
-
expect(store.getGrant("g-keep")).toBeDefined();
|
|
575
|
-
});
|
|
576
|
-
});
|
|
577
|
-
// =========================================================================
|
|
578
|
-
// Group 9: FTS5 Search
|
|
579
|
-
// =========================================================================
|
|
580
|
-
describe("FTS5 search", () => {
|
|
581
|
-
it("searchMessages in fts mode finds matching messages", () => {
|
|
582
|
-
const convId = createTestConversation(store);
|
|
583
|
-
store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "the quick brown fox", contentHash: "h1", tokenCount: 4 });
|
|
584
|
-
store.insertMessage({ conversationId: convId, seq: 2, role: "user", content: "lazy dog jumps", contentHash: "h2", tokenCount: 3 });
|
|
585
|
-
store.insertMessage({ conversationId: convId, seq: 3, role: "user", content: "brown bear sleeps", contentHash: "h3", tokenCount: 3 });
|
|
586
|
-
store.insertMessage({ conversationId: convId, seq: 4, role: "user", content: "hello world", contentHash: "h4", tokenCount: 2 });
|
|
587
|
-
store.insertMessage({ conversationId: convId, seq: 5, role: "user", content: "another brown animal", contentHash: "h5", tokenCount: 3 });
|
|
588
|
-
const results = store.searchMessages(convId, "brown", { mode: "fts", limit: 10 });
|
|
589
|
-
expect(results.length).toBeGreaterThanOrEqual(2);
|
|
590
|
-
// All results should contain "brown" content
|
|
591
|
-
for (const r of results) {
|
|
592
|
-
expect(r.content.toLowerCase()).toContain("brown");
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
it("searchMessages in fts mode respects limit", () => {
|
|
596
|
-
const convId = createTestConversation(store);
|
|
597
|
-
for (let i = 1; i <= 10; i++) {
|
|
598
|
-
store.insertMessage({
|
|
599
|
-
conversationId: convId,
|
|
600
|
-
seq: i,
|
|
601
|
-
role: "user",
|
|
602
|
-
content: `matching keyword phrase ${i}`,
|
|
603
|
-
contentHash: `hk${i}`,
|
|
604
|
-
tokenCount: 3,
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
const results = store.searchMessages(convId, "keyword", { mode: "fts", limit: 3 });
|
|
608
|
-
expect(results).toHaveLength(3);
|
|
609
|
-
});
|
|
610
|
-
it("searchMessages in regex mode uses LIKE pre-filter", () => {
|
|
611
|
-
const convId = createTestConversation(store);
|
|
612
|
-
store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "error code 404 not found", contentHash: "hr1", tokenCount: 5 });
|
|
613
|
-
store.insertMessage({ conversationId: convId, seq: 2, role: "user", content: "error code 500 server", contentHash: "hr2", tokenCount: 4 });
|
|
614
|
-
store.insertMessage({ conversationId: convId, seq: 3, role: "user", content: "all good here", contentHash: "hr3", tokenCount: 3 });
|
|
615
|
-
const results = store.searchMessages(convId, "error.*\\d+", { mode: "regex", limit: 10 });
|
|
616
|
-
expect(results).toHaveLength(2);
|
|
617
|
-
});
|
|
618
|
-
it("searchSummaries in fts mode finds matching summaries", () => {
|
|
619
|
-
const convId = createTestConversation(store);
|
|
620
|
-
store.insertSummary({ summaryId: "sf1", conversationId: convId, kind: "leaf", depth: 0, content: "discussion about databases", tokenCount: 5 });
|
|
621
|
-
store.insertSummary({ summaryId: "sf2", conversationId: convId, kind: "leaf", depth: 0, content: "review of architecture patterns", tokenCount: 5 });
|
|
622
|
-
store.insertSummary({ summaryId: "sf3", conversationId: convId, kind: "leaf", depth: 0, content: "database migration plan", tokenCount: 5 });
|
|
623
|
-
const results = store.searchSummaries(convId, "database", { mode: "fts", limit: 10 });
|
|
624
|
-
// "databases" stems to "databas" via porter, "database" stems to "databas" -- should match
|
|
625
|
-
expect(results.length).toBeGreaterThanOrEqual(1);
|
|
626
|
-
});
|
|
627
|
-
it("searchMessages returns empty for no matches", () => {
|
|
628
|
-
const convId = createTestConversation(store);
|
|
629
|
-
store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "hello world", contentHash: "hn", tokenCount: 2 });
|
|
630
|
-
const results = store.searchMessages(convId, "xyznonexistent", { mode: "fts", limit: 10 });
|
|
631
|
-
expect(results).toEqual([]);
|
|
632
|
-
});
|
|
633
|
-
});
|
|
634
|
-
// =========================================================================
|
|
635
|
-
// Group 10: deleteConversation (bulk)
|
|
636
|
-
// =========================================================================
|
|
637
|
-
describe("deleteConversation", () => {
|
|
638
|
-
it("deleteConversation removes all related data", () => {
|
|
639
|
-
const convId = createTestConversation(store);
|
|
640
|
-
// Insert messages
|
|
641
|
-
const m1 = store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "msg1", contentHash: "h1", tokenCount: 1 });
|
|
642
|
-
const m2 = store.insertMessage({ conversationId: convId, seq: 2, role: "assistant", content: "msg2", contentHash: "h2", tokenCount: 1 });
|
|
643
|
-
// Insert parts
|
|
644
|
-
store.insertParts(m1, [{ ordinal: 0, partType: "text", content: "part1" }]);
|
|
645
|
-
// Insert summaries
|
|
646
|
-
store.insertSummary({ summaryId: "sdel1", conversationId: convId, kind: "leaf", depth: 0, content: "sum1", tokenCount: 5 });
|
|
647
|
-
store.insertSummary({ summaryId: "sdel2", conversationId: convId, kind: "condensed", depth: 1, content: "sum2", tokenCount: 5 });
|
|
648
|
-
// Link summary to messages
|
|
649
|
-
store.linkSummaryMessages("sdel1", [m1, m2]);
|
|
650
|
-
store.linkSummaryParents("sdel2", ["sdel1"]);
|
|
651
|
-
// Context items
|
|
652
|
-
store.replaceContextItems(convId, [
|
|
653
|
-
{ ordinal: 0, itemType: "message", messageId: m1 },
|
|
654
|
-
{ ordinal: 1, itemType: "summary", summaryId: "sdel1" },
|
|
655
|
-
]);
|
|
656
|
-
// Large file
|
|
657
|
-
store.insertLargeFile({
|
|
658
|
-
fileId: "fdel",
|
|
659
|
-
conversationId: convId,
|
|
660
|
-
storagePath: "/tmp/fdel",
|
|
661
|
-
});
|
|
662
|
-
// Grant
|
|
663
|
-
store.createGrant({
|
|
664
|
-
grantId: "gdel",
|
|
665
|
-
issuerSession: "sess-del",
|
|
666
|
-
conversationIds: [convId],
|
|
667
|
-
expiresAt: "2099-12-31 23:59:59",
|
|
668
|
-
});
|
|
669
|
-
// Delete everything
|
|
670
|
-
store.deleteConversation(convId);
|
|
671
|
-
// Verify all tables are empty for that conversation
|
|
672
|
-
expect(store.getConversation(convId)).toBeUndefined();
|
|
673
|
-
const msgs = db.prepare("SELECT COUNT(*) as c FROM ctx_messages WHERE conversation_id = ?").get(convId);
|
|
674
|
-
expect(msgs.c).toBe(0);
|
|
675
|
-
const parts = db.prepare("SELECT COUNT(*) as c FROM ctx_message_parts").get();
|
|
676
|
-
expect(parts.c).toBe(0);
|
|
677
|
-
const sums = db.prepare("SELECT COUNT(*) as c FROM ctx_summaries WHERE conversation_id = ?").get(convId);
|
|
678
|
-
expect(sums.c).toBe(0);
|
|
679
|
-
const sumMsgs = db.prepare("SELECT COUNT(*) as c FROM ctx_summary_messages").get();
|
|
680
|
-
expect(sumMsgs.c).toBe(0);
|
|
681
|
-
const sumParents = db.prepare("SELECT COUNT(*) as c FROM ctx_summary_parents").get();
|
|
682
|
-
expect(sumParents.c).toBe(0);
|
|
683
|
-
const items = db.prepare("SELECT COUNT(*) as c FROM ctx_context_items WHERE conversation_id = ?").get(convId);
|
|
684
|
-
expect(items.c).toBe(0);
|
|
685
|
-
const files = db.prepare("SELECT COUNT(*) as c FROM ctx_large_files WHERE conversation_id = ?").get(convId);
|
|
686
|
-
expect(files.c).toBe(0);
|
|
687
|
-
});
|
|
688
|
-
it("deleteConversation cleans up FTS entries", () => {
|
|
689
|
-
const convId = createTestConversation(store);
|
|
690
|
-
// Insert messages with FTS
|
|
691
|
-
store.insertMessage({ conversationId: convId, seq: 1, role: "user", content: "fts cleanup verification msg", contentHash: "hfts1", tokenCount: 4 });
|
|
692
|
-
store.insertMessage({ conversationId: convId, seq: 2, role: "user", content: "another fts cleanup msg", contentHash: "hfts2", tokenCount: 4 });
|
|
693
|
-
// Insert summaries with FTS
|
|
694
|
-
store.insertSummary({ summaryId: "sfts1", conversationId: convId, kind: "leaf", depth: 0, content: "fts cleanup verification summary", tokenCount: 5 });
|
|
695
|
-
// Verify FTS entries exist before delete
|
|
696
|
-
const msgFtsBefore = db.prepare("SELECT COUNT(*) as c FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'cleanup'").get();
|
|
697
|
-
expect(msgFtsBefore.c).toBeGreaterThan(0);
|
|
698
|
-
const sumFtsBefore = db.prepare("SELECT COUNT(*) as c FROM ctx_summaries_fts WHERE content MATCH 'cleanup'").get();
|
|
699
|
-
expect(sumFtsBefore.c).toBeGreaterThan(0);
|
|
700
|
-
// Delete conversation
|
|
701
|
-
store.deleteConversation(convId);
|
|
702
|
-
// Verify FTS entries are cleaned up
|
|
703
|
-
const msgFtsAfter = db.prepare("SELECT COUNT(*) as c FROM ctx_messages_fts WHERE ctx_messages_fts MATCH 'cleanup'").get();
|
|
704
|
-
expect(msgFtsAfter.c).toBe(0);
|
|
705
|
-
const sumFtsAfter = db.prepare("SELECT COUNT(*) as c FROM ctx_summaries_fts WHERE content MATCH 'cleanup'").get();
|
|
706
|
-
expect(sumFtsAfter.c).toBe(0);
|
|
707
|
-
});
|
|
708
|
-
});
|