soulbridge 0.0.108 → 0.0.110

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.
@@ -1,6 +1,6 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
2
  import type { SoulStore } from './store.ts';
3
- export declare const SCHEMA_VERSION = 13;
3
+ export declare const SCHEMA_VERSION = 14;
4
4
  export declare const AFFECT_RETENTION_MS: number;
5
5
  export declare class DbNewerThanBinaryError extends Error {
6
6
  readonly code = "DB_NEWER_THAN_BINARY";
@@ -13,6 +13,7 @@ export declare class DbNewerThanBinaryError extends Error {
13
13
  * old table stays as a read-only legacy copy.
14
14
  */
15
15
  /** v6 → v7: turn threads (a topic's turns need not be adjacent) plus turns.thread_id. */
16
+ export declare function upgradeToV14(db: DatabaseSync): void;
16
17
  export declare function upgradeToV13(db: DatabaseSync): void;
17
18
  export declare function upgradeToV12(db: DatabaseSync): void;
18
19
  export declare function upgradeToV11(db: DatabaseSync): void;
@@ -12,7 +12,7 @@ import { DEFAULT_EMOTION } from './types.js';
12
12
  import { FACT_STATUS, MEMORY_STATUS } from './config/status.js';
13
13
  import { FACT_ROLES } from './config/domains.js';
14
14
  import { bm25, buildCorpusStats, tokenize } from './scoring.js';
15
- export const SCHEMA_VERSION = 13;
15
+ export const SCHEMA_VERSION = 14;
16
16
  /** How many recent pairs are considered before scoring. Bounded so recall stays O(candidates). */
17
17
  const RECALL_CANDIDATES = 500;
18
18
  export const AFFECT_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
@@ -302,6 +302,13 @@ function upgradeToV3(db) {
302
302
  * old table stays as a read-only legacy copy.
303
303
  */
304
304
  /** v6 → v7: turn threads (a topic's turns need not be adjacent) plus turns.thread_id. */
305
+ export function upgradeToV14(db) {
306
+ // Correct the semantics v13 used: a dated statement stops describing the present when the
307
+ // event is over. "小林在 10-24 被裁员" is valid on 10-24, not forever — otherwise every
308
+ // historical event would look like a current state.
309
+ db.exec(`UPDATE facts SET valid_until = COALESCE(at_end, at)
310
+ WHERE valid_until IS NULL AND at IS NOT NULL AND status <> 'retracted'`);
311
+ }
305
312
  export function upgradeToV13(db) {
306
313
  // Two axes of time, like the legacy memory table had:
307
314
  // created_at when we recorded it (never changes)
@@ -491,8 +498,8 @@ export function openSqlite(file) {
491
498
  upgradeToV7(db);
492
499
  if (version < 8)
493
500
  upgradeToV8(db);
494
- if (version < 13)
495
- upgradeToV13(db);
501
+ if (version < 14)
502
+ upgradeToV14(db);
496
503
  // v6 adds tables only; SCHEMA creates them on every open, nothing to copy.
497
504
  setVersion(db, SCHEMA_VERSION);
498
505
  db.exec('COMMIT');
@@ -865,7 +872,12 @@ export function openSqlite(file) {
865
872
  valid_from, valid_until, place_id, status, confidence, source, source_turn_id, superseded_by,
866
873
  created_at)
867
874
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
868
- .run(id, spec.soulId, spec.userId, spec.statement, spec.typeName ?? null, role, spec.holder ?? 'world', spec.at ?? null, spec.atText ?? null, spec.atEnd ?? null, spec.atPrecision ?? 'unknown', spec.validFrom ?? spec.at ?? createdAt, spec.validUntil ?? null, spec.placeId ?? null, spec.status ?? 'active', spec.confidence ?? 1, spec.source ?? 'experienced', spec.sourceTurnId ?? null, spec.supersededBy ?? null, createdAt);
875
+ .run(id, spec.soulId, spec.userId, spec.statement, spec.typeName ?? null, role, spec.holder ?? 'world', spec.at ?? null, spec.atText ?? null, spec.atEnd ?? null, spec.atPrecision ?? 'unknown', spec.validFrom ?? spec.at ?? createdAt,
876
+ // A dated statement describes the world at that moment. For an event, validity
877
+ // ends when it is over (at, or at_end for a range); a standing state has no date
878
+ // and stays open until something replaces it. "Current" is therefore one query:
879
+ // valid_until IS NULL OR valid_until > now.
880
+ spec.validUntil ?? spec.atEnd ?? spec.at ?? null, spec.placeId ?? null, spec.status ?? 'active', spec.confidence ?? 1, spec.source ?? 'experienced', spec.sourceTurnId ?? null, spec.supersededBy ?? null, createdAt);
869
881
  if (Number(inserted.changes) > 0) {
870
882
  const insertSlot = db.prepare(`INSERT INTO fact_slots (fact_id, slot, value_type, entity_id, value_num, value_text, ref_fact_id)
871
883
  VALUES (?, ?, ?, ?, ?, ?, ?)`);
@@ -918,14 +930,17 @@ export function openSqlite(file) {
918
930
  return rows.map((row) => rowToFact(row, slots));
919
931
  },
920
932
  supersedeFact(oldId, newId) {
921
- // The old claim stopped holding when the new one was recorded. `valid_until` is the
922
- // time axis; `status` is the lifecycle — both are written, and they answer different
923
- // questions ("was it ever true?" vs "is it still the current state?").
924
- const recordedAt = Number(db.prepare('SELECT created_at FROM facts WHERE id = ?').get(newId)?.created_at ??
925
- Date.now());
933
+ // A new claim ends the old one when the new claim became true — not when we happened to
934
+ // record it. "小林在 A 公司" ends at the layoff (10-24), not at the moment the layoff was
935
+ // mentioned. Falls back to the record time only when the successor has no validity start
936
+ // (an undated claim still replaces the old one now).
937
+ const successor = db
938
+ .prepare('SELECT created_at, valid_from FROM facts WHERE id = ?')
939
+ .get(newId);
940
+ const endedAt = Number(successor?.valid_from ?? successor?.created_at ?? Date.now());
926
941
  withRetry(() => db
927
942
  .prepare(`UPDATE facts SET status = '${FACT_STATUS.retracted}', superseded_by = ?, valid_until = ? WHERE id = ?`)
928
- .run(newId, recordedAt, oldId));
943
+ .run(newId, endedAt, oldId));
929
944
  },
930
945
  /* ---------------- gaps ---------------- */
931
946
  putGap(spec) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soulbridge",
3
- "version": "0.0.108",
3
+ "version": "0.0.110",
4
4
  "description": "A personality layer for any agent: continuity, forgetting, and growth across tools.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",