@jini-ai/sqlite 0.3.0 → 0.5.0

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.
Files changed (115) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +23 -110
  3. package/dist/index.d.ts +17 -9
  4. package/dist/index.js +12 -9
  5. package/package.json +11 -7
  6. package/dist/backend-config.d.ts +0 -42
  7. package/dist/backend-config.d.ts.map +0 -1
  8. package/dist/backend-config.js +0 -57
  9. package/dist/backend-config.js.map +0 -1
  10. package/dist/db/agent-sessions/agent-sessions.d.ts +0 -79
  11. package/dist/db/agent-sessions/agent-sessions.d.ts.map +0 -1
  12. package/dist/db/agent-sessions/agent-sessions.js +0 -121
  13. package/dist/db/agent-sessions/agent-sessions.js.map +0 -1
  14. package/dist/db/agent-sessions/index.d.ts +0 -6
  15. package/dist/db/agent-sessions/index.d.ts.map +0 -1
  16. package/dist/db/agent-sessions/index.js +0 -6
  17. package/dist/db/agent-sessions/index.js.map +0 -1
  18. package/dist/db/capabilities/capabilities.d.ts +0 -39
  19. package/dist/db/capabilities/capabilities.d.ts.map +0 -1
  20. package/dist/db/capabilities/capabilities.js +0 -54
  21. package/dist/db/capabilities/capabilities.js.map +0 -1
  22. package/dist/db/capabilities/index.d.ts +0 -4
  23. package/dist/db/capabilities/index.d.ts.map +0 -1
  24. package/dist/db/capabilities/index.js +0 -4
  25. package/dist/db/capabilities/index.js.map +0 -1
  26. package/dist/db/chat-history/index.d.ts +0 -15
  27. package/dist/db/chat-history/index.d.ts.map +0 -1
  28. package/dist/db/chat-history/index.js +0 -15
  29. package/dist/db/chat-history/index.js.map +0 -1
  30. package/dist/db/chat-history/schema.d.ts +0 -61
  31. package/dist/db/chat-history/schema.d.ts.map +0 -1
  32. package/dist/db/chat-history/schema.js +0 -80
  33. package/dist/db/chat-history/schema.js.map +0 -1
  34. package/dist/db/chat-history/store.d.ts +0 -41
  35. package/dist/db/chat-history/store.d.ts.map +0 -1
  36. package/dist/db/chat-history/store.js +0 -254
  37. package/dist/db/chat-history/store.js.map +0 -1
  38. package/dist/db/connection/connection.d.ts +0 -18
  39. package/dist/db/connection/connection.d.ts.map +0 -1
  40. package/dist/db/connection/connection.js +0 -48
  41. package/dist/db/connection/connection.js.map +0 -1
  42. package/dist/db/connection/index.d.ts +0 -3
  43. package/dist/db/connection/index.d.ts.map +0 -1
  44. package/dist/db/connection/index.js +0 -3
  45. package/dist/db/connection/index.js.map +0 -1
  46. package/dist/db/conversations/conversations.d.ts +0 -109
  47. package/dist/db/conversations/conversations.d.ts.map +0 -1
  48. package/dist/db/conversations/conversations.js +0 -274
  49. package/dist/db/conversations/conversations.js.map +0 -1
  50. package/dist/db/conversations/index.d.ts +0 -3
  51. package/dist/db/conversations/index.d.ts.map +0 -1
  52. package/dist/db/conversations/index.js +0 -3
  53. package/dist/db/conversations/index.js.map +0 -1
  54. package/dist/db/core/index.d.ts +0 -6
  55. package/dist/db/core/index.d.ts.map +0 -1
  56. package/dist/db/core/index.js +0 -6
  57. package/dist/db/core/index.js.map +0 -1
  58. package/dist/db/core/json.d.ts +0 -13
  59. package/dist/db/core/json.d.ts.map +0 -1
  60. package/dist/db/core/json.js +0 -22
  61. package/dist/db/core/json.js.map +0 -1
  62. package/dist/db/core/rows.d.ts +0 -18
  63. package/dist/db/core/rows.d.ts.map +0 -1
  64. package/dist/db/core/rows.js +0 -17
  65. package/dist/db/core/rows.js.map +0 -1
  66. package/dist/db/core/types.d.ts +0 -14
  67. package/dist/db/core/types.d.ts.map +0 -1
  68. package/dist/db/core/types.js +0 -2
  69. package/dist/db/core/types.js.map +0 -1
  70. package/dist/db/index.d.ts +0 -24
  71. package/dist/db/index.d.ts.map +0 -1
  72. package/dist/db/index.js +0 -22
  73. package/dist/db/index.js.map +0 -1
  74. package/dist/db/messages/index.d.ts +0 -4
  75. package/dist/db/messages/index.d.ts.map +0 -1
  76. package/dist/db/messages/index.js +0 -4
  77. package/dist/db/messages/index.js.map +0 -1
  78. package/dist/db/messages/messages.d.ts +0 -129
  79. package/dist/db/messages/messages.d.ts.map +0 -1
  80. package/dist/db/messages/messages.js +0 -246
  81. package/dist/db/messages/messages.js.map +0 -1
  82. package/dist/db/projects/index.d.ts +0 -4
  83. package/dist/db/projects/index.d.ts.map +0 -1
  84. package/dist/db/projects/index.js +0 -4
  85. package/dist/db/projects/index.js.map +0 -1
  86. package/dist/db/projects/projects.d.ts +0 -122
  87. package/dist/db/projects/projects.d.ts.map +0 -1
  88. package/dist/db/projects/projects.js +0 -311
  89. package/dist/db/projects/projects.js.map +0 -1
  90. package/dist/db/schema/index.d.ts +0 -3
  91. package/dist/db/schema/index.d.ts.map +0 -1
  92. package/dist/db/schema/index.js +0 -3
  93. package/dist/db/schema/index.js.map +0 -1
  94. package/dist/db/schema/migrate.d.ts +0 -29
  95. package/dist/db/schema/migrate.d.ts.map +0 -1
  96. package/dist/db/schema/migrate.js +0 -78
  97. package/dist/db/schema/migrate.js.map +0 -1
  98. package/dist/db/tool-catalog/index.d.ts +0 -6
  99. package/dist/db/tool-catalog/index.d.ts.map +0 -1
  100. package/dist/db/tool-catalog/index.js +0 -6
  101. package/dist/db/tool-catalog/index.js.map +0 -1
  102. package/dist/db/tool-catalog/tool-catalog.d.ts +0 -70
  103. package/dist/db/tool-catalog/tool-catalog.d.ts.map +0 -1
  104. package/dist/db/tool-catalog/tool-catalog.js +0 -87
  105. package/dist/db/tool-catalog/tool-catalog.js.map +0 -1
  106. package/dist/db-inspect.d.ts +0 -62
  107. package/dist/db-inspect.d.ts.map +0 -1
  108. package/dist/db-inspect.js +0 -153
  109. package/dist/db-inspect.js.map +0 -1
  110. package/dist/event-log.d.ts +0 -33
  111. package/dist/event-log.d.ts.map +0 -1
  112. package/dist/event-log.js +0 -191
  113. package/dist/event-log.js.map +0 -1
  114. package/dist/index.d.ts.map +0 -1
  115. package/dist/index.js.map +0 -1
