@clude/sdk 3.0.4 → 3.3.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.
package/dist/sdk/index.js CHANGED
@@ -67,7 +67,20 @@ var init_config = __esm({
67
67
  accessToken: requiredUnlessSiteOnly("X_ACCESS_TOKEN"),
68
68
  accessSecret: requiredUnlessSiteOnly("X_ACCESS_SECRET"),
69
69
  botUserId: requiredUnlessSiteOnly("X_BOT_USER_ID"),
70
- creatorUserId: optional("CREATOR_USER_ID", "")
70
+ creatorUserId: optional("CREATOR_USER_ID", ""),
71
+ /**
72
+ * Bearer token for the public "$ANSEM LIVE" feed (GET /api/ansem/feed).
73
+ * Reads-only X app bearer — used server-side for GET /2/tweets/search/recent.
74
+ * Empty → the feed endpoint returns { enabled:false } and the frontend panel hides.
75
+ * NEVER sent to the browser.
76
+ */
77
+ searchBearer: optional("X_SEARCH_BEARER", ""),
78
+ /** Min poll gap for the on-demand $ANSEM feed (ms). Idle → no polling → $0. */
79
+ ansemFeedIntervalMs: parseInt(optional("ANSEM_FEED_INTERVAL_MS", "90000"), 10),
80
+ /** Posts older than ~15min must clear this like floor (fresh posts are exempt). */
81
+ ansemFeedMinLikes: parseInt(optional("ANSEM_FEED_MIN_LIKES", "3"), 10),
82
+ /** Hard daily read-cap: once exceeded, serve the stale buffer (cost stop-loss). */
83
+ ansemFeedDailyReadCap: parseInt(optional("ANSEM_FEED_DAILY_READ_CAP", "40000"), 10)
71
84
  },
72
85
  supabase: {
73
86
  url: requiredUnlessSiteOnly("SUPABASE_URL"),
@@ -85,7 +98,9 @@ var init_config = __esm({
85
98
  optional("SOLANA_NETWORK", "mainnet-beta") === "devnet" ? "https://api.devnet.solana.com" : "https://api.mainnet-beta.solana.com"
86
99
  ),
87
100
  botWalletPrivateKey: optional("BOT_WALLET_PRIVATE_KEY", ""),
88
- cludeTokenMint: optional("CLUUDE_TOKEN_MINT", "")
101
+ cludeTokenMint: optional("CLUUDE_TOKEN_MINT", ""),
102
+ /** Deployed memory-registry Anchor program ID. Empty → on-chain registration disabled. */
103
+ memoryRegistryProgramId: optional("CLUDE_PROGRAM_ID", "")
89
104
  },
90
105
  server: {
91
106
  port: parseInt(optional("PORT", "3000"), 10),
@@ -123,6 +138,69 @@ var init_config = __esm({
123
138
  freePromoCreditUsdc: parseFloat(optional("FREE_PROMO_CREDIT_USDC", "1")),
124
139
  freePromoExpiry: optional("FREE_PROMO_EXPIRY", "")
125
140
  },
141
+ memory: {
142
+ /**
143
+ * Memory 3.0 C2: route storeMemory enrichment (embed/link/extract) through the durable
144
+ * memory_write_jobs outbox instead of fire-and-forget. Default OFF — fire-and-forget stays
145
+ * the default until the worker is proven. Requires migrations 044 + 045; degrades gracefully
146
+ * (falls back to fire-and-forget) if unapplied.
147
+ */
148
+ outboxEnabled: optional("MEMORY_OUTBOX", "false") === "true",
149
+ /**
150
+ * Memory 3.0 C1: write-time reconciliation, SHADOW slice (LLM-free). When on, each write records
151
+ * a PROPOSED reconcile op (add / needs_router / skip) into memory_reconciliation_log WITHOUT
152
+ * applying it — the labeled sample the enforce path must earn its turn-on from. Default OFF;
153
+ * runs fully detached after embedMemory (zero write latency); degrades gracefully if migration
154
+ * 046 is unapplied. Disabled under BENCH_MODE. The router is a later slice; only the cosine gate
155
+ * runs here.
156
+ */
157
+ reconcileEnabled: optional("MEMORY_RECONCILE", "false") === "true",
158
+ /** Screen floor passed to match_memories_temporal — LOW so max_cosine is captured for below-LO
159
+ * writes (LO is tuned from the shadow data, not fixed up-front). */
160
+ reconcileFloor: Number(optional("MEMORY_RECONCILE_FLOOR", "0.5")),
161
+ /** "similar enough to reconcile" boundary → needs_router (vs add). A starting probe, not gospel. */
162
+ reconcileLo: Number(optional("MEMORY_RECONCILE_LO", "0.85")),
163
+ /** hi/mid band LABEL boundary for analysis (not a decision boundary). */
164
+ reconcileHi: Number(optional("MEMORY_RECONCILE_HI", "0.95")),
165
+ /**
166
+ * C1 slice 1.5: the LLM router. Its own sub-flag (default OFF, independent of reconcileEnabled)
167
+ * so gate-only instrumentation runs first and LO is calibrated from that data before any LLM
168
+ * spend. When on, a >= LO write is classified add/update/noop by reconcileModel (still SHADOW —
169
+ * the op is logged, never applied). Requires OpenRouter configured.
170
+ */
171
+ reconcileRouter: optional("MEMORY_RECONCILE_ROUTER", "false") === "true",
172
+ /** Router model — an explicit id (NEVER a cognitiveFunction, which would silently override it
173
+ * with the fast llama slot). Haiku-class default: capable enough for dup-vs-update, cheap. */
174
+ reconcileModel: optional("MEMORY_RECONCILE_MODEL", "anthropic/claude-haiku-4.5"),
175
+ /** Per-owner soft daily cap on router calls (approximate, per-process) — bounds LLM spend. */
176
+ reconcileBudget: Number(optional("MEMORY_RECONCILE_BUDGET", "200"))
177
+ },
178
+ oauth: {
179
+ /** HMAC secret for signing OAuth access-token JWTs. Empty disables the OAuth AS — bearer API-key auth still works. */
180
+ signingSecret: optional("OAUTH_SIGNING_SECRET", ""),
181
+ /** Issuer/audience identifier baked into tokens. Falls back to the request origin when empty. */
182
+ issuer: optional("OAUTH_ISSUER", ""),
183
+ /** Access-token lifetime in seconds (default 1h). */
184
+ accessTtlSec: parseInt(optional("OAUTH_ACCESS_TTL_SEC", "3600"), 10),
185
+ /** Refresh-token lifetime in seconds (default 30d). */
186
+ refreshTtlSec: parseInt(optional("OAUTH_REFRESH_TTL_SEC", "2592000"), 10),
187
+ /** Authorization-code lifetime in seconds (default 60s). */
188
+ codeTtlSec: parseInt(optional("OAUTH_CODE_TTL_SEC", "60"), 10)
189
+ },
190
+ stripe: {
191
+ /**
192
+ * Stripe secret API key (sk_live_… / sk_test_…). Empty disables the
193
+ * marketplace Stripe rail — the orchestrator/webhook fail closed without it.
194
+ * SDK/MCP consumers never need this, so it stays optional() (not required()).
195
+ */
196
+ secretKey: optional("STRIPE_SECRET_KEY", ""),
197
+ /**
198
+ * Stripe webhook signing secret (whsec_…) used by stripe.webhooks.constructEvent
199
+ * to verify the raw body BEFORE any DB write (Risk R6). Empty ⇒ every webhook is
200
+ * rejected, so a misconfigured deploy can never process an unverified event.
201
+ */
202
+ webhookSecret: optional("STRIPE_WEBHOOK_SECRET", "")
203
+ },
126
204
  campaign: {
127
205
  startDate: optional("CAMPAIGN_START", "")
128
206
  },
@@ -136,6 +214,51 @@ var init_config = __esm({
136
214
  queryApiKey: optional("EMBEDDING_QUERY_API_KEY", ""),
137
215
  queryModel: optional("EMBEDDING_QUERY_MODEL", "")
138
216
  },
217
+ migration: {
218
+ /**
219
+ * Database backend that getDb() targets during the GCP parallel-run.
220
+ * 'supabase' (default, current live infra) | 'cloudsql' (PostgREST /
221
+ * self-hosted Supabase in front of Cloud SQL). Flip per-layer for the
222
+ * shadow-stack cutover; Supabase stays the source of truth until sunset.
223
+ */
224
+ dbTarget: optional("DB_TARGET", "supabase"),
225
+ /** PostgREST endpoint for the Cloud SQL backend (used only when DB_TARGET=cloudsql). */
226
+ cloudsqlUrl: optional("CLOUDSQL_PGREST_URL", ""),
227
+ /** Service key for the Cloud SQL PostgREST backend (used only when DB_TARGET=cloudsql). */
228
+ cloudsqlServiceKey: optional("CLOUDSQL_SERVICE_KEY", ""),
229
+ /**
230
+ * Active embedding vector space for ingest + recall. 'voyage' (default,
231
+ * current corpus) | 'vertex' (shadow column, only after the LongMemEval gate
232
+ * passes). Separate from EMBEDDING_PROVIDER so the swap is A/B, not one-way.
233
+ */
234
+ embeddingActive: optional("EMBEDDING_ACTIVE", "voyage"),
235
+ /**
236
+ * Whether THIS process runs the in-server singleton timers (recall canary,
237
+ * marketplace delivery poller, title-mint reconciliation). Default true =
238
+ * current Railway behavior. Set false on the Cloud Run server so exactly one
239
+ * owner (the worker) runs them and autoscaling never duplicates them.
240
+ */
241
+ runInProcessTimers: optional("RUN_INPROCESS_TIMERS", "true") === "true"
242
+ },
243
+ vertex: {
244
+ /**
245
+ * Vertex AI embedding backend (the GCP replacement for Voyage), used only when
246
+ * ingest/backfill/recall target the 'vertex' space (EMBEDDING_ACTIVE=vertex or an
247
+ * explicit generateEmbeddingForSpace('vertex') call). Separate from config.embedding
248
+ * so the Vertex space can be backfilled + A/B-gated while Voyage stays live.
249
+ */
250
+ project: optional("VERTEX_PROJECT", optional("GCP_PROJECT", "")),
251
+ location: optional("VERTEX_LOCATION", "us-central1"),
252
+ model: optional("VERTEX_EMBEDDING_MODEL", "gemini-embedding-001"),
253
+ /** Output dims — MRL-truncated to 1024 so vector(1024) columns + HNSW + match_* RPCs are unchanged. */
254
+ dimensions: parseInt(optional("VERTEX_EMBEDDING_DIMENSIONS", "1024"), 10),
255
+ /**
256
+ * Optional static OAuth token override (from `gcloud auth print-access-token`) for
257
+ * local dev / smoke tests. Empty in prod: Cloud Run mints a token from the attached
258
+ * service account via the metadata server (SDK-free), no static key stored.
259
+ */
260
+ accessToken: optional("VERTEX_ACCESS_TOKEN", "")
261
+ },
139
262
  openrouter: {
140
263
  apiKey: optional("OPENROUTER_API_KEY", ""),
141
264
  model: optional("OPENROUTER_MODEL", "meta-llama/llama-3.3-70b-instruct")
@@ -149,6 +272,35 @@ var init_config = __esm({
149
272
  tavily: {
150
273
  apiKey: optional("TAVILY_API_KEY", "")
151
274
  },
275
+ higgsfield: {
276
+ /** Higgsfield API key ID — first half of the V2 `Authorization: Key <id>:<secret>` pair (server-only). Empty → /api/ansem/speak returns 501. */
277
+ apiKey: optional("HIGGSFIELD_API_KEY", ""),
278
+ /** Higgsfield API key SECRET — second half of the V2 `Key <id>:<secret>` pair. */
279
+ apiSecret: optional("HIGGSFIELD_API_SECRET", ""),
280
+ /** REST base URL (Higgsfield V2 API). */
281
+ apiBase: optional("HIGGSFIELD_API_BASE", "https://platform.higgsfield.ai"),
282
+ /**
283
+ * V2 model path for the seed_audio TTS model: create is POST {apiBase}/{ttsEndpoint}.
284
+ * Verified live against the Higgsfield V2 API — bytedance/seed-audio-1.0.
285
+ */
286
+ ttsEndpoint: optional("HIGGSFIELD_TTS_ENDPOINT", "bytedance/seed-audio-1.0"),
287
+ /** Ansem voice — Higgsfield "Sterling" preset (founder's choice). */
288
+ voiceId: optional("ANSEM_VOICE_ID", "dc382508-c8bd-443c-8cb2-46e57b8d2e6f"),
289
+ voiceType: optional("ANSEM_VOICE_TYPE", "preset"),
290
+ /** Deep-voice tuning: seed_audio pitch_rate / speech_rate (integers, default 0). */
291
+ voicePitch: parseInt(optional("ANSEM_VOICE_PITCH", "-9"), 10),
292
+ voiceSpeechRate: parseInt(optional("ANSEM_VOICE_SPEECH_RATE", "-12"), 10),
293
+ /** Output audio format (seed_audio supports wav|mp3|pcm|ogg_opus). */
294
+ audioFormat: optional("ANSEM_VOICE_FORMAT", "mp3")
295
+ },
296
+ elevenlabs: {
297
+ /** ElevenLabs API key (server-only). Set → PRIMARY TTS (~1-3s, no lag); empty → falls back to Higgsfield. */
298
+ apiKey: optional("ELEVENLABS_API_KEY", ""),
299
+ /** Voice id — default "Brian" (deep, resonant, american). Swap via ELEVENLABS_VOICE_ID. */
300
+ voiceId: optional("ELEVENLABS_VOICE_ID", "nPczCjzI2devNBz1zQrb"),
301
+ /** Model — turbo for lowest latency. */
302
+ model: optional("ELEVENLABS_MODEL", "eleven_turbo_v2_5")
303
+ },
152
304
  privy: {
153
305
  appId: optional("PRIVY_APP_ID", ""),
154
306
  appSecret: optional("PRIVY_APP_SECRET", ""),
@@ -230,28 +382,70 @@ var init_logger = __esm({
230
382
  }
231
383
  });
232
384
 
385
+ // packages/shared/src/core/migration-profile.ts
386
+ function defaultSupabaseConnection() {
387
+ return { url: config.supabase.url, serviceKey: config.supabase.serviceKey };
388
+ }
389
+ function resolveDbConnection(profile = config.migration, supabase2 = defaultSupabaseConnection()) {
390
+ if (!DB_TARGETS.includes(profile.dbTarget)) {
391
+ throw new Error(
392
+ `Invalid DB_TARGET '${profile.dbTarget}' (expected 'supabase' | 'cloudsql')`
393
+ );
394
+ }
395
+ if (profile.dbTarget === "cloudsql") {
396
+ if (!profile.cloudsqlUrl || !profile.cloudsqlServiceKey) {
397
+ throw new Error(
398
+ "DB_TARGET=cloudsql requires CLOUDSQL_PGREST_URL and CLOUDSQL_SERVICE_KEY"
399
+ );
400
+ }
401
+ return { url: profile.cloudsqlUrl, serviceKey: profile.cloudsqlServiceKey };
402
+ }
403
+ return { url: supabase2.url, serviceKey: supabase2.serviceKey };
404
+ }
405
+ function activeEmbeddingSpace(profile = config.migration) {
406
+ if (!EMBEDDING_SPACES.includes(profile.embeddingActive)) {
407
+ throw new Error(
408
+ `Invalid EMBEDDING_ACTIVE '${profile.embeddingActive}' (expected 'voyage' | 'vertex')`
409
+ );
410
+ }
411
+ return profile.embeddingActive;
412
+ }
413
+ function vectorRpcName(baseRpc, profile = config.migration) {
414
+ return activeEmbeddingSpace(profile) === "vertex" ? `${baseRpc}_vertex` : baseRpc;
415
+ }
416
+ var DB_TARGETS, EMBEDDING_SPACES;
417
+ var init_migration_profile = __esm({
418
+ "packages/shared/src/core/migration-profile.ts"() {
419
+ "use strict";
420
+ init_config();
421
+ DB_TARGETS = ["supabase", "cloudsql"];
422
+ EMBEDDING_SPACES = ["voyage", "vertex"];
423
+ }
424
+ });
425
+
233
426
  // packages/shared/src/core/database.ts
234
427
  var database_exports = {};
235
428
  __export(database_exports, {
236
429
  _setDb: () => _setDb,
237
430
  claimForProcessing: () => claimForProcessing,
238
431
  getDb: () => getDb,
432
+ getSchemaDriftReport: () => getSchemaDriftReport,
239
433
  initDatabase: () => initDatabase,
240
434
  isAlreadyProcessed: () => isAlreadyProcessed,
241
435
  markProcessed: () => markProcessed
242
436
  });
243
437
  function getDb() {
244
438
  if (!supabase) {
245
- supabase = (0, import_supabase_js.createClient)(config.supabase.url, config.supabase.serviceKey);
246
- log.info("Supabase client initialized");
439
+ const conn = resolveDbConnection();
440
+ supabase = (0, import_supabase_js.createClient)(conn.url, conn.serviceKey);
441
+ log.info({ dbTarget: config.migration.dbTarget }, "Database client initialized");
247
442
  }
248
443
  return supabase;
249
444
  }
250
445
  function _setDb(client2) {
251
446
  supabase = client2;
252
447
  }
253
- async function initDatabase() {
254
- const db = getDb();
448
+ async function runBootDdl(db) {
255
449
  try {
256
450
  const { error } = await db.rpc("exec_sql", {
257
451
  query: `
@@ -323,6 +517,7 @@ async function initDatabase() {
323
517
  input_memory_ids BIGINT[] DEFAULT '{}',
324
518
  output TEXT NOT NULL,
325
519
  new_memories_created BIGINT[] DEFAULT '{}',
520
+ owner_wallet TEXT,
326
521
  created_at TIMESTAMPTZ DEFAULT NOW()
327
522
  );
328
523
 
@@ -348,13 +543,18 @@ async function initDatabase() {
348
543
  is_active BOOLEAN DEFAULT TRUE,
349
544
  metadata JSONB DEFAULT '{}',
350
545
  owner_wallet TEXT,
351
- privy_did TEXT
546
+ privy_did TEXT,
547
+ email TEXT
352
548
  );
353
549
 
550
+ -- Backfill: older deployments may have agent_keys without the email column.
551
+ ALTER TABLE agent_keys ADD COLUMN IF NOT EXISTS email TEXT;
552
+
354
553
  CREATE INDEX IF NOT EXISTS idx_agent_keys_api_key ON agent_keys(api_key);
355
554
  CREATE INDEX IF NOT EXISTS idx_agent_keys_owner ON agent_keys(owner_wallet);
356
555
  CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_owner_unique ON agent_keys(owner_wallet) WHERE owner_wallet IS NOT NULL;
357
556
  CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_privy_did ON agent_keys(privy_did) WHERE privy_did IS NOT NULL;
557
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_keys_email ON agent_keys(email) WHERE email IS NOT NULL AND is_active = true;
358
558
 
359
559
  -- Cortex recall performance: owner_wallet scoped queries
360
560
  CREATE INDEX IF NOT EXISTS idx_cortex_owner_recall ON memories(owner_wallet, decay_factor DESC, created_at DESC);
@@ -402,7 +602,7 @@ async function initDatabase() {
402
602
  target_id BIGINT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
403
603
  link_type TEXT NOT NULL CHECK (link_type IN (
404
604
  'supports', 'contradicts', 'elaborates', 'causes', 'follows', 'relates', 'resolves',
405
- 'happens_before', 'happens_after', 'concurrent_with'
605
+ 'supersedes', 'happens_before', 'happens_after', 'concurrent_with'
406
606
  )),
407
607
  strength REAL DEFAULT 0.5,
408
608
  created_at TIMESTAMPTZ DEFAULT NOW(),
@@ -437,7 +637,11 @@ async function initDatabase() {
437
637
  WHERE ml.source_id = ANY(seed_ids)
438
638
  AND ml.target_id != ALL(seed_ids)
439
639
  AND ml.strength >= min_strength
440
- AND (filter_owner IS NULL OR m.owner_wallet = filter_owner)
640
+ AND (
641
+ filter_owner IS NULL
642
+ OR (filter_owner = '__BOT_OWN__' AND m.owner_wallet IS NULL)
643
+ OR m.owner_wallet = filter_owner
644
+ )
441
645
  UNION
442
646
  SELECT DISTINCT ON (ml.source_id, ml.link_type)
443
647
  ml.source_id AS memory_id,
@@ -449,7 +653,11 @@ async function initDatabase() {
449
653
  WHERE ml.target_id = ANY(seed_ids)
450
654
  AND ml.source_id != ALL(seed_ids)
451
655
  AND ml.strength >= min_strength
452
- AND (filter_owner IS NULL OR m.owner_wallet = filter_owner)
656
+ AND (
657
+ filter_owner IS NULL
658
+ OR (filter_owner = '__BOT_OWN__' AND m.owner_wallet IS NULL)
659
+ OR m.owner_wallet = filter_owner
660
+ )
453
661
  ORDER BY strength DESC
454
662
  LIMIT max_results;
455
663
  $$;
@@ -477,6 +685,10 @@ async function initDatabase() {
477
685
  ALTER TABLE dream_logs ADD CONSTRAINT dream_logs_session_type_check
478
686
  CHECK (session_type IN ('consolidation', 'reflection', 'emergence', 'compaction', 'decay', 'contradiction_resolution'));
479
687
 
688
+ -- Migration 021: per-owner attribution for dream_logs (fixes totalDreamSessions leak)
689
+ ALTER TABLE dream_logs ADD COLUMN IF NOT EXISTS owner_wallet TEXT;
690
+ CREATE INDEX IF NOT EXISTS idx_dream_logs_owner ON dream_logs(owner_wallet);
691
+
480
692
  -- Migration: add 'resolves' + temporal link types
481
693
  ALTER TABLE memory_links DROP CONSTRAINT IF EXISTS memory_links_link_type_check;
482
694
  ALTER TABLE memory_links ADD CONSTRAINT memory_links_link_type_check
@@ -658,6 +870,170 @@ async function initDatabase() {
658
870
  CREATE INDEX IF NOT EXISTS idx_memories_event_date ON memories(event_date)
659
871
  WHERE event_date IS NOT NULL;
660
872
 
873
+ -- Lexical index for keyword/BM25 search (encryption \xA79). content_tokens is
874
+ -- app-maintained; ts_summary (summary-only) is added on fresh deploys that lack it.
875
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS content_tokens tsvector;
876
+ CREATE INDEX IF NOT EXISTS idx_memories_content_tokens ON memories USING GIN(content_tokens);
877
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS provider_delegated BOOLEAN DEFAULT TRUE;
878
+ CREATE INDEX IF NOT EXISTS idx_memories_delegated ON memories(provider_delegated) WHERE encrypted = TRUE;
879
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS summary_ciphertext TEXT;
880
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS embedding_ciphertext TEXT;
881
+
882
+ -- Memory 3.0 Phase 1 (migration 044): bi-temporal validity + provenance (additive/nullable, inert until MEMORY_RECONCILE)
883
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS valid_from TIMESTAMPTZ;
884
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS invalid_at TIMESTAMPTZ;
885
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS superseded_by TEXT;
886
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS fact_key TEXT;
887
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS extractor_version TEXT;
888
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS extraction_confidence REAL;
889
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS source_turn_ref JSONB;
890
+ ALTER TABLE memories ADD COLUMN IF NOT EXISTS hash_id_v2 TEXT;
891
+ CREATE INDEX IF NOT EXISTS idx_memories_valid ON memories(id) WHERE invalid_at IS NULL;
892
+ CREATE INDEX IF NOT EXISTS idx_memories_fact_key ON memories(fact_key) WHERE fact_key IS NOT NULL;
893
+ CREATE INDEX IF NOT EXISTS idx_memories_hash_id_v2 ON memories(hash_id_v2) WHERE hash_id_v2 IS NOT NULL;
894
+
895
+ -- Memory 3.0 Phase 1 (migration 044): the C2 durable write outbox.
896
+ CREATE TABLE IF NOT EXISTS memory_write_jobs (
897
+ id BIGSERIAL PRIMARY KEY,
898
+ memory_id BIGINT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
899
+ job_type TEXT NOT NULL CHECK (job_type IN ('enrich', 'embed', 'link', 'extract', 'reconcile', 'backfill_v2')),
900
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'done', 'failed')),
901
+ attempts INT NOT NULL DEFAULT 0,
902
+ next_retry_at TIMESTAMPTZ DEFAULT NOW(),
903
+ last_error TEXT,
904
+ created_at TIMESTAMPTZ DEFAULT NOW(),
905
+ updated_at TIMESTAMPTZ DEFAULT NOW()
906
+ );
907
+ CREATE INDEX IF NOT EXISTS idx_write_jobs_claim ON memory_write_jobs(status, next_retry_at) WHERE status IN ('pending', 'failed');
908
+ CREATE INDEX IF NOT EXISTS idx_write_jobs_memory ON memory_write_jobs(memory_id);
909
+ CREATE INDEX IF NOT EXISTS idx_write_jobs_running ON memory_write_jobs(updated_at) WHERE status = 'running';
910
+
911
+ -- Memory 3.0 C2 outbox \u2014 MIRROR of migration 045 (byte-equivalent). The table above is in
912
+ -- the boot blob, so the claim RPC + idempotency objects MUST be too: otherwise a
913
+ -- boot-provisioned box gets the table but not the RPC and the worker silently never drains
914
+ -- (the migration-028 class). Keep in sync with 045.
915
+ DO $do$
916
+ BEGIN
917
+ ALTER TABLE memory_write_jobs DROP CONSTRAINT IF EXISTS memory_write_jobs_job_type_check;
918
+ ALTER TABLE memory_write_jobs ADD CONSTRAINT memory_write_jobs_job_type_check
919
+ CHECK (job_type IN ('enrich', 'embed', 'link', 'extract', 'reconcile', 'backfill_v2'));
920
+ EXCEPTION WHEN undefined_table THEN NULL; END $do$;
921
+
922
+ CREATE OR REPLACE FUNCTION claim_memory_write_jobs(
923
+ p_limit INT DEFAULT 20, p_stale_running INTERVAL DEFAULT INTERVAL '15 minutes', p_owner TEXT DEFAULT NULL
924
+ )
925
+ RETURNS TABLE (id BIGINT, memory_id BIGINT, job_type TEXT, attempts INT, owner_wallet TEXT)
926
+ LANGUAGE plpgsql AS $claimfn$
927
+ BEGIN
928
+ RETURN QUERY
929
+ UPDATE memory_write_jobs j
930
+ SET status = 'running', attempts = j.attempts + 1, updated_at = NOW()
931
+ FROM (
932
+ SELECT jj.id FROM memory_write_jobs jj JOIN memories m ON m.id = jj.memory_id
933
+ WHERE ((jj.status IN ('pending','failed') AND jj.next_retry_at IS NOT NULL AND jj.next_retry_at <= NOW())
934
+ OR (jj.status = 'running' AND jj.updated_at < NOW() - p_stale_running))
935
+ AND (p_owner IS NULL OR m.owner_wallet = p_owner OR (p_owner = '__BOT_OWN__' AND m.owner_wallet IS NULL))
936
+ ORDER BY jj.next_retry_at ASC NULLS LAST LIMIT p_limit FOR UPDATE SKIP LOCKED
937
+ ) claimed
938
+ WHERE j.id = claimed.id
939
+ RETURNING j.id, j.memory_id, j.job_type, j.attempts,
940
+ (SELECT mm.owner_wallet FROM memories mm WHERE mm.id = j.memory_id);
941
+ END; $claimfn$;
942
+
943
+ DO $do$
944
+ BEGIN
945
+ DELETE FROM memory_links a USING memory_links b
946
+ WHERE a.ctid < b.ctid AND a.source_id = b.source_id AND a.target_id = b.target_id AND a.link_type = b.link_type;
947
+ ALTER TABLE memory_links DROP CONSTRAINT IF EXISTS memory_links_unique_edge;
948
+ ALTER TABLE memory_links ADD CONSTRAINT memory_links_unique_edge UNIQUE (source_id, target_id, link_type);
949
+ EXCEPTION WHEN undefined_table THEN NULL; END $do$;
950
+
951
+ CREATE OR REPLACE FUNCTION upsert_entity_relation(
952
+ p_src BIGINT, p_tgt BIGINT, p_type TEXT, p_evidence BIGINT DEFAULT NULL, p_strength REAL DEFAULT 0.5
953
+ )
954
+ RETURNS VOID LANGUAGE sql AS $uerfn$
955
+ INSERT INTO entity_relations (source_entity_id, target_entity_id, relation_type, strength, evidence_memory_ids)
956
+ VALUES (p_src, p_tgt, p_type, LEAST(1.0, p_strength),
957
+ CASE WHEN p_evidence IS NULL THEN '{}'::bigint[] ELSE ARRAY[p_evidence] END)
958
+ ON CONFLICT (source_entity_id, target_entity_id, relation_type) DO UPDATE
959
+ SET strength = LEAST(1.0, entity_relations.strength +
960
+ CASE WHEN p_evidence IS NULL OR p_evidence = ANY(entity_relations.evidence_memory_ids) THEN 0.0 ELSE 0.1 END),
961
+ evidence_memory_ids = (SELECT ARRAY(SELECT DISTINCT e FROM unnest(entity_relations.evidence_memory_ids || EXCLUDED.evidence_memory_ids) AS e));
962
+ $uerfn$;
963
+
964
+ -- Memory 3.0 C1 (migration 046): reconciliation SHADOW decision log. MIRROR \u2014 byte-equivalent
965
+ -- to migration 046. Records a PROPOSED reconcile op per write without applying it, so enforce
966
+ -- can be greenlit from a labeled sample. Deliberately NOT added to CORE_TABLES (dormant,
967
+ -- default-off; must not trip SCHEMA DRIFT at boot on an un-migrated prod box \u2014 C2 precedent).
968
+ CREATE TABLE IF NOT EXISTS memory_reconciliation_log (
969
+ id BIGSERIAL PRIMARY KEY,
970
+ memory_id BIGINT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
971
+ owner_wallet TEXT,
972
+ mode TEXT NOT NULL DEFAULT 'shadow' CHECK (mode IN ('shadow','enforce')),
973
+ proposed_op TEXT NOT NULL CHECK (proposed_op IN ('add','update','noop','needs_router','skip')),
974
+ target_memory_id BIGINT,
975
+ max_cosine REAL,
976
+ band TEXT CHECK (band IN ('hi','mid','lo','none')),
977
+ router_used BOOLEAN NOT NULL DEFAULT false,
978
+ router_model TEXT,
979
+ fact_key TEXT,
980
+ reason TEXT,
981
+ label TEXT,
982
+ labeled_at TIMESTAMPTZ,
983
+ gate_version TEXT,
984
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
985
+ );
986
+ CREATE INDEX IF NOT EXISTS idx_reconcile_log_owner_created ON memory_reconciliation_log(owner_wallet, created_at);
987
+ CREATE INDEX IF NOT EXISTS idx_reconcile_log_op ON memory_reconciliation_log(proposed_op);
988
+ CREATE INDEX IF NOT EXISTS idx_reconcile_log_router ON memory_reconciliation_log(mode, router_used);
989
+
990
+ DO $do$
991
+ BEGIN
992
+ IF NOT EXISTS (
993
+ SELECT 1 FROM information_schema.columns
994
+ WHERE table_name = 'memories' AND column_name = 'ts_summary'
995
+ ) THEN
996
+ ALTER TABLE memories ADD COLUMN ts_summary tsvector GENERATED ALWAYS AS (
997
+ setweight(to_tsvector('english', COALESCE(summary, '')), 'A')
998
+ ) STORED;
999
+ CREATE INDEX IF NOT EXISTS idx_memories_ts_summary ON memories USING GIN(ts_summary);
1000
+ END IF;
1001
+ END $do$;
1002
+
1003
+ -- Semantic search across memory-level embeddings with metadata filtering (the always-on
1004
+ -- recall vector lane). MIRROR of migration 028 / supabase-schema.sql \u2014 8-arg filter_tags form.
1005
+ CREATE OR REPLACE FUNCTION match_memories(
1006
+ query_embedding vector(1024),
1007
+ match_threshold float DEFAULT 0.3,
1008
+ match_count int DEFAULT 10,
1009
+ filter_types text[] DEFAULT NULL,
1010
+ filter_user text DEFAULT NULL,
1011
+ min_decay float DEFAULT 0.1,
1012
+ filter_owner text DEFAULT NULL,
1013
+ filter_tags text[] DEFAULT NULL
1014
+ )
1015
+ RETURNS TABLE (id bigint, similarity float)
1016
+ LANGUAGE plpgsql AS $$
1017
+ BEGIN
1018
+ RETURN QUERY
1019
+ SELECT m.id, (1 - (m.embedding <=> query_embedding))::float AS similarity
1020
+ FROM memories m
1021
+ WHERE m.embedding IS NOT NULL
1022
+ AND m.decay_factor >= min_decay
1023
+ AND (filter_types IS NULL OR m.memory_type = ANY(filter_types))
1024
+ AND (filter_user IS NULL OR m.related_user = filter_user)
1025
+ AND (
1026
+ filter_owner IS NULL
1027
+ OR (filter_owner = '__BOT_OWN__' AND m.owner_wallet IS NULL)
1028
+ OR m.owner_wallet = filter_owner
1029
+ )
1030
+ AND (filter_tags IS NULL OR m.tags && filter_tags)
1031
+ AND (1 - (m.embedding <=> query_embedding)) > match_threshold
1032
+ ORDER BY m.embedding <=> query_embedding
1033
+ LIMIT match_count;
1034
+ END;
1035
+ $$;
1036
+
661
1037
  -- Temporal-aware semantic search RPC (Exp 9)
662
1038
  CREATE OR REPLACE FUNCTION match_memories_temporal(
663
1039
  query_embedding vector(1024),
@@ -681,7 +1057,11 @@ async function initDatabase() {
681
1057
  AND m.decay_factor >= min_decay
682
1058
  AND (filter_types IS NULL OR m.memory_type = ANY(filter_types))
683
1059
  AND (filter_user IS NULL OR m.related_user = filter_user)
684
- AND (filter_owner IS NULL OR m.owner_wallet = filter_owner)
1060
+ AND (
1061
+ filter_owner IS NULL
1062
+ OR (filter_owner = '__BOT_OWN__' AND m.owner_wallet IS NULL)
1063
+ OR m.owner_wallet = filter_owner
1064
+ )
685
1065
  AND (filter_tags IS NULL OR m.tags && filter_tags)
686
1066
  AND (1 - (m.embedding <=> query_embedding)) > match_threshold
687
1067
  AND (start_date IS NULL OR COALESCE(m.event_date, m.created_at) >= start_date)
@@ -691,8 +1071,124 @@ async function initDatabase() {
691
1071
  END;
692
1072
  $$;
693
1073
 
694
- -- BM25-ranked full-text search RPC (Exp 8)
695
- -- Note: ts_summary column requires manual migration (GENERATED ALWAYS AS is not ALTER-able)
1074
+ -- Fragment-level semantic search with deduplication to parent memory. Returns the highest
1075
+ -- similarity fragment per memory (the non-skipExpansion recall lane). MIRROR of
1076
+ -- supabase-schema.sql \u2014 6-arg migration-043 form; the 4-arg (migration 009) call still
1077
+ -- resolves against it via parameter defaults, so a boot-provisioned box is never left on a
1078
+ -- stale fragment signature.
1079
+ CREATE OR REPLACE FUNCTION match_memory_fragments(
1080
+ query_embedding vector(1024),
1081
+ match_threshold float DEFAULT 0.3,
1082
+ match_count int DEFAULT 10,
1083
+ filter_owner text DEFAULT NULL,
1084
+ min_decay float DEFAULT 0.0,
1085
+ filter_types text[] DEFAULT NULL
1086
+ )
1087
+ RETURNS TABLE (memory_id bigint, max_similarity float)
1088
+ LANGUAGE plpgsql AS $$
1089
+ BEGIN
1090
+ RETURN QUERY
1091
+ SELECT f.memory_id, MAX((1 - (f.embedding <=> query_embedding))::float) AS max_similarity
1092
+ FROM memory_fragments f
1093
+ JOIN memories m ON m.id = f.memory_id
1094
+ WHERE f.embedding IS NOT NULL
1095
+ AND (1 - (f.embedding <=> query_embedding)) > match_threshold
1096
+ AND m.decay_factor >= min_decay
1097
+ AND (filter_types IS NULL OR m.memory_type = ANY(filter_types))
1098
+ AND (
1099
+ filter_owner IS NULL
1100
+ OR (filter_owner = '__BOT_OWN__' AND m.owner_wallet IS NULL)
1101
+ OR m.owner_wallet = filter_owner
1102
+ )
1103
+ GROUP BY f.memory_id
1104
+ ORDER BY max_similarity DESC
1105
+ LIMIT match_count;
1106
+ END;
1107
+ $$;
1108
+
1109
+ -- Encryption: owner key registry + per-memory wrapped DEKs (encryption \xA79, Plan 1 sync).
1110
+ CREATE TABLE IF NOT EXISTS encryption_keys (
1111
+ owner_wallet TEXT PRIMARY KEY,
1112
+ x25519_pubkey TEXT NOT NULL,
1113
+ verifier_ct TEXT NOT NULL,
1114
+ created_at TIMESTAMPTZ DEFAULT NOW(),
1115
+ updated_at TIMESTAMPTZ DEFAULT NOW()
1116
+ );
1117
+ CREATE TABLE IF NOT EXISTS memory_dek_wraps (
1118
+ memory_id BIGINT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
1119
+ recipient TEXT NOT NULL,
1120
+ wrapped_dek TEXT NOT NULL,
1121
+ wrap_pubkey TEXT NOT NULL,
1122
+ holder_wallet TEXT, -- (034) names the title_holder; NULL for owner/provider
1123
+ created_at TIMESTAMPTZ DEFAULT NOW(),
1124
+ CONSTRAINT memory_dek_wraps_recipient_chk CHECK (recipient IN ('owner', 'provider', 'title_holder')),
1125
+ CONSTRAINT memory_dek_wraps_holder_chk CHECK ((recipient = 'title_holder') = (holder_wallet IS NOT NULL))
1126
+ );
1127
+ -- (036) holder-aware uniqueness: one owner + one provider per memory (NULLS NOT DISTINCT),
1128
+ -- and one title_holder per (memory, holder) so a sale's seller + buyer wraps coexist (RT7).
1129
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_dek_wraps_identity
1130
+ ON memory_dek_wraps (memory_id, recipient, holder_wallet) NULLS NOT DISTINCT;
1131
+ CREATE INDEX IF NOT EXISTS idx_dek_wraps_memory ON memory_dek_wraps(memory_id);
1132
+
1133
+ -- Populate content_tokens from a transient plaintext arg (PostgREST can't express to_tsvector inline).
1134
+ -- setweight 'B' mirrors the old combined ts_summary weighting (summary 'A' > content 'B').
1135
+ CREATE OR REPLACE FUNCTION set_memory_content_tokens(p_memory_id bigint, p_text text)
1136
+ RETURNS void LANGUAGE sql AS $fn$
1137
+ UPDATE memories
1138
+ SET content_tokens = CASE
1139
+ WHEN p_text IS NULL OR p_text = '' THEN NULL
1140
+ ELSE setweight(to_tsvector('english', p_text), 'B')
1141
+ END
1142
+ WHERE id = p_memory_id;
1143
+ $fn$;
1144
+
1145
+ -- Atomic revoke: clear plaintext + drop the provider wrap in one transaction (encryption \xA77).
1146
+ CREATE OR REPLACE FUNCTION revoke_memory(p_memory_id bigint, p_summary_ct text, p_embedding_ct text)
1147
+ RETURNS void LANGUAGE plpgsql AS $rev$
1148
+ BEGIN
1149
+ UPDATE memories SET
1150
+ summary = '',
1151
+ summary_ciphertext = p_summary_ct,
1152
+ embedding = NULL,
1153
+ embedding_ciphertext = NULLIF(p_embedding_ct, ''),
1154
+ content_tokens = NULL,
1155
+ provider_delegated = false
1156
+ WHERE id = p_memory_id;
1157
+ DELETE FROM memory_dek_wraps WHERE memory_id = p_memory_id AND recipient = 'provider';
1158
+ -- Fragment parity (migration 043): fragments hold plaintext + live embeddings.
1159
+ DELETE FROM memory_fragments WHERE memory_id = p_memory_id;
1160
+ END;
1161
+ $rev$;
1162
+
1163
+ -- Atomic re-delegate (encryption \xA77) \u2014 inverse of revoke_memory. Restores plaintext
1164
+ -- summary/embedding, rebuilds content_tokens via the canonical builder, clears ciphertext
1165
+ -- cols, sets provider_delegated=true, (re-)inserts the provider wrap.
1166
+ CREATE OR REPLACE FUNCTION redelegate_memory(
1167
+ p_memory_id bigint,
1168
+ p_summary text,
1169
+ p_embedding text,
1170
+ p_content text,
1171
+ p_wrapped_dek text,
1172
+ p_wrap_pubkey text
1173
+ )
1174
+ RETURNS void LANGUAGE plpgsql AS $redel$
1175
+ BEGIN
1176
+ UPDATE memories SET
1177
+ summary = p_summary,
1178
+ summary_ciphertext = NULL,
1179
+ embedding = NULLIF(p_embedding, '')::vector,
1180
+ embedding_ciphertext = NULL,
1181
+ provider_delegated = true
1182
+ WHERE id = p_memory_id;
1183
+ PERFORM set_memory_content_tokens(p_memory_id, p_content);
1184
+ INSERT INTO memory_dek_wraps (memory_id, recipient, wrapped_dek, wrap_pubkey)
1185
+ VALUES (p_memory_id, 'provider', p_wrapped_dek, p_wrap_pubkey)
1186
+ ON CONFLICT (memory_id, recipient) DO UPDATE
1187
+ SET wrapped_dek = EXCLUDED.wrapped_dek, wrap_pubkey = EXCLUDED.wrap_pubkey;
1188
+ END;
1189
+ $redel$;
1190
+
1191
+ -- BM25-ranked full-text search RPC (Exp 8) \u2014 dual-column (ts_summary + content_tokens)
696
1192
  CREATE OR REPLACE FUNCTION bm25_search_memories(
697
1193
  search_query text,
698
1194
  match_count int DEFAULT 20,
@@ -711,11 +1207,17 @@ async function initDatabase() {
711
1207
  RETURN;
712
1208
  END IF;
713
1209
  RETURN QUERY
714
- SELECT m.id, ts_rank_cd(m.ts_summary, tsquery_val, 32)::float AS rank
1210
+ SELECT m.id,
1211
+ (ts_rank_cd(COALESCE(m.ts_summary, ''::tsvector), tsquery_val, 32)
1212
+ + ts_rank_cd(COALESCE(m.content_tokens, ''::tsvector), tsquery_val, 32))::float AS rank
715
1213
  FROM memories m
716
- WHERE m.ts_summary @@ tsquery_val
1214
+ WHERE (m.ts_summary @@ tsquery_val OR m.content_tokens @@ tsquery_val)
717
1215
  AND m.decay_factor >= min_decay
718
- AND (filter_owner IS NULL OR m.owner_wallet = filter_owner)
1216
+ AND (
1217
+ filter_owner IS NULL
1218
+ OR (filter_owner = '__BOT_OWN__' AND m.owner_wallet IS NULL)
1219
+ OR m.owner_wallet = filter_owner
1220
+ )
719
1221
  AND (filter_types IS NULL OR m.memory_type = ANY(filter_types))
720
1222
  AND (filter_tags IS NULL OR m.tags && filter_tags)
721
1223
  ORDER BY rank DESC
@@ -744,9 +1246,31 @@ async function initDatabase() {
744
1246
  tokens_prompt INTEGER,
745
1247
  tokens_completion INTEGER,
746
1248
  memory_ids INTEGER[],
1249
+ frontier_tokens INTEGER,
1250
+ memories_used INTEGER,
1251
+ tokens_saved INTEGER,
747
1252
  created_at TIMESTAMPTZ DEFAULT NOW()
748
1253
  );
749
1254
  CREATE INDEX IF NOT EXISTS idx_chat_msg_conv ON chat_messages(conversation_id, created_at);
1255
+ CREATE INDEX IF NOT EXISTS idx_chat_msg_saved ON chat_messages(created_at) WHERE tokens_saved IS NOT NULL;
1256
+ -- Proof counter totals. "today" is UTC-based (resets 00:00 UTC). Full-table aggregate:
1257
+ -- callers MUST use a server-side TTL cache (see proof.routes.ts). Mirror of migration 026.
1258
+ CREATE OR REPLACE FUNCTION proof_tokens_saved_totals()
1259
+ RETURNS TABLE(
1260
+ measured_saved bigint,
1261
+ measured_today bigint,
1262
+ measured_frontier bigint,
1263
+ historical_prompt_sum bigint,
1264
+ n bigint
1265
+ ) LANGUAGE sql STABLE AS $$
1266
+ SELECT
1267
+ COALESCE(SUM(tokens_saved) FILTER (WHERE tokens_saved IS NOT NULL), 0)::bigint,
1268
+ COALESCE(SUM(tokens_saved) FILTER (WHERE tokens_saved IS NOT NULL AND created_at >= date_trunc('day', now())), 0)::bigint,
1269
+ COALESCE(SUM(frontier_tokens) FILTER (WHERE tokens_saved IS NOT NULL), 0)::bigint,
1270
+ COALESCE(SUM(tokens_prompt) FILTER (WHERE tokens_saved IS NULL), 0)::bigint,
1271
+ COUNT(*) FILTER (WHERE tokens_saved IS NOT NULL)::bigint
1272
+ FROM chat_messages;
1273
+ $$;
750
1274
 
751
1275
  -- Chat billing: balances, top-ups, and per-message usage
752
1276
  CREATE TABLE IF NOT EXISTS chat_balances (
@@ -788,6 +1312,63 @@ async function initDatabase() {
788
1312
  created_at TIMESTAMPTZ DEFAULT NOW()
789
1313
  );
790
1314
  CREATE INDEX IF NOT EXISTS idx_chat_usage_wallet ON chat_usage(wallet_address, created_at DESC);
1315
+
1316
+ -- Wiki pack installations: which packs (Workspace, Compliance, Sales)
1317
+ -- each wallet has installed. Drives the topic rail in /wiki and
1318
+ -- the auto-categorisation rules applied to incoming memories.
1319
+ CREATE TABLE IF NOT EXISTS wiki_pack_installations (
1320
+ id BIGSERIAL PRIMARY KEY,
1321
+ owner_wallet TEXT NOT NULL,
1322
+ pack_id TEXT NOT NULL,
1323
+ installed_at TIMESTAMPTZ DEFAULT NOW(),
1324
+ UNIQUE (owner_wallet, pack_id)
1325
+ );
1326
+ CREATE INDEX IF NOT EXISTS idx_wiki_pack_installations_owner
1327
+ ON wiki_pack_installations(owner_wallet);
1328
+
1329
+ -- Migration 019: corrected access-boost RPC (no importance mutation on read).
1330
+ -- Re-asserted on every boot so the read->rank->read feedback loop can't return.
1331
+ -- importance_boosts is kept for call-site signature compatibility and ignored.
1332
+ -- Drop the stale single-arg overload (migration 003) so the two-arg version is
1333
+ -- unambiguous and the lockdown below can't be bypassed via an unrevoked overload.
1334
+ DROP FUNCTION IF EXISTS batch_boost_memory_access(bigint[]);
1335
+ CREATE OR REPLACE FUNCTION batch_boost_memory_access(
1336
+ memory_ids BIGINT[],
1337
+ importance_boosts DOUBLE PRECISION[] DEFAULT NULL
1338
+ ) RETURNS void LANGUAGE plpgsql AS $fn$
1339
+ BEGIN
1340
+ UPDATE memories
1341
+ SET access_count = access_count + 1,
1342
+ last_accessed = NOW(),
1343
+ decay_factor = LEAST(1.0, decay_factor + 0.1)
1344
+ WHERE id = ANY(memory_ids);
1345
+ END;
1346
+ $fn$;
1347
+
1348
+ -- Migration 020: lock down dangerous SECURITY DEFINER RPCs to service_role only.
1349
+ -- exec_sql/boost RPCs must never be callable by anon/authenticated (the publishable
1350
+ -- key ships in client apps). Wrapped so a not-yet-created function never aborts boot.
1351
+ DO $do$ BEGIN
1352
+ REVOKE EXECUTE ON FUNCTION exec_sql(text) FROM PUBLIC, anon, authenticated;
1353
+ GRANT EXECUTE ON FUNCTION exec_sql(text) TO service_role;
1354
+ EXCEPTION WHEN undefined_function THEN NULL; END $do$;
1355
+
1356
+ DO $do$ BEGIN
1357
+ REVOKE EXECUTE ON FUNCTION batch_boost_memory_access(bigint[], double precision[]) FROM PUBLIC, anon, authenticated;
1358
+ GRANT EXECUTE ON FUNCTION batch_boost_memory_access(bigint[], double precision[]) TO service_role;
1359
+ EXCEPTION WHEN undefined_function THEN NULL; END $do$;
1360
+
1361
+ DO $do$ BEGIN
1362
+ REVOKE EXECUTE ON FUNCTION boost_memory_importance(bigint, double precision, double precision) FROM PUBLIC, anon, authenticated;
1363
+ GRANT EXECUTE ON FUNCTION boost_memory_importance(bigint, double precision, double precision) TO service_role;
1364
+ EXCEPTION WHEN undefined_function THEN NULL; END $do$;
1365
+
1366
+ -- Migration 022 (guard portion): reject empty content on NEW writes in every env.
1367
+ -- NOT VALID enforces on new INSERT/UPDATE without scanning existing rows; the
1368
+ -- one-time cleanup of legacy blank rows + VALIDATE lives in the manual 022 file.
1369
+ DO $do$ BEGIN
1370
+ ALTER TABLE memories ADD CONSTRAINT memories_content_nonempty CHECK (length(btrim(content)) > 0) NOT VALID;
1371
+ EXCEPTION WHEN duplicate_object THEN NULL; END $do$;
791
1372
  `
792
1373
  });
793
1374
  if (error) {
@@ -796,7 +1377,75 @@ async function initDatabase() {
796
1377
  } catch {
797
1378
  log.warn("rpc exec_sql not available. Create tables via Supabase SQL editor.");
798
1379
  }
799
- log.info("Database initialized");
1380
+ }
1381
+ function getSchemaDriftReport() {
1382
+ return lastSchemaReport;
1383
+ }
1384
+ async function verifyCoreSchema(db) {
1385
+ const missingTables = [];
1386
+ const probeErrors = [];
1387
+ for (const table of CORE_TABLES) {
1388
+ try {
1389
+ const { error } = await db.from(table).select("id", { head: true, count: "exact" }).limit(1);
1390
+ if (error) {
1391
+ if (MISSING_RE.test(error.message ?? "")) missingTables.push(table);
1392
+ else probeErrors.push(`${table}: ${error.message}`);
1393
+ }
1394
+ } catch (err) {
1395
+ probeErrors.push(`${table}: ${err instanceof Error ? err.message : String(err)}`);
1396
+ }
1397
+ }
1398
+ const brokenRpcs = [];
1399
+ for (const probe of CORE_RPC_PROBES) {
1400
+ try {
1401
+ const { error } = await db.rpc(probe.name, probe.args);
1402
+ if (error && MISSING_RE.test(error.message ?? "")) brokenRpcs.push(probe.name);
1403
+ } catch (err) {
1404
+ probeErrors.push(`${probe.name}: ${err instanceof Error ? err.message : String(err)}`);
1405
+ }
1406
+ }
1407
+ return { missingTables, brokenRpcs, probeErrors };
1408
+ }
1409
+ async function initDatabase(dbOverride) {
1410
+ const db = dbOverride ?? getDb();
1411
+ const mode = process.env.INIT_DB_MODE ?? "auto";
1412
+ if (mode === "replay") {
1413
+ await runBootDdl(db);
1414
+ lastSchemaReport = { at: (/* @__PURE__ */ new Date()).toISOString(), status: "replayed", missingTables: [], brokenRpcs: [] };
1415
+ log.info("Database initialized (legacy replay mode)");
1416
+ return;
1417
+ }
1418
+ let { missingTables, brokenRpcs, probeErrors } = await verifyCoreSchema(db);
1419
+ if (mode === "auto" && missingTables.includes("memories")) {
1420
+ log.info("Fresh database detected (no memories table) \u2014 running boot DDL");
1421
+ await runBootDdl(db);
1422
+ ({ missingTables, brokenRpcs, probeErrors } = await verifyCoreSchema(db));
1423
+ const healthy2 = missingTables.length === 0 && brokenRpcs.length === 0;
1424
+ lastSchemaReport = {
1425
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1426
+ status: healthy2 ? "fresh-bootstrapped" : "drift",
1427
+ missingTables,
1428
+ brokenRpcs
1429
+ };
1430
+ if (healthy2) log.info("Database bootstrapped from boot DDL");
1431
+ else log.error({ missingTables, brokenRpcs }, "SCHEMA DRIFT after fresh bootstrap \u2014 check exec_sql availability");
1432
+ return;
1433
+ }
1434
+ if (probeErrors.length > 0 && missingTables.length === 0 && brokenRpcs.length === 0) {
1435
+ lastSchemaReport = { at: (/* @__PURE__ */ new Date()).toISOString(), status: "unknown", missingTables, brokenRpcs };
1436
+ log.warn({ probeErrors: probeErrors.slice(0, 3) }, "Schema verification inconclusive (probe errors); skipping");
1437
+ return;
1438
+ }
1439
+ const healthy = missingTables.length === 0 && brokenRpcs.length === 0;
1440
+ lastSchemaReport = { at: (/* @__PURE__ */ new Date()).toISOString(), status: healthy ? "ok" : "drift", missingTables, brokenRpcs };
1441
+ if (healthy) {
1442
+ log.info("Database schema verified (boot blob frozen; migrations are the single schema writer)");
1443
+ } else {
1444
+ log.error(
1445
+ { missingTables, brokenRpcs },
1446
+ "SCHEMA DRIFT \u2014 core objects missing on an established database; apply the corresponding migration by hand (the boot blob no longer auto-repairs)"
1447
+ );
1448
+ }
800
1449
  }
801
1450
  async function isAlreadyProcessed(tweetId) {
802
1451
  const db = getDb();
@@ -829,14 +1478,44 @@ async function markProcessed(tweetId, feature, responseTweetId, extra) {
829
1478
  processed_at: (/* @__PURE__ */ new Date()).toISOString()
830
1479
  });
831
1480
  }
832
- var import_supabase_js, log, supabase;
1481
+ var import_supabase_js, log, supabase, CORE_TABLES, CORE_RPC_PROBES, lastSchemaReport, MISSING_RE;
833
1482
  var init_database = __esm({
834
1483
  "packages/shared/src/core/database.ts"() {
835
1484
  "use strict";
836
1485
  import_supabase_js = require("@supabase/supabase-js");
837
1486
  init_config();
838
1487
  init_logger();
1488
+ init_migration_profile();
839
1489
  log = createChildLogger("database");
1490
+ CORE_TABLES = [
1491
+ "memories",
1492
+ "memory_fragments",
1493
+ "memory_links",
1494
+ "agent_keys",
1495
+ "dream_logs",
1496
+ "rate_limits",
1497
+ "chat_conversations",
1498
+ "chat_messages"
1499
+ ];
1500
+ CORE_RPC_PROBES = [
1501
+ { name: "get_linked_memories", args: { seed_ids: [], min_strength: 0.1, max_results: 1, filter_owner: null } },
1502
+ { name: "bm25_search_memories", args: { search_query: "", match_count: 1, min_decay: 0.1, filter_owner: null } },
1503
+ // Vector recall lanes (memory.ts recall). match_memories is the always-on memory-level lane;
1504
+ // match_memory_fragments is the fragment lane (non-skipExpansion path). Both are silently absent
1505
+ // from a stale boot blob, so probe them so the gap surfaces as drift instead of a dead lane.
1506
+ // Vector-safe args (null embedding + match_count 0) make each a no-op read. filter_tags pins the
1507
+ // migration-028 8-arg match_memories overload recall calls unconditionally (catches a pre-028 box
1508
+ // where recall would break); the fragment probe stays the 4/6-arg common subset (min_decay +
1509
+ // filter_types are opt-in via MEMORY_FRAGMENT_FILTERS) so it never false-drifts a pre-043 box
1510
+ // still serving the migration-009 4-arg form.
1511
+ { name: "match_memories", args: { query_embedding: null, match_count: 0, filter_owner: null, filter_tags: null } },
1512
+ { name: "match_memory_fragments", args: { query_embedding: null, match_count: 0, filter_owner: null } },
1513
+ // Memory 3.0 C2: probe the outbox claim RPC so a table-without-RPC state (the §6 hazard) is
1514
+ // caught at boot as drift rather than silently never draining.
1515
+ { name: "claim_memory_write_jobs", args: { p_limit: 0 } }
1516
+ ];
1517
+ lastSchemaReport = null;
1518
+ MISSING_RE = /does not exist|could not find|schema cache/i;
840
1519
  }
841
1520
  });
842
1521
 
@@ -949,6 +1628,17 @@ var init_guardrails = __esm({
949
1628
  }
950
1629
  });
951
1630
 
1631
+ // packages/shared/src/core/model-capabilities.ts
1632
+ function modelRejectsSamplingParams(model) {
1633
+ if (!model) return false;
1634
+ return /opus[-_.]?4[-_.]?[789]\b|opus[-_.]?[5-9]\b/i.test(model);
1635
+ }
1636
+ var init_model_capabilities = __esm({
1637
+ "packages/shared/src/core/model-capabilities.ts"() {
1638
+ "use strict";
1639
+ }
1640
+ });
1641
+
952
1642
  // packages/shared/src/core/openrouter-client.ts
953
1643
  function getModelForFunction(fn) {
954
1644
  return COGNITIVE_MODEL_MAP[fn] || OPENROUTER_MODELS["llama-70b"];
@@ -1015,7 +1705,8 @@ async function generateOpenRouterResponse(opts) {
1015
1705
  model,
1016
1706
  messages,
1017
1707
  max_tokens: maxTokens,
1018
- temperature: opts.temperature ?? 0.7
1708
+ // Opus 4.7+ via OpenRouter rejects temperature/top_p strip for those models.
1709
+ ...modelRejectsSamplingParams(model) ? {} : { temperature: opts.temperature ?? 0.7 }
1019
1710
  })
1020
1711
  });
1021
1712
  if (!response.ok) {
@@ -1046,15 +1737,18 @@ var init_openrouter_client = __esm({
1046
1737
  "packages/shared/src/core/openrouter-client.ts"() {
1047
1738
  "use strict";
1048
1739
  init_logger();
1740
+ init_model_capabilities();
1049
1741
  log3 = createChildLogger("openrouter");
1050
1742
  OPENROUTER_API_URL = "https://openrouter.ai/api/v1";
1051
1743
  OPENROUTER_MODELS = {
1052
1744
  // Frontier (Anthropic)
1745
+ "claude-opus-4.7": "anthropic/claude-opus-4.7",
1053
1746
  "claude-opus-4.6": "anthropic/claude-opus-4.6",
1054
1747
  "claude-sonnet-4.6": "anthropic/claude-sonnet-4.6",
1055
1748
  "claude-opus-4.5": "anthropic/claude-opus-4.5",
1056
1749
  "claude-sonnet-4.5": "anthropic/claude-sonnet-4.5",
1057
1750
  // Frontier (Other providers)
1751
+ "gpt-5.5": "openai/gpt-5.5",
1058
1752
  "gpt-5.4": "openai/gpt-5.4",
1059
1753
  "grok-4.1": "x-ai/grok-4.1-fast",
1060
1754
  "gemini-3-pro": "google/gemini-3-pro-preview",
@@ -1106,7 +1800,7 @@ var init_openrouter_client = __esm({
1106
1800
  });
1107
1801
 
1108
1802
  // packages/shared/src/utils/constants.ts
1109
- var MEMO_PROGRAM_ID, SOLSCAN_TX_BASE_URL, TWEET_MAX_LENGTH, BOT_X_HANDLE, MEMORY_MIN_DECAY, MEMORY_MAX_CONTENT_LENGTH, MEMORY_MAX_SUMMARY_LENGTH, DECAY_RATES, RECENCY_DECAY_BASE, RETRIEVAL_WEIGHT_RECENCY, RETRIEVAL_WEIGHT_RELEVANCE, RETRIEVAL_WEIGHT_IMPORTANCE, RETRIEVAL_WEIGHT_VECTOR, KNOWLEDGE_TYPE_BOOST, VECTOR_MATCH_THRESHOLD, BOND_TYPE_WEIGHTS, RETRIEVAL_WEIGHT_GRAPH, RETRIEVAL_WEIGHT_COOCCURRENCE, LINK_SIMILARITY_THRESHOLD, MAX_AUTO_LINKS, LINK_CO_RETRIEVAL_BOOST, INTERNAL_MEMORY_SOURCES, INTERNAL_IMPORTANCE_BOOST, REFLECTION_IMPORTANCE_THRESHOLD, REFLECTION_MIN_INTERVAL_MS, WHALE_SELL_COOLDOWN_MS, MEMO_MAX_LENGTH;
1803
+ var MEMO_PROGRAM_ID, SOLSCAN_TX_BASE_URL, TWEET_MAX_LENGTH, BOT_X_HANDLE, MEMORY_MIN_DECAY, MEMORY_MAX_CONTENT_LENGTH, MEMORY_MAX_SUMMARY_LENGTH, DECAY_RATES, RECENCY_DECAY_BASE, RETRIEVAL_WEIGHT_RECENCY, RETRIEVAL_WEIGHT_RELEVANCE, RETRIEVAL_WEIGHT_IMPORTANCE, RETRIEVAL_WEIGHT_VECTOR, RETRIEVAL_WEIGHT_BM25, KNOWLEDGE_TYPE_BOOST, VECTOR_MATCH_THRESHOLD, BOND_TYPE_WEIGHTS, RETRIEVAL_WEIGHT_GRAPH, RETRIEVAL_WEIGHT_COOCCURRENCE, LINK_SIMILARITY_THRESHOLD, MAX_AUTO_LINKS, LINK_CO_RETRIEVAL_BOOST, INTERNAL_MEMORY_SOURCES, EMBEDDING_FRAGMENT_MAX_LENGTH, REFLECTION_IMPORTANCE_THRESHOLD, REFLECTION_MIN_INTERVAL_MS, WHALE_SELL_COOLDOWN_MS, MEMO_MAX_LENGTH;
1110
1804
  var init_constants = __esm({
1111
1805
  "packages/shared/src/utils/constants.ts"() {
1112
1806
  "use strict";
@@ -1130,10 +1824,11 @@ var init_constants = __esm({
1130
1824
  // Journal entries persist like knowledge
1131
1825
  };
1132
1826
  RECENCY_DECAY_BASE = 0.995;
1133
- RETRIEVAL_WEIGHT_RECENCY = 1;
1134
- RETRIEVAL_WEIGHT_RELEVANCE = 2;
1135
- RETRIEVAL_WEIGHT_IMPORTANCE = 2;
1136
- RETRIEVAL_WEIGHT_VECTOR = 4;
1827
+ RETRIEVAL_WEIGHT_RECENCY = Number(process.env.RETRIEVAL_WEIGHT_RECENCY ?? "1.0");
1828
+ RETRIEVAL_WEIGHT_RELEVANCE = Number(process.env.RETRIEVAL_WEIGHT_RELEVANCE ?? "2.0");
1829
+ RETRIEVAL_WEIGHT_IMPORTANCE = Number(process.env.RETRIEVAL_WEIGHT_IMPORTANCE ?? "2.0");
1830
+ RETRIEVAL_WEIGHT_VECTOR = Number(process.env.RETRIEVAL_WEIGHT_VECTOR ?? "4.0");
1831
+ RETRIEVAL_WEIGHT_BM25 = Number(process.env.RETRIEVAL_WEIGHT_BM25 ?? "0.15");
1137
1832
  KNOWLEDGE_TYPE_BOOST = {
1138
1833
  semantic: 0.15,
1139
1834
  // Distilled knowledge gets meaningful boost
@@ -1150,6 +1845,8 @@ var init_constants = __esm({
1150
1845
  BOND_TYPE_WEIGHTS = {
1151
1846
  causes: 1,
1152
1847
  supports: 0.9,
1848
+ supersedes: 0.85,
1849
+ // decisive replacement: new fact overrides an older one (Memory 3.0 C1)
1153
1850
  concurrent_with: 0.8,
1154
1851
  resolves: 0.8,
1155
1852
  happens_before: 0.7,
@@ -1174,7 +1871,7 @@ var init_constants = __esm({
1174
1871
  "dream-cycle",
1175
1872
  "meditation"
1176
1873
  ]);
1177
- INTERNAL_IMPORTANCE_BOOST = 5e-3;
1874
+ EMBEDDING_FRAGMENT_MAX_LENGTH = 2e3;
1178
1875
  REFLECTION_IMPORTANCE_THRESHOLD = 2;
1179
1876
  REFLECTION_MIN_INTERVAL_MS = 30 * 60 * 1e3;
1180
1877
  WHALE_SELL_COOLDOWN_MS = 30 * 60 * 1e3;
@@ -1271,10 +1968,12 @@ ${userContent || "(no message, just a mention)"}`;
1271
1968
  cognitiveFunction: options.cognitiveFunction
1272
1969
  });
1273
1970
  } else {
1971
+ const directModel = getModel();
1274
1972
  const response = await getClient().messages.create({
1275
- model: getModel(),
1973
+ model: directModel,
1276
1974
  max_tokens: options.maxTokens || 1024,
1277
- temperature: 0.9,
1975
+ // Opus 4.7+ rejects temperature/top_p (HTTP 400) — strip for those models.
1976
+ ...modelRejectsSamplingParams(directModel) ? {} : { temperature: 0.9 },
1278
1977
  system: systemPrompt,
1279
1978
  messages: [{ role: "user", content: userContent }]
1280
1979
  });
@@ -1301,10 +2000,11 @@ ${userContent || "(no message, just a mention)"}`;
1301
2000
  }
1302
2001
  async function generateImportanceScore(description) {
1303
2002
  const importancePrompt = process.env.CLUDE_IMPORTANCE_PROMPT || "You rate the importance of events for an AI agent. Respond with ONLY a single integer from 1 to 10. 1 = purely mundane. 5 = moderately important. 10 = extremely significant.";
2003
+ const scoreModel = getModel();
1304
2004
  const response = await getClient().messages.create({
1305
- model: getModel(),
2005
+ model: scoreModel,
1306
2006
  max_tokens: 10,
1307
- temperature: 0,
2007
+ ...modelRejectsSamplingParams(scoreModel) ? {} : { temperature: 0 },
1308
2008
  system: importancePrompt,
1309
2009
  messages: [{
1310
2010
  role: "user",
@@ -1333,6 +2033,7 @@ var init_claude_client = __esm({
1333
2033
  init_guardrails();
1334
2034
  init_openrouter_client();
1335
2035
  init_constants();
2036
+ init_model_capabilities();
1336
2037
  log4 = createChildLogger("claude-client");
1337
2038
  _systemPromptProvider = () => "You are an AI assistant with persistent memory.";
1338
2039
  _responsePostProcessor = (t) => t;
@@ -1353,11 +2054,18 @@ var init_claude_client = __esm({
1353
2054
  var embeddings_exports = {};
1354
2055
  __export(embeddings_exports, {
1355
2056
  _configureEmbeddings: () => _configureEmbeddings,
2057
+ _fetchMetadataToken: () => _fetchMetadataToken,
2058
+ _resetVertexAuthCache: () => _resetVertexAuthCache,
1356
2059
  generateEmbedding: () => generateEmbedding,
2060
+ generateEmbeddingForSpace: () => generateEmbeddingForSpace,
1357
2061
  generateEmbeddings: () => generateEmbeddings,
1358
2062
  generateQueryEmbedding: () => generateQueryEmbedding,
2063
+ generateQueryEmbeddingForSpace: () => generateQueryEmbeddingForSpace,
2064
+ generateVertexEmbedding: () => generateVertexEmbedding,
2065
+ generateVertexEmbeddings: () => generateVertexEmbeddings,
1359
2066
  getCachedEmbedding: () => getCachedEmbedding,
1360
2067
  isEmbeddingEnabled: () => isEmbeddingEnabled,
2068
+ isVertexConfigured: () => isVertexConfigured,
1361
2069
  setCachedEmbedding: () => setCachedEmbedding
1362
2070
  });
1363
2071
  function getCachedEmbedding(text) {
@@ -1501,7 +2209,80 @@ async function generateEmbeddings(texts) {
1501
2209
  return texts.map(() => null);
1502
2210
  }
1503
2211
  }
1504
- var log5, PROVIDERS, _enabled, _overrideConfig, EMBEDDING_CACHE_MAX, EMBEDDING_CACHE_TTL_MS, _embeddingCache;
2212
+ function _resetVertexAuthCache() {
2213
+ _vertexToken = null;
2214
+ }
2215
+ async function _fetchMetadataToken() {
2216
+ const res = await fetch(METADATA_TOKEN_URL, { headers: { "Metadata-Flavor": "Google" } });
2217
+ if (!res.ok) throw new Error(`Vertex metadata token fetch failed: ${res.status}`);
2218
+ const j = await res.json();
2219
+ return { token: j.access_token, expiresInSec: j.expires_in };
2220
+ }
2221
+ async function getVertexAccessToken() {
2222
+ const override = config.vertex.accessToken;
2223
+ if (override) return override;
2224
+ const now = Date.now();
2225
+ if (_vertexToken && _vertexToken.expiresAtMs > now + 6e4) return _vertexToken.token;
2226
+ const { token, expiresInSec } = await _fetchMetadataToken();
2227
+ _vertexToken = { token, expiresAtMs: now + expiresInSec * 1e3 };
2228
+ return token;
2229
+ }
2230
+ function vertexPredictUrl(model) {
2231
+ const { project, location } = config.vertex;
2232
+ return `https://${location}-aiplatform.googleapis.com/v1/projects/${project}/locations/${location}/publishers/google/models/${model}:predict`;
2233
+ }
2234
+ function isVertexConfigured() {
2235
+ return !!config.vertex.project;
2236
+ }
2237
+ async function generateVertexEmbeddings(texts, dimensions) {
2238
+ if (texts.length === 0) return [];
2239
+ const { project, model, dimensions: defaultDims } = config.vertex;
2240
+ if (!project) {
2241
+ log5.warn("Vertex embeddings requested but VERTEX_PROJECT is unset");
2242
+ return texts.map(() => null);
2243
+ }
2244
+ const outputDimensionality = dimensions ?? defaultDims;
2245
+ const startMs = Date.now();
2246
+ try {
2247
+ const token = await getVertexAccessToken();
2248
+ const res = await fetch(vertexPredictUrl(model), {
2249
+ method: "POST",
2250
+ headers: {
2251
+ "Content-Type": "application/json",
2252
+ "Authorization": `Bearer ${token}`
2253
+ },
2254
+ body: JSON.stringify({
2255
+ instances: texts.map((t) => ({ content: t.slice(0, 8e3) })),
2256
+ parameters: { outputDimensionality }
2257
+ })
2258
+ });
2259
+ if (!res.ok) {
2260
+ const errText = await res.text();
2261
+ log5.error({ status: res.status, body: errText.slice(0, 200), provider: "vertex" }, "Vertex embedding API error");
2262
+ return texts.map(() => null);
2263
+ }
2264
+ const data = await res.json();
2265
+ const preds = data.predictions ?? [];
2266
+ log5.debug({ provider: "vertex", model, count: texts.length, elapsed: Date.now() - startMs }, "Vertex embeddings generated");
2267
+ return texts.map((_, i) => preds[i]?.embeddings?.values ?? null);
2268
+ } catch (err) {
2269
+ log5.error({ err, provider: "vertex" }, "Vertex embedding generation failed");
2270
+ return texts.map(() => null);
2271
+ }
2272
+ }
2273
+ async function generateVertexEmbedding(text, dimensions) {
2274
+ const [vec] = await generateVertexEmbeddings([text], dimensions);
2275
+ return vec ?? null;
2276
+ }
2277
+ async function generateEmbeddingForSpace(space, text) {
2278
+ if (space === "vertex") return generateVertexEmbedding(text);
2279
+ return generateEmbedding(text);
2280
+ }
2281
+ async function generateQueryEmbeddingForSpace(space, text) {
2282
+ if (space === "vertex") return generateVertexEmbedding(text);
2283
+ return generateQueryEmbedding(text);
2284
+ }
2285
+ var log5, PROVIDERS, _enabled, _overrideConfig, EMBEDDING_CACHE_MAX, EMBEDDING_CACHE_TTL_MS, _embeddingCache, _vertexToken, METADATA_TOKEN_URL;
1505
2286
  var init_embeddings = __esm({
1506
2287
  "packages/shared/src/core/embeddings.ts"() {
1507
2288
  "use strict";
@@ -1534,6 +2315,8 @@ var init_embeddings = __esm({
1534
2315
  EMBEDDING_CACHE_MAX = 200;
1535
2316
  EMBEDDING_CACHE_TTL_MS = 30 * 60 * 1e3;
1536
2317
  _embeddingCache = /* @__PURE__ */ new Map();
2318
+ _vertexToken = null;
2319
+ METADATA_TOKEN_URL = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
1537
2320
  }
1538
2321
  });
1539
2322
 
@@ -1674,8 +2457,8 @@ function deriveRegistryPDA(authority) {
1674
2457
  );
1675
2458
  }
1676
2459
  function anchorDiscriminator(name) {
1677
- const { createHash: createHash2 } = require("crypto");
1678
- const hash = createHash2("sha256").update(`global:${name}`).digest();
2460
+ const { createHash: createHash3 } = require("crypto");
2461
+ const hash = createHash3("sha256").update(`global:${name}`).digest();
1679
2462
  return hash.subarray(0, 8);
1680
2463
  }
1681
2464
  async function initializeRegistry() {
@@ -1840,6 +2623,39 @@ var init_solana_client = __esm({
1840
2623
  }
1841
2624
  });
1842
2625
 
2626
+ // packages/shared/src/core/memory-grounding.ts
2627
+ function byMemoryDateAsc(a, b) {
2628
+ const ta = a.created_at ? Date.parse(a.created_at) : NaN;
2629
+ const tb = b.created_at ? Date.parse(b.created_at) : NaN;
2630
+ if (Number.isNaN(ta) && Number.isNaN(tb)) return 0;
2631
+ if (Number.isNaN(ta)) return 1;
2632
+ if (Number.isNaN(tb)) return -1;
2633
+ return ta - tb;
2634
+ }
2635
+ function renderGroundedLine(m, suffix = "") {
2636
+ const date = m.created_at ? new Date(m.created_at).toISOString().slice(0, 10) : "";
2637
+ const stamp = date ? `[${date}] ` : "";
2638
+ const summary = (m.summary || "").trim();
2639
+ const content = (m.content || "").trim();
2640
+ let body;
2641
+ if (!content || content === summary) {
2642
+ body = summary || content;
2643
+ } else if (content.length <= GROUNDED_CONTENT_MAX) {
2644
+ body = summary ? `${summary} \u2014 ${content}` : content;
2645
+ } else {
2646
+ const slice = content.slice(0, GROUNDED_CONTENT_MAX);
2647
+ body = summary ? `${summary} \u2014 ${slice}\u2026` : `${slice}\u2026`;
2648
+ }
2649
+ return `- ${stamp}${body}${suffix}`;
2650
+ }
2651
+ var GROUNDED_CONTENT_MAX;
2652
+ var init_memory_grounding = __esm({
2653
+ "packages/shared/src/core/memory-grounding.ts"() {
2654
+ "use strict";
2655
+ GROUNDED_CONTENT_MAX = 600;
2656
+ }
2657
+ });
2658
+
1843
2659
  // packages/shared/src/utils/format.ts
1844
2660
  function timeAgo(dateStr) {
1845
2661
  const ms = Date.now() - new Date(dateStr).getTime();
@@ -1890,6 +2706,338 @@ var init_utils = __esm({
1890
2706
  }
1891
2707
  });
1892
2708
 
2709
+ // packages/tokenization/src/content-hash.ts
2710
+ function stableStringify(value) {
2711
+ if (value === null) return "null";
2712
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2713
+ return JSON.stringify(value);
2714
+ }
2715
+ if (Array.isArray(value)) {
2716
+ return "[" + value.map(stableStringify).join(",") + "]";
2717
+ }
2718
+ if (typeof value === "object") {
2719
+ const obj = value;
2720
+ const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
2721
+ const pairs = keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k]));
2722
+ return "{" + pairs.join(",") + "}";
2723
+ }
2724
+ throw new Error(`stableStringify: unsupported value of type ${typeof value}`);
2725
+ }
2726
+ function normaliseString(s) {
2727
+ return s.normalize("NFC").trim();
2728
+ }
2729
+ function normaliseNullable(s) {
2730
+ return s === null ? null : normaliseString(s);
2731
+ }
2732
+ function normaliseTags(tags) {
2733
+ const trimmed = tags.map(normaliseString).filter((t) => t.length > 0);
2734
+ return Array.from(new Set(trimmed)).sort();
2735
+ }
2736
+ function canonicaliseMemory(input) {
2737
+ const normalised = {
2738
+ algorithm: HASH_ALGORITHM,
2739
+ content: normaliseString(input.content),
2740
+ created_at: input.created_at,
2741
+ memory_type: input.memory_type,
2742
+ owner_wallet: normaliseNullable(input.owner_wallet),
2743
+ related_user: normaliseNullable(input.related_user),
2744
+ related_wallet: normaliseNullable(input.related_wallet),
2745
+ source: normaliseNullable(input.source),
2746
+ tags: normaliseTags(input.tags)
2747
+ };
2748
+ return stableStringify(normalised);
2749
+ }
2750
+ function memoryContentHash(input) {
2751
+ return (0, import_node_crypto.createHash)("sha256").update(canonicaliseMemory(input), "utf8").digest("hex");
2752
+ }
2753
+ var import_node_crypto, HASH_ALGORITHM;
2754
+ var init_content_hash = __esm({
2755
+ "packages/tokenization/src/content-hash.ts"() {
2756
+ "use strict";
2757
+ import_node_crypto = require("node:crypto");
2758
+ HASH_ALGORITHM = "memory-hash-v1";
2759
+ }
2760
+ });
2761
+
2762
+ // packages/tokenization/src/pack-merkle.ts
2763
+ var LEAF_PREFIX, INNER_PREFIX;
2764
+ var init_pack_merkle = __esm({
2765
+ "packages/tokenization/src/pack-merkle.ts"() {
2766
+ "use strict";
2767
+ LEAF_PREFIX = Buffer.from([0]);
2768
+ INNER_PREFIX = Buffer.from([1]);
2769
+ }
2770
+ });
2771
+
2772
+ // packages/tokenization/src/mint-client.ts
2773
+ var init_mint_client = __esm({
2774
+ "packages/tokenization/src/mint-client.ts"() {
2775
+ "use strict";
2776
+ }
2777
+ });
2778
+
2779
+ // packages/tokenization/src/tokenize-memory.ts
2780
+ var init_tokenize_memory = __esm({
2781
+ "packages/tokenization/src/tokenize-memory.ts"() {
2782
+ "use strict";
2783
+ init_content_hash();
2784
+ }
2785
+ });
2786
+
2787
+ // packages/tokenization/src/tokenize-batch.ts
2788
+ var init_tokenize_batch = __esm({
2789
+ "packages/tokenization/src/tokenize-batch.ts"() {
2790
+ "use strict";
2791
+ init_content_hash();
2792
+ init_pack_merkle();
2793
+ }
2794
+ });
2795
+
2796
+ // packages/tokenization/src/tokenize-pack.ts
2797
+ var init_tokenize_pack = __esm({
2798
+ "packages/tokenization/src/tokenize-pack.ts"() {
2799
+ "use strict";
2800
+ init_pack_merkle();
2801
+ }
2802
+ });
2803
+
2804
+ // packages/tokenization/src/verify.ts
2805
+ var init_verify = __esm({
2806
+ "packages/tokenization/src/verify.ts"() {
2807
+ "use strict";
2808
+ init_pack_merkle();
2809
+ }
2810
+ });
2811
+
2812
+ // packages/tokenization/src/index.ts
2813
+ var init_src = __esm({
2814
+ "packages/tokenization/src/index.ts"() {
2815
+ "use strict";
2816
+ init_content_hash();
2817
+ init_pack_merkle();
2818
+ init_mint_client();
2819
+ init_tokenize_memory();
2820
+ init_tokenize_batch();
2821
+ init_tokenize_pack();
2822
+ init_verify();
2823
+ }
2824
+ });
2825
+
2826
+ // packages/shared/src/wiki-packs.ts
2827
+ function getPackManifest(id) {
2828
+ return ALL_PACK_MANIFESTS.find((p) => p.id === id);
2829
+ }
2830
+ function matchesKeyword(text, phrase) {
2831
+ if (!phrase) return false;
2832
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2833
+ const re = new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i");
2834
+ return re.test(text);
2835
+ }
2836
+ function autoCategorizeTags(opts) {
2837
+ const haystack = [
2838
+ opts.content || "",
2839
+ opts.summary || "",
2840
+ ...opts.existingTags || []
2841
+ ].join(" ");
2842
+ const matched = new Set(opts.existingTags || []);
2843
+ const ids = /* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...opts.installedPackIds]);
2844
+ for (const packId of ids) {
2845
+ const pack = getPackManifest(packId);
2846
+ if (!pack) continue;
2847
+ for (const rule of pack.rules) {
2848
+ if (matched.has(rule.topicId)) continue;
2849
+ const hasMatch = rule.keywords.some((kw) => matchesKeyword(haystack, kw));
2850
+ if (!hasMatch) continue;
2851
+ const isVetoed = (rule.excludeKeywords || []).some((kw) => matchesKeyword(haystack, kw));
2852
+ if (isVetoed) continue;
2853
+ matched.add(rule.topicId);
2854
+ }
2855
+ }
2856
+ return Array.from(matched);
2857
+ }
2858
+ function topicEmbedSources(installedPackIds) {
2859
+ const out = [];
2860
+ const seen = /* @__PURE__ */ new Set();
2861
+ const ids = /* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...installedPackIds]);
2862
+ for (const packId of ids) {
2863
+ const pack = getPackManifest(packId);
2864
+ if (!pack) continue;
2865
+ for (const t of pack.topics) {
2866
+ if (seen.has(t.id)) continue;
2867
+ seen.add(t.id);
2868
+ const sectionTitles = (t.sectionTemplates || []).map((s) => s.title).join(". ");
2869
+ const text = [t.name, t.summary, sectionTitles].filter(Boolean).join(" \u2014 ");
2870
+ out.push({ topicId: t.id, packId, text });
2871
+ }
2872
+ }
2873
+ return out;
2874
+ }
2875
+ function cosineSimilarity(a, b) {
2876
+ if (a.length !== b.length || a.length === 0) return 0;
2877
+ let dot = 0;
2878
+ let normA = 0;
2879
+ let normB = 0;
2880
+ for (let i = 0; i < a.length; i++) {
2881
+ dot += a[i] * b[i];
2882
+ normA += a[i] * a[i];
2883
+ normB += b[i] * b[i];
2884
+ }
2885
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
2886
+ return denom === 0 ? 0 : dot / denom;
2887
+ }
2888
+ function semanticTagMatches(memoryEmbedding, topicEmbeddings, threshold = EMBEDDING_TAG_THRESHOLD) {
2889
+ const matched = [];
2890
+ for (const { topicId, embedding } of topicEmbeddings) {
2891
+ const sim = cosineSimilarity(memoryEmbedding, embedding);
2892
+ if (sim >= threshold) matched.push(topicId);
2893
+ }
2894
+ return matched;
2895
+ }
2896
+ var WORKSPACE_PACK, COMPLIANCE_PACK, SALES_PACK, ALL_PACK_MANIFESTS, DEFAULT_PACK_ID, EMBEDDING_TAG_THRESHOLD;
2897
+ var init_wiki_packs = __esm({
2898
+ "packages/shared/src/wiki-packs.ts"() {
2899
+ "use strict";
2900
+ WORKSPACE_PACK = {
2901
+ id: "workspace",
2902
+ name: "Workspace Essentials",
2903
+ vendor: "Clude",
2904
+ vertical: "General",
2905
+ version: "1.0.0",
2906
+ installedByDefault: true,
2907
+ description: "The default knowledge-worker pack. Roadmaps, decisions, customer research, hiring, team process \u2014 everything you'd want a colleague to be able to skim.",
2908
+ topics: [
2909
+ { id: "q3-roadmap", name: "Q3 Roadmap", cluster: "architecture", color: "#2244FF", summary: "What ships this quarter, what got cut, and the calls behind those decisions." },
2910
+ { id: "auth-migration", name: "Auth Migration", cluster: "architecture", color: "#2244FF", summary: "Moving from session cookies to JWT \u2014 incidents, fixes, and the rollback plan." },
2911
+ { id: "customer-research", name: "Customer Research", cluster: "research", color: "#F59E0B", summary: "Patterns surfaced across customer calls." },
2912
+ { id: "pricing-model", name: "Pricing Model", cluster: "product", color: "#10B981", summary: "Per-token vs per-seat \u2014 the active disagreement." },
2913
+ { id: "demo-day-prep", name: "Demo Day Prep", cluster: "product", color: "#10B981", summary: "What works in the live demo, what breaks." },
2914
+ { id: "hiring", name: "Hiring Pipeline", cluster: "product", color: "#10B981", summary: "Candidates in flight and interview-signal patterns." },
2915
+ { id: "team-process", name: "Team Process", cluster: "self", color: "#8B5CF6", summary: "What's working in how the team operates and what isn't." },
2916
+ { id: "design-decisions", name: "Design Decisions", cluster: "research", color: "#F59E0B", summary: "API shapes, naming choices, and the reasons behind them." }
2917
+ ],
2918
+ rules: [
2919
+ { topicId: "q3-roadmap", keywords: ["roadmap", "q3", "quarter", "sprint plan", "priority"] },
2920
+ { topicId: "auth-migration", keywords: ["auth", "jwt", "session cookie", "oauth", "sso", "token migration"] },
2921
+ { topicId: "customer-research", keywords: ["customer call", "interview", "feedback", "user research", "persona"] },
2922
+ { topicId: "pricing-model", keywords: ["pricing", "per-token", "per-seat", "subscription", "billing tier"] },
2923
+ { topicId: "demo-day-prep", keywords: ["demo", "rehearsal", "investor", "pitch"] },
2924
+ {
2925
+ topicId: "hiring",
2926
+ keywords: ["candidate", "interview", "hire", "phone screen", "onsite", "offer"],
2927
+ // Suppress when "hire" is in the wrong context — got fired, rejected an offer,
2928
+ // hire-purchase, etc. The classic substring-match false positive.
2929
+ excludeKeywords: ["fired me", "i was fired", "hire purchase", "rejected my offer", "rejected the offer"]
2930
+ },
2931
+ { topicId: "team-process", keywords: ["standup", "retro", "sprint", "1:1", "process", "team"] },
2932
+ { topicId: "design-decisions", keywords: ["api design", "schema", "naming", "rfc", "design doc"] }
2933
+ ]
2934
+ };
2935
+ COMPLIANCE_PACK = {
2936
+ id: "compliance",
2937
+ name: "Clude Compliance",
2938
+ vendor: "Clude",
2939
+ vertical: "Compliance",
2940
+ version: "1.0.0",
2941
+ description: "Auto-organises every compliance-relevant decision your agents make into an audit-ready wiki. Audit logs, evidence collection, regulator asks, policy decisions \u2014 each with a cryptographic receipt anchored to Solana.",
2942
+ topics: [
2943
+ {
2944
+ id: "audit-logs",
2945
+ name: "Audit Logs",
2946
+ cluster: "architecture",
2947
+ color: "#0EA5E9",
2948
+ summary: "Every flagged agent action with attribution, timestamp, and a Solana-anchored hash a regulator can verify without trusting us.",
2949
+ sectionTemplates: [
2950
+ { id: "recent-events", title: "Recent flagged events", kind: "overview" },
2951
+ { id: "anomalies", title: "Anomalies & escalations", kind: "concern" },
2952
+ { id: "retention", title: "Retention policy", kind: "decision" }
2953
+ ]
2954
+ },
2955
+ {
2956
+ id: "evidence",
2957
+ name: "Evidence Collection",
2958
+ cluster: "product",
2959
+ color: "#06B6D4",
2960
+ summary: "Documentation gathered for SOC2, HIPAA, and ISO 27001 \u2014 what we have, what we still need, and what auditors have already accepted.",
2961
+ sectionTemplates: [
2962
+ { id: "have", title: "What we have", kind: "highlight" },
2963
+ { id: "gaps", title: "Gaps to fill", kind: "action" },
2964
+ { id: "accepted", title: "What auditors accepted", kind: "decision" }
2965
+ ]
2966
+ },
2967
+ {
2968
+ id: "regulator-asks",
2969
+ name: "Regulator Asks",
2970
+ cluster: "research",
2971
+ color: "#F59E0B",
2972
+ summary: "Specific requests from regulators \u2014 owner, deadline, response status. Nothing falls through the cracks.",
2973
+ sectionTemplates: [
2974
+ { id: "open", title: "Open requests", kind: "action" },
2975
+ { id: "responded", title: "Responded", kind: "decision" },
2976
+ { id: "patterns", title: "Recurring patterns", kind: "highlight" }
2977
+ ]
2978
+ },
2979
+ {
2980
+ id: "policy-decisions",
2981
+ name: "Policy Decisions",
2982
+ cluster: "self",
2983
+ color: "#8B5CF6",
2984
+ summary: "Calls made about what the agents are allowed to do \u2014 data handling, escalation triggers, redaction rules \u2014 each anchored on-chain.",
2985
+ sectionTemplates: [
2986
+ { id: "standing", title: "Standing policy", kind: "decision" },
2987
+ { id: "changes", title: "Recent changes", kind: "overview" },
2988
+ { id: "open", title: "Open questions", kind: "question" }
2989
+ ]
2990
+ },
2991
+ {
2992
+ id: "soc2-status",
2993
+ name: "SOC2 Status",
2994
+ cluster: "product",
2995
+ color: "#10B981",
2996
+ summary: "Where we are in the SOC2 audit cycle, what controls are passing, what still needs evidence.",
2997
+ sectionTemplates: [
2998
+ { id: "controls", title: "Control status", kind: "overview" },
2999
+ { id: "next", title: "Up next", kind: "action" }
3000
+ ]
3001
+ }
3002
+ ],
3003
+ rules: [
3004
+ { topicId: "audit-logs", keywords: ["audit", "audit log", "trail", "attribution", "flagged"] },
3005
+ { topicId: "evidence", keywords: ["evidence", "soc2", "hipaa", "iso 27001", "compliance evidence", "control"] },
3006
+ { topicId: "regulator-asks", keywords: ["regulator", "subpoena", "compliance request", "data request"] },
3007
+ { topicId: "policy-decisions", keywords: ["policy", "redaction", "pii", "escalation rule", "data handling"] },
3008
+ { topicId: "soc2-status", keywords: ["soc2", "audit cycle", "control test"] }
3009
+ ]
3010
+ };
3011
+ SALES_PACK = {
3012
+ id: "sales",
3013
+ name: "Sales Intelligence",
3014
+ vendor: "Clude",
3015
+ vertical: "Sales",
3016
+ version: "1.0.0",
3017
+ description: "Auto-organises pipeline conversations, deal blockers, objection patterns, and post-call follow-ups. Built for AEs who hate CRM data entry.",
3018
+ topics: [
3019
+ { id: "pipeline", name: "Pipeline", cluster: "product", color: "#10B981", summary: "Active deals, stages, blockers." },
3020
+ { id: "objections", name: "Objections", cluster: "research", color: "#F59E0B", summary: "Patterns across discovery calls." },
3021
+ { id: "follow-ups", name: "Follow-ups", cluster: "product", color: "#10B981", summary: "What you committed to send and to whom." },
3022
+ { id: "champions", name: "Champions", cluster: "self", color: "#8B5CF6", summary: "Who is championing your product internally at each account." }
3023
+ ],
3024
+ rules: [
3025
+ { topicId: "pipeline", keywords: ["deal", "opportunity", "stage", "close date", "mql", "sql"] },
3026
+ { topicId: "objections", keywords: ["objection", "concern raised", "pushback", "competitor mentioned"] },
3027
+ { topicId: "follow-ups", keywords: ["follow up", "send", "next step", "committed to"] },
3028
+ { topicId: "champions", keywords: ["champion", "sponsor", "advocate", "introduced me to"] }
3029
+ ]
3030
+ };
3031
+ ALL_PACK_MANIFESTS = [
3032
+ WORKSPACE_PACK,
3033
+ COMPLIANCE_PACK,
3034
+ SALES_PACK
3035
+ ];
3036
+ DEFAULT_PACK_ID = "workspace";
3037
+ EMBEDDING_TAG_THRESHOLD = 0.78;
3038
+ }
3039
+ });
3040
+
1893
3041
  // packages/brain/src/experimental/config.ts
1894
3042
  function envBool(key, fallback) {
1895
3043
  const val = process.env[key];
@@ -1975,26 +3123,509 @@ var init_bm25_search = __esm({
1975
3123
  }
1976
3124
  });
1977
3125
 
1978
- // packages/shared/src/core/encryption.ts
1979
- var encryption_exports = {};
1980
- __export(encryption_exports, {
1981
- configureEncryption: () => configureEncryption,
1982
- decryptContent: () => decryptContent,
1983
- decryptMemoryBatch: () => decryptMemoryBatch,
1984
- encryptContent: () => encryptContent,
1985
- getEncryptionPubkey: () => getEncryptionPubkey,
1986
- isEncryptionEnabled: () => isEncryptionEnabled
1987
- });
1988
- async function configureEncryption(solanaSecretKey) {
1989
- if (solanaSecretKey.length !== 64) {
1990
- throw new Error("Encryption requires a 64-byte Ed25519 secret key");
3126
+ // packages/brain/src/memory/content-tokens.ts
3127
+ async function setContentTokens(db, memoryId, plaintext) {
3128
+ try {
3129
+ const { error } = await db.rpc("set_memory_content_tokens", {
3130
+ p_memory_id: memoryId,
3131
+ p_text: plaintext
3132
+ });
3133
+ if (error) {
3134
+ log8.warn({ memoryId, error: error.message }, "set_memory_content_tokens failed (keyword recall degraded)");
3135
+ }
3136
+ } catch (err) {
3137
+ log8.warn({ memoryId, err }, "set_memory_content_tokens threw (keyword recall degraded)");
1991
3138
  }
1992
- const seed = solanaSecretKey.slice(0, 32);
1993
- const derived = await hkdfAsync("sha256", seed, HKDF_SALT, HKDF_INFO, 32);
1994
- encryptionKey = new Uint8Array(derived);
1995
- const keypair = import_tweetnacl2.default.sign.keyPair.fromSecretKey(solanaSecretKey);
3139
+ }
3140
+ var log8;
3141
+ var init_content_tokens = __esm({
3142
+ "packages/brain/src/memory/content-tokens.ts"() {
3143
+ "use strict";
3144
+ init_logger();
3145
+ log8 = createChildLogger("content-tokens");
3146
+ }
3147
+ });
3148
+
3149
+ // packages/brain/src/memory/reconcile-router.ts
3150
+ function shortReason(x) {
3151
+ return typeof x === "string" ? x.slice(0, 200) : "";
3152
+ }
3153
+ function parseRouterReply(raw, candidateIds, model) {
3154
+ const fail = (reason) => ({ op: "add", targetId: null, reason, model });
3155
+ let obj;
3156
+ try {
3157
+ obj = JSON.parse(raw);
3158
+ } catch {
3159
+ const m = raw.match(/\{[\s\S]*\}/);
3160
+ if (!m) return fail("router_parse_error");
3161
+ try {
3162
+ obj = JSON.parse(m[0]);
3163
+ } catch {
3164
+ return fail("router_parse_error");
3165
+ }
3166
+ }
3167
+ if (obj === null || typeof obj !== "object" || Array.isArray(obj)) return fail("router_invalid_shape");
3168
+ const rec = obj;
3169
+ const op = rec.op;
3170
+ if (op !== "add" && op !== "update" && op !== "noop") return fail("router_invalid_op");
3171
+ if (op === "add") return { op: "add", targetId: null, reason: shortReason(rec.reason), model };
3172
+ const rawTarget = rec.targetId ?? rec.target_id;
3173
+ const targetId = typeof rawTarget === "number" ? rawTarget : Number(rawTarget);
3174
+ if (!Number.isInteger(targetId) || !candidateIds.has(targetId)) return fail("router_invalid_target");
3175
+ return { op, targetId, reason: shortReason(rec.reason), model };
3176
+ }
3177
+ function buildPayload(newFact, candidates) {
3178
+ const lines = candidates.map(
3179
+ (c) => `[id ${c.id}] date: ${c.eventDate ?? "unknown"} \u2014 ${c.summary}`
3180
+ );
3181
+ return `NEW fact:
3182
+ date: ${newFact.eventDate ?? "unknown"} \u2014 ${newFact.summary}
3183
+
3184
+ Candidate facts:
3185
+ ${lines.join("\n")}`;
3186
+ }
3187
+ function isRouterConfigured() {
3188
+ return config.memory.reconcileRouter && !!config.memory.reconcileModel && isOpenRouterEnabled();
3189
+ }
3190
+ function utcDay() {
3191
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3192
+ }
3193
+ function rollDay() {
3194
+ const d = utcDay();
3195
+ if (d !== budgetDay) {
3196
+ budgetDay = d;
3197
+ budgetByOwner.clear();
3198
+ }
3199
+ }
3200
+ function budgetKey(owner) {
3201
+ return owner ?? "__BOT_OWN__";
3202
+ }
3203
+ function routerBudgetAvailable(owner) {
3204
+ rollDay();
3205
+ return (budgetByOwner.get(budgetKey(owner)) ?? 0) < config.memory.reconcileBudget;
3206
+ }
3207
+ function noteRouterCall(owner) {
3208
+ rollDay();
3209
+ const k = budgetKey(owner);
3210
+ budgetByOwner.set(k, (budgetByOwner.get(k) ?? 0) + 1);
3211
+ }
3212
+ async function routeReconcile(newFact, candidates) {
3213
+ const model = config.memory.reconcileModel;
3214
+ const candidateIds = new Set(candidates.map((c) => c.id));
3215
+ try {
3216
+ const raw = await generateOpenRouterResponse({
3217
+ systemPrompt: ROUTER_SYSTEM,
3218
+ messages: [{ role: "user", content: buildPayload(newFact, candidates) }],
3219
+ model,
3220
+ // explicit — never cognitiveFunction (which overrides model with the fast llama slot)
3221
+ maxTokens: 150,
3222
+ temperature: 0
3223
+ });
3224
+ return parseRouterReply(raw, candidateIds, model);
3225
+ } catch (err) {
3226
+ log9.warn({ err: err?.message }, "reconcile router call failed \u2014 defaulting to add");
3227
+ return { op: "add", targetId: null, reason: "router_error", model };
3228
+ }
3229
+ }
3230
+ var log9, ROUTER_SYSTEM, budgetDay, budgetByOwner;
3231
+ var init_reconcile_router = __esm({
3232
+ "packages/brain/src/memory/reconcile-router.ts"() {
3233
+ "use strict";
3234
+ init_config();
3235
+ init_logger();
3236
+ init_openrouter_client();
3237
+ log9 = createChildLogger("reconcile-router");
3238
+ ROUTER_SYSTEM = 'You are a memory reconciliation classifier. Given a NEW fact and a list of EXISTING candidate facts that all belong to the SAME user, decide the relationship of the NEW fact to the candidates. Respond with ONLY a single JSON object, no prose, no markdown.\nFormat: {"op":"add"|"update"|"noop","targetId":<candidate id or null>,"reason":"<short>"}\n- "noop": NEW states the SAME thing as candidate <targetId> (a duplicate). Set targetId.\n- "update": NEW corrects or SUPERSEDES candidate <targetId> \u2014 same underlying fact, changed value or a newer date. Set targetId to that candidate id.\n- "add": NEW is distinct from every candidate. targetId must be null.\nWhen unsure, choose "add". NEVER invent a targetId that is not in the candidate list.';
3239
+ budgetDay = "";
3240
+ budgetByOwner = /* @__PURE__ */ new Map();
3241
+ }
3242
+ });
3243
+
3244
+ // packages/brain/src/memory/reconcile-shadow.ts
3245
+ function logReconcileDegradeOnce(err, memoryId) {
3246
+ if (reconcileDegradeLogged) return;
3247
+ reconcileDegradeLogged = true;
3248
+ const e = err;
3249
+ log10.warn(
3250
+ { code: e?.code, message: e?.message, memoryId },
3251
+ "Reconcile shadow degraded (migration 046 unapplied?) \u2014 skipping; write unaffected"
3252
+ );
3253
+ }
3254
+ function gateVersion() {
3255
+ const { reconcileFloor: f, reconcileLo: lo, reconcileHi: hi } = config.memory;
3256
+ return `c1-shadow-v1:floor=${f}:lo=${lo}:hi=${hi}`;
3257
+ }
3258
+ async function insertShadowRow(row) {
3259
+ try {
3260
+ const { error } = await getDb().from("memory_reconciliation_log").insert(row);
3261
+ if (error) logReconcileDegradeOnce(error, row.memory_id);
3262
+ } catch (err) {
3263
+ logReconcileDegradeOnce(err, row.memory_id);
3264
+ }
3265
+ }
3266
+ async function runReconcileShadow(memoryId, scope) {
3267
+ if (process.env.BENCH_MODE === "true") return;
3268
+ const ownerWalletForRow = scope === SCOPE_BOT_OWN ? null : scope;
3269
+ const gv = gateVersion();
3270
+ const db = getDb();
3271
+ const skip = (reason) => insertShadowRow({
3272
+ memory_id: memoryId,
3273
+ owner_wallet: ownerWalletForRow,
3274
+ mode: "shadow",
3275
+ proposed_op: "skip",
3276
+ target_memory_id: null,
3277
+ max_cosine: null,
3278
+ band: "none",
3279
+ router_used: false,
3280
+ fact_key: null,
3281
+ reason,
3282
+ gate_version: gv
3283
+ });
3284
+ const add = (reason, maxCosine2, band2) => insertShadowRow({
3285
+ memory_id: memoryId,
3286
+ owner_wallet: ownerWalletForRow,
3287
+ mode: "shadow",
3288
+ proposed_op: "add",
3289
+ target_memory_id: null,
3290
+ max_cosine: maxCosine2,
3291
+ band: band2,
3292
+ router_used: false,
3293
+ fact_key: null,
3294
+ reason,
3295
+ gate_version: gv
3296
+ });
3297
+ let embedding;
3298
+ let newSummary = "";
3299
+ let newEventDate = null;
3300
+ try {
3301
+ const { data, error } = await db.from("memories").select("embedding, summary, event_date, created_at").eq("id", memoryId).maybeSingle();
3302
+ if (error) {
3303
+ logReconcileDegradeOnce(error, memoryId);
3304
+ return;
3305
+ }
3306
+ const row = data;
3307
+ embedding = row?.embedding ?? null;
3308
+ newSummary = row?.summary ?? "";
3309
+ newEventDate = row?.event_date ?? row?.created_at ?? null;
3310
+ } catch (err) {
3311
+ logReconcileDegradeOnce(err, memoryId);
3312
+ return;
3313
+ }
3314
+ if (!embedding) {
3315
+ await skip("no_embedding");
3316
+ return;
3317
+ }
3318
+ const queryEmbedding = typeof embedding === "string" ? embedding : JSON.stringify(embedding);
3319
+ let candidates;
3320
+ try {
3321
+ const { data, error } = await db.rpc("match_memories_temporal", {
3322
+ query_embedding: queryEmbedding,
3323
+ match_threshold: config.memory.reconcileFloor,
3324
+ match_count: K_CANDIDATES,
3325
+ start_date: null,
3326
+ end_date: null,
3327
+ filter_types: null,
3328
+ filter_user: null,
3329
+ min_decay: 0,
3330
+ filter_owner: scope,
3331
+ filter_tags: null
3332
+ });
3333
+ if (error) {
3334
+ logReconcileDegradeOnce(error, memoryId);
3335
+ return;
3336
+ }
3337
+ candidates = data ?? [];
3338
+ } catch (err) {
3339
+ logReconcileDegradeOnce(err, memoryId);
3340
+ return;
3341
+ }
3342
+ candidates = candidates.filter((c) => c.id !== memoryId);
3343
+ if (candidates.length === 0) {
3344
+ await add("no_candidate", null, "none");
3345
+ return;
3346
+ }
3347
+ const ids = candidates.map((c) => c.id);
3348
+ let validIds;
3349
+ const hydratedById = /* @__PURE__ */ new Map();
3350
+ try {
3351
+ const base = db.from("memories").select("id, owner_wallet, invalid_at, summary, event_date, created_at").in("id", ids);
3352
+ const scoped = scope === SCOPE_BOT_OWN ? base.is("owner_wallet", null) : base.eq("owner_wallet", scope);
3353
+ const { data, error } = await scoped;
3354
+ if (error) {
3355
+ logReconcileDegradeOnce(error, memoryId);
3356
+ return;
3357
+ }
3358
+ const rows = data ?? [];
3359
+ const valid = rows.filter((r) => r.invalid_at == null).filter((r) => scope === SCOPE_BOT_OWN ? r.owner_wallet == null : r.owner_wallet === scope);
3360
+ validIds = new Set(valid.map((r) => r.id));
3361
+ for (const r of valid) hydratedById.set(r.id, { summary: r.summary ?? "", eventDate: r.event_date ?? r.created_at ?? null });
3362
+ } catch (err) {
3363
+ logReconcileDegradeOnce(err, memoryId);
3364
+ return;
3365
+ }
3366
+ const surviving = candidates.filter((c) => validIds.has(c.id));
3367
+ if (surviving.length === 0) {
3368
+ await add("no_valid_candidate", null, "none");
3369
+ return;
3370
+ }
3371
+ const maxCosine = surviving[0].similarity;
3372
+ const { reconcileLo: LO, reconcileHi: HI } = config.memory;
3373
+ if (maxCosine < LO) {
3374
+ await add("below_lo", maxCosine, "lo");
3375
+ return;
3376
+ }
3377
+ const band = maxCosine >= HI ? "hi" : "mid";
3378
+ const top = surviving[0];
3379
+ if (isRouterConfigured() && routerBudgetAvailable(ownerWalletForRow)) {
3380
+ noteRouterCall(ownerWalletForRow);
3381
+ const routerCandidates = surviving.map((c) => ({
3382
+ id: c.id,
3383
+ summary: hydratedById.get(c.id)?.summary ?? "",
3384
+ eventDate: hydratedById.get(c.id)?.eventDate ?? null
3385
+ }));
3386
+ const decision = await routeReconcile({ summary: newSummary, eventDate: newEventDate }, routerCandidates);
3387
+ await insertShadowRow({
3388
+ memory_id: memoryId,
3389
+ owner_wallet: ownerWalletForRow,
3390
+ mode: "shadow",
3391
+ proposed_op: decision.op,
3392
+ target_memory_id: decision.op === "add" ? null : decision.targetId,
3393
+ max_cosine: maxCosine,
3394
+ band,
3395
+ router_used: true,
3396
+ router_model: decision.model,
3397
+ fact_key: null,
3398
+ reason: decision.reason || "router",
3399
+ gate_version: gv
3400
+ });
3401
+ return;
3402
+ }
3403
+ await insertShadowRow({
3404
+ memory_id: memoryId,
3405
+ owner_wallet: ownerWalletForRow,
3406
+ mode: "shadow",
3407
+ proposed_op: "needs_router",
3408
+ target_memory_id: top.id,
3409
+ max_cosine: maxCosine,
3410
+ band,
3411
+ router_used: false,
3412
+ fact_key: null,
3413
+ reason: "similar_prior_fact",
3414
+ gate_version: gv
3415
+ });
3416
+ }
3417
+ var log10, SCOPE_BOT_OWN, K_CANDIDATES, reconcileDegradeLogged;
3418
+ var init_reconcile_shadow = __esm({
3419
+ "packages/brain/src/memory/reconcile-shadow.ts"() {
3420
+ "use strict";
3421
+ init_database();
3422
+ init_config();
3423
+ init_logger();
3424
+ init_reconcile_router();
3425
+ log10 = createChildLogger("reconcile-shadow");
3426
+ SCOPE_BOT_OWN = "__BOT_OWN__";
3427
+ K_CANDIDATES = 6;
3428
+ reconcileDegradeLogged = false;
3429
+ }
3430
+ });
3431
+
3432
+ // packages/shared/src/core/owner-key-constants.ts
3433
+ var HKDF_SALT, HKDF_INFO;
3434
+ var init_owner_key_constants = __esm({
3435
+ "packages/shared/src/core/owner-key-constants.ts"() {
3436
+ "use strict";
3437
+ HKDF_SALT = "clude-cortex-v1";
3438
+ HKDF_INFO = "memory-encryption-x25519-v2";
3439
+ }
3440
+ });
3441
+
3442
+ // packages/shared/src/core/memory-envelope.ts
3443
+ function generateDek() {
3444
+ return import_tweetnacl2.default.randomBytes(import_tweetnacl2.default.secretbox.keyLength);
3445
+ }
3446
+ function encryptField(plaintext, dek) {
3447
+ const nonce = import_tweetnacl2.default.randomBytes(NONCE_LENGTH);
3448
+ const message = new TextEncoder().encode(plaintext);
3449
+ const ct = import_tweetnacl2.default.secretbox(message, nonce, dek);
3450
+ const combined = new Uint8Array(NONCE_LENGTH + ct.length);
3451
+ combined.set(nonce, 0);
3452
+ combined.set(ct, NONCE_LENGTH);
3453
+ return Buffer.from(combined).toString("base64");
3454
+ }
3455
+ function decryptField(encrypted, dek) {
3456
+ try {
3457
+ const combined = Buffer.from(encrypted, "base64");
3458
+ if (combined.length < NONCE_LENGTH + 1) return null;
3459
+ const nonce = combined.subarray(0, NONCE_LENGTH);
3460
+ const ct = combined.subarray(NONCE_LENGTH);
3461
+ const pt = import_tweetnacl2.default.secretbox.open(ct, nonce, dek);
3462
+ if (!pt) return null;
3463
+ return new TextDecoder().decode(pt);
3464
+ } catch {
3465
+ return null;
3466
+ }
3467
+ }
3468
+ function wrapDek(dek, recipientPubkey) {
3469
+ const ephemeral = import_tweetnacl2.default.box.keyPair();
3470
+ const nonce = import_tweetnacl2.default.randomBytes(BOX_NONCE_LENGTH);
3471
+ const boxed = import_tweetnacl2.default.box(dek, nonce, recipientPubkey, ephemeral.secretKey);
3472
+ const combined = new Uint8Array(BOX_NONCE_LENGTH + boxed.length);
3473
+ combined.set(nonce, 0);
3474
+ combined.set(boxed, BOX_NONCE_LENGTH);
3475
+ return {
3476
+ wrapped: Buffer.from(combined).toString("base64"),
3477
+ wrapPubkey: Buffer.from(ephemeral.publicKey).toString("base64")
3478
+ };
3479
+ }
3480
+ function unwrapDek(wrapped, wrapPubkey, recipientSecretKey) {
3481
+ try {
3482
+ const combined = Buffer.from(wrapped, "base64");
3483
+ if (combined.length < BOX_NONCE_LENGTH + 1) return null;
3484
+ const nonce = combined.subarray(0, BOX_NONCE_LENGTH);
3485
+ const boxed = combined.subarray(BOX_NONCE_LENGTH);
3486
+ const ephemeralPub = Buffer.from(wrapPubkey, "base64");
3487
+ if (ephemeralPub.length !== import_tweetnacl2.default.box.publicKeyLength) return null;
3488
+ const out = import_tweetnacl2.default.box.open(boxed, nonce, ephemeralPub, recipientSecretKey);
3489
+ return out ?? null;
3490
+ } catch {
3491
+ return null;
3492
+ }
3493
+ }
3494
+ async function deriveOwnerEncryptionKeypair(signature) {
3495
+ const seed = await hkdfAsync("sha256", signature, HKDF_SALT, HKDF_INFO, 32);
3496
+ return import_tweetnacl2.default.box.keyPair.fromSecretKey(new Uint8Array(seed));
3497
+ }
3498
+ var import_tweetnacl2, import_crypto, import_util, hkdfAsync, NONCE_LENGTH, BOX_NONCE_LENGTH;
3499
+ var init_memory_envelope = __esm({
3500
+ "packages/shared/src/core/memory-envelope.ts"() {
3501
+ "use strict";
3502
+ import_tweetnacl2 = __toESM(require("tweetnacl"));
3503
+ import_crypto = require("crypto");
3504
+ import_util = require("util");
3505
+ init_owner_key_constants();
3506
+ hkdfAsync = (0, import_util.promisify)(import_crypto.hkdf);
3507
+ NONCE_LENGTH = import_tweetnacl2.default.secretbox.nonceLength;
3508
+ BOX_NONCE_LENGTH = import_tweetnacl2.default.box.nonceLength;
3509
+ }
3510
+ });
3511
+
3512
+ // packages/shared/src/core/encryption-keys.ts
3513
+ function loadProviderKeypair() {
3514
+ if (providerKeypair) return providerKeypair;
3515
+ const b64 = process.env.PMP_PROVIDER_ENC_SECRET;
3516
+ if (!b64) throw new Error("PMP_PROVIDER_ENC_SECRET is not set");
3517
+ const secret = Buffer.from(b64, "base64");
3518
+ if (secret.length !== import_tweetnacl3.default.box.secretKeyLength) {
3519
+ throw new Error(`PMP_PROVIDER_ENC_SECRET must decode to 32 bytes (got ${secret.length})`);
3520
+ }
3521
+ providerKeypair = import_tweetnacl3.default.box.keyPair.fromSecretKey(new Uint8Array(secret));
3522
+ log11.info("Provider encryption keypair loaded");
3523
+ return providerKeypair;
3524
+ }
3525
+ async function getOwnerEncryptionKey(ownerWallet) {
3526
+ const { data, error } = await getDb().from("encryption_keys").select("x25519_pubkey, verifier_ct").eq("owner_wallet", ownerWallet).maybeSingle();
3527
+ if (error) throw new Error(`getOwnerEncryptionKey failed: ${error.message}`);
3528
+ if (!data) return null;
3529
+ return { x25519_pubkey: data.x25519_pubkey, verifier_ct: data.verifier_ct };
3530
+ }
3531
+ var import_tweetnacl3, log11, providerKeypair;
3532
+ var init_encryption_keys = __esm({
3533
+ "packages/shared/src/core/encryption-keys.ts"() {
3534
+ "use strict";
3535
+ import_tweetnacl3 = __toESM(require("tweetnacl"));
3536
+ init_database();
3537
+ init_logger();
3538
+ log11 = createChildLogger("encryption-keys");
3539
+ providerKeypair = null;
3540
+ }
3541
+ });
3542
+
3543
+ // packages/brain/src/memory/memory-encryption.ts
3544
+ function isMemoryEncryptionEnabled() {
3545
+ if (process.env.PMP_ENCRYPTION_ENABLED !== "true") return false;
3546
+ try {
3547
+ loadProviderKeypair();
3548
+ return true;
3549
+ } catch {
3550
+ return false;
3551
+ }
3552
+ }
3553
+ async function getBotOwnerKeypair() {
3554
+ if (botKeypair) return botKeypair;
3555
+ const wallet = getBotWallet();
3556
+ if (!wallet) return null;
3557
+ const sig = import_tweetnacl4.default.sign.detached(new TextEncoder().encode(DERIVE_MESSAGE), wallet.secretKey);
3558
+ botKeypair = await deriveOwnerEncryptionKeypair(sig);
3559
+ return botKeypair;
3560
+ }
3561
+ async function resolveOwnerPublicKey(ownerWallet) {
3562
+ const botWallet2 = getBotWallet();
3563
+ const botAddr = botWallet2?.publicKey.toBase58();
3564
+ if (!ownerWallet || botAddr && ownerWallet === botAddr) {
3565
+ const kp = await getBotOwnerKeypair();
3566
+ return kp ? Buffer.from(kp.publicKey).toString("base64") : null;
3567
+ }
3568
+ const row = await getOwnerEncryptionKey(ownerWallet);
3569
+ return row?.x25519_pubkey ?? null;
3570
+ }
3571
+ async function encryptForStorage(plaintext, ownerWallet) {
3572
+ if (!isMemoryEncryptionEnabled()) return null;
3573
+ const ownerPubkey = await resolveOwnerPublicKey(ownerWallet);
3574
+ if (!ownerPubkey) {
3575
+ log12.warn("No owner encryption key resolvable \u2014 storing plaintext (encrypted:false)");
3576
+ return null;
3577
+ }
3578
+ const dek = generateDek();
3579
+ const ciphertext = encryptField(plaintext, dek);
3580
+ const ownerWrap = wrapDek(dek, Buffer.from(ownerPubkey, "base64"));
3581
+ const provWrap = wrapDek(dek, loadProviderKeypair().publicKey);
3582
+ return {
3583
+ ciphertext,
3584
+ ownerPubkey,
3585
+ wraps: [
3586
+ { recipient: "owner", wrapped_dek: ownerWrap.wrapped, wrap_pubkey: ownerWrap.wrapPubkey },
3587
+ { recipient: "provider", wrapped_dek: provWrap.wrapped, wrap_pubkey: provWrap.wrapPubkey }
3588
+ ]
3589
+ };
3590
+ }
3591
+ function delegationStateForWrite(hasEnvelope) {
3592
+ return hasEnvelope ? true : null;
3593
+ }
3594
+ var import_tweetnacl4, log12, DERIVE_MESSAGE, botKeypair;
3595
+ var init_memory_encryption = __esm({
3596
+ "packages/brain/src/memory/memory-encryption.ts"() {
3597
+ "use strict";
3598
+ import_tweetnacl4 = __toESM(require("tweetnacl"));
3599
+ init_memory_envelope();
3600
+ init_encryption_keys();
3601
+ init_solana_client();
3602
+ init_logger();
3603
+ log12 = createChildLogger("memory-encryption");
3604
+ DERIVE_MESSAGE = "clude-memory-encryption-v1";
3605
+ botKeypair = null;
3606
+ }
3607
+ });
3608
+
3609
+ // packages/shared/src/core/encryption.ts
3610
+ var encryption_exports = {};
3611
+ __export(encryption_exports, {
3612
+ configureEncryption: () => configureEncryption,
3613
+ decryptContent: () => decryptContent,
3614
+ decryptMemoryBatch: () => decryptMemoryBatch,
3615
+ encryptContent: () => encryptContent,
3616
+ getEncryptionPubkey: () => getEncryptionPubkey,
3617
+ isEncryptionEnabled: () => isEncryptionEnabled
3618
+ });
3619
+ async function configureEncryption(solanaSecretKey) {
3620
+ if (solanaSecretKey.length !== 64) {
3621
+ throw new Error("Encryption requires a 64-byte Ed25519 secret key");
3622
+ }
3623
+ const seed = solanaSecretKey.slice(0, 32);
3624
+ const derived = await hkdfAsync2("sha256", seed, HKDF_SALT2, HKDF_INFO2, 32);
3625
+ encryptionKey = new Uint8Array(derived);
3626
+ const keypair = import_tweetnacl5.default.sign.keyPair.fromSecretKey(solanaSecretKey);
1996
3627
  encryptionPubkey = Buffer.from(keypair.publicKey).toString("base64");
1997
- log8.info({ pubkey: encryptionPubkey.slice(0, 12) + "..." }, "Encryption configured");
3628
+ log13.info({ pubkey: encryptionPubkey.slice(0, 12) + "..." }, "Encryption configured");
1998
3629
  }
1999
3630
  function isEncryptionEnabled() {
2000
3631
  return encryptionKey !== null;
@@ -2006,12 +3637,12 @@ function encryptContent(plaintext) {
2006
3637
  if (!encryptionKey) {
2007
3638
  throw new Error("Encryption not configured. Call configureEncryption() first.");
2008
3639
  }
2009
- const nonce = import_tweetnacl2.default.randomBytes(NONCE_LENGTH);
3640
+ const nonce = import_tweetnacl5.default.randomBytes(NONCE_LENGTH2);
2010
3641
  const messageBytes = new TextEncoder().encode(plaintext);
2011
- const ciphertext = import_tweetnacl2.default.secretbox(messageBytes, nonce, encryptionKey);
2012
- const combined = new Uint8Array(NONCE_LENGTH + ciphertext.length);
3642
+ const ciphertext = import_tweetnacl5.default.secretbox(messageBytes, nonce, encryptionKey);
3643
+ const combined = new Uint8Array(NONCE_LENGTH2 + ciphertext.length);
2013
3644
  combined.set(nonce, 0);
2014
- combined.set(ciphertext, NONCE_LENGTH);
3645
+ combined.set(ciphertext, NONCE_LENGTH2);
2015
3646
  return Buffer.from(combined).toString("base64");
2016
3647
  }
2017
3648
  function decryptContent(encrypted) {
@@ -2020,12 +3651,12 @@ function decryptContent(encrypted) {
2020
3651
  }
2021
3652
  try {
2022
3653
  const combined = Buffer.from(encrypted, "base64");
2023
- if (combined.length < NONCE_LENGTH + 1) {
3654
+ if (combined.length < NONCE_LENGTH2 + 1) {
2024
3655
  return null;
2025
3656
  }
2026
- const nonce = combined.subarray(0, NONCE_LENGTH);
2027
- const ciphertext = combined.subarray(NONCE_LENGTH);
2028
- const plaintext = import_tweetnacl2.default.secretbox.open(ciphertext, nonce, encryptionKey);
3657
+ const nonce = combined.subarray(0, NONCE_LENGTH2);
3658
+ const ciphertext = combined.subarray(NONCE_LENGTH2);
3659
+ const plaintext = import_tweetnacl5.default.secretbox.open(ciphertext, nonce, encryptionKey);
2029
3660
  if (!plaintext) {
2030
3661
  return null;
2031
3662
  }
@@ -2039,36 +3670,94 @@ function decryptMemoryBatch(memories) {
2039
3670
  for (const mem of memories) {
2040
3671
  if (!mem.encrypted) continue;
2041
3672
  if (mem.encryption_pubkey && mem.encryption_pubkey !== encryptionPubkey) {
2042
- log8.debug({ memPubkey: mem.encryption_pubkey?.slice(0, 12) }, "Skipping memory encrypted by different key");
3673
+ log13.debug({ memPubkey: mem.encryption_pubkey?.slice(0, 12) }, "Skipping memory encrypted by different key");
2043
3674
  continue;
2044
3675
  }
2045
3676
  const decrypted = decryptContent(mem.content);
2046
3677
  if (decrypted !== null) {
2047
3678
  mem.content = decrypted;
2048
3679
  } else {
2049
- log8.warn("Failed to decrypt memory content \u2014 wrong key or corrupted data");
3680
+ log13.warn("Failed to decrypt memory content \u2014 wrong key or corrupted data");
2050
3681
  }
2051
3682
  }
2052
3683
  return memories;
2053
3684
  }
2054
- var import_tweetnacl2, import_crypto, import_util, log8, hkdfAsync, HKDF_SALT, HKDF_INFO, NONCE_LENGTH, encryptionKey, encryptionPubkey;
3685
+ var import_tweetnacl5, import_crypto2, import_util2, log13, hkdfAsync2, HKDF_SALT2, HKDF_INFO2, NONCE_LENGTH2, encryptionKey, encryptionPubkey;
2055
3686
  var init_encryption = __esm({
2056
3687
  "packages/shared/src/core/encryption.ts"() {
2057
3688
  "use strict";
2058
- import_tweetnacl2 = __toESM(require("tweetnacl"));
2059
- import_crypto = require("crypto");
2060
- import_util = require("util");
3689
+ import_tweetnacl5 = __toESM(require("tweetnacl"));
3690
+ import_crypto2 = require("crypto");
3691
+ import_util2 = require("util");
2061
3692
  init_logger();
2062
- log8 = createChildLogger("encryption");
2063
- hkdfAsync = (0, import_util.promisify)(import_crypto.hkdf);
2064
- HKDF_SALT = "clude-cortex-v1";
2065
- HKDF_INFO = "memory-encryption";
2066
- NONCE_LENGTH = import_tweetnacl2.default.secretbox.nonceLength;
3693
+ log13 = createChildLogger("encryption");
3694
+ hkdfAsync2 = (0, import_util2.promisify)(import_crypto2.hkdf);
3695
+ HKDF_SALT2 = "clude-cortex-v1";
3696
+ HKDF_INFO2 = "memory-encryption";
3697
+ NONCE_LENGTH2 = import_tweetnacl5.default.secretbox.nonceLength;
2067
3698
  encryptionKey = null;
2068
3699
  encryptionPubkey = null;
2069
3700
  }
2070
3701
  });
2071
3702
 
3703
+ // packages/brain/src/memory/memory-decryption.ts
3704
+ async function fetchProviderWraps(ids) {
3705
+ const map = /* @__PURE__ */ new Map();
3706
+ if (ids.length === 0) return map;
3707
+ const { data, error } = await getDb().from("memory_dek_wraps").select("memory_id, wrapped_dek, wrap_pubkey").eq("recipient", "provider").in("memory_id", ids);
3708
+ if (error || !data) return map;
3709
+ for (const r of data) {
3710
+ map.set(r.memory_id, { wrapped_dek: r.wrapped_dek, wrap_pubkey: r.wrap_pubkey });
3711
+ }
3712
+ return map;
3713
+ }
3714
+ async function decryptMemories(memories) {
3715
+ if (!memories || memories.length === 0) return memories;
3716
+ const encryptedRows = memories.filter((m) => m.encrypted === true);
3717
+ if (encryptedRows.length === 0) return memories;
3718
+ let providerSecret = null;
3719
+ try {
3720
+ providerSecret = loadProviderKeypair().secretKey;
3721
+ } catch {
3722
+ providerSecret = null;
3723
+ }
3724
+ const wraps = providerSecret ? await fetchProviderWraps(encryptedRows.map((m) => m.id)) : /* @__PURE__ */ new Map();
3725
+ const legacyRows = [];
3726
+ for (const mem of encryptedRows) {
3727
+ const wrap = wraps.get(mem.id);
3728
+ if (providerSecret && wrap) {
3729
+ const dek = unwrapDek(wrap.wrapped_dek, wrap.wrap_pubkey, providerSecret);
3730
+ const plain = dek ? decryptField(mem.content, dek) : null;
3731
+ if (plain !== null) {
3732
+ mem.content = plain;
3733
+ continue;
3734
+ }
3735
+ log14.debug({ id: mem.id }, "Envelope decrypt failed \u2014 leaving ciphertext");
3736
+ continue;
3737
+ }
3738
+ legacyRows.push(mem);
3739
+ }
3740
+ if (legacyRows.length > 0) decryptMemoryBatch(legacyRows);
3741
+ return memories;
3742
+ }
3743
+ async function decryptOneContent(row) {
3744
+ if (row.encrypted !== true) return row.content;
3745
+ const [out] = await decryptMemories([{ ...row }]);
3746
+ return out.content !== row.content ? out.content : null;
3747
+ }
3748
+ var log14;
3749
+ var init_memory_decryption = __esm({
3750
+ "packages/brain/src/memory/memory-decryption.ts"() {
3751
+ "use strict";
3752
+ init_database();
3753
+ init_memory_envelope();
3754
+ init_encryption_keys();
3755
+ init_encryption();
3756
+ init_logger();
3757
+ log14 = createChildLogger("memory-decryption");
3758
+ }
3759
+ });
3760
+
2072
3761
  // packages/brain/src/events/event-bus.ts
2073
3762
  var event_bus_exports = {};
2074
3763
  __export(event_bus_exports, {
@@ -2121,16 +3810,16 @@ async function findOrCreateEntity(name, entityType, opts) {
2121
3810
  mention_count: 1
2122
3811
  }).select().single();
2123
3812
  if (error) {
2124
- log9.error({ error: error.message, name }, "Failed to create entity");
3813
+ log15.error({ error: error.message, name }, "Failed to create entity");
2125
3814
  return null;
2126
3815
  }
2127
- log9.debug({ id: newEntity.id, name, type: entityType }, "Entity created");
3816
+ log15.debug({ id: newEntity.id, name, type: entityType }, "Entity created");
2128
3817
  embedEntity(newEntity.id, name, opts?.description).catch(
2129
- (err) => log9.debug({ err }, "Entity embedding failed")
3818
+ (err) => log15.debug({ err }, "Entity embedding failed")
2130
3819
  );
2131
3820
  return newEntity;
2132
3821
  } catch (err) {
2133
- log9.error({ err, name }, "Entity findOrCreate failed");
3822
+ log15.error({ err, name }, "Entity findOrCreate failed");
2134
3823
  return null;
2135
3824
  }
2136
3825
  }
@@ -2152,7 +3841,7 @@ async function createEntityMention(entityId, memoryId, context, salience = 0.5)
2152
3841
  salience: Math.max(0, Math.min(1, salience))
2153
3842
  }, { onConflict: "entity_id,memory_id" });
2154
3843
  if (error) {
2155
- log9.debug({ error: error.message, entityId, memoryId }, "Entity mention failed");
3844
+ log15.debug({ error: error.message, entityId, memoryId }, "Entity mention failed");
2156
3845
  }
2157
3846
  }
2158
3847
  async function getMemoriesByEntity(entityId, opts) {
@@ -2164,12 +3853,12 @@ async function getMemoriesByEntity(entityId, opts) {
2164
3853
  `).eq("entity_id", entityId).order("salience", { ascending: false }).limit(opts?.limit || 20);
2165
3854
  const { data, error } = await query;
2166
3855
  if (error) {
2167
- log9.error({ error: error.message, entityId }, "Failed to get memories by entity");
3856
+ log15.error({ error: error.message, entityId }, "Failed to get memories by entity");
2168
3857
  return [];
2169
3858
  }
2170
3859
  const { getOwnerWallet: getOwnerWallet4 } = (init_memory(), __toCommonJS(memory_exports));
2171
3860
  const ownerWallet = getOwnerWallet4();
2172
- const memories = (data || []).map((d) => d.memories).filter((m) => m !== null).filter((m) => !opts?.memoryTypes || opts.memoryTypes.includes(m.memory_type)).filter((m) => !ownerWallet || m.owner_wallet === ownerWallet);
3861
+ const memories = (data || []).map((d) => d.memories).filter((m) => m !== null).filter((m) => m.provider_delegated !== false).filter((m) => !opts?.memoryTypes || opts.memoryTypes.includes(m.memory_type)).filter((m) => !ownerWallet || m.owner_wallet === ownerWallet);
2173
3862
  return memories;
2174
3863
  }
2175
3864
  async function getEntityCooccurrences(entityId, opts) {
@@ -2181,33 +3870,27 @@ async function getEntityCooccurrences(entityId, opts) {
2181
3870
  max_results: opts?.maxResults ?? 10
2182
3871
  });
2183
3872
  if (error || !data) {
2184
- log9.debug({ error: error?.message, entityId }, "Entity co-occurrence lookup failed");
3873
+ log15.debug({ error: error?.message, entityId }, "Entity co-occurrence lookup failed");
2185
3874
  return [];
2186
3875
  }
2187
3876
  return data;
2188
3877
  } catch (err) {
2189
- log9.debug({ err, entityId }, "Entity co-occurrence skipped (RPC unavailable)");
3878
+ log15.debug({ err, entityId }, "Entity co-occurrence skipped (RPC unavailable)");
2190
3879
  return [];
2191
3880
  }
2192
3881
  }
2193
3882
  async function createEntityRelation(sourceEntityId, targetEntityId, relationType, evidenceMemoryId, strength = 0.5) {
2194
3883
  if (sourceEntityId === targetEntityId) return;
2195
3884
  const db = getDb();
2196
- const { data: existing } = await db.from("entity_relations").select("id, strength, evidence_memory_ids").eq("source_entity_id", sourceEntityId).eq("target_entity_id", targetEntityId).eq("relation_type", relationType).single();
2197
- if (existing) {
2198
- const newEvidence = evidenceMemoryId && !existing.evidence_memory_ids.includes(evidenceMemoryId) ? [...existing.evidence_memory_ids, evidenceMemoryId] : existing.evidence_memory_ids;
2199
- await db.from("entity_relations").update({
2200
- strength: Math.min(1, existing.strength + 0.1),
2201
- evidence_memory_ids: newEvidence
2202
- }).eq("id", existing.id);
2203
- } else {
2204
- await db.from("entity_relations").insert({
2205
- source_entity_id: sourceEntityId,
2206
- target_entity_id: targetEntityId,
2207
- relation_type: relationType,
2208
- strength,
2209
- evidence_memory_ids: evidenceMemoryId ? [evidenceMemoryId] : []
2210
- });
3885
+ const { error } = await db.rpc("upsert_entity_relation", {
3886
+ p_src: sourceEntityId,
3887
+ p_tgt: targetEntityId,
3888
+ p_type: relationType,
3889
+ p_evidence: evidenceMemoryId ?? null,
3890
+ p_strength: strength
3891
+ });
3892
+ if (error) {
3893
+ log15.warn({ err: error.message, sourceEntityId, targetEntityId, relationType }, "upsert_entity_relation failed");
2211
3894
  }
2212
3895
  }
2213
3896
  function extractEntitiesFromText(text) {
@@ -2287,11 +3970,11 @@ async function extractAndLinkEntities(memoryId, content, summary, relatedUser) {
2287
3970
  // base co-occurrence strength
2288
3971
  );
2289
3972
  } catch (err) {
2290
- log9.debug({ err, source: entityIds[i].name, target: entityIds[j].name }, "Failed to create entity relation");
3973
+ log15.debug({ err, source: entityIds[i].name, target: entityIds[j].name }, "Failed to create entity relation");
2291
3974
  }
2292
3975
  }
2293
3976
  }
2294
- log9.debug({ memoryId, entityCount: extracted.length, relations: Math.max(0, entityIds.length * (entityIds.length - 1) / 2) }, "Entities extracted, linked, and related");
3977
+ log15.debug({ memoryId, entityCount: extracted.length, relations: Math.max(0, entityIds.length * (entityIds.length - 1) / 2) }, "Entities extracted, linked, and related");
2295
3978
  }
2296
3979
  async function findSimilarEntities(query, opts) {
2297
3980
  if (!isEmbeddingEnabled()) return [];
@@ -2305,7 +3988,7 @@ async function findSimilarEntities(query, opts) {
2305
3988
  filter_types: opts?.entityTypes || null
2306
3989
  });
2307
3990
  if (error) {
2308
- log9.debug({ error: error.message }, "Entity vector search failed");
3991
+ log15.debug({ error: error.message }, "Entity vector search failed");
2309
3992
  return [];
2310
3993
  }
2311
3994
  const ids = (data || []).map((d) => d.id);
@@ -2313,14 +3996,14 @@ async function findSimilarEntities(query, opts) {
2313
3996
  const { data: entities } = await db.from("entities").select("*").in("id", ids);
2314
3997
  return entities || [];
2315
3998
  }
2316
- var log9;
3999
+ var log15;
2317
4000
  var init_graph = __esm({
2318
4001
  "packages/brain/src/memory/graph.ts"() {
2319
4002
  "use strict";
2320
4003
  init_database();
2321
4004
  init_logger();
2322
4005
  init_embeddings();
2323
- log9 = createChildLogger("memory-graph");
4006
+ log15 = createChildLogger("memory-graph");
2324
4007
  }
2325
4008
  });
2326
4009
 
@@ -2348,8 +4031,11 @@ var init_owner_context = __esm({
2348
4031
  // packages/brain/src/memory/memory.ts
2349
4032
  var memory_exports = {};
2350
4033
  __export(memory_exports, {
2351
- SCOPE_BOT_OWN: () => SCOPE_BOT_OWN,
4034
+ SCOPE_BOT_OWN: () => SCOPE_BOT_OWN2,
4035
+ _resetOutboxDegradeLog: () => _resetOutboxDegradeLog,
2352
4036
  _setOwnerWallet: () => _setOwnerWallet,
4037
+ applyOwnerPostGuard: () => applyOwnerPostGuard,
4038
+ buildFragmentRpcArgs: () => buildFragmentRpcArgs,
2353
4039
  calculateImportance: () => calculateImportance,
2354
4040
  createMemoryLink: () => createMemoryLink,
2355
4041
  createMemoryLinksBatch: () => createMemoryLinksBatch,
@@ -2360,11 +4046,15 @@ __export(memory_exports, {
2360
4046
  formatMemoryContext: () => formatMemoryContext,
2361
4047
  generateHashId: () => generateHashId,
2362
4048
  getMemoryStats: () => getMemoryStats,
4049
+ getOwnerScope: () => getOwnerScope,
2363
4050
  getOwnerWallet: () => getOwnerWallet,
2364
4051
  getRecentMemories: () => getRecentMemories,
2365
4052
  getSelfModel: () => getSelfModel,
2366
4053
  hydrateMemories: () => hydrateMemories,
2367
4054
  inferConcepts: () => inferConcepts,
4055
+ invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
4056
+ isBenchMode: () => isBenchMode,
4057
+ isOwnerScopeFailClosed: () => isOwnerScopeFailClosed,
2368
4058
  isValidHashId: () => isValidHashId,
2369
4059
  listMemories: () => listMemories,
2370
4060
  markJepaQueried: () => markJepaQueried,
@@ -2372,11 +4062,15 @@ __export(memory_exports, {
2372
4062
  moodToValence: () => moodToValence,
2373
4063
  recallMemories: () => recallMemories,
2374
4064
  recallMemorySummaries: () => recallMemorySummaries,
4065
+ runEnrichPipeline: () => runEnrichPipeline,
2375
4066
  scopeToOwner: () => scopeToOwner,
4067
+ scoreImportanceOnWrite: () => scoreImportanceOnWrite,
2376
4068
  scoreImportanceWithLLM: () => scoreImportanceWithLLM,
2377
4069
  scoreMemory: () => scoreMemory,
4070
+ shouldTrackAccess: () => shouldTrackAccess,
2378
4071
  storeDreamLog: () => storeDreamLog,
2379
4072
  storeMemory: () => storeMemory,
4073
+ storeMemoryWithOutbox: () => storeMemoryWithOutbox,
2380
4074
  updateMemory: () => updateMemory
2381
4075
  });
2382
4076
  function getCachedEmbedding2(query) {
@@ -2405,18 +4099,51 @@ function getOwnerWallet() {
2405
4099
  if (contextWallet !== void 0) return contextWallet;
2406
4100
  return _ownerWallet;
2407
4101
  }
2408
- function scopeToOwner(query) {
4102
+ function isOwnerScopeFailClosed() {
4103
+ return process.env.OWNER_SCOPE_FAILCLOSED === "true";
4104
+ }
4105
+ function getOwnerScope() {
2409
4106
  const wallet = getOwnerWallet();
2410
- if (wallet === SCOPE_BOT_OWN) {
4107
+ if (wallet) return wallet;
4108
+ return isOwnerScopeFailClosed() ? SCOPE_BOT_OWN2 : null;
4109
+ }
4110
+ function scopeToOwner(query) {
4111
+ const scope = getOwnerScope();
4112
+ if (scope === SCOPE_BOT_OWN2) {
2411
4113
  return query.is("owner_wallet", null);
2412
4114
  }
2413
- if (wallet) {
2414
- return query.eq("owner_wallet", wallet);
4115
+ if (scope) {
4116
+ return query.eq("owner_wallet", scope);
2415
4117
  }
2416
4118
  return query;
2417
4119
  }
4120
+ function applyOwnerPostGuard(results, scope) {
4121
+ if (!scope) return { kept: results, stripped: 0 };
4122
+ const kept = scope === SCOPE_BOT_OWN2 ? results.filter((m) => m.owner_wallet == null) : results.filter((m) => m.owner_wallet === scope);
4123
+ return { kept, stripped: results.length - kept.length };
4124
+ }
4125
+ function isBenchMode() {
4126
+ return process.env.BENCH_MODE === "true";
4127
+ }
4128
+ function shouldTrackAccess(opts) {
4129
+ if (isBenchMode()) return false;
4130
+ return opts.trackAccess !== false;
4131
+ }
4132
+ function buildFragmentRpcArgs(opts) {
4133
+ const args = {
4134
+ query_embedding: opts.embeddingJson,
4135
+ match_threshold: opts.matchThreshold,
4136
+ match_count: opts.matchCount,
4137
+ filter_owner: getOwnerScope()
4138
+ };
4139
+ if (process.env.MEMORY_FRAGMENT_FILTERS === "true") {
4140
+ args.min_decay = opts.minDecay;
4141
+ args.filter_types = opts.memoryTypes && opts.memoryTypes.length > 0 ? opts.memoryTypes : null;
4142
+ }
4143
+ return args;
4144
+ }
2418
4145
  function generateHashId() {
2419
- return `${HASH_ID_PREFIX}-${(0, import_crypto2.randomBytes)(4).toString("hex")}`;
4146
+ return `${HASH_ID_PREFIX}-${(0, import_crypto3.randomBytes)(4).toString("hex")}`;
2420
4147
  }
2421
4148
  function isValidHashId(id) {
2422
4149
  return /^clude-[a-f0-9]{8}$/.test(id);
@@ -2451,6 +4178,23 @@ function inferConcepts(summary, source, tags) {
2451
4178
  concepts.push("identity_evolution");
2452
4179
  return [...new Set(concepts)];
2453
4180
  }
4181
+ function scoreImportanceOnWrite(opts) {
4182
+ const base = {
4183
+ self_model: 0.75,
4184
+ semantic: 0.7,
4185
+ procedural: 0.65,
4186
+ introspective: 0.6,
4187
+ episodic: 0.45
4188
+ };
4189
+ let score = base[opts.type] ?? 0.5;
4190
+ const len = (opts.content ?? "").trim().length;
4191
+ score += Math.min(len / 1e3, 0.15);
4192
+ if (opts.tags && opts.tags.length) score += 0.05;
4193
+ const concepts = opts.concepts ?? inferConcepts(opts.summary, opts.source, opts.tags || []);
4194
+ if (concepts.length) score += 0.05;
4195
+ if (INTERNAL_MEMORY_SOURCES.has(opts.source)) score -= 0.05;
4196
+ return Math.max(0, Math.min(1, score));
4197
+ }
2454
4198
  function normalizeSummary(summary) {
2455
4199
  return summary.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<id>").replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[^\s]*/g, "<ts>").toLowerCase().trim();
2456
4200
  }
@@ -2467,27 +4211,104 @@ function isDuplicateWrite(source, summary) {
2467
4211
  }
2468
4212
  return false;
2469
4213
  }
4214
+ async function getInstalledPackIdsCached(ownerWallet) {
4215
+ const key = ownerWallet ?? "__no_owner__";
4216
+ const now = Date.now();
4217
+ const cached = installedPacksCache.get(key);
4218
+ if (cached && cached.expiresAt > now) return cached.ids;
4219
+ let ids = [DEFAULT_PACK_ID];
4220
+ if (ownerWallet) {
4221
+ try {
4222
+ const db = getDb();
4223
+ const { data, error } = await db.from("wiki_pack_installations").select("pack_id").eq("owner_wallet", ownerWallet);
4224
+ if (!error && data) {
4225
+ const fromDb = data.map((r) => r.pack_id);
4226
+ ids = Array.from(/* @__PURE__ */ new Set([DEFAULT_PACK_ID, ...fromDb]));
4227
+ }
4228
+ } catch (err) {
4229
+ log16.debug({ err }, "Failed to fetch installed packs (using default only)");
4230
+ }
4231
+ }
4232
+ installedPacksCache.set(key, { ids, expiresAt: now + INSTALLED_PACKS_TTL_MS });
4233
+ if (installedPacksCache.size > 200) {
4234
+ for (const [k, v] of installedPacksCache) {
4235
+ if (v.expiresAt <= now) installedPacksCache.delete(k);
4236
+ }
4237
+ }
4238
+ return ids;
4239
+ }
4240
+ function invalidateInstalledPacksCache(ownerWallet) {
4241
+ if (ownerWallet) installedPacksCache.delete(ownerWallet);
4242
+ else installedPacksCache.clear();
4243
+ }
4244
+ async function ensureTopicEmbeddings(installedPackIds) {
4245
+ const sources = topicEmbedSources(installedPackIds);
4246
+ const missing = sources.filter((s) => !topicEmbeddingCache.has(s.topicId));
4247
+ if (missing.length === 0) {
4248
+ return new Map(sources.map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)]));
4249
+ }
4250
+ if (topicEmbeddingPopulationLock) {
4251
+ await topicEmbeddingPopulationLock;
4252
+ }
4253
+ const stillMissing = sources.filter((s) => !topicEmbeddingCache.has(s.topicId));
4254
+ if (stillMissing.length === 0) {
4255
+ return new Map(sources.map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)]));
4256
+ }
4257
+ topicEmbeddingPopulationLock = (async () => {
4258
+ try {
4259
+ const texts = stillMissing.map((s) => s.text);
4260
+ const embeddings = await generateEmbeddings(texts);
4261
+ stillMissing.forEach((s, i) => {
4262
+ const emb = embeddings[i];
4263
+ if (emb) topicEmbeddingCache.set(s.topicId, emb);
4264
+ });
4265
+ } catch (err) {
4266
+ log16.warn({ err }, "Failed to populate topic embeddings cache");
4267
+ } finally {
4268
+ topicEmbeddingPopulationLock = null;
4269
+ }
4270
+ })();
4271
+ await topicEmbeddingPopulationLock;
4272
+ return new Map(
4273
+ sources.filter((s) => topicEmbeddingCache.has(s.topicId)).map((s) => [s.topicId, topicEmbeddingCache.get(s.topicId)])
4274
+ );
4275
+ }
2470
4276
  async function storeMemory(opts) {
4277
+ const trimmedContent = (opts.content ?? "").trim();
4278
+ if (!trimmedContent) {
4279
+ log16.warn({ source: opts.source }, "Rejected empty-content memory");
4280
+ return null;
4281
+ }
2471
4282
  if (opts.source.startsWith("shiro_") && isDuplicateWrite(opts.source, opts.summary)) {
2472
- log10.debug({ source: opts.source, summary: opts.summary.slice(0, 60) }, "Skipping duplicate memory write");
4283
+ log16.debug({ source: opts.source, summary: opts.summary.slice(0, 60) }, "Skipping duplicate memory write");
2473
4284
  return null;
2474
4285
  }
2475
4286
  const db = getDb();
4287
+ const ownerWallet = getOwnerWallet();
4288
+ const importance = clamp(opts.importance ?? scoreImportanceOnWrite(opts), 0, 1);
2476
4289
  const concepts = opts.concepts || inferConcepts(opts.summary, opts.source, opts.tags || []);
4290
+ const installedPackIds = await getInstalledPackIdsCached(ownerWallet);
4291
+ const taggedTags = autoCategorizeTags({
4292
+ content: opts.content,
4293
+ summary: opts.summary,
4294
+ existingTags: opts.tags || [],
4295
+ installedPackIds
4296
+ });
2477
4297
  const hashId = generateHashId();
2478
4298
  try {
2479
- const plaintextContent = opts.content.slice(0, MEMORY_MAX_CONTENT_LENGTH);
2480
- const shouldEncrypt = isEncryptionEnabled();
2481
- const storedContent = shouldEncrypt ? encryptContent(plaintextContent) : plaintextContent;
4299
+ const plaintextContent = trimmedContent.slice(0, MEMORY_MAX_CONTENT_LENGTH);
4300
+ const envelope = await encryptForStorage(plaintextContent, ownerWallet || null);
4301
+ const legacyEncrypt = !envelope && isEncryptionEnabled();
4302
+ const storedContent = envelope ? envelope.ciphertext : legacyEncrypt ? encryptContent(plaintextContent) : plaintextContent;
2482
4303
  const { data, error } = await db.from("memories").insert({
2483
4304
  hash_id: hashId,
2484
4305
  memory_type: opts.type,
2485
4306
  content: storedContent,
2486
4307
  summary: opts.summary.slice(0, MEMORY_MAX_SUMMARY_LENGTH),
2487
- tags: opts.tags || [],
4308
+ tags: taggedTags,
2488
4309
  concepts,
2489
4310
  emotional_valence: clamp(opts.emotionalValence ?? 0, -1, 1),
2490
- importance: clamp(opts.importance ?? 0.5, 0, 1),
4311
+ importance,
2491
4312
  source: opts.source,
2492
4313
  source_id: opts.sourceId || null,
2493
4314
  related_user: opts.relatedUser || null,
@@ -2495,43 +4316,83 @@ async function storeMemory(opts) {
2495
4316
  metadata: opts.metadata || {},
2496
4317
  evidence_ids: opts.evidenceIds || [],
2497
4318
  compacted: false,
2498
- encrypted: shouldEncrypt,
2499
- encryption_pubkey: shouldEncrypt ? getEncryptionPubkey() : null,
2500
- owner_wallet: getOwnerWallet() || null
2501
- }).select("id, hash_id").single();
4319
+ encrypted: envelope !== null || legacyEncrypt,
4320
+ encryption_pubkey: envelope ? envelope.ownerPubkey : legacyEncrypt ? getEncryptionPubkey() : null,
4321
+ provider_delegated: delegationStateForWrite(envelope !== null),
4322
+ // true|null at write; false is revoke-only (§7)
4323
+ owner_wallet: ownerWallet || null
4324
+ }).select("id, hash_id, content, memory_type, owner_wallet, created_at, tags, source, related_user, related_wallet").single();
2502
4325
  if (error) {
2503
- log10.error({ error: error.message }, "Failed to store memory");
4326
+ log16.error({ error: error.message }, "Failed to store memory");
2504
4327
  return null;
2505
4328
  }
2506
- log10.debug({
4329
+ log16.debug({
2507
4330
  id: data.id,
2508
4331
  hashId: data.hash_id,
2509
4332
  type: opts.type,
2510
4333
  summary: opts.summary.slice(0, 60),
2511
- importance: opts.importance,
4334
+ importance,
2512
4335
  concepts
2513
4336
  }, "Memory stored");
4337
+ await setContentTokens(db, data.id, plaintextContent);
4338
+ if (envelope) {
4339
+ const { error: wrapErr } = await db.from("memory_dek_wraps").insert(
4340
+ envelope.wraps.map((w) => ({ memory_id: data.id, ...w }))
4341
+ );
4342
+ if (wrapErr) {
4343
+ log16.error({ id: data.id, error: wrapErr.message }, "DEK wrap write failed \u2014 reverting memory to plaintext");
4344
+ await db.from("memories").update({
4345
+ content: plaintextContent,
4346
+ encrypted: false,
4347
+ encryption_pubkey: null,
4348
+ provider_delegated: null
4349
+ }).eq("id", data.id);
4350
+ }
4351
+ }
2514
4352
  eventBus.emit("memory:stored", {
2515
- importance: clamp(opts.importance ?? 0.5, 0, 1),
4353
+ importance,
2516
4354
  memoryType: opts.type,
2517
4355
  source: opts.source
2518
4356
  });
2519
- commitMemoryToChain(data.id, opts).catch((err) => log10.warn({ err }, "On-chain memory commit failed"));
2520
- embedMemory(data.id, opts).catch((err) => log10.warn({ err }, "Embedding generation failed"));
2521
- autoLinkMemory(data.id, opts).catch((err) => log10.warn({ err }, "Auto-linking failed"));
2522
- extractAndLinkEntitiesForMemory(data.id, opts).catch((err) => log10.debug({ err }, "Entity extraction failed"));
4357
+ commitMemoryToChain(data.id, opts, data, plaintextContent, envelope !== null || legacyEncrypt).catch(
4358
+ (err) => log16.warn({ err }, "On-chain memory commit failed")
4359
+ );
4360
+ let enqueued = false;
4361
+ if (config.memory.outboxEnabled) {
4362
+ enqueued = await enqueueEnrichJob(db, data.id);
4363
+ }
4364
+ if (!enqueued) {
4365
+ const embedP = embedMemory(data.id, opts);
4366
+ embedP.catch((err) => log16.warn({ err }, "Embedding generation failed"));
4367
+ autoLinkMemory(data.id, opts).catch((err) => log16.warn({ err }, "Auto-linking failed"));
4368
+ extractAndLinkEntitiesForMemory(data.id, opts).catch((err) => log16.debug({ err }, "Entity extraction failed"));
4369
+ if (config.memory.reconcileEnabled) {
4370
+ const reconcileScope = ownerWallet || SCOPE_BOT_OWN2;
4371
+ embedP.then(() => runReconcileShadow(data.id, reconcileScope)).catch(() => {
4372
+ });
4373
+ }
4374
+ }
2523
4375
  return data.id;
2524
4376
  } catch (err) {
2525
- log10.error({ err }, "Memory store failed");
4377
+ log16.error({ err }, "Memory store failed");
2526
4378
  return null;
2527
4379
  }
2528
4380
  }
2529
- async function commitMemoryToChain(memoryId, opts) {
2530
- if (opts.source === "demo" || opts.source === "demo-maas" || opts.source === "locomo-benchmark" || opts.source === "longmemeval-benchmark") return;
2531
- const contentHashBuf = (0, import_crypto2.createHash)("sha256").update(opts.content).digest();
4381
+ async function commitMemoryToChain(memoryId, opts, row, plaintextContent, encrypted) {
4382
+ if (TOKENISATION_SKIP_SOURCES.has(opts.source)) return;
4383
+ const canonicalHash = memoryContentHash({
4384
+ content: plaintextContent,
4385
+ memory_type: row.memory_type,
4386
+ owner_wallet: row.owner_wallet,
4387
+ created_at: row.created_at,
4388
+ tags: row.tags ?? [],
4389
+ source: row.source,
4390
+ related_user: row.related_user,
4391
+ related_wallet: row.related_wallet
4392
+ });
4393
+ const contentHashBuf = Buffer.from(canonicalHash, "hex");
2532
4394
  let signature = null;
2533
4395
  if (isRegistryEnabled()) {
2534
- const encrypted = isEncryptionEnabled();
2535
4396
  signature = await registerMemoryOnChain(
2536
4397
  contentHashBuf,
2537
4398
  opts.type,
@@ -2541,24 +4402,146 @@ async function commitMemoryToChain(memoryId, opts) {
2541
4402
  );
2542
4403
  }
2543
4404
  if (!signature) {
2544
- const contentHashHex = contentHashBuf.toString("hex");
2545
- const memo = `clude:v1:sha256:${contentHashHex}`;
4405
+ const memo = `clude:v1:sha256:${canonicalHash}`;
2546
4406
  signature = await writeMemo(memo);
2547
4407
  }
2548
- if (!signature) return;
2549
4408
  const db = getDb();
2550
- await db.from("memories").update({ solana_signature: signature }).eq("id", memoryId);
2551
- log10.debug({ memoryId, signature: signature.slice(0, 16) }, "Memory committed on-chain");
4409
+ if (!signature) {
4410
+ await db.from("memories").update({ content_hash: canonicalHash, tokenization_status: "failed" }).eq("id", memoryId);
4411
+ return;
4412
+ }
4413
+ await db.from("memories").update({
4414
+ solana_signature: signature,
4415
+ content_hash: canonicalHash,
4416
+ cnft_address: signature,
4417
+ // PDA-based v0.1 — assetId = tx sig; LightMintClient (v0.2) will use real cNFT mints
4418
+ cnft_tx_sig: signature,
4419
+ cnft_tree: null,
4420
+ cnft_leaf_index: null,
4421
+ tokenization_status: "minted",
4422
+ tokenized_at: (/* @__PURE__ */ new Date()).toISOString()
4423
+ }).eq("id", memoryId);
4424
+ log16.debug({ memoryId, signature: signature.slice(0, 16) }, "Memory committed on-chain (PMP + legacy)");
4425
+ }
4426
+ function logOutboxDegradeOnce(err, memoryId) {
4427
+ if (outboxDegradeLogged) return;
4428
+ outboxDegradeLogged = true;
4429
+ const e = err;
4430
+ log16.warn(
4431
+ { code: e?.code, message: e?.message, memoryId },
4432
+ "Outbox enqueue degraded \u2014 falling back to fire-and-forget (migration 044 unapplied?)"
4433
+ );
2552
4434
  }
2553
- async function embedMemory(memoryId, opts) {
4435
+ async function enqueueEnrichJob(db, memoryId) {
4436
+ try {
4437
+ const { error } = await db.from("memory_write_jobs").insert({ memory_id: memoryId, job_type: "enrich" });
4438
+ if (error) {
4439
+ logOutboxDegradeOnce(error, memoryId);
4440
+ return false;
4441
+ }
4442
+ return true;
4443
+ } catch (err) {
4444
+ logOutboxDegradeOnce(err, memoryId);
4445
+ return false;
4446
+ }
4447
+ }
4448
+ function _resetOutboxDegradeLog() {
4449
+ outboxDegradeLogged = false;
4450
+ }
4451
+ async function runEnrichPipeline(memoryId, opts) {
4452
+ await embedMemory(memoryId, opts, { replaceFragments: true });
4453
+ await autoLinkMemory(memoryId, opts);
4454
+ await extractAndLinkEntitiesForMemory(memoryId, opts);
4455
+ if (config.memory.reconcileEnabled) {
4456
+ await runReconcileShadow(memoryId, getOwnerWallet() || SCOPE_BOT_OWN2);
4457
+ }
4458
+ }
4459
+ async function storeMemoryWithOutbox(opts) {
4460
+ const id = await storeMemory(opts);
4461
+ if (id === null) return null;
4462
+ const db = getDb();
4463
+ const { data: row } = await db.from("memories").select("hash_id").eq("id", id).maybeSingle();
4464
+ const { count } = await db.from("memory_write_jobs").select("id", { count: "exact", head: true }).eq("memory_id", id);
4465
+ return { id, hash_id: row?.hash_id ?? "", jobs_queued: count ?? 0 };
4466
+ }
4467
+ async function embedMemory(memoryId, opts, embedOpts = {}) {
2554
4468
  if (!isEmbeddingEnabled()) return;
2555
4469
  const db = getDb();
2556
- const embeddings = await generateEmbeddings([opts.summary]);
4470
+ const fragments = [];
4471
+ fragments.push({ type: "summary", text: opts.summary });
4472
+ const content = opts.content.slice(0, MEMORY_MAX_CONTENT_LENGTH);
4473
+ if (content.length > EMBEDDING_FRAGMENT_MAX_LENGTH) {
4474
+ const sentences = content.match(/[^.!?\n]+[.!?\n]+/g) || [content];
4475
+ let chunk = "";
4476
+ for (const sentence of sentences) {
4477
+ if (chunk.length + sentence.length > EMBEDDING_FRAGMENT_MAX_LENGTH && chunk.length > 0) {
4478
+ fragments.push({ type: "content_chunk", text: chunk.trim() });
4479
+ chunk = "";
4480
+ }
4481
+ chunk += sentence;
4482
+ }
4483
+ if (chunk.trim()) fragments.push({ type: "content_chunk", text: chunk.trim() });
4484
+ } else {
4485
+ fragments.push({ type: "content_chunk", text: content });
4486
+ }
4487
+ const allLabels = [...opts.tags || [], ...opts.concepts || inferConcepts(opts.summary, opts.source, opts.tags || [])];
4488
+ if (allLabels.length > 0) {
4489
+ fragments.push({ type: "tag_context", text: `Context: ${allLabels.join(", ")}. ${opts.summary}` });
4490
+ }
4491
+ const embeddings = await generateEmbeddings(fragments.map((f) => f.text));
2557
4492
  const summaryEmbedding = embeddings[0];
2558
- if (summaryEmbedding) {
2559
- await db.from("memories").update({ embedding: JSON.stringify(summaryEmbedding) }).eq("id", memoryId);
4493
+ if (!summaryEmbedding) {
4494
+ log16.debug({ memoryId }, "No embedding generated; skipping semantic tagging");
4495
+ return;
4496
+ }
4497
+ const vertexEmbeddings = isVertexConfigured() ? await generateVertexEmbeddings(fragments.map((f) => f.text)) : [];
4498
+ const vertexSummary = vertexEmbeddings[0];
4499
+ await db.from("memories").update({
4500
+ embedding: JSON.stringify(summaryEmbedding),
4501
+ ...vertexSummary ? { embedding_vertex: JSON.stringify(vertexSummary) } : {}
4502
+ }).eq("id", memoryId);
4503
+ const fragmentRows = fragments.map((f, i) => ({
4504
+ memory_id: memoryId,
4505
+ fragment_type: f.type,
4506
+ content: f.text.slice(0, EMBEDDING_FRAGMENT_MAX_LENGTH),
4507
+ embedding: embeddings[i] ? JSON.stringify(embeddings[i]) : null,
4508
+ ...vertexEmbeddings[i] ? { embedding_vertex: JSON.stringify(vertexEmbeddings[i]) } : {}
4509
+ })).filter((r) => r.embedding !== null);
4510
+ if (fragmentRows.length > 0) {
4511
+ if (embedOpts.replaceFragments) {
4512
+ await db.from("memory_fragments").delete().eq("memory_id", memoryId);
4513
+ }
4514
+ const { error } = await db.from("memory_fragments").insert(fragmentRows);
4515
+ if (error) {
4516
+ log16.warn({ err: error.message, memoryId }, "Failed to store memory fragments (summary embedding intact)");
4517
+ }
4518
+ }
4519
+ try {
4520
+ const ownerWallet = getOwnerWallet();
4521
+ const installedPacks = await getInstalledPackIdsCached(ownerWallet);
4522
+ const topicEmbeddings = await ensureTopicEmbeddings(installedPacks);
4523
+ if (topicEmbeddings.size > 0) {
4524
+ const semantic = semanticTagMatches(
4525
+ summaryEmbedding,
4526
+ Array.from(topicEmbeddings, ([topicId, embedding]) => ({ topicId, embedding }))
4527
+ );
4528
+ if (semantic.length > 0) {
4529
+ const { data: row } = await db.from("memories").select("tags").eq("id", memoryId).maybeSingle();
4530
+ const existing = row?.tags ?? [];
4531
+ const merged = Array.from(/* @__PURE__ */ new Set([...existing, ...semantic]));
4532
+ if (merged.length !== existing.length) {
4533
+ await db.from("memories").update({ tags: merged }).eq("id", memoryId);
4534
+ log16.debug({
4535
+ memoryId,
4536
+ added: semantic.filter((t) => !existing.includes(t))
4537
+ }, "Semantic pack tags appended");
4538
+ }
4539
+ }
4540
+ }
4541
+ } catch (err) {
4542
+ log16.debug({ err, memoryId }, "Semantic tagging skipped");
2560
4543
  }
2561
- log10.debug({ memoryId }, "Memory embedded");
4544
+ log16.debug({ memoryId }, "Memory embedded");
2562
4545
  }
2563
4546
  async function expandQuery(query) {
2564
4547
  if (!isOpenRouterEnabled()) return [query];
@@ -2576,10 +4559,10 @@ async function expandQuery(query) {
2576
4559
  new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 3e3))
2577
4560
  ]);
2578
4561
  const expansions = response.split("\n").map((l) => l.trim()).filter((l) => l.length > 5 && l.length < 200).slice(0, 3);
2579
- log10.debug({ original: query, expansions: expansions.length }, "Query expanded");
4562
+ log16.debug({ original: query, expansions: expansions.length }, "Query expanded");
2580
4563
  return [query, ...expansions];
2581
4564
  } catch (err) {
2582
- log10.debug({ err }, "Query expansion failed, using original");
4565
+ log16.debug({ err }, "Query expansion failed, using original");
2583
4566
  return [query];
2584
4567
  }
2585
4568
  }
@@ -2592,11 +4575,12 @@ async function recallMemories(opts) {
2592
4575
  let vectorScores = opts._vectorScores || /* @__PURE__ */ new Map();
2593
4576
  let primaryQueryEmbedding = null;
2594
4577
  const vectorSearchPromise = queries.length > 0 && isEmbeddingEnabled() && !opts._vectorScores ? (async () => {
4578
+ const space = activeEmbeddingSpace();
2595
4579
  const queryEmbeddings = await Promise.all(
2596
4580
  queries.map(async (q) => {
2597
4581
  const cached = getCachedEmbedding2(q);
2598
4582
  if (cached) return cached;
2599
- const emb = await generateQueryEmbedding(q);
4583
+ const emb = await generateQueryEmbeddingForSpace(space, q);
2600
4584
  if (emb) setCachedEmbedding2(q, emb);
2601
4585
  return emb;
2602
4586
  })
@@ -2604,39 +4588,69 @@ async function recallMemories(opts) {
2604
4588
  const validEmbeddings = queryEmbeddings.filter((e) => e !== null);
2605
4589
  if (validEmbeddings.length > 0) primaryQueryEmbedding = validEmbeddings[0];
2606
4590
  if (validEmbeddings.length === 0) {
2607
- log10.debug("All query embeddings returned null, using keyword-only retrieval");
4591
+ log16.debug("All query embeddings returned null, using keyword-only retrieval");
2608
4592
  return;
2609
4593
  }
2610
4594
  try {
2611
- const allSearches = validEmbeddings.map(
2612
- (emb) => Promise.resolve(db.rpc("match_memories", {
2613
- query_embedding: JSON.stringify(emb),
2614
- match_threshold: VECTOR_MATCH_THRESHOLD,
2615
- match_count: limit * (opts.skipExpansion ? 12 : 4),
2616
- filter_types: opts.memoryTypes || null,
2617
- filter_user: opts.relatedUser || null,
2618
- min_decay: minDecay,
2619
- filter_owner: getOwnerWallet() || null,
2620
- filter_tags: opts.tags && opts.tags.length > 0 ? opts.tags : null
2621
- })).then((r) => r.data || [])
2622
- );
4595
+ const allSearches = validEmbeddings.flatMap((emb) => {
4596
+ const searches = [
4597
+ Promise.resolve(db.rpc(vectorRpcName("match_memories"), {
4598
+ query_embedding: JSON.stringify(emb),
4599
+ match_threshold: VECTOR_MATCH_THRESHOLD,
4600
+ match_count: limit * (opts.skipExpansion ? 12 : 4),
4601
+ filter_types: opts.memoryTypes || null,
4602
+ filter_user: opts.relatedUser || null,
4603
+ min_decay: minDecay,
4604
+ filter_owner: getOwnerScope(),
4605
+ filter_tags: opts.tags && opts.tags.length > 0 ? opts.tags : null
4606
+ })).then((r) => r.data || [])
4607
+ ];
4608
+ if (!opts.skipExpansion) {
4609
+ searches.push(
4610
+ Promise.resolve(db.rpc(vectorRpcName("match_memory_fragments"), buildFragmentRpcArgs({
4611
+ embeddingJson: JSON.stringify(emb),
4612
+ matchThreshold: VECTOR_MATCH_THRESHOLD,
4613
+ matchCount: limit * 2,
4614
+ minDecay,
4615
+ memoryTypes: opts.memoryTypes
4616
+ }))).then((r) => r.data || [])
4617
+ );
4618
+ }
4619
+ return searches;
4620
+ });
2623
4621
  const results2 = await Promise.all(allSearches);
2624
- for (const batch of results2) {
2625
- for (const m of batch) {
2626
- const current = vectorScores.get(m.id) || 0;
2627
- vectorScores.set(m.id, Math.max(current, m.similarity));
4622
+ let fragmentHits = 0;
4623
+ if (opts.skipExpansion) {
4624
+ for (const batch of results2) {
4625
+ for (const m of batch) {
4626
+ const current = vectorScores.get(m.id) || 0;
4627
+ vectorScores.set(m.id, Math.max(current, m.similarity));
4628
+ }
4629
+ }
4630
+ } else {
4631
+ for (let i = 0; i < results2.length; i++) {
4632
+ const isFragmentBatch = i % 2 === 1;
4633
+ for (const r of results2[i]) {
4634
+ const id = isFragmentBatch ? r.memory_id : r.id;
4635
+ const sim = isFragmentBatch ? r.max_similarity : r.similarity;
4636
+ if (!id) continue;
4637
+ const current = vectorScores.get(id) || 0;
4638
+ vectorScores.set(id, Math.max(current, sim));
4639
+ if (isFragmentBatch) fragmentHits++;
4640
+ }
2628
4641
  }
2629
4642
  }
2630
- log10.debug({
4643
+ log16.debug({
2631
4644
  queryVariants: validEmbeddings.length,
2632
4645
  uniqueMemories: vectorScores.size,
4646
+ fragmentHits,
2633
4647
  fastMode: !!opts.skipExpansion
2634
4648
  }, "Vector search completed");
2635
4649
  } catch (err) {
2636
- log10.warn({ err }, "Vector search RPC failed, falling back to keyword retrieval");
4650
+ log16.warn({ err }, "Vector search RPC failed, falling back to keyword retrieval");
2637
4651
  }
2638
4652
  })() : Promise.resolve();
2639
- let importanceQuery = db.from("memories").select("*").gte("decay_factor", minDecay).not("source", "in", '("demo","demo-maas")').order("importance", { ascending: false }).order("created_at", { ascending: false }).limit(limit * 3);
4653
+ let importanceQuery = db.from("memories").select("*").gte("decay_factor", minDecay).not("source", "in", '("demo","demo-maas")').not("provider_delegated", "is", false).order("importance", { ascending: false }).order("created_at", { ascending: false }).limit(limit * 3);
2640
4654
  importanceQuery = scopeToOwner(importanceQuery);
2641
4655
  if (opts.memoryTypes && opts.memoryTypes.length > 0) {
2642
4656
  importanceQuery = importanceQuery.in("memory_type", opts.memoryTypes);
@@ -2659,7 +4673,12 @@ async function recallMemories(opts) {
2659
4673
  return await bm25SearchMemories(opts.query, {
2660
4674
  limit: limit * 2,
2661
4675
  minDecay,
2662
- filterOwner: _ownerWallet || void 0,
4676
+ // Use getOwnerWallet() (AsyncLocalStorage-aware), NOT the bare module-level
4677
+ // _ownerWallet. withOwnerWallet() sets the async context, not the module var,
4678
+ // so the bare var is null under hosted/wrapped calls and BM25 would search the
4679
+ // ENTIRE table unscoped — burying the owner's exact-match facts. This is why
4680
+ // BM25 silently never surfaced bench facts even after the RPC was fixed.
4681
+ filterOwner: getOwnerScope() || void 0,
2663
4682
  filterTypes: opts.memoryTypes || void 0,
2664
4683
  filterTags: opts.tags || void 0
2665
4684
  });
@@ -2713,7 +4732,7 @@ async function recallMemories(opts) {
2713
4732
  }
2714
4733
  }
2715
4734
  }
2716
- if (seedsAdded > 0) log10.debug({ seedsAdded, seedsTotal: knowledgeSeeds.length }, "Knowledge seeds added to candidates");
4735
+ if (seedsAdded > 0) log16.debug({ seedsAdded, seedsTotal: knowledgeSeeds.length }, "Knowledge seeds added to candidates");
2717
4736
  }
2718
4737
  }
2719
4738
  const bm25Scores = /* @__PURE__ */ new Map();
@@ -2734,18 +4753,18 @@ async function recallMemories(opts) {
2734
4753
  }
2735
4754
  }
2736
4755
  }
2737
- log10.debug({ bm25Hits: bm25Results.length, bm25New: bm25MissingIds.length }, "BM25 search added candidates");
4756
+ log16.debug({ bm25Hits: bm25Results.length, bm25New: bm25MissingIds.length }, "BM25 search added candidates");
2738
4757
  }
2739
4758
  if (error) {
2740
- log10.error({ error: error.message }, "Memory recall query failed");
4759
+ log16.error({ error: error.message }, "Memory recall query failed");
2741
4760
  return [];
2742
4761
  }
2743
- let candidates = decryptMemoryBatch(data || []);
4762
+ let candidates = await decryptMemories(data || []);
2744
4763
  if (vectorScores.size > 0) {
2745
4764
  const metadataIds = new Set(candidates.map((m) => m.id));
2746
4765
  const missingIds = [...vectorScores.keys()].filter((id) => !metadataIds.has(id));
2747
4766
  if (missingIds.length > 0) {
2748
- let vectorQuery = db.from("memories").select("*").in("id", missingIds);
4767
+ let vectorQuery = db.from("memories").select("*").in("id", missingIds).gte("decay_factor", minDecay).not("embedding", "is", null);
2749
4768
  vectorQuery = scopeToOwner(vectorQuery);
2750
4769
  if (opts.memoryTypes && opts.memoryTypes.length > 0) {
2751
4770
  vectorQuery = vectorQuery.in("memory_type", opts.memoryTypes);
@@ -2754,7 +4773,7 @@ async function recallMemories(opts) {
2754
4773
  vectorQuery = vectorQuery.overlaps("tags", opts.tags);
2755
4774
  }
2756
4775
  const { data: vectorOnly } = await vectorQuery;
2757
- if (vectorOnly) candidates = [...candidates, ...decryptMemoryBatch(vectorOnly)];
4776
+ if (vectorOnly) candidates = [...candidates, ...await decryptMemories(vectorOnly)];
2758
4777
  }
2759
4778
  }
2760
4779
  if (candidates.length === 0) return [];
@@ -2778,10 +4797,10 @@ async function recallMemories(opts) {
2778
4797
  if (entities.length > 0) {
2779
4798
  const resultIdSet = new Set(results.map((m) => m.id));
2780
4799
  for (const entity of entities) {
2781
- const entityMemories = await getMemoriesByEntity(entity.id, {
4800
+ const entityMemories = await decryptMemories(await getMemoriesByEntity(entity.id, {
2782
4801
  limit: Math.ceil(limit / 2),
2783
4802
  memoryTypes: opts.memoryTypes
2784
- });
4803
+ }));
2785
4804
  for (const mem of entityMemories) {
2786
4805
  addLinkPath(mem.id, "entity");
2787
4806
  if (!resultIdSet.has(mem.id)) {
@@ -2793,17 +4812,17 @@ async function recallMemories(opts) {
2793
4812
  }
2794
4813
  }
2795
4814
  }
2796
- log10.debug({ entities: entities.map((e) => e.name) }, "Entity-aware recall applied");
4815
+ log16.debug({ entities: entities.map((e) => e.name) }, "Entity-aware recall applied");
2797
4816
  let cooccurrenceAdded = 0;
2798
4817
  const cooccurrenceNames = [];
2799
4818
  for (const entity of entities) {
2800
4819
  const cooccurrences = await getEntityCooccurrences(entity.id, { minCooccurrence: 2, maxResults: 3 });
2801
4820
  for (const cooc of cooccurrences) {
2802
4821
  if (cooccurrenceAdded >= limit) break;
2803
- const coMems = await getMemoriesByEntity(cooc.related_entity_id, {
4822
+ const coMems = await decryptMemories(await getMemoriesByEntity(cooc.related_entity_id, {
2804
4823
  limit: 3,
2805
4824
  memoryTypes: opts.memoryTypes
2806
- });
4825
+ }));
2807
4826
  for (const mem of coMems) {
2808
4827
  if (cooccurrenceAdded >= limit) break;
2809
4828
  addLinkPath(mem.id, "entity");
@@ -2823,11 +4842,11 @@ async function recallMemories(opts) {
2823
4842
  }
2824
4843
  }
2825
4844
  if (cooccurrenceAdded > 0) {
2826
- log10.debug({ cooccurrenceAdded, cooccurrenceEntities: cooccurrenceNames.length }, "Entity co-occurrence recall applied");
4845
+ log16.debug({ cooccurrenceAdded, cooccurrenceEntities: cooccurrenceNames.length }, "Entity co-occurrence recall applied");
2827
4846
  }
2828
4847
  }
2829
4848
  } catch (err) {
2830
- log10.debug({ err }, "Entity-aware recall skipped");
4849
+ log16.debug({ err }, "Entity-aware recall skipped");
2831
4850
  }
2832
4851
  }
2833
4852
  if (results.length > 0) {
@@ -2837,17 +4856,17 @@ async function recallMemories(opts) {
2837
4856
  seed_ids: seedIds,
2838
4857
  min_strength: 0.2,
2839
4858
  max_results: limit,
2840
- filter_owner: getOwnerWallet() || null
4859
+ filter_owner: getOwnerScope()
2841
4860
  });
2842
4861
  if (linked && linked.length > 0) {
2843
4862
  const resultIdSet = new Set(seedIds);
2844
4863
  const graphCandidateIds = linked.filter((l) => !resultIdSet.has(l.memory_id)).map((l) => l.memory_id);
2845
4864
  if (graphCandidateIds.length > 0) {
2846
- let graphQuery = db.from("memories").select("*").in("id", graphCandidateIds);
4865
+ let graphQuery = db.from("memories").select("*").in("id", graphCandidateIds).not("provider_delegated", "is", false);
2847
4866
  graphQuery = scopeToOwner(graphQuery);
2848
4867
  const { data: graphMemories } = await graphQuery;
2849
4868
  if (graphMemories && graphMemories.length > 0) {
2850
- decryptMemoryBatch(graphMemories);
4869
+ await decryptMemories(graphMemories);
2851
4870
  const linkBoostMap = /* @__PURE__ */ new Map();
2852
4871
  for (const l of linked) {
2853
4872
  const bondWeight = BOND_TYPE_WEIGHTS[l.link_type] ?? 0.4;
@@ -2861,7 +4880,7 @@ async function recallMemories(opts) {
2861
4880
  _score: scoreMemory(mem, scoredOpts) + RETRIEVAL_WEIGHT_GRAPH * (linkBoostMap.get(mem.id) || 0)
2862
4881
  }));
2863
4882
  results = [...results, ...graphScored].sort((a, b) => b._score - a._score).slice(0, limit);
2864
- log10.debug({
4883
+ log16.debug({
2865
4884
  graphExpanded: graphMemories.length,
2866
4885
  linkedTotal: linked.length,
2867
4886
  bondTypes: [...new Set(linked.map((l) => l.link_type))]
@@ -2870,7 +4889,7 @@ async function recallMemories(opts) {
2870
4889
  }
2871
4890
  }
2872
4891
  } catch (err) {
2873
- log10.debug({ err }, "Graph expansion skipped (RPC unavailable)");
4892
+ log16.debug({ err }, "Graph expansion skipped (RPC unavailable)");
2874
4893
  }
2875
4894
  }
2876
4895
  if (results.length >= 3) {
@@ -2886,23 +4905,23 @@ async function recallMemories(opts) {
2886
4905
  ...results.slice(0, results.length - replaceCount),
2887
4906
  ...diverseCandidates.slice(0, replaceCount)
2888
4907
  ].sort((a, b) => b._score - a._score).slice(0, limit);
2889
- log10.debug({ injectedTypes: diverseCandidates.map((m) => m.memory_type) }, "Type diversity applied");
4908
+ log16.debug({ injectedTypes: diverseCandidates.map((m) => m.memory_type) }, "Type diversity applied");
2890
4909
  }
2891
4910
  }
2892
4911
  }
2893
- const finalOwner = getOwnerWallet();
2894
- if (finalOwner) {
2895
- const beforeCount = results.length;
2896
- results = results.filter((m) => m.owner_wallet === finalOwner);
2897
- if (results.length < beforeCount) {
2898
- log10.warn({ stripped: beforeCount - results.length, owner: finalOwner }, "Owner guard stripped foreign memories from recall results");
4912
+ const finalScope = getOwnerScope();
4913
+ {
4914
+ const { kept, stripped } = applyOwnerPostGuard(results, finalScope);
4915
+ if (stripped > 0) {
4916
+ log16.warn({ stripped, botOwn: finalScope === SCOPE_BOT_OWN2 }, "Owner guard stripped foreign memories from recall results");
2899
4917
  }
4918
+ results = kept;
2900
4919
  }
2901
- if (opts.trackAccess !== false) {
4920
+ if (shouldTrackAccess(opts)) {
2902
4921
  const ids = results.map((m) => m.id);
2903
4922
  const sources = results.map((m) => m.source || "");
2904
- updateMemoryAccess(ids, sources).catch((err) => log10.warn({ err }, "Memory access tracking failed"));
2905
- reinforceCoRetrievedLinks(ids).catch((err) => log10.debug({ err }, "Link reinforcement failed"));
4923
+ updateMemoryAccess(ids, sources).catch((err) => log16.warn({ err }, "Memory access tracking failed"));
4924
+ reinforceCoRetrievedLinks(ids).catch((err) => log16.debug({ err }, "Link reinforcement failed"));
2906
4925
  }
2907
4926
  for (const m of results) {
2908
4927
  const paths = linkPathMap.get(m.id);
@@ -2910,7 +4929,7 @@ async function recallMemories(opts) {
2910
4929
  m.link_path = [...paths];
2911
4930
  }
2912
4931
  }
2913
- log10.debug({
4932
+ log16.debug({
2914
4933
  recalled: results.length,
2915
4934
  topScore: results[0]?._score?.toFixed(3),
2916
4935
  query: opts.query?.slice(0, 40),
@@ -2920,7 +4939,7 @@ async function recallMemories(opts) {
2920
4939
  }, "Memories recalled");
2921
4940
  return results;
2922
4941
  } catch (err) {
2923
- log10.error({ err }, "Memory recall failed");
4942
+ log16.error({ err }, "Memory recall failed");
2924
4943
  return [];
2925
4944
  }
2926
4945
  }
@@ -2934,7 +4953,7 @@ function extractQueryTerms(query) {
2934
4953
  }
2935
4954
  function scoreMemory(mem, opts) {
2936
4955
  const now = Date.now();
2937
- const hoursSinceAccess = (now - new Date(mem.last_accessed).getTime()) / (1e3 * 60 * 60);
4956
+ const hoursSinceAccess = Math.max(0, (now - new Date(mem.last_accessed).getTime()) / (1e3 * 60 * 60));
2938
4957
  const recency = Math.pow(RECENCY_DECAY_BASE, hoursSinceAccess);
2939
4958
  let textScore = 0.5;
2940
4959
  if (opts.query) {
@@ -2970,7 +4989,7 @@ function scoreMemory(mem, opts) {
2970
4989
  }
2971
4990
  const bm25Rank = opts._bm25Scores?.get(mem.id) || 0;
2972
4991
  if (bm25Rank > 0) {
2973
- rawScore += 0.15 * Math.min(bm25Rank, 1);
4992
+ rawScore += RETRIEVAL_WEIGHT_BM25 * Math.min(bm25Rank, 1);
2974
4993
  }
2975
4994
  const typeBoost = KNOWLEDGE_TYPE_BOOST[mem.memory_type] || 0;
2976
4995
  rawScore += typeBoost;
@@ -2991,7 +5010,7 @@ async function recallMemorySummaries(opts) {
2991
5010
  const limit = opts.limit || 10;
2992
5011
  const minDecay = opts.minDecay ?? 0.1;
2993
5012
  try {
2994
- let query = db.from("memories").select("id, memory_type, summary, tags, concepts, importance, decay_factor, created_at, source").gte("decay_factor", minDecay).not("source", "in", '("demo","demo-maas")').order("importance", { ascending: false }).order("created_at", { ascending: false }).limit(limit);
5013
+ let query = db.from("memories").select("id, memory_type, summary, tags, concepts, importance, decay_factor, created_at, source").gte("decay_factor", minDecay).not("source", "in", '("demo","demo-maas")').not("provider_delegated", "is", false).order("importance", { ascending: false }).order("created_at", { ascending: false }).limit(limit);
2995
5014
  query = scopeToOwner(query);
2996
5015
  if (opts.memoryTypes && opts.memoryTypes.length > 0) {
2997
5016
  query = query.in("memory_type", opts.memoryTypes);
@@ -3010,12 +5029,12 @@ async function recallMemorySummaries(opts) {
3010
5029
  }
3011
5030
  const { data, error } = await query;
3012
5031
  if (error) {
3013
- log10.error({ error: error.message }, "Memory summary recall failed");
5032
+ log16.error({ error: error.message }, "Memory summary recall failed");
3014
5033
  return [];
3015
5034
  }
3016
5035
  return data || [];
3017
5036
  } catch (err) {
3018
- log10.error({ err }, "Memory summary recall failed");
5037
+ log16.error({ err }, "Memory summary recall failed");
3019
5038
  return [];
3020
5039
  }
3021
5040
  }
@@ -3027,28 +5046,23 @@ async function hydrateMemories(ids) {
3027
5046
  query = scopeToOwner(query);
3028
5047
  const { data, error } = await query;
3029
5048
  if (error) {
3030
- log10.error({ error: error.message }, "Memory hydration failed");
5049
+ log16.error({ error: error.message }, "Memory hydration failed");
3031
5050
  return [];
3032
5051
  }
3033
- return decryptMemoryBatch(data || []);
5052
+ return await decryptMemories(data || []);
3034
5053
  } catch (err) {
3035
- log10.error({ err }, "Memory hydration failed");
5054
+ log16.error({ err }, "Memory hydration failed");
3036
5055
  return [];
3037
5056
  }
3038
5057
  }
3039
- async function updateMemoryAccess(ids, sources = []) {
5058
+ async function updateMemoryAccess(ids, _sources = []) {
3040
5059
  if (ids.length === 0) return;
3041
5060
  const db = getDb();
3042
- const importanceBoosts = ids.map((_, i) => {
3043
- const source = sources[i] || "";
3044
- return INTERNAL_MEMORY_SOURCES.has(source) ? INTERNAL_IMPORTANCE_BOOST : 0.02;
3045
- });
3046
5061
  const { error } = await db.rpc("batch_boost_memory_access", {
3047
- memory_ids: ids,
3048
- importance_boosts: importanceBoosts
5062
+ memory_ids: ids
3049
5063
  });
3050
5064
  if (error) {
3051
- log10.warn({ error: error.message, ids }, "Batch memory access update failed");
5065
+ log16.warn({ error: error.message, ids }, "Batch memory access update failed");
3052
5066
  }
3053
5067
  }
3054
5068
  async function createMemoryLink(sourceId, targetId, linkType, strength = 0.5) {
@@ -3061,7 +5075,7 @@ async function createMemoryLink(sourceId, targetId, linkType, strength = 0.5) {
3061
5075
  strength: clamp(strength, 0, 1)
3062
5076
  }, { onConflict: "source_id,target_id,link_type" });
3063
5077
  if (error) {
3064
- log10.debug({ error: error.message, sourceId, targetId, linkType }, "Link creation failed");
5078
+ log16.debug({ error: error.message, sourceId, targetId, linkType }, "Link creation failed");
3065
5079
  }
3066
5080
  }
3067
5081
  async function createMemoryLinksBatch(links) {
@@ -3083,7 +5097,7 @@ async function createMemoryLinksBatch(links) {
3083
5097
  const db = getDb();
3084
5098
  const { error } = await db.from("memory_links").upsert(rows, { onConflict: "source_id,target_id,link_type" });
3085
5099
  if (error) {
3086
- log10.debug({ error: error.message, count: rows.length }, "Batch link creation failed");
5100
+ log16.debug({ error: error.message, count: rows.length }, "Batch link creation failed");
3087
5101
  }
3088
5102
  }
3089
5103
  async function autoLinkMemory(memoryId, opts) {
@@ -3102,7 +5116,7 @@ async function autoLinkMemory(memoryId, opts) {
3102
5116
  query_embedding: JSON.stringify(embedding),
3103
5117
  match_threshold: LINK_SIMILARITY_THRESHOLD,
3104
5118
  match_count: MAX_AUTO_LINKS * 2,
3105
- filter_owner: getOwnerWallet() || null
5119
+ filter_owner: getOwnerScope()
3106
5120
  });
3107
5121
  if (similar) {
3108
5122
  const similarIds = similar.map((s) => s.id).filter((id) => id !== memoryId);
@@ -3151,7 +5165,7 @@ async function autoLinkMemory(memoryId, opts) {
3151
5165
  }
3152
5166
  if (pendingLinks.length > 0) {
3153
5167
  await createMemoryLinksBatch(pendingLinks);
3154
- log10.debug({ memoryId, linksCreated: pendingLinks.length }, "Auto-linked memory");
5168
+ log16.debug({ memoryId, linksCreated: pendingLinks.length }, "Auto-linked memory");
3155
5169
  }
3156
5170
  }
3157
5171
  function classifyLinkType(newMem, candidate, newConcepts) {
@@ -3173,16 +5187,16 @@ async function reinforceCoRetrievedLinks(ids) {
3173
5187
  boost_amount: LINK_CO_RETRIEVAL_BOOST
3174
5188
  });
3175
5189
  if (error) {
3176
- log10.debug({ error: error.message }, "Link reinforcement RPC failed");
5190
+ log16.debug({ error: error.message }, "Link reinforcement RPC failed");
3177
5191
  } else if (data && data > 0) {
3178
- log10.debug({ boosted: data }, "Co-retrieval link reinforcement applied");
5192
+ log16.debug({ boosted: data }, "Co-retrieval link reinforcement applied");
3179
5193
  }
3180
5194
  }
3181
5195
  async function extractAndLinkEntitiesForMemory(memoryId, opts) {
3182
5196
  try {
3183
5197
  await extractAndLinkEntities(memoryId, opts.content, opts.summary, opts.relatedUser);
3184
5198
  } catch (err) {
3185
- log10.debug({ err, memoryId }, "Entity extraction failed");
5199
+ log16.debug({ err, memoryId }, "Entity extraction failed");
3186
5200
  }
3187
5201
  }
3188
5202
  async function decayMemories() {
@@ -3198,17 +5212,17 @@ async function decayMemories() {
3198
5212
  cutoff
3199
5213
  });
3200
5214
  if (error) {
3201
- log10.warn({ error: error.message, memType }, "Batch decay failed for type");
5215
+ log16.warn({ error: error.message, memType }, "Batch decay failed for type");
3202
5216
  continue;
3203
5217
  }
3204
5218
  totalDecayed += data || 0;
3205
5219
  }
3206
5220
  if (totalDecayed > 0) {
3207
- log10.info({ decayed: totalDecayed }, "Type-specific memory decay applied");
5221
+ log16.info({ decayed: totalDecayed }, "Type-specific memory decay applied");
3208
5222
  }
3209
5223
  return totalDecayed;
3210
5224
  } catch (err) {
3211
- log10.error({ err }, "Memory decay failed");
5225
+ log16.error({ err }, "Memory decay failed");
3212
5226
  return 0;
3213
5227
  }
3214
5228
  }
@@ -3218,7 +5232,7 @@ async function deleteMemory(id) {
3218
5232
  query = scopeToOwner(query);
3219
5233
  const { error } = await query;
3220
5234
  if (error) {
3221
- log10.error({ error: error.message, id }, "Failed to delete memory");
5235
+ log16.error({ error: error.message, id }, "Failed to delete memory");
3222
5236
  return false;
3223
5237
  }
3224
5238
  return true;
@@ -3236,7 +5250,7 @@ async function updateMemory(id, patches) {
3236
5250
  query = scopeToOwner(query);
3237
5251
  const { error } = await query;
3238
5252
  if (error) {
3239
- log10.error({ error: error.message, id }, "Failed to update memory");
5253
+ log16.error({ error: error.message, id }, "Failed to update memory");
3240
5254
  return false;
3241
5255
  }
3242
5256
  return true;
@@ -3257,10 +5271,10 @@ async function listMemories(opts) {
3257
5271
  if (opts.min_importance !== void 0) dataQ = dataQ.gte("importance", opts.min_importance);
3258
5272
  const { data, error } = await dataQ;
3259
5273
  if (error) {
3260
- log10.error({ error: error.message }, "Failed to list memories");
5274
+ log16.error({ error: error.message }, "Failed to list memories");
3261
5275
  return { memories: [], total: 0 };
3262
5276
  }
3263
- return { memories: decryptMemoryBatch(data || []), total: count ?? 0 };
5277
+ return { memories: await decryptMemories(data || []), total: count ?? 0 };
3264
5278
  }
3265
5279
  async function getMemoryStats() {
3266
5280
  const db = getDb();
@@ -3278,25 +5292,31 @@ async function getMemoryStats() {
3278
5292
  embeddedCount: 0
3279
5293
  };
3280
5294
  try {
3281
- let countQuery = db.from("memories").select("id", { count: "exact", head: true }).gt("decay_factor", MEMORY_MIN_DECAY);
5295
+ let countQuery = db.from("memories").select("id", { count: "exact", head: true });
3282
5296
  countQuery = scopeToOwner(countQuery);
3283
5297
  const { count: totalCount } = await countQuery;
3284
5298
  stats.total = totalCount || 0;
3285
- let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).gt("decay_factor", MEMORY_MIN_DECAY).not("embedding", "is", null);
5299
+ let embeddedQuery = db.from("memories").select("id", { count: "exact", head: true }).not("embedding", "is", null);
3286
5300
  embeddedQuery = scopeToOwner(embeddedQuery);
3287
5301
  const { count: embCount } = await embeddedQuery;
3288
5302
  stats.embeddedCount = embCount || 0;
3289
- const PAGE_SIZE = 5e3;
5303
+ const TYPES = ["episodic", "semantic", "procedural", "self_model", "introspective"];
5304
+ await Promise.all(TYPES.map(async (type) => {
5305
+ let q = db.from("memories").select("id", { count: "exact", head: true }).eq("memory_type", type);
5306
+ q = scopeToOwner(q);
5307
+ const { count: count2 } = await q;
5308
+ stats.byType[type] = count2 || 0;
5309
+ }));
5310
+ const PAGE_SIZE = 1e3;
5311
+ const MAX_PAGES = 20;
3290
5312
  let allMemories = [];
3291
- let page = 0;
3292
- while (true) {
3293
- let pageQuery = db.from("memories").select("memory_type, importance, decay_factor, created_at, related_user, tags, concepts").gt("decay_factor", MEMORY_MIN_DECAY).range(page * PAGE_SIZE, (page + 1) * PAGE_SIZE - 1);
5313
+ for (let page = 0; page < MAX_PAGES; page++) {
5314
+ let pageQuery = db.from("memories").select("importance, decay_factor, created_at, related_user, owner_wallet, tags, concepts").order("id", { ascending: true }).range(page * PAGE_SIZE, (page + 1) * PAGE_SIZE - 1);
3294
5315
  pageQuery = scopeToOwner(pageQuery);
3295
5316
  const { data: pageData } = await pageQuery;
3296
5317
  if (!pageData || pageData.length === 0) break;
3297
5318
  allMemories = allMemories.concat(pageData);
3298
5319
  if (pageData.length < PAGE_SIZE) break;
3299
- page++;
3300
5320
  }
3301
5321
  if (allMemories.length > 0) {
3302
5322
  let impSum = 0;
@@ -3305,11 +5325,10 @@ async function getMemoryStats() {
3305
5325
  const conceptCounts = {};
3306
5326
  const users = /* @__PURE__ */ new Set();
3307
5327
  for (const m of allMemories) {
3308
- const type = m.memory_type;
3309
- if (type in stats.byType) stats.byType[type]++;
3310
5328
  impSum += m.importance;
3311
5329
  decaySum += m.decay_factor;
3312
5330
  if (m.related_user) users.add(m.related_user);
5331
+ if (m.owner_wallet) users.add(m.owner_wallet);
3313
5332
  if (m.tags) {
3314
5333
  for (const tag of m.tags) {
3315
5334
  tagCounts[tag] = (tagCounts[tag] || 0) + 1;
@@ -3330,13 +5349,15 @@ async function getMemoryStats() {
3330
5349
  stats.oldestMemory = sorted[0] || null;
3331
5350
  stats.newestMemory = sorted[sorted.length - 1] || null;
3332
5351
  }
3333
- const { count, error: dreamError } = await db.from("dream_logs").select("id", { count: "exact", head: true });
5352
+ let dreamQuery = db.from("dream_logs").select("id", { count: "exact", head: true });
5353
+ dreamQuery = scopeToOwner(dreamQuery);
5354
+ const { count, error: dreamError } = await dreamQuery;
3334
5355
  if (dreamError) {
3335
- log10.warn({ error: dreamError.message }, "Failed to count dream logs");
5356
+ log16.warn({ error: dreamError.message }, "Failed to count dream logs");
3336
5357
  }
3337
5358
  stats.totalDreamSessions = count || 0;
3338
5359
  } catch (err) {
3339
- log10.error({ err }, "Failed to get memory stats");
5360
+ log16.error({ err }, "Failed to get memory stats");
3340
5361
  }
3341
5362
  return stats;
3342
5363
  }
@@ -3350,10 +5371,10 @@ async function getRecentMemories(hours, types, limit) {
3350
5371
  }
3351
5372
  const { data, error } = await query;
3352
5373
  if (error) {
3353
- log10.error({ error: error.message }, "Failed to get recent memories");
5374
+ log16.error({ error: error.message }, "Failed to get recent memories");
3354
5375
  return [];
3355
5376
  }
3356
- return decryptMemoryBatch(data || []);
5377
+ return await decryptMemories(data || []);
3357
5378
  }
3358
5379
  async function getSelfModel() {
3359
5380
  const db = getDb();
@@ -3361,10 +5382,10 @@ async function getSelfModel() {
3361
5382
  query = scopeToOwner(query);
3362
5383
  const { data, error } = await query;
3363
5384
  if (error) {
3364
- log10.error({ error: error.message }, "Failed to get self model");
5385
+ log16.error({ error: error.message }, "Failed to get self model");
3365
5386
  return [];
3366
5387
  }
3367
- return decryptMemoryBatch(data || []);
5388
+ return await decryptMemories(data || []);
3368
5389
  }
3369
5390
  async function storeDreamLog(sessionType, inputMemoryIds, output, newMemoryIds) {
3370
5391
  const db = getDb();
@@ -3372,54 +5393,48 @@ async function storeDreamLog(sessionType, inputMemoryIds, output, newMemoryIds)
3372
5393
  session_type: sessionType,
3373
5394
  input_memory_ids: inputMemoryIds,
3374
5395
  output: output.slice(0, MEMORY_MAX_CONTENT_LENGTH),
3375
- new_memories_created: newMemoryIds
5396
+ new_memories_created: newMemoryIds,
5397
+ // Stamp owner so getMemoryStats can scope the dream count (migration 021).
5398
+ owner_wallet: getOwnerWallet() === SCOPE_BOT_OWN2 ? null : getOwnerWallet()
3376
5399
  });
3377
5400
  if (error) {
3378
- log10.error({ error: error.message }, "Failed to store dream log");
5401
+ log16.error({ error: error.message }, "Failed to store dream log");
3379
5402
  }
3380
5403
  }
3381
5404
  function formatMemoryContext(memories) {
3382
5405
  if (memories.length === 0) return "";
3383
5406
  const lines = ["## Memory Recall"];
3384
- const episodic = memories.filter((m) => m.memory_type === "episodic");
3385
- const semantic = memories.filter((m) => m.memory_type === "semantic");
3386
- const procedural = memories.filter((m) => m.memory_type === "procedural");
3387
- const selfModel = memories.filter((m) => m.memory_type === "self_model");
3388
- const introspective = memories.filter((m) => m.memory_type === "introspective");
5407
+ const episodic = memories.filter((m) => m.memory_type === "episodic").sort(byMemoryDateAsc);
5408
+ const semantic = memories.filter((m) => m.memory_type === "semantic").sort(byMemoryDateAsc);
5409
+ const procedural = memories.filter((m) => m.memory_type === "procedural").sort(byMemoryDateAsc);
5410
+ const selfModel = memories.filter((m) => m.memory_type === "self_model").sort(byMemoryDateAsc);
5411
+ const introspective = memories.filter((m) => m.memory_type === "introspective").sort(byMemoryDateAsc);
3389
5412
  if (episodic.length > 0) {
3390
5413
  lines.push("### Past Interactions");
3391
- for (const m of episodic) {
3392
- lines.push(`- [${timeAgo(m.created_at)}] ${m.summary}`);
3393
- }
5414
+ for (const m of episodic) lines.push(renderGroundedLine(m));
3394
5415
  }
3395
5416
  if (semantic.length > 0) {
3396
5417
  lines.push("### Things You Know");
3397
- for (const m of semantic) {
3398
- lines.push(`- ${m.summary}`);
3399
- }
5418
+ for (const m of semantic) lines.push(renderGroundedLine(m));
3400
5419
  }
3401
5420
  if (procedural.length > 0) {
3402
5421
  lines.push("### Learned Strategies (from past outcomes)");
3403
5422
  for (const m of procedural) {
3404
5423
  const meta = m.metadata;
3405
5424
  const confidence = meta?.positiveRate != null ? ` [${Math.round(meta.positiveRate * 100)}% success rate, based on ${meta.basedOn || "?"} interactions]` : "";
3406
- lines.push(`- ${m.summary}${confidence}`);
5425
+ lines.push(renderGroundedLine(m, confidence));
3407
5426
  }
3408
5427
  }
3409
5428
  if (introspective.length > 0) {
3410
5429
  lines.push("### Your Own Reflections");
3411
- for (const m of introspective) {
3412
- lines.push(`- [${timeAgo(m.created_at)}] ${m.summary}`);
3413
- }
5430
+ for (const m of introspective) lines.push(renderGroundedLine(m));
3414
5431
  }
3415
5432
  if (selfModel.length > 0) {
3416
5433
  lines.push("### Self-Observations");
3417
- for (const m of selfModel) {
3418
- lines.push(`- ${m.summary}`);
3419
- }
5434
+ for (const m of selfModel) lines.push(renderGroundedLine(m));
3420
5435
  }
3421
5436
  lines.push("");
3422
- lines.push("You REMEMBER these interactions and facts. Reference them naturally if relevant.");
5437
+ lines.push(CONTEXT_GROUNDING_RULES);
3423
5438
  if (procedural.length > 0) {
3424
5439
  lines.push("");
3425
5440
  lines.push("IMPORTANT: You MUST follow the Learned Strategies above. They are behavioral rules you derived from analyzing your own past successes and failures. Apply them to this response.");
@@ -3444,10 +5459,10 @@ async function scoreImportanceWithLLM(description, fallbackOpts) {
3444
5459
  if (!isNaN(parsed) && parsed >= 1 && parsed <= 10) {
3445
5460
  return parsed / 10;
3446
5461
  }
3447
- log10.warn({ response }, "LLM importance score unparseable, using fallback");
5462
+ log16.warn({ response }, "LLM importance score unparseable, using fallback");
3448
5463
  return calculateImportance(fallbackOpts || {});
3449
5464
  } catch (err) {
3450
- log10.warn({ err }, "LLM importance scoring failed, using fallback");
5465
+ log16.warn({ err }, "LLM importance scoring failed, using fallback");
3451
5466
  return calculateImportance(fallbackOpts || {});
3452
5467
  }
3453
5468
  }
@@ -3471,7 +5486,8 @@ async function matchByEmbedding(opts) {
3471
5486
  query_embedding: JSON.stringify(opts.embedding),
3472
5487
  match_threshold: opts.threshold,
3473
5488
  match_count: opts.limit,
3474
- filter_owner: opts.ownerWallet ?? null
5489
+ // Explicit ownerWallet wins; otherwise the ambient scope (sentinel-aware, C0).
5490
+ filter_owner: opts.ownerWallet ?? getOwnerScope()
3475
5491
  });
3476
5492
  return (data ?? []).map((r) => ({
3477
5493
  id: r.id,
@@ -3494,32 +5510,52 @@ function moodToValence(mood) {
3494
5510
  return 0;
3495
5511
  }
3496
5512
  }
3497
- var import_crypto2, EMBED_CACHE_MAX, embeddingCache, _ownerWallet, SCOPE_BOT_OWN, HASH_ID_PREFIX, log10, DEDUP_TTL_MS, dedupCache, STOPWORDS;
5513
+ var import_crypto3, EMBED_CACHE_MAX, embeddingCache, _ownerWallet, SCOPE_BOT_OWN2, HASH_ID_PREFIX, log16, DEDUP_TTL_MS, dedupCache, installedPacksCache, INSTALLED_PACKS_TTL_MS, topicEmbeddingCache, topicEmbeddingPopulationLock, TOKENISATION_SKIP_SOURCES, outboxDegradeLogged, STOPWORDS, CONTEXT_GROUNDING_RULES;
3498
5514
  var init_memory = __esm({
3499
5515
  "packages/brain/src/memory/memory.ts"() {
3500
5516
  "use strict";
3501
5517
  init_database();
5518
+ init_config();
3502
5519
  init_logger();
5520
+ init_memory_grounding();
3503
5521
  init_utils();
3504
5522
  init_claude_client();
3505
5523
  init_solana_client();
5524
+ init_src();
3506
5525
  init_embeddings();
5526
+ init_migration_profile();
5527
+ init_wiki_packs();
3507
5528
  init_config2();
3508
5529
  init_bm25_search();
5530
+ init_content_tokens();
5531
+ init_reconcile_shadow();
5532
+ init_memory_encryption();
3509
5533
  init_openrouter_client();
3510
5534
  init_encryption();
5535
+ init_memory_decryption();
3511
5536
  init_event_bus();
3512
- import_crypto2 = require("crypto");
5537
+ import_crypto3 = require("crypto");
3513
5538
  init_graph();
3514
5539
  init_owner_context();
3515
5540
  EMBED_CACHE_MAX = 200;
3516
5541
  embeddingCache = /* @__PURE__ */ new Map();
3517
5542
  _ownerWallet = null;
3518
- SCOPE_BOT_OWN = "__BOT_OWN__";
5543
+ SCOPE_BOT_OWN2 = "__BOT_OWN__";
3519
5544
  HASH_ID_PREFIX = "clude";
3520
- log10 = createChildLogger("memory");
5545
+ log16 = createChildLogger("memory");
3521
5546
  DEDUP_TTL_MS = 10 * 60 * 1e3;
3522
5547
  dedupCache = /* @__PURE__ */ new Map();
5548
+ installedPacksCache = /* @__PURE__ */ new Map();
5549
+ INSTALLED_PACKS_TTL_MS = 6e4;
5550
+ topicEmbeddingCache = /* @__PURE__ */ new Map();
5551
+ topicEmbeddingPopulationLock = null;
5552
+ TOKENISATION_SKIP_SOURCES = /* @__PURE__ */ new Set([
5553
+ "demo",
5554
+ "demo-maas",
5555
+ "locomo-benchmark",
5556
+ "longmemeval-benchmark"
5557
+ ]);
5558
+ outboxDegradeLogged = false;
3523
5559
  STOPWORDS = /* @__PURE__ */ new Set([
3524
5560
  "the",
3525
5561
  "a",
@@ -3604,18 +5640,92 @@ var init_memory = __esm({
3604
5640
  "our",
3605
5641
  "their"
3606
5642
  ]);
5643
+ CONTEXT_GROUNDING_RULES = `Ground specific claims \u2014 facts, preferences, history, dates, numbers, names \u2014 in the memories above; if a detail isn't written here, say you don't remember it rather than guessing. Each line is dated: when two memories disagree, read them as a timeline \u2014 use the most recent value for a question about now, and the value that was in effect at the time for a question about a specific past date ("as of \u2026"); never blend conflicting values. Don't invent memories or details that aren't above. General knowledge is unaffected \u2014 answer those normally.`;
5644
+ }
5645
+ });
5646
+
5647
+ // packages/brain/src/memory/memory-revoke.ts
5648
+ async function revokeMemory(db, memoryId) {
5649
+ const { data: mem } = await db.from("memories").select("summary, embedding, encrypted, provider_delegated").eq("id", memoryId).maybeSingle();
5650
+ if (!mem || mem.encrypted !== true || mem.provider_delegated === false) {
5651
+ return { revoked: false, reason: "not_delegated" };
5652
+ }
5653
+ const { data: wrap } = await db.from("memory_dek_wraps").select("wrapped_dek, wrap_pubkey").eq("memory_id", memoryId).eq("recipient", "provider").maybeSingle();
5654
+ if (!wrap) return { revoked: false, reason: "no_provider_wrap" };
5655
+ let providerSecret;
5656
+ try {
5657
+ providerSecret = loadProviderKeypair().secretKey;
5658
+ } catch {
5659
+ return { revoked: false, reason: "no_provider_key" };
5660
+ }
5661
+ const dek = unwrapDek(wrap.wrapped_dek, wrap.wrap_pubkey, providerSecret);
5662
+ if (!dek) return { revoked: false, reason: "unwrap_failed" };
5663
+ const summaryCt = encryptField(String(mem.summary ?? ""), dek);
5664
+ const embeddingCt = mem.embedding != null ? encryptField(String(mem.embedding), dek) : "";
5665
+ const { error } = await db.rpc("revoke_memory", {
5666
+ p_memory_id: memoryId,
5667
+ p_summary_ct: summaryCt,
5668
+ p_embedding_ct: embeddingCt
5669
+ });
5670
+ if (error) {
5671
+ log17.error({ memoryId, error: error.message }, "revoke_memory RPC failed");
5672
+ return { revoked: false, reason: "rpc_failed" };
5673
+ }
5674
+ log17.info({ memoryId }, "Memory revoked \u2014 provider access destroyed");
5675
+ return { revoked: true };
5676
+ }
5677
+ async function redelegateMemory(db, memoryId, providerWrap) {
5678
+ const { data: mem } = await db.from("memories").select("content, summary_ciphertext, embedding_ciphertext, encrypted").eq("id", memoryId).maybeSingle();
5679
+ if (!mem || mem.encrypted !== true) return { redelegated: false, reason: "not_encrypted" };
5680
+ let providerSecret;
5681
+ try {
5682
+ providerSecret = loadProviderKeypair().secretKey;
5683
+ } catch {
5684
+ return { redelegated: false, reason: "no_provider_key" };
5685
+ }
5686
+ const dek = unwrapDek(providerWrap.wrapped_dek, providerWrap.wrap_pubkey, providerSecret);
5687
+ if (!dek) return { redelegated: false, reason: "invalid_wrap" };
5688
+ const content = decryptField(String(mem.content ?? ""), dek);
5689
+ if (content === null) return { redelegated: false, reason: "invalid_wrap" };
5690
+ const summary = mem.summary_ciphertext ? decryptField(mem.summary_ciphertext, dek) : "";
5691
+ const embedding = mem.embedding_ciphertext ? decryptField(mem.embedding_ciphertext, dek) : "";
5692
+ const { error } = await db.rpc("redelegate_memory", {
5693
+ p_memory_id: memoryId,
5694
+ p_summary: summary ?? "",
5695
+ p_embedding: embedding ?? "",
5696
+ p_content: content,
5697
+ p_wrapped_dek: providerWrap.wrapped_dek,
5698
+ p_wrap_pubkey: providerWrap.wrap_pubkey
5699
+ });
5700
+ if (error) {
5701
+ log17.error({ memoryId, error: error.message }, "redelegate_memory RPC failed");
5702
+ return { redelegated: false, reason: "rpc_failed" };
5703
+ }
5704
+ log17.info({ memoryId }, "Memory re-delegated \u2014 provider access restored");
5705
+ return { redelegated: true };
5706
+ }
5707
+ var log17;
5708
+ var init_memory_revoke = __esm({
5709
+ "packages/brain/src/memory/memory-revoke.ts"() {
5710
+ "use strict";
5711
+ init_memory_envelope();
5712
+ init_encryption_keys();
5713
+ init_logger();
5714
+ log17 = createChildLogger("memory-revoke");
3607
5715
  }
3608
5716
  });
3609
5717
 
3610
5718
  // packages/brain/src/memory/index.ts
3611
5719
  var memory_exports2 = {};
3612
5720
  __export(memory_exports2, {
3613
- SCOPE_BOT_OWN: () => SCOPE_BOT_OWN,
5721
+ SCOPE_BOT_OWN: () => SCOPE_BOT_OWN2,
3614
5722
  _setOwnerWallet: () => _setOwnerWallet,
3615
5723
  calculateImportance: () => calculateImportance,
3616
5724
  createMemoryLink: () => createMemoryLink,
3617
5725
  createMemoryLinksBatch: () => createMemoryLinksBatch,
3618
5726
  decayMemories: () => decayMemories,
5727
+ decryptMemories: () => decryptMemories,
5728
+ decryptOneContent: () => decryptOneContent,
3619
5729
  deleteMemory: () => deleteMemory,
3620
5730
  formatMemoryContext: () => formatMemoryContext,
3621
5731
  generateHashId: () => generateHashId,
@@ -3625,11 +5735,14 @@ __export(memory_exports2, {
3625
5735
  getSelfModel: () => getSelfModel,
3626
5736
  hydrateMemories: () => hydrateMemories,
3627
5737
  inferConcepts: () => inferConcepts,
5738
+ invalidateInstalledPacksCache: () => invalidateInstalledPacksCache,
3628
5739
  isValidHashId: () => isValidHashId,
3629
5740
  listMemories: () => listMemories,
3630
5741
  moodToValence: () => moodToValence,
3631
5742
  recallMemories: () => recallMemories,
3632
5743
  recallMemorySummaries: () => recallMemorySummaries,
5744
+ redelegateMemory: () => redelegateMemory,
5745
+ revokeMemory: () => revokeMemory,
3633
5746
  scopeToOwner: () => scopeToOwner,
3634
5747
  scoreImportanceWithLLM: () => scoreImportanceWithLLM,
3635
5748
  scoreMemory: () => scoreMemory,
@@ -3641,6 +5754,8 @@ var init_memory2 = __esm({
3641
5754
  "packages/brain/src/memory/index.ts"() {
3642
5755
  "use strict";
3643
5756
  init_memory();
5757
+ init_memory_decryption();
5758
+ init_memory_revoke();
3644
5759
  }
3645
5760
  });
3646
5761
 
@@ -3770,7 +5885,7 @@ function shuffle(arr) {
3770
5885
  async function runDeepConnectionPhase(ownerWallet) {
3771
5886
  const SKIPPED = { linksCreated: 0, skipped: true, topLinks: [] };
3772
5887
  if (process.env.JEPA_ENABLED !== "true") {
3773
- log11.debug("JEPA disabled \u2014 skipping deep connection phase");
5888
+ log18.debug("JEPA disabled \u2014 skipping deep connection phase");
3774
5889
  return SKIPPED;
3775
5890
  }
3776
5891
  const jepaClient = getJepaClient();
@@ -3793,16 +5908,16 @@ async function runDeepConnectionPhase(ownerWallet) {
3793
5908
  }
3794
5909
  }
3795
5910
  if (candidates.length === 0) {
3796
- log11.debug("No candidate memories \u2014 skipping deep connection phase");
5911
+ log18.debug("No candidate memories \u2014 skipping deep connection phase");
3797
5912
  return SKIPPED;
3798
5913
  }
3799
5914
  const queriedRecently = await fetchJepaQueriedSince(Date.now() - TWENTY_FOUR_HOURS_MS);
3800
5915
  const toProcess = candidates.filter((m) => !queriedRecently.has(m.id));
3801
5916
  if (toProcess.length === 0) {
3802
- log11.debug("All candidates JEPA-queried in last 24 h \u2014 skipping");
5917
+ log18.debug("All candidates JEPA-queried in last 24 h \u2014 skipping");
3803
5918
  return SKIPPED;
3804
5919
  }
3805
- log11.info({ total: candidates.length, toProcess: toProcess.length }, "Deep connection phase: processing memories");
5920
+ log18.info({ total: candidates.length, toProcess: toProcess.length }, "Deep connection phase: processing memories");
3806
5921
  const allLinks = [];
3807
5922
  const topLinks = [];
3808
5923
  for (const mem of toProcess) {
@@ -3888,16 +6003,16 @@ async function runDeepConnectionPhase(ownerWallet) {
3888
6003
  new_memories_created: []
3889
6004
  });
3890
6005
  } catch (err) {
3891
- log11.warn({ err }, "Failed to write deep_connection dream log");
6006
+ log18.warn({ err }, "Failed to write deep_connection dream log");
3892
6007
  }
3893
- log11.info({ linksCreated: allLinks.length, topLinks: topLinks.length }, "Deep connection phase complete");
6008
+ log18.info({ linksCreated: allLinks.length, topLinks: topLinks.length }, "Deep connection phase complete");
3894
6009
  return {
3895
6010
  linksCreated: allLinks.length,
3896
6011
  skipped: false,
3897
6012
  topLinks
3898
6013
  };
3899
6014
  }
3900
- var log11, TWENTY_FOUR_HOURS_MS, RECENT_SAMPLE, HIGH_IMPORTANCE_SAMPLE, HIGH_IMPORTANCE_THRESHOLD, EMBEDDING_SIMILARITY_THRESHOLD, MATCH_LIMIT, TOP_LINKS_PER_MEMORY, _jepaClient;
6015
+ var log18, TWENTY_FOUR_HOURS_MS, RECENT_SAMPLE, HIGH_IMPORTANCE_SAMPLE, HIGH_IMPORTANCE_THRESHOLD, EMBEDDING_SIMILARITY_THRESHOLD, MATCH_LIMIT, TOP_LINKS_PER_MEMORY, _jepaClient;
3901
6016
  var init_deep_connection = __esm({
3902
6017
  "packages/brain/src/memory/dream/deep-connection.ts"() {
3903
6018
  "use strict";
@@ -3905,7 +6020,7 @@ var init_deep_connection = __esm({
3905
6020
  init_logger();
3906
6021
  init_memory();
3907
6022
  init_jepa();
3908
- log11 = createChildLogger("dream-deep-connection");
6023
+ log18 = createChildLogger("dream-deep-connection");
3909
6024
  TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
3910
6025
  RECENT_SAMPLE = 15;
3911
6026
  HIGH_IMPORTANCE_SAMPLE = 5;
@@ -3940,7 +6055,7 @@ async function throttlePost() {
3940
6055
  const elapsed = now - lastPostTime;
3941
6056
  if (elapsed < MIN_POST_GAP_MS) {
3942
6057
  const wait = MIN_POST_GAP_MS - elapsed;
3943
- log12.debug({ waitMs: wait }, "Throttling post");
6058
+ log19.debug({ waitMs: wait }, "Throttling post");
3944
6059
  await new Promise((r) => setTimeout(r, wait));
3945
6060
  }
3946
6061
  lastPostTime = Date.now();
@@ -3952,7 +6067,7 @@ async function withRetry(fn) {
3952
6067
  if (err?.code === 429 || err?.data?.status === 429) {
3953
6068
  const retryAfter = Number(err?.rateLimit?.reset || 0) * 1e3 - Date.now();
3954
6069
  const wait = Math.max(retryAfter, 6e4);
3955
- log12.warn({ waitMs: wait }, "X API 429 \u2014 backing off");
6070
+ log19.warn({ waitMs: wait }, "X API 429 \u2014 backing off");
3956
6071
  await new Promise((r) => setTimeout(r, wait));
3957
6072
  return fn();
3958
6073
  }
@@ -3973,20 +6088,20 @@ function smartTruncate(text, maxLength = MAX_TWEET_LENGTH) {
3973
6088
  }
3974
6089
  async function postReply(tweetId, text) {
3975
6090
  const truncated = smartTruncate(stripEmDashes(text));
3976
- log12.info({ tweetId, originalLength: text.length, length: truncated.length }, "Posting reply");
6091
+ log19.info({ tweetId, originalLength: text.length, length: truncated.length }, "Posting reply");
3977
6092
  await throttlePost();
3978
6093
  const result = await withRetry(() => rwClient.v2.reply(truncated, tweetId));
3979
6094
  return result.data.id;
3980
6095
  }
3981
6096
  async function postTweet(text) {
3982
6097
  const truncated = smartTruncate(stripEmDashes(text));
3983
- log12.info({ originalLength: text.length, length: truncated.length }, "Posting tweet");
6098
+ log19.info({ originalLength: text.length, length: truncated.length }, "Posting tweet");
3984
6099
  await throttlePost();
3985
6100
  const result = await withRetry(() => rwClient.v2.tweet(truncated));
3986
6101
  return result.data.id;
3987
6102
  }
3988
6103
  async function postThread(texts) {
3989
- log12.info({ count: texts.length }, "Posting thread");
6104
+ log19.info({ count: texts.length }, "Posting thread");
3990
6105
  const tweetIds = [];
3991
6106
  let previousId;
3992
6107
  for (const text of texts) {
@@ -4040,7 +6155,7 @@ function getUsernameOrId(userId) {
4040
6155
  return _usernameCache.get(userId) || userId;
4041
6156
  }
4042
6157
  async function getMentions(sinceId) {
4043
- log12.debug({ sinceId }, "Fetching mentions");
6158
+ log19.debug({ sinceId }, "Fetching mentions");
4044
6159
  const params = {
4045
6160
  "tweet.fields": "created_at,author_id,conversation_id,text",
4046
6161
  "expansions": "author_id",
@@ -4079,12 +6194,12 @@ async function getTweetWithContext(tweetId, depth = 3) {
4079
6194
  }
4080
6195
  return { tweet, parents };
4081
6196
  } catch (err) {
4082
- log12.warn({ tweetId, err }, "Failed to fetch tweet context");
6197
+ log19.warn({ tweetId, err }, "Failed to fetch tweet context");
4083
6198
  return { tweet: { id: tweetId, text: "" }, parents: [] };
4084
6199
  }
4085
6200
  }
4086
6201
  async function searchHashtagTweets(sinceId, maxResults = 100) {
4087
- log12.info({ sinceId, maxResults }, "Searching campaign tweets");
6202
+ log19.info({ sinceId, maxResults }, "Searching campaign tweets");
4088
6203
  try {
4089
6204
  const query = `(@${BOT_X_HANDLE} OR #CludeHackathon OR ($CLUDE (hackathon OR "10 days" OR "growing a brain" OR "blockchain brain"))) -is:retweet`;
4090
6205
  const params = {
@@ -4117,15 +6232,15 @@ async function searchHashtagTweets(sinceId, maxResults = 100) {
4117
6232
  } : void 0
4118
6233
  });
4119
6234
  }
4120
- log12.info({ found: tweets.length }, "Campaign tweet search complete");
6235
+ log19.info({ found: tweets.length }, "Campaign tweet search complete");
4121
6236
  return tweets;
4122
6237
  } catch (err) {
4123
- log12.error({ err }, "Failed to search campaign tweets");
6238
+ log19.error({ err }, "Failed to search campaign tweets");
4124
6239
  return [];
4125
6240
  }
4126
6241
  }
4127
6242
  async function searchTokenMentions(sinceId, maxResults = 50) {
4128
- log12.info({ sinceId, maxResults }, "Searching $CLUDE mentions");
6243
+ log19.info({ sinceId, maxResults }, "Searching $CLUDE mentions");
4129
6244
  try {
4130
6245
  const query = `($CLUDE OR @${BOT_X_HANDLE}) -is:retweet`;
4131
6246
  const params = {
@@ -4158,15 +6273,15 @@ async function searchTokenMentions(sinceId, maxResults = 50) {
4158
6273
  } : void 0
4159
6274
  });
4160
6275
  }
4161
- log12.info({ found: tweets.length }, "Token mention search complete");
6276
+ log19.info({ found: tweets.length }, "Token mention search complete");
4162
6277
  return tweets;
4163
6278
  } catch (err) {
4164
- log12.error({ err }, "Failed to search token mentions");
6279
+ log19.error({ err }, "Failed to search token mentions");
4165
6280
  return [];
4166
6281
  }
4167
6282
  }
4168
6283
  async function getUserTweets(userId, sinceId, maxResults = 10) {
4169
- log12.debug({ userId, sinceId }, "Fetching user tweets");
6284
+ log19.debug({ userId, sinceId }, "Fetching user tweets");
4170
6285
  try {
4171
6286
  const params = {
4172
6287
  "tweet.fields": "created_at,author_id,public_metrics",
@@ -4192,7 +6307,7 @@ async function getUserTweets(userId, sinceId, maxResults = 10) {
4192
6307
  }
4193
6308
  return tweets;
4194
6309
  } catch (err) {
4195
- log12.warn({ userId, err }, "Failed to fetch user tweets");
6310
+ log19.warn({ userId, err }, "Failed to fetch user tweets");
4196
6311
  return [];
4197
6312
  }
4198
6313
  }
@@ -4219,7 +6334,7 @@ async function refreshTweetMetrics(tweetIds) {
4219
6334
  }
4220
6335
  }
4221
6336
  } catch (err) {
4222
- log12.warn({ err, batchSize: batch.length }, "Failed to refresh tweet metrics batch");
6337
+ log19.warn({ err, batchSize: batch.length }, "Failed to refresh tweet metrics batch");
4223
6338
  }
4224
6339
  }
4225
6340
  return metricsMap;
@@ -4235,11 +6350,11 @@ async function getUserById(userId) {
4235
6350
  });
4236
6351
  return result.data;
4237
6352
  } catch (err) {
4238
- log12.warn({ userId, err }, "Failed to fetch user");
6353
+ log19.warn({ userId, err }, "Failed to fetch user");
4239
6354
  return null;
4240
6355
  }
4241
6356
  }
4242
- var import_twitter_api_v2, log12, client, rwClient, MIN_POST_GAP_MS, lastPostTime, MAX_TWEET_LENGTH, _usernameCache;
6357
+ var import_twitter_api_v2, log19, client, rwClient, MIN_POST_GAP_MS, lastPostTime, MAX_TWEET_LENGTH, _usernameCache;
4243
6358
  var init_x_client = __esm({
4244
6359
  "packages/shared/src/core/x-client.ts"() {
4245
6360
  "use strict";
@@ -4247,7 +6362,7 @@ var init_x_client = __esm({
4247
6362
  init_config();
4248
6363
  init_logger();
4249
6364
  init_constants();
4250
- log12 = createChildLogger("x-client");
6365
+ log19 = createChildLogger("x-client");
4251
6366
  client = new import_twitter_api_v2.TwitterApi({
4252
6367
  appKey: config.x.apiKey,
4253
6368
  appSecret: config.x.apiSecret,
@@ -4293,10 +6408,10 @@ Trigger: "${record.trigger.slice(0, 200)}"` : "");
4293
6408
  ...record.metadata
4294
6409
  }
4295
6410
  });
4296
- log13.info({ actionId: record.actionId, feature: record.feature, memoryId }, "Action logged");
6411
+ log20.info({ actionId: record.actionId, feature: record.feature, memoryId }, "Action logged");
4297
6412
  return memoryId;
4298
6413
  } catch (err) {
4299
- log13.error({ err, actionId: record.actionId }, "Failed to log action");
6414
+ log20.error({ err, actionId: record.actionId }, "Failed to log action");
4300
6415
  return null;
4301
6416
  }
4302
6417
  }
@@ -4326,10 +6441,10 @@ Original action: ${record.actionId}`;
4326
6441
  }
4327
6442
  });
4328
6443
  await updateActionImportance(record.actionId, record.sentiment);
4329
- log13.info({ actionId: record.actionId, sentiment: record.sentiment, memoryId }, "Outcome logged");
6444
+ log20.info({ actionId: record.actionId, sentiment: record.sentiment, memoryId }, "Outcome logged");
4330
6445
  return memoryId;
4331
6446
  } catch (err) {
4332
- log13.error({ err, actionId: record.actionId }, "Failed to log outcome");
6447
+ log20.error({ err, actionId: record.actionId }, "Failed to log outcome");
4333
6448
  return null;
4334
6449
  }
4335
6450
  }
@@ -4347,9 +6462,9 @@ async function updateActionImportance(actionId, sentiment) {
4347
6462
  tags.push(`outcome:${sentiment}`);
4348
6463
  const newImportance = sentiment === "positive" ? Math.min(1, currentImportance + 0.15) : sentiment === "negative" ? Math.min(1, currentImportance + 0.2) : currentImportance;
4349
6464
  await db.from("memories").update({ importance: newImportance, tags }).eq("id", mem.id);
4350
- log13.debug({ actionId, oldImportance: currentImportance, newImportance }, "Action importance updated");
6465
+ log20.debug({ actionId, oldImportance: currentImportance, newImportance }, "Action importance updated");
4351
6466
  } catch (err) {
4352
- log13.error({ err, actionId }, "Failed to update action importance");
6467
+ log20.error({ err, actionId }, "Failed to update action importance");
4353
6468
  }
4354
6469
  }
4355
6470
  async function refineStrategies() {
@@ -4362,7 +6477,7 @@ async function refineStrategies() {
4362
6477
  if (ownerWallet) recentQuery = recentQuery.eq("owner_wallet", ownerWallet);
4363
6478
  const { data: recentActions } = await recentQuery;
4364
6479
  if (!recentActions || recentActions.length === 0) {
4365
- log13.debug("No recent actions to analyze");
6480
+ log20.debug("No recent actions to analyze");
4366
6481
  return lessons;
4367
6482
  }
4368
6483
  const withOutcome = recentActions.filter(
@@ -4375,7 +6490,7 @@ async function refineStrategies() {
4375
6490
  (m) => (m.tags || []).includes("outcome:negative")
4376
6491
  );
4377
6492
  if (withOutcome.length < 3) {
4378
- log13.debug({ total: recentActions.length, withOutcome: withOutcome.length }, "Not enough outcome data for strategy refinement");
6493
+ log20.debug({ total: recentActions.length, withOutcome: withOutcome.length }, "Not enough outcome data for strategy refinement");
4379
6494
  return lessons;
4380
6495
  }
4381
6496
  const byFeature = /* @__PURE__ */ new Map();
@@ -4420,7 +6535,7 @@ async function refineStrategies() {
4420
6535
  }
4421
6536
  });
4422
6537
  lessons.push(lesson);
4423
- log13.info({ feature, lesson: lesson.slice(0, 100) }, "New strategy learned");
6538
+ log20.info({ feature, lesson: lesson.slice(0, 100) }, "New strategy learned");
4424
6539
  }
4425
6540
  }
4426
6541
  if (positive.length > 0) {
@@ -4428,7 +6543,7 @@ async function refineStrategies() {
4428
6543
  }
4429
6544
  return lessons;
4430
6545
  } catch (err) {
4431
- log13.error({ err }, "Strategy refinement failed");
6546
+ log20.error({ err }, "Strategy refinement failed");
4432
6547
  return lessons;
4433
6548
  }
4434
6549
  }
@@ -4452,7 +6567,7 @@ Write a concise procedural rule (1-2 sentences) that captures this successful st
4452
6567
  });
4453
6568
  return response.trim();
4454
6569
  } catch (err) {
4455
- log13.error({ err, feature }, "Failed to generate lesson");
6570
+ log20.error({ err, feature }, "Failed to generate lesson");
4456
6571
  return null;
4457
6572
  }
4458
6573
  }
@@ -4474,11 +6589,11 @@ async function reinforceSuccessfulStrategies(positiveActions) {
4474
6589
  if (relevantPositive.length > 0) {
4475
6590
  const newImportance = Math.min(1, (strategy.importance || 0.5) + 0.05 * relevantPositive.length);
4476
6591
  await db.from("memories").update({ importance: newImportance, access_count: (strategy.access_count || 0) + 1 }).eq("id", strategy.id);
4477
- log13.debug({ strategyId: strategy.id, feature, boost: relevantPositive.length }, "Strategy reinforced");
6592
+ log20.debug({ strategyId: strategy.id, feature, boost: relevantPositive.length }, "Strategy reinforced");
4478
6593
  }
4479
6594
  }
4480
6595
  } catch (err) {
4481
- log13.error({ err }, "Failed to reinforce strategies");
6596
+ log20.error({ err }, "Failed to reinforce strategies");
4482
6597
  }
4483
6598
  }
4484
6599
  async function trackSocialOutcomes() {
@@ -4532,16 +6647,16 @@ async function trackSocialOutcomes() {
4532
6647
  tracked++;
4533
6648
  }
4534
6649
  } catch {
4535
- log13.debug({ replyId }, "Could not fetch tweet metrics");
6650
+ log20.debug({ replyId }, "Could not fetch tweet metrics");
4536
6651
  }
4537
6652
  }
4538
- log13.info({ tracked }, "Social outcomes tracked");
6653
+ log20.info({ tracked }, "Social outcomes tracked");
4539
6654
  } catch (err) {
4540
- log13.error({ err }, "Social outcome tracking failed");
6655
+ log20.error({ err }, "Social outcome tracking failed");
4541
6656
  }
4542
6657
  return tracked;
4543
6658
  }
4544
- var log13;
6659
+ var log20;
4545
6660
  var init_action_learning = __esm({
4546
6661
  "packages/brain/src/memory/action-learning.ts"() {
4547
6662
  "use strict";
@@ -4549,7 +6664,7 @@ var init_action_learning = __esm({
4549
6664
  init_logger();
4550
6665
  init_database();
4551
6666
  init_claude_client();
4552
- log13 = createChildLogger("action-learning");
6667
+ log20 = createChildLogger("action-learning");
4553
6668
  }
4554
6669
  });
4555
6670
 
@@ -4576,7 +6691,7 @@ function accumulateImportance(importance) {
4576
6691
  const timeSinceLastReflection = Date.now() - lastReflectionTime;
4577
6692
  const pastMinInterval = timeSinceLastReflection >= REFLECTION_MIN_INTERVAL_MS;
4578
6693
  if (importanceAccumulator >= REFLECTION_IMPORTANCE_THRESHOLD && pastMinInterval && !reflectionInProgress) {
4579
- log14.info({
6694
+ log21.info({
4580
6695
  accumulator: importanceAccumulator.toFixed(2),
4581
6696
  threshold: REFLECTION_IMPORTANCE_THRESHOLD,
4582
6697
  minutesSinceLast: Math.round(timeSinceLastReflection / 6e4)
@@ -4586,7 +6701,7 @@ function accumulateImportance(importance) {
4586
6701
  try {
4587
6702
  await triggerReflection();
4588
6703
  } catch (err) {
4589
- log14.error({ err }, "Event-driven reflection failed");
6704
+ log21.error({ err }, "Event-driven reflection failed");
4590
6705
  }
4591
6706
  });
4592
6707
  }
@@ -4594,7 +6709,7 @@ function accumulateImportance(importance) {
4594
6709
  async function triggerReflection() {
4595
6710
  reflectionInProgress = true;
4596
6711
  try {
4597
- log14.info({ accumulator: importanceAccumulator.toFixed(2) }, "=== DREAM CYCLE TRIGGERED ===");
6712
+ log21.info({ accumulator: importanceAccumulator.toFixed(2) }, "=== DREAM CYCLE TRIGGERED ===");
4598
6713
  await Promise.race([
4599
6714
  (async () => {
4600
6715
  await runConsolidation();
@@ -4609,9 +6724,9 @@ async function triggerReflection() {
4609
6724
  await sleep(3e3);
4610
6725
  try {
4611
6726
  const deepResult = await runDeepConnectionPhase();
4612
- log14.info({ linksCreated: deepResult.linksCreated }, "JEPA deep connection complete");
6727
+ log21.info({ linksCreated: deepResult.linksCreated }, "JEPA deep connection complete");
4613
6728
  } catch (err) {
4614
- log14.warn({ err }, "JEPA deep connection phase failed, continuing");
6729
+ log21.warn({ err }, "JEPA deep connection phase failed, continuing");
4615
6730
  }
4616
6731
  await sleep(3e3);
4617
6732
  await runEmergence();
@@ -4623,9 +6738,9 @@ async function triggerReflection() {
4623
6738
  importanceAccumulator = 0;
4624
6739
  lastReflectionTime = Date.now();
4625
6740
  await saveAccumulator();
4626
- log14.info("=== DREAM CYCLE COMPLETE ===");
6741
+ log21.info("=== DREAM CYCLE COMPLETE ===");
4627
6742
  } catch (err) {
4628
- log14.error({ err }, "Dream cycle failed or timed out");
6743
+ log21.error({ err }, "Dream cycle failed or timed out");
4629
6744
  } finally {
4630
6745
  reflectionInProgress = false;
4631
6746
  }
@@ -4637,7 +6752,7 @@ async function loadAccumulator() {
4637
6752
  if (data) {
4638
6753
  importanceAccumulator = (data.count || 0) / 100;
4639
6754
  lastReflectionTime = new Date(data.window_start).getTime();
4640
- log14.debug({
6755
+ log21.debug({
4641
6756
  accumulator: importanceAccumulator.toFixed(2),
4642
6757
  lastReflection: new Date(lastReflectionTime).toISOString()
4643
6758
  }, "Loaded reflection accumulator");
@@ -4654,7 +6769,7 @@ async function saveAccumulator() {
4654
6769
  window_start: new Date(lastReflectionTime).toISOString()
4655
6770
  });
4656
6771
  } catch (err) {
4657
- log14.warn({ err }, "Failed to save reflection accumulator");
6772
+ log21.warn({ err }, "Failed to save reflection accumulator");
4658
6773
  }
4659
6774
  }
4660
6775
  function parseEvidenceCitations(text, sourceMemories) {
@@ -4698,14 +6813,14 @@ ${memoryDump}`,
4698
6813
  return response.split("\n").map((l) => l.trim()).filter((l) => l.length > 10 && l.includes("?")).slice(0, 3);
4699
6814
  }
4700
6815
  async function runConsolidation() {
4701
- log14.info("Dream phase 1: CONSOLIDATION starting");
6816
+ log21.info("Dream phase 1: CONSOLIDATION starting");
4702
6817
  const recentSummaries = await recallMemorySummaries({
4703
6818
  memoryTypes: ["episodic"],
4704
6819
  limit: 20,
4705
6820
  trackAccess: false
4706
6821
  });
4707
6822
  if (recentSummaries.length < 3) {
4708
- log14.info({ count: recentSummaries.length }, "Too few recent memories for consolidation");
6823
+ log21.info({ count: recentSummaries.length }, "Too few recent memories for consolidation");
4709
6824
  return;
4710
6825
  }
4711
6826
  const recentEpisodic = await getRecentMemories(6, ["episodic"], 20);
@@ -4713,14 +6828,14 @@ async function runConsolidation() {
4713
6828
  try {
4714
6829
  focalPoints = await generateFocalPoints(recentEpisodic);
4715
6830
  } catch (err) {
4716
- log14.warn({ err }, "Focal point generation failed, using direct consolidation");
6831
+ log21.warn({ err }, "Focal point generation failed, using direct consolidation");
4717
6832
  focalPoints = [];
4718
6833
  }
4719
6834
  if (focalPoints.length === 0) {
4720
6835
  await runDirectConsolidation(recentEpisodic);
4721
6836
  return;
4722
6837
  }
4723
- log14.info({ focalPoints }, "Focal points generated");
6838
+ log21.info({ focalPoints }, "Focal points generated");
4724
6839
  const allNewIds = [];
4725
6840
  const allInputIds = new Set(recentEpisodic.map((m) => m.id));
4726
6841
  for (const question of focalPoints) {
@@ -4745,7 +6860,7 @@ ${numberedMemories}`,
4745
6860
  });
4746
6861
  const { text, evidenceIds } = parseEvidenceCitations(response, relevant);
4747
6862
  if (isCopoutResponse(text)) {
4748
- log14.warn({ question }, "Consolidation produced cop-out response \u2014 discarding");
6863
+ log21.warn({ question }, "Consolidation produced cop-out response \u2014 discarding");
4749
6864
  continue;
4750
6865
  }
4751
6866
  const id = await storeMemory({
@@ -4776,7 +6891,7 @@ Semantic insights: ${allNewIds.length - proceduralIds.length}
4776
6891
  Procedural patterns: ${proceduralIds.length}`,
4777
6892
  allNewIds
4778
6893
  );
4779
- log14.info({ focalPoints: focalPoints.length, insights: allNewIds.length, procedural: proceduralIds.length }, "Focal-point consolidation complete");
6894
+ log21.info({ focalPoints: focalPoints.length, insights: allNewIds.length, procedural: proceduralIds.length }, "Focal-point consolidation complete");
4780
6895
  }
4781
6896
  async function runDirectConsolidation(recentEpisodic) {
4782
6897
  const memoryDump = recentEpisodic.map(
@@ -4797,7 +6912,7 @@ Total interactions: ${recentEpisodic.length}`,
4797
6912
  for (const obs of observations.slice(0, 3)) {
4798
6913
  const { text, evidenceIds } = parseEvidenceCitations(obs, recentEpisodic);
4799
6914
  if (isCopoutResponse(text)) {
4800
- log14.warn("Direct consolidation produced cop-out response \u2014 discarding");
6915
+ log21.warn("Direct consolidation produced cop-out response \u2014 discarding");
4801
6916
  continue;
4802
6917
  }
4803
6918
  const id = await storeMemory({
@@ -4820,24 +6935,24 @@ Total interactions: ${recentEpisodic.length}`,
4820
6935
  response,
4821
6936
  newIds
4822
6937
  );
4823
- log14.info({ observations: newIds.length, procedural: proceduralIds.length }, "Direct consolidation complete");
6938
+ log21.info({ observations: newIds.length, procedural: proceduralIds.length }, "Direct consolidation complete");
4824
6939
  }
4825
6940
  async function runCompaction() {
4826
- log14.info("Dream phase 1.5: COMPACTION starting");
6941
+ log21.info("Dream phase 1.5: COMPACTION starting");
4827
6942
  const db = getDb();
4828
6943
  let compactionQuery = db.from("memories").select("id, hash_id, memory_type, summary, tags, concepts, importance, decay_factor, created_at").eq("memory_type", "episodic").eq("compacted", false).lt("decay_factor", 0.3).lt("importance", 0.5).lt("created_at", new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3).toISOString()).order("created_at", { ascending: true }).limit(30);
4829
6944
  const ownerWallet = getOwnerWallet();
4830
6945
  if (ownerWallet) compactionQuery = compactionQuery.eq("owner_wallet", ownerWallet);
4831
6946
  const { data: candidates, error } = await compactionQuery;
4832
6947
  if (error) {
4833
- log14.error({ error: error.message }, "Failed to fetch compaction candidates");
6948
+ log21.error({ error: error.message }, "Failed to fetch compaction candidates");
4834
6949
  return;
4835
6950
  }
4836
6951
  if (!candidates || candidates.length < 5) {
4837
- log14.debug({ count: candidates?.length || 0 }, "Too few candidates for compaction");
6952
+ log21.debug({ count: candidates?.length || 0 }, "Too few candidates for compaction");
4838
6953
  return;
4839
6954
  }
4840
- log14.info({ count: candidates.length }, "Found compaction candidates");
6955
+ log21.info({ count: candidates.length }, "Found compaction candidates");
4841
6956
  const groups = /* @__PURE__ */ new Map();
4842
6957
  for (const mem of candidates) {
4843
6958
  const concept = mem.concepts?.[0] || "general";
@@ -4881,10 +6996,10 @@ ${memoryDump}`,
4881
6996
  });
4882
6997
  }
4883
6998
  compactedCount += memories.length;
4884
- log14.debug({ concept, count: memories.length }, "Memory group compacted");
6999
+ log21.debug({ concept, count: memories.length }, "Memory group compacted");
4885
7000
  }
4886
7001
  } catch (err) {
4887
- log14.warn({ err, concept }, "Failed to compact memory group");
7002
+ log21.warn({ err, concept }, "Failed to compact memory group");
4888
7003
  }
4889
7004
  }
4890
7005
  if (compactedCount > 0) {
@@ -4895,10 +7010,10 @@ ${memoryDump}`,
4895
7010
  summaryIds
4896
7011
  );
4897
7012
  }
4898
- log14.info({ compacted: compactedCount, summaries: summaryIds.length }, "Compaction complete");
7013
+ log21.info({ compacted: compactedCount, summaries: summaryIds.length }, "Compaction complete");
4899
7014
  }
4900
7015
  async function runReflection() {
4901
- log14.info("Dream phase 2: REFLECTION starting");
7016
+ log21.info("Dream phase 2: REFLECTION starting");
4902
7017
  const selfModel = await getSelfModel();
4903
7018
  const recentSemantic = await getRecentMemories(48, ["semantic"], 10);
4904
7019
  const stats = await getMemoryStats();
@@ -4921,7 +7036,7 @@ async function runReflection() {
4921
7036
  });
4922
7037
  const { text, evidenceIds } = parseEvidenceCitations(response, allInputMemories);
4923
7038
  if (isCopoutResponse(text)) {
4924
- log14.warn("Reflection produced cop-out response \u2014 discarding");
7039
+ log21.warn("Reflection produced cop-out response \u2014 discarding");
4925
7040
  await storeDreamLog("reflection", allInputMemories.map((m) => m.id), "(discarded \u2014 cop-out)", []);
4926
7041
  return;
4927
7042
  }
@@ -4953,10 +7068,10 @@ async function runReflection() {
4953
7068
  response,
4954
7069
  id ? [id] : []
4955
7070
  );
4956
- log14.info({ evidenceCount: evidenceIds.length }, "Reflection complete (progressive disclosure enabled)");
7071
+ log21.info({ evidenceCount: evidenceIds.length }, "Reflection complete (progressive disclosure enabled)");
4957
7072
  }
4958
7073
  async function runContradictionResolution() {
4959
- log14.info("Dream phase 2.5: CONTRADICTION RESOLUTION starting");
7074
+ log21.info("Dream phase 2.5: CONTRADICTION RESOLUTION starting");
4960
7075
  const db = getDb();
4961
7076
  let pairs;
4962
7077
  try {
@@ -4965,22 +7080,22 @@ async function runContradictionResolution() {
4965
7080
  filter_owner: getOwnerWallet() || null
4966
7081
  });
4967
7082
  if (error || !data || data.length === 0) {
4968
- log14.debug({ error: error?.message }, "No unresolved contradictions found \u2014 skipping phase");
7083
+ log21.debug({ error: error?.message }, "No unresolved contradictions found \u2014 skipping phase");
4969
7084
  return;
4970
7085
  }
4971
7086
  pairs = data;
4972
7087
  } catch (err) {
4973
- log14.debug({ err }, "Contradiction resolution skipped (RPC unavailable)");
7088
+ log21.debug({ err }, "Contradiction resolution skipped (RPC unavailable)");
4974
7089
  return;
4975
7090
  }
4976
- log14.info({ pairCount: pairs.length }, "Found unresolved contradictions");
7091
+ log21.info({ pairCount: pairs.length }, "Found unresolved contradictions");
4977
7092
  const allIds = [...new Set(pairs.flatMap((p) => [p.source_id, p.target_id]))];
4978
7093
  let memFetchQuery = db.from("memories").select("*").in("id", allIds);
4979
7094
  const ownerWallet = getOwnerWallet();
4980
7095
  if (ownerWallet) memFetchQuery = memFetchQuery.eq("owner_wallet", ownerWallet);
4981
7096
  const { data: memories } = await memFetchQuery;
4982
7097
  if (!memories || memories.length === 0) {
4983
- log14.warn("Could not fetch contradiction memories \u2014 skipping phase");
7098
+ log21.warn("Could not fetch contradiction memories \u2014 skipping phase");
4984
7099
  return;
4985
7100
  }
4986
7101
  const memoryMap = new Map(memories.map((m) => [m.id, m]));
@@ -5008,7 +7123,7 @@ ${numberedContext}`,
5008
7123
  });
5009
7124
  const { text, evidenceIds } = parseEvidenceCitations(response, sourceMemories);
5010
7125
  if (isCopoutResponse(text)) {
5011
- log14.warn({ memA: memA.id, memB: memB.id }, "Contradiction resolution produced cop-out \u2014 skipping pair");
7126
+ log21.warn({ memA: memA.id, memB: memB.id }, "Contradiction resolution produced cop-out \u2014 skipping pair");
5012
7127
  continue;
5013
7128
  }
5014
7129
  outputs.push(text);
@@ -5032,9 +7147,9 @@ ${numberedContext}`,
5032
7147
  const newDecay = Math.max(0.05, weaker.decay_factor * 0.8);
5033
7148
  const { error: decayErr } = await db.from("memories").update({ decay_factor: newDecay }).eq("id", weaker.id);
5034
7149
  if (!decayErr) {
5035
- log14.debug({ memoryId: weaker.id, newDecay: newDecay.toFixed(3) }, "Accelerated decay on weaker contradicting memory");
7150
+ log21.debug({ memoryId: weaker.id, newDecay: newDecay.toFixed(3) }, "Accelerated decay on weaker contradicting memory");
5036
7151
  } else {
5037
- log14.warn({ error: decayErr.message, memoryId: weaker.id }, "Failed to accelerate decay on contradicting memory");
7152
+ log21.warn({ error: decayErr.message, memoryId: weaker.id }, "Failed to accelerate decay on contradicting memory");
5038
7153
  }
5039
7154
  }
5040
7155
  }
@@ -5046,29 +7161,29 @@ ${numberedContext}`,
5046
7161
  newMemoryIds
5047
7162
  );
5048
7163
  }
5049
- log14.info({ resolved: outputs.length, skipped: pairs.length - outputs.length }, "Contradiction resolution complete");
7164
+ log21.info({ resolved: outputs.length, skipped: pairs.length - outputs.length }, "Contradiction resolution complete");
5050
7165
  }
5051
7166
  async function runLearning() {
5052
- log14.info("--- Learning phase: tracking outcomes & refining strategies ---");
7167
+ log21.info("--- Learning phase: tracking outcomes & refining strategies ---");
5053
7168
  try {
5054
7169
  const { trackSocialOutcomes: trackSocialOutcomes2, refineStrategies: refineStrategies2 } = await Promise.resolve().then(() => (init_action_learning(), action_learning_exports));
5055
7170
  const tracked = await trackSocialOutcomes2();
5056
7171
  if (tracked > 0) {
5057
- log14.info({ tracked }, "Social outcomes tracked");
7172
+ log21.info({ tracked }, "Social outcomes tracked");
5058
7173
  }
5059
7174
  const lessons = await refineStrategies2();
5060
7175
  if (lessons.length > 0) {
5061
- log14.info({ lessons: lessons.length }, "New strategies learned");
7176
+ log21.info({ lessons: lessons.length }, "New strategies learned");
5062
7177
  for (const lesson of lessons) {
5063
- log14.info({ lesson: lesson.slice(0, 150) }, "Learned strategy");
7178
+ log21.info({ lesson: lesson.slice(0, 150) }, "Learned strategy");
5064
7179
  }
5065
7180
  }
5066
7181
  } catch (err) {
5067
- log14.error({ err }, "Learning phase failed");
7182
+ log21.error({ err }, "Learning phase failed");
5068
7183
  }
5069
7184
  }
5070
7185
  async function runEmergence() {
5071
- log14.info("Dream phase 3: EMERGENCE starting");
7186
+ log21.info("Dream phase 3: EMERGENCE starting");
5072
7187
  const selfModel = await getSelfModel();
5073
7188
  const stats = await getMemoryStats();
5074
7189
  const randomMemories = await getRecentMemories(168, ["episodic"], 30);
@@ -5116,9 +7231,9 @@ async function runEmergence() {
5116
7231
  if (_emergenceHandler) {
5117
7232
  try {
5118
7233
  await _emergenceHandler(response);
5119
- log14.info("Emergence thought sent to SDK handler");
7234
+ log21.info("Emergence thought sent to SDK handler");
5120
7235
  } catch (err) {
5121
- log14.error({ err }, "SDK emergence handler failed");
7236
+ log21.error({ err }, "SDK emergence handler failed");
5122
7237
  }
5123
7238
  } else {
5124
7239
  const canPost = await checkRateLimit("global:emergence-tweet", 1, 720);
@@ -5126,15 +7241,15 @@ async function runEmergence() {
5126
7241
  try {
5127
7242
  const { postTweet: postTweet2 } = (init_x_client(), __toCommonJS(x_client_exports));
5128
7243
  await postTweet2(response);
5129
- log14.info("Emergence thought posted to X");
7244
+ log21.info("Emergence thought posted to X");
5130
7245
  } catch (err) {
5131
- log14.error({ err }, "Failed to post emergence thought");
7246
+ log21.error({ err }, "Failed to post emergence thought");
5132
7247
  }
5133
7248
  } else if (!hasDepth) {
5134
- log14.debug({ selfModelCount: selfModel.length }, "Emergence not deep enough to post \u2014 skipping tweet");
7249
+ log21.debug({ selfModelCount: selfModel.length }, "Emergence not deep enough to post \u2014 skipping tweet");
5135
7250
  }
5136
7251
  }
5137
- log14.info("Emergence complete");
7252
+ log21.info("Emergence complete");
5138
7253
  }
5139
7254
  async function extractProceduralInsights(recentEpisodic) {
5140
7255
  if (recentEpisodic.length < 3) return [];
@@ -5155,7 +7270,7 @@ ${numberedMemories}`,
5155
7270
  for (const pattern of patterns.slice(0, 2)) {
5156
7271
  const { text, evidenceIds } = parseEvidenceCitations(pattern, recentEpisodic);
5157
7272
  if (isCopoutResponse(text)) {
5158
- log14.warn("Procedural extraction produced cop-out response \u2014 discarding");
7273
+ log21.warn("Procedural extraction produced cop-out response \u2014 discarding");
5159
7274
  continue;
5160
7275
  }
5161
7276
  const id = await storeMemory({
@@ -5170,10 +7285,10 @@ ${numberedMemories}`,
5170
7285
  });
5171
7286
  if (id) newIds.push(id);
5172
7287
  }
5173
- log14.info({ count: newIds.length }, "Procedural insights extracted");
7288
+ log21.info({ count: newIds.length }, "Procedural insights extracted");
5174
7289
  return newIds;
5175
7290
  } catch (err) {
5176
- log14.warn({ err }, "Procedural extraction failed");
7291
+ log21.warn({ err }, "Procedural extraction failed");
5177
7292
  return [];
5178
7293
  }
5179
7294
  }
@@ -5200,33 +7315,33 @@ function buildReflectionStats(stats) {
5200
7315
  return lines.join("\n");
5201
7316
  }
5202
7317
  async function startDreamCycle() {
5203
- log14.info("Starting dream cycle scheduler");
7318
+ log21.info("Starting dream cycle scheduler");
5204
7319
  await loadAccumulator();
5205
7320
  const cron = require("node-cron");
5206
7321
  dreamCron = cron.schedule("0 */6 * * *", async () => {
5207
7322
  if (reflectionInProgress) {
5208
- log14.info("Scheduled dream cycle skipped \u2014 reflection already in progress");
7323
+ log21.info("Scheduled dream cycle skipped \u2014 reflection already in progress");
5209
7324
  return;
5210
7325
  }
5211
- log14.info({
7326
+ log21.info({
5212
7327
  accumulator: importanceAccumulator.toFixed(2)
5213
7328
  }, "=== SCHEDULED DREAM CYCLE (6h fallback) ===");
5214
7329
  await triggerReflection();
5215
7330
  });
5216
7331
  decayCron = cron.schedule("0 3 * * *", async () => {
5217
- log14.info("Running memory decay");
7332
+ log21.info("Running memory decay");
5218
7333
  try {
5219
7334
  await decayMemories();
5220
7335
  } catch (err) {
5221
- log14.error({ err }, "Memory decay failed");
7336
+ log21.error({ err }, "Memory decay failed");
5222
7337
  }
5223
7338
  });
5224
7339
  setTimeout(async () => {
5225
- log14.info("Running initial dream cycle");
7340
+ log21.info("Running initial dream cycle");
5226
7341
  try {
5227
7342
  await triggerReflection();
5228
7343
  } catch (err) {
5229
- log14.error({ err }, "Initial dream cycle failed");
7344
+ log21.error({ err }, "Initial dream cycle failed");
5230
7345
  }
5231
7346
  }, 12e4);
5232
7347
  }
@@ -5239,12 +7354,12 @@ function stopDreamCycle() {
5239
7354
  decayCron.stop();
5240
7355
  decayCron = null;
5241
7356
  }
5242
- log14.info("Dream cycle stopped");
7357
+ log21.info("Dream cycle stopped");
5243
7358
  }
5244
7359
  function sleep(ms) {
5245
7360
  return new Promise((resolve) => setTimeout(resolve, ms));
5246
7361
  }
5247
- var log14, COPOUT_PATTERNS, importanceAccumulator, lastReflectionTime, reflectionInProgress, REFLECTION_TIMEOUT_MS, _emergenceHandler, dreamCron, decayCron;
7362
+ var log21, COPOUT_PATTERNS, importanceAccumulator, lastReflectionTime, reflectionInProgress, REFLECTION_TIMEOUT_MS, _emergenceHandler, dreamCron, decayCron;
5248
7363
  var init_cycle = __esm({
5249
7364
  "packages/brain/src/memory/dream/cycle.ts"() {
5250
7365
  "use strict";
@@ -5256,7 +7371,7 @@ var init_cycle = __esm({
5256
7371
  init_constants();
5257
7372
  init_format();
5258
7373
  init_deep_connection();
5259
- log14 = createChildLogger("dream-cycle");
7374
+ log21 = createChildLogger("dream-cycle");
5260
7375
  COPOUT_PATTERNS = [
5261
7376
  /^good question/i,
5262
7377
  /^let me think about that/i,
@@ -5281,7 +7396,7 @@ async function findClinamen(opts) {
5281
7396
  const limit = opts.limit || DEFAULT_LIMIT;
5282
7397
  const minImportance = opts.minImportance ?? MIN_IMPORTANCE;
5283
7398
  const maxRelevance = opts.maxRelevance ?? MAX_RELEVANCE_SIM;
5284
- log15.debug({ context: opts.context.slice(0, 60), limit }, "Searching for clinamen");
7399
+ log22.debug({ context: opts.context.slice(0, 60), limit }, "Searching for clinamen");
5285
7400
  let contextEmbedding = null;
5286
7401
  if (isEmbeddingEnabled()) {
5287
7402
  const cached = getCachedEmbedding(opts.context);
@@ -5293,7 +7408,7 @@ async function findClinamen(opts) {
5293
7408
  }
5294
7409
  }
5295
7410
  if (!contextEmbedding) {
5296
- log15.debug("No embedding available \u2014 falling back to importance-only clinamen");
7411
+ log22.debug("No embedding available \u2014 falling back to importance-only clinamen");
5297
7412
  return importanceOnlyClinamen(db, limit, minImportance, opts.memoryTypes);
5298
7413
  }
5299
7414
  const cutoff = new Date(Date.now() - MIN_AGE_HOURS * 60 * 60 * 1e3).toISOString();
@@ -5304,7 +7419,7 @@ async function findClinamen(opts) {
5304
7419
  }
5305
7420
  const { data: candidates, error } = await query;
5306
7421
  if (error || !candidates || candidates.length === 0) {
5307
- log15.debug({ error: error?.message }, "No clinamen candidates found");
7422
+ log22.debug({ error: error?.message }, "No clinamen candidates found");
5308
7423
  return [];
5309
7424
  }
5310
7425
  const scored = [];
@@ -5330,7 +7445,7 @@ async function findClinamen(opts) {
5330
7445
  }
5331
7446
  scored.sort((a, b) => b._divergence - a._divergence);
5332
7447
  const results = scored.slice(0, limit);
5333
- log15.info({
7448
+ log22.info({
5334
7449
  candidates: candidates.length,
5335
7450
  afterFilter: scored.length,
5336
7451
  returned: results.length,
@@ -5356,7 +7471,7 @@ async function importanceOnlyClinamen(db, limit, minImportance, memoryTypes) {
5356
7471
  _relevanceSim: 0
5357
7472
  }));
5358
7473
  }
5359
- var log15, MIN_IMPORTANCE, MAX_RELEVANCE_SIM, MIN_AGE_HOURS, CANDIDATE_POOL_SIZE, DEFAULT_LIMIT;
7474
+ var log22, MIN_IMPORTANCE, MAX_RELEVANCE_SIM, MIN_AGE_HOURS, CANDIDATE_POOL_SIZE, DEFAULT_LIMIT;
5360
7475
  var init_clinamen = __esm({
5361
7476
  "packages/brain/src/memory/clinamen.ts"() {
5362
7477
  "use strict";
@@ -5364,7 +7479,7 @@ var init_clinamen = __esm({
5364
7479
  init_memory();
5365
7480
  init_embeddings();
5366
7481
  init_logger();
5367
- log15 = createChildLogger("clinamen");
7482
+ log22 = createChildLogger("clinamen");
5368
7483
  MIN_IMPORTANCE = 0.6;
5369
7484
  MAX_RELEVANCE_SIM = 0.35;
5370
7485
  MIN_AGE_HOURS = 24;
@@ -5400,13 +7515,13 @@ async function selectSeeds() {
5400
7515
  memoryTypes: ["episodic", "semantic", "procedural"]
5401
7516
  });
5402
7517
  if (clinamenMemories.length > 0) {
5403
- log16.info({
7518
+ log23.info({
5404
7519
  count: clinamenMemories.length,
5405
7520
  summaries: clinamenMemories.map((m) => m.summary.slice(0, 40))
5406
7521
  }, "Clinamen seeds selected (anomaly retrieval)");
5407
7522
  }
5408
7523
  } catch (err) {
5409
- log16.debug({ err }, "Clinamen retrieval failed \u2014 falling back to random");
7524
+ log23.debug({ err }, "Clinamen retrieval failed \u2014 falling back to random");
5410
7525
  const older = await getRecentMemories(720, ["episodic", "semantic", "procedural"], 50);
5411
7526
  if (older.length > 0) {
5412
7527
  clinamenMemories = [older[Math.floor(Math.random() * older.length)]];
@@ -5439,18 +7554,18 @@ ${summaries}`,
5439
7554
  }
5440
7555
  async function runActiveReflection() {
5441
7556
  if (reflectionInProgress2) {
5442
- log16.info("Active reflection already in progress \u2014 skipping");
7557
+ log23.info("Active reflection already in progress \u2014 skipping");
5443
7558
  return null;
5444
7559
  }
5445
7560
  reflectionInProgress2 = true;
5446
7561
  try {
5447
- log16.info("=== ACTIVE REFLECTION STARTING ===");
7562
+ log23.info("=== ACTIVE REFLECTION STARTING ===");
5448
7563
  const { seeds, theme } = await selectSeeds();
5449
7564
  if (seeds.length < MIN_MEMORIES_FOR_REFLECTION) {
5450
- log16.info({ count: seeds.length }, "Too few memories for reflection \u2014 skipping");
7565
+ log23.info({ count: seeds.length }, "Too few memories for reflection \u2014 skipping");
5451
7566
  return null;
5452
7567
  }
5453
- log16.info({ seedCount: seeds.length, theme }, "Reflection seeds selected");
7568
+ log23.info({ seedCount: seeds.length, theme }, "Reflection seeds selected");
5454
7569
  const selfModel = await getSelfModel();
5455
7570
  const stats = await getMemoryStats();
5456
7571
  const previousReflections = await getRecentMemories(72, ["introspective"], 3);
@@ -5481,10 +7596,10 @@ async function runActiveReflection() {
5481
7596
  cognitiveFunction: "reflect"
5482
7597
  });
5483
7598
  if (!journal || journal.trim().length < 50) {
5484
- log16.warn("Reflection produced empty or trivial output \u2014 discarding");
7599
+ log23.warn("Reflection produced empty or trivial output \u2014 discarding");
5485
7600
  return null;
5486
7601
  }
5487
- log16.info({ length: journal.length, theme }, "Journal entry generated");
7602
+ log23.info({ length: journal.length, theme }, "Journal entry generated");
5488
7603
  const title = await generateTitle(journal);
5489
7604
  const memoryId = await storeMemory({
5490
7605
  type: "introspective",
@@ -5518,17 +7633,17 @@ ${journal}`,
5518
7633
  if (_reflectionHandler) {
5519
7634
  try {
5520
7635
  await _reflectionHandler(result);
5521
- log16.info("Reflection sent to SDK handler");
7636
+ log23.info("Reflection sent to SDK handler");
5522
7637
  } catch (err) {
5523
- log16.error({ err }, "SDK reflection handler failed");
7638
+ log23.error({ err }, "SDK reflection handler failed");
5524
7639
  }
5525
7640
  } else {
5526
7641
  await maybePostThread(result);
5527
7642
  }
5528
- log16.info({ memoryId, title }, "=== ACTIVE REFLECTION COMPLETE ===");
7643
+ log23.info({ memoryId, title }, "=== ACTIVE REFLECTION COMPLETE ===");
5529
7644
  return result;
5530
7645
  } catch (err) {
5531
- log16.error({ err }, "Active reflection failed");
7646
+ log23.error({ err }, "Active reflection failed");
5532
7647
  return null;
5533
7648
  } finally {
5534
7649
  reflectionInProgress2 = false;
@@ -5552,25 +7667,25 @@ async function maybePostThread(journal) {
5552
7667
  const hour = (/* @__PURE__ */ new Date()).getUTCHours();
5553
7668
  const isQuietHours = hour >= 23 || hour < 8;
5554
7669
  if (isQuietHours) {
5555
- log16.debug("Quiet hours \u2014 skipping thread post");
7670
+ log23.debug("Quiet hours \u2014 skipping thread post");
5556
7671
  return;
5557
7672
  }
5558
7673
  if (journal.text.length < 300) {
5559
- log16.debug("Journal too short for thread \u2014 skipping post");
7674
+ log23.debug("Journal too short for thread \u2014 skipping post");
5560
7675
  return;
5561
7676
  }
5562
7677
  const canPost = await checkRateLimit(THREAD_RATE_KEY, THREAD_RATE_LIMIT, THREAD_RATE_WINDOW);
5563
7678
  if (!canPost) {
5564
- log16.debug("Thread rate limit hit \u2014 skipping post");
7679
+ log23.debug("Thread rate limit hit \u2014 skipping post");
5565
7680
  return;
5566
7681
  }
5567
7682
  try {
5568
7683
  const tweets = formatAsThread(journal);
5569
7684
  const { postThread: postThread2 } = (init_x_client(), __toCommonJS(x_client_exports));
5570
7685
  const ids = await postThread2(tweets);
5571
- log16.info({ tweetCount: ids.length, title: journal.title }, "Reflection thread posted to X");
7686
+ log23.info({ tweetCount: ids.length, title: journal.title }, "Reflection thread posted to X");
5572
7687
  } catch (err) {
5573
- log16.error({ err }, "Failed to post reflection thread");
7688
+ log23.error({ err }, "Failed to post reflection thread");
5574
7689
  }
5575
7690
  }
5576
7691
  function formatAsThread(journal) {
@@ -5593,29 +7708,29 @@ A reflection \u{1F9F5}`;
5593
7708
  return tweets.slice(0, MAX_THREAD_TWEETS);
5594
7709
  }
5595
7710
  async function startActiveReflection() {
5596
- log16.info("Starting active reflection scheduler");
7711
+ log23.info("Starting active reflection scheduler");
5597
7712
  const cron = require("node-cron");
5598
7713
  reflectionCron = cron.schedule("30 1,4,7,10,13,16,19,22 * * *", async () => {
5599
7714
  if (reflectionInProgress2) {
5600
- log16.info("Scheduled reflection skipped \u2014 already in progress");
7715
+ log23.info("Scheduled reflection skipped \u2014 already in progress");
5601
7716
  return;
5602
7717
  }
5603
- log16.info("=== SCHEDULED ACTIVE REFLECTION ===");
7718
+ log23.info("=== SCHEDULED ACTIVE REFLECTION ===");
5604
7719
  await Promise.race([
5605
7720
  runActiveReflection(),
5606
7721
  new Promise(
5607
7722
  (_, reject) => setTimeout(() => reject(new Error("Active reflection timed out")), REFLECTION_TIMEOUT_MS2)
5608
7723
  )
5609
7724
  ]).catch((err) => {
5610
- log16.error({ err }, "Scheduled active reflection failed or timed out");
7725
+ log23.error({ err }, "Scheduled active reflection failed or timed out");
5611
7726
  });
5612
7727
  });
5613
7728
  setTimeout(async () => {
5614
- log16.info("Running initial active reflection");
7729
+ log23.info("Running initial active reflection");
5615
7730
  try {
5616
7731
  await runActiveReflection();
5617
7732
  } catch (err) {
5618
- log16.error({ err }, "Initial active reflection failed");
7733
+ log23.error({ err }, "Initial active reflection failed");
5619
7734
  }
5620
7735
  }, 30 * 60 * 1e3);
5621
7736
  }
@@ -5624,9 +7739,9 @@ function stopActiveReflection() {
5624
7739
  reflectionCron.stop();
5625
7740
  reflectionCron = null;
5626
7741
  }
5627
- log16.info("Active reflection stopped");
7742
+ log23.info("Active reflection stopped");
5628
7743
  }
5629
- var log16, REFLECTION_INTERVAL_MS, MIN_MEMORIES_FOR_REFLECTION, MAX_JOURNAL_TOKENS, MAX_THREAD_TWEETS, THREAD_RATE_KEY, THREAD_RATE_LIMIT, THREAD_RATE_WINDOW, reflectionInProgress2, reflectionCron, REFLECTION_TIMEOUT_MS2, _reflectionHandler;
7744
+ var log23, REFLECTION_INTERVAL_MS, MIN_MEMORIES_FOR_REFLECTION, MAX_JOURNAL_TOKENS, MAX_THREAD_TWEETS, THREAD_RATE_KEY, THREAD_RATE_LIMIT, THREAD_RATE_WINDOW, reflectionInProgress2, reflectionCron, REFLECTION_TIMEOUT_MS2, _reflectionHandler;
5630
7745
  var init_active_reflection = __esm({
5631
7746
  "packages/brain/src/memory/active-reflection.ts"() {
5632
7747
  "use strict";
@@ -5636,7 +7751,7 @@ var init_active_reflection = __esm({
5636
7751
  init_logger();
5637
7752
  init_constants();
5638
7753
  init_clinamen();
5639
- log16 = createChildLogger("active-reflection");
7754
+ log23 = createChildLogger("active-reflection");
5640
7755
  REFLECTION_INTERVAL_MS = 3 * 60 * 60 * 1e3;
5641
7756
  MIN_MEMORIES_FOR_REFLECTION = 5;
5642
7757
  MAX_JOURNAL_TOKENS = 1500;
@@ -6037,10 +8152,10 @@ var Cortex = class {
6037
8152
  this.requireSelfHosted("verifyOnChain");
6038
8153
  const { hydrateMemories: hydrateMemories2 } = (init_memory2(), __toCommonJS(memory_exports2));
6039
8154
  const { verifyMemoryOnChain: verifyMemoryOnChain2 } = (init_solana_client(), __toCommonJS(solana_client_exports));
6040
- const { createHash: createHash2 } = require("crypto");
8155
+ const { createHash: createHash3 } = require("crypto");
6041
8156
  const memories = await hydrateMemories2([memoryId]);
6042
8157
  if (memories.length === 0) return false;
6043
- const contentHash = createHash2("sha256").update(memories[0].content).digest();
8158
+ const contentHash = createHash3("sha256").update(memories[0].content).digest();
6044
8159
  return verifyMemoryOnChain2(contentHash);
6045
8160
  }
6046
8161
  /** Clean up resources and stop schedules. */
@@ -6066,7 +8181,7 @@ var Cortex = class {
6066
8181
  };
6067
8182
 
6068
8183
  // packages/brain/src/sdk/cortex-v2.ts
6069
- var import_crypto3 = __toESM(require("crypto"));
8184
+ var import_crypto4 = __toESM(require("crypto"));
6070
8185
  var DEFAULT_ROUTES = {
6071
8186
  routes: {
6072
8187
  embed: { provider: "voyage", model: "voyage-4-large" },
@@ -6127,7 +8242,7 @@ var CortexV2 = class extends Cortex {
6127
8242
  });
6128
8243
  }
6129
8244
  const pack = {
6130
- id: import_crypto3.default.randomUUID(),
8245
+ id: import_crypto4.default.randomUUID(),
6131
8246
  name: opts.name,
6132
8247
  description: opts.description,
6133
8248
  memories: memories.map((m) => ({