@@ -1,39 +0,0 @@
1
- /**
2
- * @module db/capabilities/capabilities
3
- *
4
- * The SQLite-backed half of `ai-control-plane.md`'s proposed `CapabilityCatalog` (§9): durable
5
- * storage for a host's registered capability definitions (name/description/schema/risk) plus an
6
- * append-only execution audit trail. New, not ported — scoped to what a real caller
7
- * (`examples/reference-web`'s chat capabilities, first) needs today rather than the full
8
- * FTS5/tag/scope/provider schema that document sketches; add those when a caller actually needs
9
- * search-at-scale instead of a small `SELECT *`. Imports only the `core/` kernel.
10
- */
11
- import type { SqliteDb } from '../core/index.js';
12
- export interface CapabilityDefinitionRecord {
13
- readonly id: string;
14
- readonly description: string;
15
- readonly inputSchema: unknown;
16
- readonly risk: string;
17
- readonly updatedAt: number;
18
- }
19
- export interface CapabilityExecutionRecord {
20
- readonly executionId: string;
21
- readonly capabilityId: string;
22
- readonly input: unknown;
23
- readonly status: string;
24
- readonly output?: unknown;
25
- readonly error?: string;
26
- readonly requestedAt: number;
27
- readonly completedAt?: number;
28
- }
29
- /** Inserts a capability definition, or overwrites the existing row with the same id (a host re-declaring its manifest at every startup, not a versioned upgrade). */
30
- export declare function upsertCapabilityDefinition(db: SqliteDb, def: Omit<CapabilityDefinitionRecord, 'updatedAt'> & {
31
- updatedAt?: number;
32
- }): void;
33
- /** Lists every registered capability definition, ordered by id. */
34
- export declare function listCapabilityDefinitions(db: SqliteDb): CapabilityDefinitionRecord[];
35
- /** Fetches one capability definition by id, or `null` if it isn't registered. */
36
- export declare function getCapabilityDefinition(db: SqliteDb, id: string): CapabilityDefinitionRecord | null;
37
- /** Appends one execution audit row. Insert-only — this is a durable trail, never updated in place. */
38
- export declare function recordCapabilityExecution(db: SqliteDb, record: CapabilityExecutionRecord): void;
39
- //# sourceMappingURL=capabilities.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../../src/db/capabilities/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAS,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGxD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAYD,qKAAqK;AACrK,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,0BAA0B,EAAE,WAAW,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAiB1I;AAED,mEAAmE;AACnE,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,QAAQ,GAAG,0BAA0B,EAAE,CAOpF;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,0BAA0B,GAAG,IAAI,CAQnG;AAED,sGAAsG;AACtG,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,yBAAyB,GAAG,IAAI,CAe/F"}
@@ -1,54 +0,0 @@
1
- import { row, rows } from '../core/index.js';
2
- function normalizeDefinition(r) {
3
- return {
4
- id: r.id,
5
- description: r.description,
6
- inputSchema: JSON.parse(r.inputSchemaJson),
7
- risk: r.risk,
8
- updatedAt: Number(r.updatedAt),
9
- };
10
- }
11
- /** Inserts a capability definition, or overwrites the existing row with the same id (a host re-declaring its manifest at every startup, not a versioned upgrade). */
12
- export function upsertCapabilityDefinition(db, def) {
13
- const updatedAt = def.updatedAt ?? Date.now();
14
- db.prepare(`INSERT INTO capability_definitions (id, description, input_schema_json, risk, updated_at)
15
- VALUES (@id, @description, @inputSchemaJson, @risk, @updatedAt)
16
- ON CONFLICT(id) DO UPDATE SET
17
- description = excluded.description,
18
- input_schema_json = excluded.input_schema_json,
19
- risk = excluded.risk,
20
- updated_at = excluded.updated_at`).run({
21
- id: def.id,
22
- description: def.description,
23
- inputSchemaJson: JSON.stringify(def.inputSchema),
24
- risk: def.risk,
25
- updatedAt,
26
- });
27
- }
28
- /** Lists every registered capability definition, ordered by id. */
29
- export function listCapabilityDefinitions(db) {
30
- return rows(db.prepare(`SELECT id, description, input_schema_json AS inputSchemaJson, risk, updated_at AS updatedAt
31
- FROM capability_definitions ORDER BY id`).all()).map(normalizeDefinition);
32
- }
33
- /** Fetches one capability definition by id, or `null` if it isn't registered. */
34
- export function getCapabilityDefinition(db, id) {
35
- const r = row(db.prepare(`SELECT id, description, input_schema_json AS inputSchemaJson, risk, updated_at AS updatedAt
36
- FROM capability_definitions WHERE id = ?`).get(id));
37
- return r ? normalizeDefinition(r) : null;
38
- }
39
- /** Appends one execution audit row. Insert-only — this is a durable trail, never updated in place. */
40
- export function recordCapabilityExecution(db, record) {
41
- db.prepare(`INSERT INTO capability_executions
42
- (execution_id, capability_id, input_json, status, output_json, error, requested_at, completed_at)
43
- VALUES (@executionId, @capabilityId, @inputJson, @status, @outputJson, @error, @requestedAt, @completedAt)`).run({
44
- executionId: record.executionId,
45
- capabilityId: record.capabilityId,
46
- inputJson: JSON.stringify(record.input ?? null),
47
- status: record.status,
48
- outputJson: record.output === undefined ? null : JSON.stringify(record.output),
49
- error: record.error ?? null,
50
- requestedAt: record.requestedAt,
51
- completedAt: record.completedAt ?? null,
52
- });
53
- }
54
- //# sourceMappingURL=capabilities.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../../src/db/capabilities/capabilities.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAqB7C,SAAS,mBAAmB,CAAC,CAAQ;IACnC,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,eAAyB,CAAC;QACpD,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED,qKAAqK;AACrK,MAAM,UAAU,0BAA0B,CAAC,EAAY,EAAE,GAA2E;IAClI,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IAC9C,EAAE,CAAC,OAAO,CACR;;;;;;wCAMoC,CACrC,CAAC,GAAG,CAAC;QACJ,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC;QAChD,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,SAAS;KACV,CAAC,CAAC;AACL,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,yBAAyB,CAAC,EAAY;IACpD,OAAO,IAAI,CACT,EAAE,CAAC,OAAO,CACR;iDAC2C,CAC5C,CAAC,GAAG,EAAE,CACR,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AAC7B,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,uBAAuB,CAAC,EAAY,EAAE,EAAU;IAC9D,MAAM,CAAC,GAAG,GAAG,CACX,EAAE,CAAC,OAAO,CACR;kDAC4C,CAC7C,CAAC,GAAG,CAAC,EAAE,CAAC,CACV,CAAC;IACF,OAAO,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3C,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,yBAAyB,CAAC,EAAY,EAAE,MAAiC;IACvF,EAAE,CAAC,OAAO,CACR;;gHAE4G,CAC7G,CAAC,GAAG,CAAC;QACJ,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC;QAC/C,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,UAAU,EAAE,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC;QAC9E,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI;QAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,IAAI;KACxC,CAAC,CAAC;AACL,CAAC"}
@@ -1,4 +0,0 @@
1
- /** @module db/capabilities/index
2
- * Public re-export surface for the capabilities/ concern subdirectory. */
3
- export * from './capabilities.js';
4
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/db/capabilities/index.ts"],"names":[],"mappings":"AAAA;0EAC0E;AAC1E,cAAc,mBAAmB,CAAC"}
@@ -1,4 +0,0 @@
1
- /** @module db/capabilities/index
2
- * Public re-export surface for the capabilities/ concern subdirectory. */
3
- export * from './capabilities.js';
4
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/db/capabilities/index.ts"],"names":[],"mappings":"AAAA;0EAC0E;AAC1E,cAAc,mBAAmB,CAAC"}
@@ -1,15 +0,0 @@
1
- /**
2
- * @module db/chat-history
3
- *
4
- * Durable, owner-scoped chat history — the SQLite side of `@jini-ai/chat/core`'s
5
- * `ChatHistoryStore`. Takes an injected handle and never opens a database of its own, so a host
6
- * with its own file (and its own migration tooling) can use it without adopting this package's
7
- * `app.sqlite`.
8
- *
9
- * Distinct from the older `db/conversations` module, which is `project_id`-scoped with no owner
10
- * predicate at all — correct for a single-user local app, unsafe for a shared one. See
11
- * `store.ts`'s header.
12
- */
13
- export * from './schema.js';
14
- export * from './store.js';
15
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/db/chat-history/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -1,15 +0,0 @@
1
- /**
2
- * @module db/chat-history
3
- *
4
- * Durable, owner-scoped chat history — the SQLite side of `@jini-ai/chat/core`'s
5
- * `ChatHistoryStore`. Takes an injected handle and never opens a database of its own, so a host
6
- * with its own file (and its own migration tooling) can use it without adopting this package's
7
- * `app.sqlite`.
8
- *
9
- * Distinct from the older `db/conversations` module, which is `project_id`-scoped with no owner
10
- * predicate at all — correct for a single-user local app, unsafe for a shared one. See
11
- * `store.ts`'s header.
12
- */
13
- export * from './schema.js';
14
- export * from './store.js';
15
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/db/chat-history/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -1,61 +0,0 @@
1
- /**
2
- * @module db/chat-history/schema
3
- *
4
- * DDL for durable chat history (`ai_chats` + `ai_chat_messages`), exported as a constant so a
5
- * host that owns its own migration tooling applies the *same* statements this package's
6
- * {@link ensureChatHistoryTables} would — the schema is defined once and cannot drift between
7
- * the two paths.
8
- *
9
- * Deliberately NOT wired into `schema/migrate.ts`, following `tool-catalog.ts`'s precedent and
10
- * for its stated reason: `migrate()` opens and owns `<dataDir>/app.sqlite`, so anything it
11
- * creates lands in *this package's* database. A host with its own database (the reference
12
- * implementation's `content.db`)
13
- * must not have a second migrator writing DDL into it behind its backup and snapshot tooling.
14
- * Such a host copies {@link CHAT_HISTORY_DDL} into its own numbered migration and never calls
15
- * `ensureChatHistoryTables`; a host with no migration system of its own calls the function.
16
- * Both end up at the same schema.
17
- *
18
- * Two tables, not one. `session_key_hash` is a bearer credential and must not repeat on every
19
- * message row; retention becomes one cascading `DELETE`; an empty conversation has to be
20
- * representable so "New chat" can exist before the first message; and a list view needs a row
21
- * to list without touching message storage at all.
22
- */
23
- import type { SqliteDb } from '../core/types.js';
24
- /**
25
- * The canonical chat-history schema.
26
- *
27
- * Notes on the parts that are easy to get wrong:
28
- *
29
- * - **`scope_id`** is the host's partition key (workspace/tenant/project). It is `NOT NULL` from
30
- * the first migration on purpose: adding it later is trivial, but *backfilling* it is not —
31
- * pre-existing rows carry no evidence of which partition they belonged to, so a late addition
32
- * forces a choice between deleting history and guessing.
33
- * - **`owner_kind` + the CHECK pair** make "a guest row with a `user_id`" unrepresentable rather
34
- * than merely discouraged.
35
- * - **`owner_id`** holds a user id for `user`, and a *hash* of the session key for `guest`. The
36
- * raw token never reaches this table.
37
- * - **`title_source`** exists so an agent-generated title cannot clobber a manual rename. That
38
- * rule is enforced in the store, but it needs a column to be enforceable at all.
39
- * - **`position`** orders messages, not `created_at`: two inserts inside the same millisecond
40
- * are ordinary under concurrent writers, and a tie there corrupts replay order permanently.
41
- * `UNIQUE (conversation_id, position)` turns a position-assignment bug into a loud constraint
42
- * violation instead of a silently scrambled transcript.
43
- * - **`expires_at NULL`** means "never expires". The retention sweep filters on
44
- * `expires_at IS NOT NULL`, so authenticated history is out of its reach by construction, not
45
- * by a predicate that could drift.
46
- */
47
- export declare const CHAT_HISTORY_DDL = "\nCREATE TABLE IF NOT EXISTS ai_chats (\n id TEXT PRIMARY KEY,\n scope_id TEXT NOT NULL,\n owner_kind TEXT NOT NULL CHECK (owner_kind IN ('user','guest')),\n owner_id TEXT NOT NULL,\n title TEXT,\n title_source TEXT NOT NULL DEFAULT 'fallback'\n CHECK (title_source IN ('fallback','generated','manual')),\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n expires_at INTEGER\n);\n\nCREATE INDEX IF NOT EXISTS idx_ai_chats_owner\n ON ai_chats(scope_id, owner_kind, owner_id, updated_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_ai_chats_expiry\n ON ai_chats(expires_at) WHERE expires_at IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS ai_chat_messages (\n id TEXT PRIMARY KEY,\n conversation_id TEXT NOT NULL REFERENCES ai_chats(id) ON DELETE CASCADE,\n role TEXT NOT NULL CHECK (role IN ('user','assistant')),\n content TEXT NOT NULL,\n agent_id TEXT,\n agent_name TEXT,\n events_json TEXT,\n attachments_json TEXT,\n run_id TEXT,\n run_status TEXT,\n position INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n started_at INTEGER,\n ended_at INTEGER,\n UNIQUE (conversation_id, position)\n);\n\nCREATE INDEX IF NOT EXISTS idx_ai_chat_messages_order\n ON ai_chat_messages(conversation_id, position);\n";
48
- /**
49
- * Creates the chat-history tables in the given database if they are absent.
50
- *
51
- * Idempotent, matching `ensureToolCatalogTables`'s convention. For a host whose database is
52
- * managed by its own migration system, prefer copying {@link CHAT_HISTORY_DDL} into a numbered
53
- * migration instead — see this module's header for why two migrators over one file is a hazard.
54
- *
55
- * The caller is responsible for `PRAGMA foreign_keys = ON`; without it the `ON DELETE CASCADE`
56
- * above is silently inert and deleting a conversation orphans its messages. This function does
57
- * not set it, because pragmas are connection-wide and a library has no business reconfiguring a
58
- * handle it was merely lent.
59
- */
60
- export declare function ensureChatHistoryTables(db: SqliteDb): void;
61
- //# sourceMappingURL=schema.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/db/chat-history/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,gBAAgB,u2CAwC5B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAE1D"}
@@ -1,80 +0,0 @@
1
- /**
2
- * The canonical chat-history schema.
3
- *
4
- * Notes on the parts that are easy to get wrong:
5
- *
6
- * - **`scope_id`** is the host's partition key (workspace/tenant/project). It is `NOT NULL` from
7
- * the first migration on purpose: adding it later is trivial, but *backfilling* it is not —
8
- * pre-existing rows carry no evidence of which partition they belonged to, so a late addition
9
- * forces a choice between deleting history and guessing.
10
- * - **`owner_kind` + the CHECK pair** make "a guest row with a `user_id`" unrepresentable rather
11
- * than merely discouraged.
12
- * - **`owner_id`** holds a user id for `user`, and a *hash* of the session key for `guest`. The
13
- * raw token never reaches this table.
14
- * - **`title_source`** exists so an agent-generated title cannot clobber a manual rename. That
15
- * rule is enforced in the store, but it needs a column to be enforceable at all.
16
- * - **`position`** orders messages, not `created_at`: two inserts inside the same millisecond
17
- * are ordinary under concurrent writers, and a tie there corrupts replay order permanently.
18
- * `UNIQUE (conversation_id, position)` turns a position-assignment bug into a loud constraint
19
- * violation instead of a silently scrambled transcript.
20
- * - **`expires_at NULL`** means "never expires". The retention sweep filters on
21
- * `expires_at IS NOT NULL`, so authenticated history is out of its reach by construction, not
22
- * by a predicate that could drift.
23
- */
24
- export const CHAT_HISTORY_DDL = `
25
- CREATE TABLE IF NOT EXISTS ai_chats (
26
- id TEXT PRIMARY KEY,
27
- scope_id TEXT NOT NULL,
28
- owner_kind TEXT NOT NULL CHECK (owner_kind IN ('user','guest')),
29
- owner_id TEXT NOT NULL,
30
- title TEXT,
31
- title_source TEXT NOT NULL DEFAULT 'fallback'
32
- CHECK (title_source IN ('fallback','generated','manual')),
33
- created_at INTEGER NOT NULL,
34
- updated_at INTEGER NOT NULL,
35
- expires_at INTEGER
36
- );
37
-
38
- CREATE INDEX IF NOT EXISTS idx_ai_chats_owner
39
- ON ai_chats(scope_id, owner_kind, owner_id, updated_at DESC);
40
-
41
- CREATE INDEX IF NOT EXISTS idx_ai_chats_expiry
42
- ON ai_chats(expires_at) WHERE expires_at IS NOT NULL;
43
-
44
- CREATE TABLE IF NOT EXISTS ai_chat_messages (
45
- id TEXT PRIMARY KEY,
46
- conversation_id TEXT NOT NULL REFERENCES ai_chats(id) ON DELETE CASCADE,
47
- role TEXT NOT NULL CHECK (role IN ('user','assistant')),
48
- content TEXT NOT NULL,
49
- agent_id TEXT,
50
- agent_name TEXT,
51
- events_json TEXT,
52
- attachments_json TEXT,
53
- run_id TEXT,
54
- run_status TEXT,
55
- position INTEGER NOT NULL,
56
- created_at INTEGER NOT NULL,
57
- started_at INTEGER,
58
- ended_at INTEGER,
59
- UNIQUE (conversation_id, position)
60
- );
61
-
62
- CREATE INDEX IF NOT EXISTS idx_ai_chat_messages_order
63
- ON ai_chat_messages(conversation_id, position);
64
- `;
65
- /**
66
- * Creates the chat-history tables in the given database if they are absent.
67
- *
68
- * Idempotent, matching `ensureToolCatalogTables`'s convention. For a host whose database is
69
- * managed by its own migration system, prefer copying {@link CHAT_HISTORY_DDL} into a numbered
70
- * migration instead — see this module's header for why two migrators over one file is a hazard.
71
- *
72
- * The caller is responsible for `PRAGMA foreign_keys = ON`; without it the `ON DELETE CASCADE`
73
- * above is silently inert and deleting a conversation orphans its messages. This function does
74
- * not set it, because pragmas are connection-wide and a library has no business reconfiguring a
75
- * handle it was merely lent.
76
- */
77
- export function ensureChatHistoryTables(db) {
78
- db.exec(CHAT_HISTORY_DDL);
79
- }
80
- //# sourceMappingURL=schema.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/db/chat-history/schema.ts"],"names":[],"mappings":"AAwBA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwC/B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CAAC,EAAY;IAClD,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AAC5B,CAAC"}
@@ -1,41 +0,0 @@
1
- /**
2
- * @module db/chat-history/store
3
- *
4
- * The SQLite implementation of `@jini-ai/chat/core`'s `ChatHistoryStore`, bound to one owner.
5
- *
6
- * The load-bearing property of this file is that **there is no way to obtain an unscoped store**.
7
- * {@link createChatHistoryStore} takes the scope up front and closes over it; every statement
8
- * below carries `scope_id = ? AND owner_kind = ? AND owner_id = ?` in its `WHERE` clause, and no
9
- * exported function accepts a bare conversation id without one. A route handler that forgets the
10
- * predicate cannot compile, because it never holds the pieces to write the query itself.
11
- *
12
- * This is a deliberate departure from this package's older `db/conversations` module, whose
13
- * `getConversation(db, id)` / `deleteConversation(db, id)` / `listMessages(db, conversationId)`
14
- * take no owner at all. That module's trust boundary is "you can open this local SQLite file",
15
- * which is correct for a single-user desktop app and unusable for a shared multi-tenant one.
16
- * Both exist; pick by trust model, not by convenience.
17
- *
18
- * The handle is **injected, never opened here**. `connection.ts`'s `openDatabase` owns
19
- * `<dataDir>/app.sqlite`; a host with its own database passes its own handle and this module
20
- * writes into it without ever knowing its path. That injection is what lets the same adapter
21
- * serve this package's daemon and a host CMS without either one adopting the other's file.
22
- */
23
- import type { ChatHistoryMaintenance, ChatHistoryStore, ChatOwnerScope } from '@jini-ai/chat';
24
- import type { SqliteDb } from '../core/types.js';
25
- /**
26
- * Returns a {@link ChatHistoryStore} that can only ever see `scope`'s own conversations.
27
- *
28
- * @param db an open `better-sqlite3` handle owned by the caller. This function neither opens nor
29
- * closes it, and does not set pragmas on it — see `schema.ts` on `foreign_keys`.
30
- * @param scope the isolation predicate, resolved from the host's authentication. For a `guest`,
31
- * `ownerId` must already be a hash of the session key; this module never hashes and never
32
- * verifies that it was hashed.
33
- * @param now injectable clock, for tests that need deterministic timestamps.
34
- */
35
- export declare function createChatHistoryStore(db: SqliteDb, scope: ChatOwnerScope, now?: () => number): ChatHistoryStore;
36
- /**
37
- * Retention, across every owner — which is why it is a separate factory rather than a method on
38
- * a scoped store. Handing this to a store would mean an unscoped store had to exist.
39
- */
40
- export declare function createChatHistoryMaintenance(db: SqliteDb): ChatHistoryMaintenance;
41
- //# sourceMappingURL=store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/db/chat-history/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAEV,sBAAsB,EACtB,gBAAgB,EAEhB,cAAc,EAGf,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAS,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AA0FxD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,cAAc,EACrB,GAAG,GAAE,MAAM,MAAiB,GAC3B,gBAAgB,CAgLlB;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,QAAQ,GAAG,sBAAsB,CAmBjF"}
@@ -1,254 +0,0 @@
1
- /** Columns that make up a `ChatConversation`, aliased once so every read projects identically. */
2
- const CONVERSATION_COLUMNS = `
3
- c.id AS id,
4
- c.title AS title,
5
- c.title_source AS titleSource,
6
- c.created_at AS createdAt,
7
- c.updated_at AS updatedAt,
8
- c.expires_at AS expiresAt,
9
- (SELECT COUNT(*) FROM ai_chat_messages m WHERE m.conversation_id = c.id) AS messageCount
10
- `;
11
- /** The isolation predicate. Every statement in this file uses this exact clause. */
12
- const OWNER_PREDICATE = `c.scope_id = ? AND c.owner_kind = ? AND c.owner_id = ?`;
13
- function toConversation(row) {
14
- return {
15
- id: row.id,
16
- title: row.title ?? null,
17
- titleSource: (row.titleSource ?? 'fallback'),
18
- messageCount: Number(row.messageCount ?? 0),
19
- createdAt: Number(row.createdAt),
20
- updatedAt: Number(row.updatedAt),
21
- ...(row.expiresAt === null || row.expiresAt === undefined ? {} : { expiresAt: Number(row.expiresAt) }),
22
- };
23
- }
24
- /**
25
- * KNOWN GAP — `resumable` and `lastRunEventId` are dropped, deliberately, and adding columns is
26
- * NOT the fix.
27
- *
28
- * `ChatMessage` declares both (`@jini-ai/chat/core`'s `messages.ts`): `resumable` marks a failed run
29
- * recoverable by resuming the agent's existing session rather than restarting it, and
30
- * `lastRunEventId` is the cursor to resume from. Neither the projection below, nor `appendMessage`'s
31
- * insert, nor `CHAT_HISTORY_DDL` carries them, so a message round-tripped through this store comes
32
- * back with both `undefined` and a failed run cannot be resumed at its cursor. It fails silently —
33
- * `toMessage` omits absent keys rather than throwing, so the caller gets a well-formed `ChatMessage`.
34
- *
35
- * Why the obvious fix is wrong. Both fields describe a RUN, and the daemon owns run state
36
- * (`@jini-ai/daemon`'s `run-lifecycle.ts` is what decides `resumable` from the terminal event). The
37
- * component writing through this store today is the host's BROWSER, which knows only what its chat
38
- * pane handed it. Adding the columns would ask the component that does not own the data to persist
39
- * it — the values would be as trustworthy as whatever the client happened to send. The ordering that
40
- * actually works is: associate a run with a conversation id first, then let the daemon persist
41
- * run-linked state as it streams, and only then add storage for it, at which point there is a
42
- * consumer to validate the shape against.
43
- *
44
- * Two further facts worth having before touching this. First, this is the multi-tenant store; its
45
- * sibling `db/messages/messages.ts` DOES carry `last_run_event_id`, so the two message stores in
46
- * this package genuinely disagree and neither is evidence about the other. Second,
47
- * `CHAT_HISTORY_DDL` is copied verbatim into the reference implementation's
48
- * `0023_ai_chat_history.sql` and guarded by a
49
- * structural parity test there; that migration is already applied, so any column added here also
50
- * needs a follow-up `ALTER TABLE` on the host side and a parity test taught to compare the pair
51
- * against this constant.
52
- */
53
- function toMessage(row) {
54
- return {
55
- id: row.id,
56
- role: row.role,
57
- content: row.content,
58
- ...(row.agentId ? { agentId: row.agentId } : {}),
59
- ...(row.agentName ? { agentName: row.agentName } : {}),
60
- ...(row.eventsJson ? { events: safeParse(row.eventsJson) } : {}),
61
- ...(row.attachmentsJson ? { attachments: safeParse(row.attachmentsJson) } : {}),
62
- ...(row.runId ? { runId: row.runId } : {}),
63
- ...(row.runStatus ? { runStatus: row.runStatus } : {}),
64
- ...(row.createdAt === null || row.createdAt === undefined ? {} : { createdAt: Number(row.createdAt) }),
65
- ...(row.startedAt === null || row.startedAt === undefined ? {} : { startedAt: Number(row.startedAt) }),
66
- ...(row.endedAt === null || row.endedAt === undefined ? {} : { endedAt: Number(row.endedAt) }),
67
- };
68
- }
69
- /**
70
- * A malformed JSON column yields `undefined` rather than throwing.
71
- *
72
- * A single corrupt `events_json` — a half-written row from a crash mid-stream, say — must not
73
- * make an entire conversation unreadable. The message's text lives in its own column and is
74
- * still recoverable; losing the event timeline for one turn is a far better outcome than a list
75
- * view that throws.
76
- */
77
- function safeParse(value) {
78
- try {
79
- return JSON.parse(value);
80
- }
81
- catch {
82
- return undefined;
83
- }
84
- }
85
- /**
86
- * Returns a {@link ChatHistoryStore} that can only ever see `scope`'s own conversations.
87
- *
88
- * @param db an open `better-sqlite3` handle owned by the caller. This function neither opens nor
89
- * closes it, and does not set pragmas on it — see `schema.ts` on `foreign_keys`.
90
- * @param scope the isolation predicate, resolved from the host's authentication. For a `guest`,
91
- * `ownerId` must already be a hash of the session key; this module never hashes and never
92
- * verifies that it was hashed.
93
- * @param now injectable clock, for tests that need deterministic timestamps.
94
- */
95
- export function createChatHistoryStore(db, scope, now = Date.now) {
96
- const owner = [scope.scopeId, scope.ownerKind, scope.ownerId];
97
- /** `true` only when this scope owns the conversation — the gate every message write passes. */
98
- function owns(conversationId) {
99
- const row = db
100
- .prepare(`SELECT 1 FROM ai_chats c WHERE c.id = ? AND ${OWNER_PREDICATE}`)
101
- .get(conversationId, ...owner);
102
- return row !== undefined;
103
- }
104
- return {
105
- async list() {
106
- const rows = db
107
- .prepare(`SELECT ${CONVERSATION_COLUMNS} FROM ai_chats c
108
- WHERE ${OWNER_PREDICATE}
109
- ORDER BY c.updated_at DESC`)
110
- .all(...owner);
111
- return rows.map(toConversation);
112
- },
113
- async get(id) {
114
- const row = db
115
- .prepare(`SELECT ${CONVERSATION_COLUMNS} FROM ai_chats c
116
- WHERE c.id = ? AND ${OWNER_PREDICATE}`)
117
- .get(id, ...owner);
118
- return row ? toConversation(row) : null;
119
- },
120
- async create(input) {
121
- const ts = now();
122
- db.prepare(`INSERT INTO ai_chats
123
- (id, scope_id, owner_kind, owner_id, title, title_source, created_at, updated_at, expires_at)
124
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.id, scope.scopeId, scope.ownerKind, scope.ownerId, input.title ?? null, input.titleSource ?? 'fallback', ts, ts, input.expiresAt ?? null);
125
- const created = await this.get(input.id);
126
- // Unreachable in practice: the INSERT above used this exact scope, so the scoped read
127
- // that follows cannot miss it. Asserted rather than `!`-ed so a future change to either
128
- // statement fails loudly here instead of returning a malformed conversation.
129
- if (!created)
130
- throw new Error(`ai_chats: created conversation ${input.id} was not readable in its own scope`);
131
- return created;
132
- },
133
- async rename(id, title, source = 'manual') {
134
- // A generated title never overwrites one the user typed. Expressed in the WHERE clause
135
- // rather than a read-then-write so a concurrent rename cannot slip between the two.
136
- const guard = source === 'generated' ? ` AND c.title_source <> 'manual'` : '';
137
- db.prepare(`UPDATE ai_chats AS c
138
- SET title = ?, title_source = ?, updated_at = ?
139
- WHERE c.id = ? AND ${OWNER_PREDICATE}${guard}`).run(title, source, now(), id, ...owner);
140
- return this.get(id);
141
- },
142
- async touch(id, options) {
143
- const ts = now();
144
- if (options?.expiresAt === undefined) {
145
- db.prepare(`UPDATE ai_chats AS c SET updated_at = ? WHERE c.id = ? AND ${OWNER_PREDICATE}`)
146
- .run(ts, id, ...owner);
147
- return;
148
- }
149
- db.prepare(`UPDATE ai_chats AS c SET updated_at = ?, expires_at = ? WHERE c.id = ? AND ${OWNER_PREDICATE}`).run(ts, options.expiresAt, id, ...owner);
150
- },
151
- async delete(id) {
152
- // Messages go via `ON DELETE CASCADE`, which requires `PRAGMA foreign_keys = ON` on this
153
- // connection. See `schema.ts` — a library setting connection-wide pragmas on a borrowed
154
- // handle would be worse than documenting the requirement.
155
- db.prepare(`DELETE FROM ai_chats AS c WHERE c.id = ? AND ${OWNER_PREDICATE}`).run(id, ...owner);
156
- },
157
- async messages(conversationId) {
158
- const rows = db
159
- .prepare(`SELECT m.id, m.role, m.content, m.agent_id AS agentId, m.agent_name AS agentName,
160
- m.events_json AS eventsJson, m.attachments_json AS attachmentsJson,
161
- m.run_id AS runId, m.run_status AS runStatus, m.created_at AS createdAt,
162
- m.started_at AS startedAt, m.ended_at AS endedAt
163
- FROM ai_chat_messages m
164
- JOIN ai_chats c ON c.id = m.conversation_id
165
- WHERE m.conversation_id = ? AND ${OWNER_PREDICATE}
166
- ORDER BY m.position`)
167
- .all(conversationId, ...owner);
168
- return rows.map(toMessage);
169
- },
170
- async appendMessage(conversationId, message) {
171
- if (!owns(conversationId))
172
- return null;
173
- // One `BEGIN IMMEDIATE` around read-then-write: `MAX(position) + 1` computed outside a
174
- // write transaction is the classic way two concurrent turns claim the same slot. The
175
- // `UNIQUE (conversation_id, position)` constraint is the backstop if this is ever bypassed.
176
- //
177
- // `owns()` above is NOT sufficient on its own, which is why the upsert below carries its own
178
- // conversation predicate. `owns()` authorizes the *conversation* being written to; the upsert's
179
- // conflict target is `id`, the GLOBAL primary key on messages. Without the `WHERE`, appending a
180
- // message id that already exists in someone else's conversation updated THAT row — the caller
181
- // legitimately owned the conversation it named, so nothing here refused it, and it then got
182
- // `null` back (a 404 at the route) after the write had landed. See the
183
- // "colliding id to its OWN chat" case in `__tests__/isolation.test.ts`.
184
- const write = db.transaction((m) => {
185
- const existing = db
186
- .prepare(`SELECT position FROM ai_chat_messages WHERE id = ? AND conversation_id = ?`)
187
- .get(m.id, conversationId);
188
- const position = existing
189
- ? Number(existing.position)
190
- : Number(db
191
- .prepare(`SELECT COALESCE(MAX(position), -1) + 1 AS next FROM ai_chat_messages WHERE conversation_id = ?`)
192
- .get(conversationId).next);
193
- db.prepare(`INSERT INTO ai_chat_messages
194
- (id, conversation_id, role, content, agent_id, agent_name, events_json,
195
- attachments_json, run_id, run_status, position, created_at, started_at, ended_at)
196
- VALUES (@id, @conversationId, @role, @content, @agentId, @agentName, @eventsJson,
197
- @attachmentsJson, @runId, @runStatus, @position, @createdAt, @startedAt, @endedAt)
198
- ON CONFLICT(id) DO UPDATE SET
199
- content = excluded.content,
200
- events_json = excluded.events_json,
201
- attachments_json = excluded.attachments_json,
202
- run_id = excluded.run_id,
203
- run_status = excluded.run_status,
204
- started_at = excluded.started_at,
205
- ended_at = excluded.ended_at
206
- WHERE ai_chat_messages.conversation_id = excluded.conversation_id`).run({
207
- id: m.id,
208
- conversationId,
209
- role: m.role,
210
- content: m.content,
211
- agentId: m.agentId ?? null,
212
- agentName: m.agentName ?? null,
213
- eventsJson: m.events ? JSON.stringify(m.events) : null,
214
- attachmentsJson: m.attachments ? JSON.stringify(m.attachments) : null,
215
- runId: m.runId ?? null,
216
- runStatus: m.runStatus ?? null,
217
- position,
218
- createdAt: m.createdAt ?? now(),
219
- startedAt: m.startedAt ?? null,
220
- endedAt: m.endedAt ?? null,
221
- });
222
- // A new message is activity on the conversation; the list orders by `updated_at`, so
223
- // skipping this would leave an actively-used chat sinking down the list.
224
- db.prepare(`UPDATE ai_chats SET updated_at = ? WHERE id = ?`).run(now(), conversationId);
225
- });
226
- write(message);
227
- const saved = await this.messages(conversationId);
228
- return saved.find((m) => m.id === message.id) ?? null;
229
- },
230
- };
231
- }
232
- /**
233
- * Retention, across every owner — which is why it is a separate factory rather than a method on
234
- * a scoped store. Handing this to a store would mean an unscoped store had to exist.
235
- */
236
- export function createChatHistoryMaintenance(db) {
237
- return {
238
- async sweepExpired(now, limit = 500) {
239
- // Bounded by `limit` so a large backlog drains in chunks. An unbounded `DELETE` holds the
240
- // write lock long enough that concurrent inserts exhaust `busy_timeout` and fail outright.
241
- // `expires_at IS NOT NULL` keeps never-expiring history structurally out of reach.
242
- const result = db
243
- .prepare(`DELETE FROM ai_chats
244
- WHERE id IN (
245
- SELECT id FROM ai_chats
246
- WHERE expires_at IS NOT NULL AND expires_at <= ?
247
- LIMIT ?
248
- )`)
249
- .run(now, limit);
250
- return result.changes;
251
- },
252
- };
253
- }
254
- //# sourceMappingURL=store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/db/chat-history/store.ts"],"names":[],"mappings":"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"}