memorix 1.4.1 → 1.4.2

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.
@@ -495,7 +495,7 @@ function closeAllDatabases() {
495
495
  _dbCache.delete(key);
496
496
  }
497
497
  }
498
- var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
498
+ var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
499
499
  var init_sqlite_db = __esm({
500
500
  "src/store/sqlite-db.ts"() {
501
501
  "use strict";
@@ -761,6 +761,17 @@ CREATE TABLE IF NOT EXISTS observation_code_refs (
761
761
  " UNIQUE(projectId, sourceEpoch)",
762
762
  ");"
763
763
  ].join("\n");
764
+ CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE = [
765
+ "CREATE TABLE IF NOT EXISTS code_state_snapshot_files (",
766
+ " snapshotId TEXT NOT NULL,",
767
+ " projectId TEXT NOT NULL,",
768
+ " fileId TEXT NOT NULL,",
769
+ " path TEXT NOT NULL,",
770
+ " contentHash TEXT NOT NULL,",
771
+ " PRIMARY KEY (snapshotId, path),",
772
+ " FOREIGN KEY (snapshotId) REFERENCES code_state_snapshots(id) ON DELETE CASCADE",
773
+ ");"
774
+ ].join("\n");
764
775
  CREATE_KNOWLEDGE_CLAIMS_TABLE = `
765
776
  CREATE TABLE IF NOT EXISTS knowledge_claims (
766
777
  id TEXT PRIMARY KEY,
@@ -971,6 +982,19 @@ CREATE TABLE IF NOT EXISTS long_term_memory_events (
971
982
  createdAt TEXT NOT NULL,
972
983
  FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
973
984
  );
985
+ `;
986
+ CREATE_MEMORY_OUTCOME_SIGNALS_TABLE = `
987
+ CREATE TABLE IF NOT EXISTS memory_outcome_signals (
988
+ id TEXT PRIMARY KEY,
989
+ projectId TEXT NOT NULL,
990
+ candidateKind TEXT NOT NULL,
991
+ candidateId TEXT NOT NULL,
992
+ kind TEXT NOT NULL,
993
+ sourceRef TEXT NOT NULL,
994
+ snapshotId TEXT,
995
+ detail TEXT,
996
+ observedAt TEXT NOT NULL
997
+ );
974
998
  `;
975
999
  CREATE_MEDIA_ASSETS_TABLE = `
976
1000
  CREATE TABLE IF NOT EXISTS media_assets (
@@ -1213,6 +1237,20 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1213
1237
  db2.exec("CREATE INDEX IF NOT EXISTS idx_code_edges_snapshot ON code_edges(projectId, snapshotId)");
1214
1238
  }
1215
1239
  },
1240
+ {
1241
+ id: "1.4-codegraph-snapshot-manifest",
1242
+ apply: (db2) => {
1243
+ db2.exec(CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE);
1244
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_code_snapshot_files_project_path ON code_state_snapshot_files(projectId, path)");
1245
+ }
1246
+ },
1247
+ {
1248
+ id: "1.4-memory-outcome-signals",
1249
+ apply: (db2) => {
1250
+ db2.exec(CREATE_MEMORY_OUTCOME_SIGNALS_TABLE);
1251
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_outcomes_candidate ON memory_outcome_signals(projectId, candidateKind, candidateId, observedAt DESC)");
1252
+ }
1253
+ },
1216
1254
  {
1217
1255
  id: "1.2-knowledge-claim-ledger",
1218
1256
  apply: (db2) => {
@@ -1411,8 +1449,8 @@ var init_mini_skill_store = __esm({
1411
1449
 
1412
1450
  // src/compact/token-budget.ts
1413
1451
  import { countTokens, isWithinTokenLimit } from "gpt-tokenizer";
1414
- function countTextTokens(text) {
1415
- return countTokens(text);
1452
+ function countTextTokens(text2) {
1453
+ return countTokens(text2);
1416
1454
  }
1417
1455
  var init_token_budget = __esm({
1418
1456
  "src/compact/token-budget.ts"() {
@@ -2165,8 +2203,8 @@ import { createHash } from "crypto";
2165
2203
  import { readFile, writeFile, mkdir } from "fs/promises";
2166
2204
  import { join as join2 } from "path";
2167
2205
  import { homedir as homedir6 } from "os";
2168
- function textHash(text) {
2169
- return createHash("sha256").update(text).digest("hex").slice(0, 16);
2206
+ function textHash(text2) {
2207
+ return createHash("sha256").update(text2).digest("hex").slice(0, 16);
2170
2208
  }
2171
2209
  async function loadDiskCache() {
2172
2210
  try {
@@ -2229,11 +2267,11 @@ var init_fastembed_provider = __esm({
2229
2267
  async embedInputs(inputs, options = {}) {
2230
2268
  return Promise.all(inputs.map((input) => this.embedInput(input, options)));
2231
2269
  }
2232
- async embed(text) {
2233
- const hash4 = textHash(text);
2270
+ async embed(text2) {
2271
+ const hash4 = textHash(text2);
2234
2272
  const cached = cache.get(hash4);
2235
2273
  if (cached) return cached;
2236
- const raw = await this.model.queryEmbed(text);
2274
+ const raw = await this.model.queryEmbed(text2);
2237
2275
  const result = Array.from(raw);
2238
2276
  if (result.length !== this.dimensions) {
2239
2277
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
@@ -2329,10 +2367,10 @@ var init_transformers_provider = __esm({
2329
2367
  async embedInputs(inputs, options = {}) {
2330
2368
  return Promise.all(inputs.map((input) => this.embedInput(input, options)));
2331
2369
  }
2332
- async embed(text) {
2333
- const cached = cache2.get(text);
2370
+ async embed(text2) {
2371
+ const cached = cache2.get(text2);
2334
2372
  if (cached) return cached;
2335
- const output = await this.extractor(text, {
2373
+ const output = await this.extractor(text2, {
2336
2374
  pooling: "mean",
2337
2375
  normalize: true
2338
2376
  });
@@ -2340,7 +2378,7 @@ var init_transformers_provider = __esm({
2340
2378
  if (result.length !== this.dimensions) {
2341
2379
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
2342
2380
  }
2343
- this.cacheSet(text, result);
2381
+ this.cacheSet(text2, result);
2344
2382
  return result;
2345
2383
  }
2346
2384
  async embedBatch(texts) {
@@ -2403,8 +2441,8 @@ function dimsCacheFile() {
2403
2441
  function cacheMetaFile() {
2404
2442
  return join3(cacheDir(), ".embedding-api-cache-meta.json");
2405
2443
  }
2406
- function normalizeText(text) {
2407
- return text.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
2444
+ function normalizeText(text2) {
2445
+ return text2.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
2408
2446
  }
2409
2447
  function cacheNamespace(config) {
2410
2448
  return [
@@ -2414,8 +2452,8 @@ function cacheNamespace(config) {
2414
2452
  config.requestedDimensions ?? "native"
2415
2453
  ].join("|");
2416
2454
  }
2417
- function textHash2(text, namespace) {
2418
- return createHash2("sha256").update(`${namespace}\0${text}`).digest("hex").slice(0, 16);
2455
+ function textHash2(text2, namespace) {
2456
+ return createHash2("sha256").update(`${namespace}\0${text2}`).digest("hex").slice(0, 16);
2419
2457
  }
2420
2458
  function inputIdentity(input, options = {}) {
2421
2459
  return JSON.stringify({
@@ -2827,8 +2865,8 @@ var init_api_provider = __esm({
2827
2865
  if (!embedding) throw new Error("API probe returned no embeddings; check model name and API key");
2828
2866
  return embedding.length;
2829
2867
  }
2830
- async embed(text, options) {
2831
- const normalized = normalizeText(text);
2868
+ async embed(text2, options) {
2869
+ const normalized = normalizeText(text2);
2832
2870
  if (isNativeGeminiEndpoint(this.config.baseUrl)) {
2833
2871
  return this.embedInput({ modality: "text", text: normalized }, { intent: "document", ...options });
2834
2872
  }
@@ -2885,7 +2923,7 @@ var init_api_provider = __esm({
2885
2923
  }
2886
2924
  async embedBatch(texts, options) {
2887
2925
  if (isNativeGeminiEndpoint(this.config.baseUrl)) {
2888
- return Promise.all(texts.map((text) => this.embedInput({ modality: "text", text }, { intent: "document", ...options })));
2926
+ return Promise.all(texts.map((text2) => this.embedInput({ modality: "text", text: text2 }, { intent: "document", ...options })));
2889
2927
  }
2890
2928
  await ensureDiskCacheLoaded();
2891
2929
  const normalizedTexts = texts.map(normalizeText);
@@ -2964,8 +3002,8 @@ var init_api_provider = __esm({
2964
3002
  }
2965
3003
  async getCachedEmbeddings(texts) {
2966
3004
  await ensureDiskCacheLoaded();
2967
- return texts.map((text) => {
2968
- const hash4 = textHash2(normalizeText(text), this.cacheKeyNamespace);
3005
+ return texts.map((text2) => {
3006
+ const hash4 = textHash2(normalizeText(text2), this.cacheKeyNamespace);
2969
3007
  return cache3.get(hash4) ?? null;
2970
3008
  });
2971
3009
  }
@@ -3195,9 +3233,9 @@ function wrapProvider(candidate) {
3195
3233
  return {
3196
3234
  name: candidate.name,
3197
3235
  dimensions: candidate.dimensions,
3198
- async embed(text, options) {
3236
+ async embed(text2, options) {
3199
3237
  try {
3200
- return await candidate.embed(text, options);
3238
+ return await candidate.embed(text2, options);
3201
3239
  } catch (error) {
3202
3240
  if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
3203
3241
  warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
@@ -3205,7 +3243,7 @@ function wrapProvider(candidate) {
3205
3243
  if (fallback) {
3206
3244
  provider = wrapProvider(fallback);
3207
3245
  warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
3208
- return provider.embed(text, options);
3246
+ return provider.embed(text2, options);
3209
3247
  }
3210
3248
  }
3211
3249
  if (isTemporaryEmbeddingFailure(error)) {
@@ -3764,8 +3802,8 @@ async function readResponseText(response, signal, maxBytes = MAX_LLM_RESPONSE_BY
3764
3802
  }
3765
3803
  }
3766
3804
  async function readResponseJson(response, signal, maxBytes = MAX_LLM_RESPONSE_BYTES) {
3767
- const text = await readResponseText(response, signal, maxBytes);
3768
- return JSON.parse(text);
3805
+ const text2 = await readResponseText(response, signal, maxBytes);
3806
+ return JSON.parse(text2);
3769
3807
  }
3770
3808
  async function* callLLMWithToolsStream(messages, tools) {
3771
3809
  if (!currentConfig) {
@@ -4318,8 +4356,8 @@ function isCjkHeavy(query) {
4318
4356
  const cjkCount = (query.match(CJK_PATTERN) || []).length;
4319
4357
  return query.length > 0 && cjkCount / query.length > 0.3;
4320
4358
  }
4321
- function normalizeExpansion(text) {
4322
- return text.trim().replace(/^[-*]\s*/, "").replace(/^["'`]+|["'`]+$/g, "").split(/\r?\n/)[0].trim();
4359
+ function normalizeExpansion(text2) {
4360
+ return text2.trim().replace(/^[-*]\s*/, "").replace(/^["'`]+|["'`]+$/g, "").split(/\r?\n/)[0].trim();
4323
4361
  }
4324
4362
  async function maybeExpandSearchQuery(query) {
4325
4363
  if (!query || !isCjkHeavy(query) || COMMAND_LIKE_QUERY.test(query)) {
@@ -4682,9 +4720,9 @@ function shouldSkipCompression(narrative, type) {
4682
4720
  if (specialChars / narrative.length > 0.35) return true;
4683
4721
  return false;
4684
4722
  }
4685
- function estimateTokens(text) {
4686
- const cjkChars = (text.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length;
4687
- const otherChars = text.length - cjkChars;
4723
+ function estimateTokens(text2) {
4724
+ const cjkChars = (text2.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length;
4725
+ const otherChars = text2.length - cjkChars;
4688
4726
  return Math.ceil(cjkChars / 1.5 + otherChars / 4);
4689
4727
  }
4690
4728
  var COMPRESS_PROMPT, COMPRESS_PROMPT_GENTLE, RERANK_PROMPT, SKIP_PATTERNS, LOW_COMPRESSION_TYPES;
@@ -4987,10 +5025,10 @@ function isEmbeddingEnabled() {
4987
5025
  function getVectorDimensions() {
4988
5026
  return embeddingEnabled ? embeddingDimensions : null;
4989
5027
  }
4990
- async function generateEmbedding(text) {
5028
+ async function generateEmbedding(text2) {
4991
5029
  const provider2 = await getEmbeddingProvider();
4992
5030
  if (!provider2) return null;
4993
- return provider2.embedInput ? provider2.embedInput({ modality: "text", text }, { intent: "document" }) : provider2.embed(text);
5031
+ return provider2.embedInput ? provider2.embedInput({ modality: "text", text: text2 }, { intent: "document" }) : provider2.embed(text2);
4994
5032
  }
4995
5033
  async function generateEmbeddingInput(input, options = {}) {
4996
5034
  const provider2 = await getEmbeddingProvider();
@@ -6322,9 +6360,9 @@ var init_entity_extractor = __esm({
6322
6360
  });
6323
6361
 
6324
6362
  // src/memory/secret-filter.ts
6325
- function applyRedaction(text) {
6326
- if (!text) return text;
6327
- let out = text;
6363
+ function applyRedaction(text2) {
6364
+ if (!text2) return text2;
6365
+ let out = text2;
6328
6366
  out = out.replace(
6329
6367
  /((?:password|passwd|pwd|secret|token|api[_-]?key|auth[_-]?key|access[_-]?key|private[_-]?key|client[_-]?secret)\s*[:=]\s*["']?)([^\s"',;\n]{6,})/gi,
6330
6368
  "$1[REDACTED]"
@@ -6338,8 +6376,8 @@ function applyRedaction(text) {
6338
6376
  out = out.replace(/\beyJ[A-Za-z0-9._-]{40,}\b/g, "[REDACTED]");
6339
6377
  return out;
6340
6378
  }
6341
- function sanitizeCredentials(text) {
6342
- return applyRedaction(text);
6379
+ function sanitizeCredentials(text2) {
6380
+ return applyRedaction(text2);
6343
6381
  }
6344
6382
  var init_secret_filter = __esm({
6345
6383
  "src/memory/secret-filter.ts"() {
@@ -6390,8 +6428,8 @@ function clampPositiveInteger(value, fallback) {
6390
6428
  return Math.max(1, Math.floor(value));
6391
6429
  }
6392
6430
  function errorText(error) {
6393
- const text = error instanceof Error ? error.message : String(error);
6394
- return sanitizeCredentials(text).slice(0, MAX_ERROR_LENGTH);
6431
+ const text2 = error instanceof Error ? error.message : String(error);
6432
+ return sanitizeCredentials(text2).slice(0, MAX_ERROR_LENGTH);
6395
6433
  }
6396
6434
  function normalizedKinds(kinds) {
6397
6435
  if (!kinds) return void 0;
@@ -7313,9 +7351,9 @@ __export(consolidation_exports, {
7313
7351
  executeConsolidation: () => executeConsolidation,
7314
7352
  findConsolidationCandidates: () => findConsolidationCandidates
7315
7353
  });
7316
- function tokenize(text) {
7354
+ function tokenize(text2) {
7317
7355
  return new Set(
7318
- text.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff\s-]/g, " ").split(/\s+/).filter((t2) => t2.length > 1)
7356
+ text2.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff\s-]/g, " ").split(/\s+/).filter((t2) => t2.length > 1)
7319
7357
  );
7320
7358
  }
7321
7359
  function jaccardSimilarity(a, b) {
@@ -16273,8 +16311,8 @@ var init_headers = __esm({
16273
16311
  });
16274
16312
 
16275
16313
  // packages/ai/dist/utils/sanitize-unicode.js
16276
- function sanitizeSurrogates(text) {
16277
- return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
16314
+ function sanitizeSurrogates(text2) {
16315
+ return text2.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
16278
16316
  }
16279
16317
  var init_sanitize_unicode = __esm({
16280
16318
  "packages/ai/dist/utils/sanitize-unicode.js"() {
@@ -16756,9 +16794,9 @@ var init_values = __esm({
16756
16794
  }
16757
16795
  return n;
16758
16796
  };
16759
- safeJSON = (text) => {
16797
+ safeJSON = (text2) => {
16760
16798
  try {
16761
- return JSON.parse(text);
16799
+ return JSON.parse(text2);
16762
16800
  } catch (err) {
16763
16801
  return void 0;
16764
16802
  }
@@ -17921,8 +17959,8 @@ async function defaultParseResponse(client, props) {
17921
17959
  const json2 = await response.json();
17922
17960
  return addRequestID(json2, response);
17923
17961
  }
17924
- const text = await response.text();
17925
- return text;
17962
+ const text2 = await response.text();
17963
+ return text2;
17926
17964
  })();
17927
17965
  loggerFor(client).debug(`[${requestLogID}] response parsed`, formatRequestDetails({
17928
17966
  retryOfRequestLogID,
@@ -43529,9 +43567,9 @@ var init_values2 = __esm({
43529
43567
  }
43530
43568
  return n;
43531
43569
  };
43532
- safeJSON2 = (text) => {
43570
+ safeJSON2 = (text2) => {
43533
43571
  try {
43534
- return JSON.parse(text);
43572
+ return JSON.parse(text2);
43535
43573
  } catch (err) {
43536
43574
  return void 0;
43537
43575
  }
@@ -44305,8 +44343,8 @@ async function defaultParseResponse2(client, props) {
44305
44343
  const json2 = await response.json();
44306
44344
  return addRequestID2(json2, response);
44307
44345
  }
44308
- const text = await response.text();
44309
- return text;
44346
+ const text2 = await response.text();
44347
+ return text2;
44310
44348
  })();
44311
44349
  loggerFor2(client).debug(`[${requestLogID}] response parsed`, formatRequestDetails2({
44312
44350
  retryOfRequestLogID,
@@ -50899,9 +50937,9 @@ function decodeSseLine(line, state) {
50899
50937
  }
50900
50938
  return null;
50901
50939
  }
50902
- function nextLineBreakIndex(text) {
50903
- const carriageReturnIndex = text.indexOf("\r");
50904
- const newlineIndex = text.indexOf("\n");
50940
+ function nextLineBreakIndex(text2) {
50941
+ const carriageReturnIndex = text2.indexOf("\r");
50942
+ const newlineIndex = text2.indexOf("\n");
50905
50943
  if (carriageReturnIndex === -1) {
50906
50944
  return newlineIndex;
50907
50945
  }
@@ -50910,18 +50948,18 @@ function nextLineBreakIndex(text) {
50910
50948
  }
50911
50949
  return Math.min(carriageReturnIndex, newlineIndex);
50912
50950
  }
50913
- function consumeLine(text) {
50914
- const lineBreakIndex = nextLineBreakIndex(text);
50951
+ function consumeLine(text2) {
50952
+ const lineBreakIndex = nextLineBreakIndex(text2);
50915
50953
  if (lineBreakIndex === -1) {
50916
50954
  return null;
50917
50955
  }
50918
50956
  let nextIndex = lineBreakIndex + 1;
50919
- if (text[lineBreakIndex] === "\r" && text[nextIndex] === "\n") {
50957
+ if (text2[lineBreakIndex] === "\r" && text2[nextIndex] === "\n") {
50920
50958
  nextIndex += 1;
50921
50959
  }
50922
50960
  return {
50923
- line: text.slice(0, lineBreakIndex),
50924
- rest: text.slice(nextIndex)
50961
+ line: text2.slice(0, lineBreakIndex),
50962
+ rest: text2.slice(nextIndex)
50925
50963
  };
50926
50964
  }
50927
50965
  async function* iterateSseMessages(body, signal) {
@@ -53088,8 +53126,8 @@ var require_common = __commonJS({
53088
53126
  }
53089
53127
  function redactString(obj, key) {
53090
53128
  if (typeof obj === "object" && obj !== null && typeof obj[key] === "string") {
53091
- const text = obj[key];
53092
- if (/grant_type=/i.test(text) || /assertion=/i.test(text) || /secret/i.test(text)) {
53129
+ const text2 = obj[key];
53130
+ if (/grant_type=/i.test(text2) || /assertion=/i.test(text2) || /secret/i.test(text2)) {
53093
53131
  obj[key] = REDACT;
53094
53132
  }
53095
53133
  }
@@ -59917,8 +59955,8 @@ var init_body = __esm({
59917
59955
  * @return Promise
59918
59956
  */
59919
59957
  async json() {
59920
- const text = await this.text();
59921
- return JSON.parse(text);
59958
+ const text2 = await this.text();
59959
+ return JSON.parse(text2);
59922
59960
  }
59923
59961
  /**
59924
59962
  * Decode response as text
@@ -61070,7 +61108,7 @@ var require_gaxios = __commonJS({
61070
61108
  var retry_js_1 = require_retry3();
61071
61109
  var stream_1 = __require("stream");
61072
61110
  var interceptor_js_1 = require_interceptor();
61073
- var randomUUID10 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
61111
+ var randomUUID11 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
61074
61112
  var HTTP_STATUS_NO_CONTENT = 204;
61075
61113
  var Gaxios = class {
61076
61114
  agentCache = /* @__PURE__ */ new Map();
@@ -61343,7 +61381,7 @@ var require_gaxios = __commonJS({
61343
61381
  */
61344
61382
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
61345
61383
  if (opts.multipart?.length) {
61346
- const boundary = await randomUUID10();
61384
+ const boundary = await randomUUID11();
61347
61385
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
61348
61386
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
61349
61387
  } else if (shouldDirectlyPassData) {
@@ -63094,18 +63132,18 @@ var require_parse = __commonJS({
63094
63132
  n: "\n",
63095
63133
  r: "\r",
63096
63134
  t: " "
63097
- }, text, error = function(m2) {
63135
+ }, text2, error = function(m2) {
63098
63136
  throw {
63099
63137
  name: "SyntaxError",
63100
63138
  message: m2,
63101
63139
  at,
63102
- text
63140
+ text: text2
63103
63141
  };
63104
63142
  }, next = function(c) {
63105
63143
  if (c && c !== ch) {
63106
63144
  error("Expected '" + c + "' instead of '" + ch + "'");
63107
63145
  }
63108
- ch = text.charAt(at);
63146
+ ch = text2.charAt(at);
63109
63147
  at += 1;
63110
63148
  return ch;
63111
63149
  }, number44 = function() {
@@ -63152,12 +63190,12 @@ var require_parse = __commonJS({
63152
63190
  var startAt = at;
63153
63191
  while (next()) {
63154
63192
  if (ch === '"') {
63155
- if (at - 1 > startAt) string474 += text.substring(startAt, at - 1);
63193
+ if (at - 1 > startAt) string474 += text2.substring(startAt, at - 1);
63156
63194
  next();
63157
63195
  return string474;
63158
63196
  }
63159
63197
  if (ch === "\\") {
63160
- if (at - 1 > startAt) string474 += text.substring(startAt, at - 1);
63198
+ if (at - 1 > startAt) string474 += text2.substring(startAt, at - 1);
63161
63199
  next();
63162
63200
  if (ch === "u") {
63163
63201
  uffff = 0;
@@ -63290,7 +63328,7 @@ var require_parse = __commonJS({
63290
63328
  };
63291
63329
  return function(source, reviver) {
63292
63330
  var result;
63293
- text = source + "";
63331
+ text2 = source + "";
63294
63332
  at = 0;
63295
63333
  ch = " ";
63296
63334
  result = value();
@@ -64202,8 +64240,8 @@ var require_crypto = __commonJS({
64202
64240
  const result = new TextDecoder().decode(uint8array);
64203
64241
  return result;
64204
64242
  }
64205
- encodeBase64StringUtf8(text) {
64206
- const uint8array = new TextEncoder().encode(text);
64243
+ encodeBase64StringUtf8(text2) {
64244
+ const uint8array = new TextEncoder().encode(text2);
64207
64245
  const result = base64js.fromByteArray(uint8array);
64208
64246
  return result;
64209
64247
  }
@@ -64272,8 +64310,8 @@ var require_crypto2 = __commonJS({
64272
64310
  decodeBase64StringUtf8(base64) {
64273
64311
  return Buffer.from(base64, "base64").toString("utf-8");
64274
64312
  }
64275
- encodeBase64StringUtf8(text) {
64276
- return Buffer.from(text, "utf-8").toString("base64");
64313
+ encodeBase64StringUtf8(text2) {
64314
+ return Buffer.from(text2, "utf-8").toString("base64");
64277
64315
  }
64278
64316
  /**
64279
64317
  * Computes the SHA-256 hash of the provided string.
@@ -84718,8 +84756,8 @@ async function defaultParseResponse3(client, props) {
84718
84756
  const json2 = await response.json();
84719
84757
  return json2;
84720
84758
  }
84721
- const text = await response.text();
84722
- return text;
84759
+ const text2 = await response.text();
84760
+ return text2;
84723
84761
  })();
84724
84762
  loggerFor3(client).debug(`[${requestLogID}] response parsed`, formatRequestDetails3({
84725
84763
  retryOfRequestLogID,
@@ -86296,7 +86334,7 @@ var init_node = __esm({
86296
86334
  if (this.candidates && this.candidates.length > 1) {
86297
86335
  console.warn("there are multiple candidates in the response, returning text from the first one.");
86298
86336
  }
86299
- let text = "";
86337
+ let text2 = "";
86300
86338
  let anyTextPartText = false;
86301
86339
  const nonTextParts = [];
86302
86340
  for (const part of (_h = (_g = (_f = (_e = this.candidates) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.content) === null || _g === void 0 ? void 0 : _g.parts) !== null && _h !== void 0 ? _h : []) {
@@ -86310,13 +86348,13 @@ var init_node = __esm({
86310
86348
  continue;
86311
86349
  }
86312
86350
  anyTextPartText = true;
86313
- text += part.text;
86351
+ text2 += part.text;
86314
86352
  }
86315
86353
  }
86316
86354
  if (nonTextParts.length > 0) {
86317
86355
  console.warn(`there are non-text parts ${nonTextParts} in the response, returning concatenation of all text parts. Please refer to the non text parts for a full response from model.`);
86318
86356
  }
86319
- return anyTextPartText ? text : void 0;
86357
+ return anyTextPartText ? text2 : void 0;
86320
86358
  }
86321
86359
  /**
86322
86360
  * Returns the concatenation of all inline data parts from the first candidate
@@ -86569,7 +86607,7 @@ var init_node = __esm({
86569
86607
  */
86570
86608
  get text() {
86571
86609
  var _a5, _b, _c;
86572
- let text = "";
86610
+ let text2 = "";
86573
86611
  let anyTextPartFound = false;
86574
86612
  const nonTextParts = [];
86575
86613
  for (const part of (_c = (_b = (_a5 = this.serverContent) === null || _a5 === void 0 ? void 0 : _a5.modelTurn) === null || _b === void 0 ? void 0 : _b.parts) !== null && _c !== void 0 ? _c : []) {
@@ -86583,13 +86621,13 @@ var init_node = __esm({
86583
86621
  continue;
86584
86622
  }
86585
86623
  anyTextPartFound = true;
86586
- text += part.text;
86624
+ text2 += part.text;
86587
86625
  }
86588
86626
  }
86589
86627
  if (nonTextParts.length > 0) {
86590
86628
  console.warn(`there are non-text parts ${nonTextParts} in the response, returning concatenation of all text parts. Please refer to the non text parts for a full response from model.`);
86591
86629
  }
86592
- return anyTextPartFound ? text : void 0;
86630
+ return anyTextPartFound ? text2 : void 0;
86593
86631
  }
86594
86632
  /**
86595
86633
  * Returns the concatenation of all inline data parts from the server content if present.
@@ -91261,9 +91299,9 @@ var init_node = __esm({
91261
91299
  }
91262
91300
  return n;
91263
91301
  };
91264
- safeJSON3 = (text) => {
91302
+ safeJSON3 = (text2) => {
91265
91303
  try {
91266
- return JSON.parse(text);
91304
+ return JSON.parse(text2);
91267
91305
  } catch (err) {
91268
91306
  return void 0;
91269
91307
  }
@@ -109126,8 +109164,8 @@ function findBoundary(buf) {
109126
109164
  return null;
109127
109165
  }
109128
109166
  function parseMessage2(chunk, parse2, state, dataRequired) {
109129
- const text = new TextDecoder().decode(chunk);
109130
- const lines = text.split(/\r\n|\r|\n/);
109167
+ const text2 = new TextDecoder().decode(chunk);
109168
+ const lines = text2.split(/\r\n|\r|\n/);
109131
109169
  const dataLines = [];
109132
109170
  const ret = {};
109133
109171
  let ignore = true;
@@ -132001,10 +132039,10 @@ function formatMistralError(error) {
132001
132039
  }
132002
132040
  return safeJsonStringify(error);
132003
132041
  }
132004
- function truncateErrorText(text, maxChars) {
132005
- if (text.length <= maxChars)
132006
- return text;
132007
- return `${text.slice(0, maxChars)}... [truncated ${text.length - maxChars} chars]`;
132042
+ function truncateErrorText(text2, maxChars) {
132043
+ if (text2.length <= maxChars)
132044
+ return text2;
132045
+ return `${text2.slice(0, maxChars)}... [truncated ${text2.length - maxChars} chars]`;
132008
132046
  }
132009
132047
  function safeJsonStringify(value) {
132010
132048
  try {
@@ -132124,7 +132162,7 @@ async function consumeChatStream(model, output, stream2, mistralStream) {
132124
132162
  continue;
132125
132163
  }
132126
132164
  if (item.type === "thinking") {
132127
- const deltaText = item.thinking.map((part) => "text" in part ? part.text : "").filter((text) => text.length > 0).join("");
132165
+ const deltaText = item.thinking.map((part) => "text" in part ? part.text : "").filter((text2) => text2.length > 0).join("");
132128
132166
  const thinkingDelta = sanitizeSurrogates(deltaText);
132129
132167
  if (!thinkingDelta)
132130
132168
  continue;
@@ -132321,8 +132359,8 @@ function toChatMessages(messages, supportsImages) {
132321
132359
  }
132322
132360
  return result;
132323
132361
  }
132324
- function buildToolResultText(text, hasImages, supportsImages, isError) {
132325
- const trimmed = text.trim();
132362
+ function buildToolResultText(text2, hasImages, supportsImages, isError) {
132363
+ const trimmed = text2.trim();
132326
132364
  const errorPrefix = isError ? "[tool error] " : "";
132327
132365
  if (trimmed.length > 0) {
132328
132366
  const imageSuffix = hasImages && !supportsImages ? "\n[tool image omitted: model does not support images]" : "";
@@ -133123,14 +133161,14 @@ async function* parseWebSocket(socket, signal, idleTimeoutMs) {
133123
133161
  };
133124
133162
  const onMessage = (event) => {
133125
133163
  void (async () => {
133126
- let text = null;
133164
+ let text2 = null;
133127
133165
  try {
133128
133166
  if (!event || typeof event !== "object" || !("data" in event))
133129
133167
  return;
133130
- text = await decodeWebSocketData(event.data);
133131
- if (!text)
133168
+ text2 = await decodeWebSocketData(event.data);
133169
+ if (!text2)
133132
133170
  return;
133133
- const parsed = JSON.parse(text);
133171
+ const parsed = JSON.parse(text2);
133134
133172
  const type = typeof parsed.type === "string" ? parsed.type : "";
133135
133173
  if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
133136
133174
  sawCompletion = true;
@@ -133141,7 +133179,7 @@ async function* parseWebSocket(socket, signal, idleTimeoutMs) {
133141
133179
  } catch (cause) {
133142
133180
  failed = new CodexProtocolError(`Invalid Codex WebSocket JSON: ${formatThrownValue(cause)}`, {
133143
133181
  cause,
133144
- payload: text
133182
+ payload: text2
133145
133183
  });
133146
133184
  done = true;
133147
133185
  wake();
@@ -141305,11 +141343,11 @@ function videoRequestConfig(input) {
141305
141343
  };
141306
141344
  }
141307
141345
  async function readMiniMaxJson(response) {
141308
- const text = await response.text();
141346
+ const text2 = await response.text();
141309
141347
  let body = {};
141310
- if (text) {
141348
+ if (text2) {
141311
141349
  try {
141312
- const parsed = JSON.parse(text);
141350
+ const parsed = JSON.parse(text2);
141313
141351
  if (isRecord(parsed)) body = parsed;
141314
141352
  } catch {
141315
141353
  throw new Error("MiniMax video API returned invalid JSON");
@@ -142130,6 +142168,7 @@ __export(store_exports, {
142130
142168
  CodeGraphStore: () => CodeGraphStore
142131
142169
  });
142132
142170
  import { randomUUID as randomUUID6 } from "crypto";
142171
+ import { posix as pathPosix } from "path";
142133
142172
  function rowToFile(row) {
142134
142173
  return {
142135
142174
  id: row.id,
@@ -142180,6 +142219,26 @@ function rowToEdge(row) {
142180
142219
  ...row.sourceEpoch != null ? { sourceEpoch: Number(row.sourceEpoch) } : {}
142181
142220
  };
142182
142221
  }
142222
+ function resolveRelativeImportTarget(sourcePath, evidence, fileByPath) {
142223
+ const target = evidence?.trim().replace(/\\/g, "/");
142224
+ if (!target?.startsWith(".")) return void 0;
142225
+ const base = normalizeCodePath(pathPosix.normalize(pathPosix.join(pathPosix.dirname(sourcePath), target)));
142226
+ if (!base || base === ".." || base.startsWith("../")) return void 0;
142227
+ const candidates = [base];
142228
+ const extensionMatch = base.match(/(\.[A-Za-z0-9]+)$/);
142229
+ const hasExtension = Boolean(extensionMatch);
142230
+ if (!hasExtension) {
142231
+ const extensions = [...new Set([...fileByPath.keys()].map((path19) => path19.match(/(\.[A-Za-z0-9]+)$/)?.[1]).filter((extension3) => Boolean(extension3)))];
142232
+ for (const extension3 of extensions) {
142233
+ candidates.push(`${base}${extension3}`, `${base}/index${extension3}`);
142234
+ }
142235
+ } else {
142236
+ const sourceStem = base.slice(0, -extensionMatch[1].length);
142237
+ const sourceExtensions = extensionMatch[1] === ".js" ? [".ts", ".tsx", ".js", ".jsx"] : extensionMatch[1] === ".mjs" ? [".mts", ".mjs"] : extensionMatch[1] === ".cjs" ? [".cts", ".cjs"] : [];
142238
+ for (const extension3 of sourceExtensions) candidates.push(`${sourceStem}${extension3}`);
142239
+ }
142240
+ return candidates.map((candidate) => fileByPath.get(candidate)).find((file) => Boolean(file));
142241
+ }
142183
142242
  function rowToRef(row) {
142184
142243
  return {
142185
142244
  id: row.id,
@@ -142240,6 +142299,18 @@ function rowToSnapshot(row) {
142240
142299
  ...row.previousSnapshotId ? { previousSnapshotId: row.previousSnapshotId } : {}
142241
142300
  };
142242
142301
  }
142302
+ function rowToSnapshotFile(row) {
142303
+ return {
142304
+ snapshotId: row.snapshotId,
142305
+ projectId: row.projectId,
142306
+ fileId: row.fileId,
142307
+ path: row.path,
142308
+ contentHash: row.contentHash
142309
+ };
142310
+ }
142311
+ function snapshotIsIncomplete(snapshot) {
142312
+ return snapshot.completeness.skippedOversizedFiles > 0 || (snapshot.completeness.unreadableFiles ?? 0) > 0 || snapshot.completeness.removalScanDeferred;
142313
+ }
142243
142314
  var CodeGraphStore;
142244
142315
  var init_store = __esm({
142245
142316
  "src/codegraph/store.ts"() {
@@ -142411,6 +142482,7 @@ var init_store = __esm({
142411
142482
  }
142412
142483
  });
142413
142484
  tx();
142485
+ this.reconcileLocalImportTargets(projectId);
142414
142486
  }
142415
142487
  /**
142416
142488
  * Reconcile only files whose source changed plus files that disappeared.
@@ -142420,7 +142492,10 @@ var init_store = __esm({
142420
142492
  const changed = input.changed.filter((delta) => delta.file.projectId === projectId);
142421
142493
  const metadataOnly = (input.metadataOnly ?? []).filter((file) => file.projectId === projectId);
142422
142494
  const removedFileIds = input.removedFileIds ?? [];
142423
- if (changed.length === 0 && metadataOnly.length === 0 && removedFileIds.length === 0) return;
142495
+ if (changed.length === 0 && metadataOnly.length === 0 && removedFileIds.length === 0) {
142496
+ this.reconcileLocalImportTargets(projectId);
142497
+ return;
142498
+ }
142424
142499
  const staleRefsForFile = this.db.prepare(`
142425
142500
  UPDATE observation_code_refs
142426
142501
  SET status = 'stale', updatedAt = ?
@@ -142430,9 +142505,18 @@ var init_store = __esm({
142430
142505
  )
142431
142506
  )
142432
142507
  `);
142433
- const deleteEdgesForFile = this.db.prepare(`
142508
+ const deleteEdgesFromFile = this.db.prepare(`
142509
+ DELETE FROM code_edges
142510
+ WHERE projectId = ? AND fromFileId = ?
142511
+ `);
142512
+ const clearImportTargetsForFile = this.db.prepare(`
142513
+ UPDATE code_edges
142514
+ SET toFileId = NULL
142515
+ WHERE projectId = ? AND toFileId = ? AND type = 'imports'
142516
+ `);
142517
+ const deleteOtherIncomingEdgesForFile = this.db.prepare(`
142434
142518
  DELETE FROM code_edges
142435
- WHERE projectId = ? AND (fromFileId = ? OR toFileId = ?)
142519
+ WHERE projectId = ? AND toFileId = ? AND type != 'imports'
142436
142520
  `);
142437
142521
  const deleteSymbolsForFile = this.db.prepare(`DELETE FROM code_symbols WHERE projectId = ? AND fileId = ?`);
142438
142522
  const deleteFile = this.db.prepare(`DELETE FROM code_files WHERE projectId = ? AND id = ?`);
@@ -142469,14 +142553,18 @@ var init_store = __esm({
142469
142553
  const staleAt = (/* @__PURE__ */ new Date()).toISOString();
142470
142554
  for (const fileId of removedFileIds) {
142471
142555
  staleRefsForFile.run(staleAt, projectId, fileId, projectId, fileId);
142472
- deleteEdgesForFile.run(projectId, fileId, fileId);
142556
+ deleteEdgesFromFile.run(projectId, fileId);
142557
+ clearImportTargetsForFile.run(projectId, fileId);
142558
+ deleteOtherIncomingEdgesForFile.run(projectId, fileId);
142473
142559
  deleteSymbolsForFile.run(projectId, fileId);
142474
142560
  deleteFile.run(projectId, fileId);
142475
142561
  }
142476
142562
  for (const delta of changed) {
142477
142563
  const fileId = delta.file.id;
142478
142564
  staleRefsForFile.run(staleAt, projectId, fileId, projectId, fileId);
142479
- deleteEdgesForFile.run(projectId, fileId, fileId);
142565
+ deleteEdgesFromFile.run(projectId, fileId);
142566
+ clearImportTargetsForFile.run(projectId, fileId);
142567
+ deleteOtherIncomingEdgesForFile.run(projectId, fileId);
142480
142568
  deleteSymbolsForFile.run(projectId, fileId);
142481
142569
  writeFile5(delta.file);
142482
142570
  for (const symbol of delta.symbols) {
@@ -142514,6 +142602,7 @@ var init_store = __esm({
142514
142602
  for (const file of metadataOnly) writeFile5(file);
142515
142603
  });
142516
142604
  tx();
142605
+ this.reconcileLocalImportTargets(projectId);
142517
142606
  }
142518
142607
  upsertObservationRefs(refs) {
142519
142608
  if (refs.length === 0) return;
@@ -142575,6 +142664,25 @@ var init_store = __esm({
142575
142664
  });
142576
142665
  tx();
142577
142666
  }
142667
+ /**
142668
+ * Lite import extraction records the original import text first. Resolve only
142669
+ * local relative imports against the current index after each refresh, so an
142670
+ * added or removed target cannot leave a fabricated file-to-file relation.
142671
+ */
142672
+ reconcileLocalImportTargets(projectId) {
142673
+ const fileById = new Map(this.listFiles(projectId).map((file) => [file.id, file]));
142674
+ const fileByPath = new Map([...fileById.values()].map((file) => [file.path, file]));
142675
+ const update2 = this.db.prepare("UPDATE code_edges SET toFileId = ? WHERE id = ?");
142676
+ const tx = this.db.transaction(() => {
142677
+ for (const edge of this.listEdges(projectId)) {
142678
+ if (edge.type !== "imports" || !edge.fromFileId) continue;
142679
+ const source = fileById.get(edge.fromFileId);
142680
+ const target = source ? resolveRelativeImportTarget(source.path, edge.evidence, fileByPath) : void 0;
142681
+ update2.run(target?.id ?? null, edge.id);
142682
+ }
142683
+ });
142684
+ tx();
142685
+ }
142578
142686
  getFile(projectId, path19) {
142579
142687
  const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(path19));
142580
142688
  return row ? rowToFile(row) : null;
@@ -142649,6 +142757,18 @@ var init_store = __esm({
142649
142757
  ORDER BY observationId, status, id
142650
142758
  `).all(projectId).map(rowToRef);
142651
142759
  }
142760
+ /** Count distinct observations whose binding to these exact files is stale. */
142761
+ countStaleObservationRefsForFiles(projectId, fileIds) {
142762
+ const ids = [...new Set(fileIds.filter(Boolean))];
142763
+ if (ids.length === 0) return 0;
142764
+ const placeholders = ids.map(() => "?").join(", ");
142765
+ const row = this.db.prepare(`
142766
+ SELECT COUNT(DISTINCT observationId) AS count
142767
+ FROM observation_code_refs
142768
+ WHERE projectId = ? AND status = 'stale' AND fileId IN (${placeholders})
142769
+ `).get(projectId, ...ids);
142770
+ return Number(row?.count ?? 0);
142771
+ }
142652
142772
  listReferencedSymbols(projectId) {
142653
142773
  return this.db.prepare(`
142654
142774
  SELECT DISTINCT symbols.*
@@ -142670,6 +142790,94 @@ var init_store = __esm({
142670
142790
  "SELECT * FROM code_state_snapshots WHERE projectId = ? ORDER BY sourceEpoch DESC LIMIT ?"
142671
142791
  ).all(projectId, safeLimit).map(rowToSnapshot);
142672
142792
  }
142793
+ listSnapshotFiles(snapshotId) {
142794
+ return this.db.prepare(`
142795
+ SELECT snapshotId, projectId, fileId, path, contentHash
142796
+ FROM code_state_snapshot_files
142797
+ WHERE snapshotId = ?
142798
+ ORDER BY path
142799
+ `).all(snapshotId).map(rowToSnapshotFile);
142800
+ }
142801
+ diffSnapshots(projectId, fromSnapshotId, toSnapshotId) {
142802
+ const snapshots = this.db.prepare(`
142803
+ SELECT * FROM code_state_snapshots WHERE id IN (?, ?)
142804
+ `).all(fromSnapshotId, toSnapshotId).map(rowToSnapshot);
142805
+ if (snapshots.length !== 2) {
142806
+ return { projectId, fromSnapshotId, toSnapshotId, available: false, reason: "missing-snapshot", changes: [] };
142807
+ }
142808
+ if (snapshots.some((snapshot) => snapshot.projectId !== projectId)) {
142809
+ return { projectId, fromSnapshotId, toSnapshotId, available: false, reason: "project-mismatch", changes: [] };
142810
+ }
142811
+ if (snapshots.some(snapshotIsIncomplete)) {
142812
+ return { projectId, fromSnapshotId, toSnapshotId, available: false, reason: "incomplete-snapshot", changes: [] };
142813
+ }
142814
+ const before = this.listSnapshotFiles(fromSnapshotId);
142815
+ const after = this.listSnapshotFiles(toSnapshotId);
142816
+ if (before.length === 0 || after.length === 0) {
142817
+ return {
142818
+ projectId,
142819
+ fromSnapshotId,
142820
+ toSnapshotId,
142821
+ available: false,
142822
+ reason: "legacy-snapshot-without-manifest",
142823
+ changes: []
142824
+ };
142825
+ }
142826
+ const beforeByPath = new Map(before.map((file) => [file.path, file]));
142827
+ const afterByPath = new Map(after.map((file) => [file.path, file]));
142828
+ const paths = [.../* @__PURE__ */ new Set([...beforeByPath.keys(), ...afterByPath.keys()])].sort();
142829
+ const changes = [];
142830
+ for (const path19 of paths) {
142831
+ const oldFile = beforeByPath.get(path19);
142832
+ const newFile = afterByPath.get(path19);
142833
+ if (!oldFile && newFile) {
142834
+ changes.push({ path: path19, kind: "added", afterHash: newFile.contentHash });
142835
+ } else if (oldFile && !newFile) {
142836
+ changes.push({ path: path19, kind: "removed", beforeHash: oldFile.contentHash });
142837
+ } else if (oldFile && newFile && oldFile.contentHash !== newFile.contentHash) {
142838
+ changes.push({ path: path19, kind: "modified", beforeHash: oldFile.contentHash, afterHash: newFile.contentHash });
142839
+ }
142840
+ }
142841
+ return { projectId, fromSnapshotId, toSnapshotId, available: true, changes };
142842
+ }
142843
+ impactSlice(projectId, changedPaths, maxRelations = 24) {
142844
+ const safeLimit = Number.isFinite(maxRelations) ? Math.max(1, Math.min(100, Math.floor(maxRelations))) : 24;
142845
+ const normalizedPaths = [...new Set(changedPaths.map(normalizeCodePath).filter(Boolean))].sort();
142846
+ const files = this.listFiles(projectId);
142847
+ const fileById = new Map(files.map((file) => [file.id, file.path]));
142848
+ const fileIdBySymbolId = new Map(this.listSymbols(projectId).map((symbol) => [symbol.id, symbol.fileId]));
142849
+ const changedIds = new Set(files.filter((file) => normalizedPaths.includes(file.path)).map((file) => file.id));
142850
+ const direct = /* @__PURE__ */ new Set();
142851
+ let relationCount = 0;
142852
+ let truncated = false;
142853
+ for (const edge of this.listEdges(projectId)) {
142854
+ const endpoints = [
142855
+ edge.fromFileId,
142856
+ edge.toFileId,
142857
+ edge.fromSymbolId ? fileIdBySymbolId.get(edge.fromSymbolId) : void 0,
142858
+ edge.toSymbolId ? fileIdBySymbolId.get(edge.toSymbolId) : void 0
142859
+ ].filter((id) => Boolean(id));
142860
+ if (!endpoints.some((id) => changedIds.has(id))) continue;
142861
+ const relatedPaths = endpoints.filter((id) => !changedIds.has(id)).map((id) => fileById.get(id)).filter((path19) => Boolean(path19));
142862
+ if (relatedPaths.length === 0) continue;
142863
+ if (relationCount >= safeLimit) {
142864
+ truncated = true;
142865
+ break;
142866
+ }
142867
+ relationCount += 1;
142868
+ for (const path19 of relatedPaths) direct.add(path19);
142869
+ }
142870
+ const snapshot = this.latestSnapshot(projectId);
142871
+ return {
142872
+ projectId,
142873
+ ...snapshot ? { snapshotId: snapshot.id } : {},
142874
+ changedPaths: normalizedPaths,
142875
+ directlyConnectedPaths: [...direct].sort(),
142876
+ relationCount,
142877
+ truncated,
142878
+ provider: snapshot?.provider ?? "lite"
142879
+ };
142880
+ }
142673
142881
  /**
142674
142882
  * Record a completed scan and mark all current structural facts with its
142675
142883
  * epoch. The snapshot is written only after refresh reconciliation succeeds,
@@ -142711,6 +142919,16 @@ var init_store = __esm({
142711
142919
  this.db.prepare(
142712
142920
  "UPDATE observation_code_refs SET snapshotId = ? WHERE projectId = ? AND status = 'current'"
142713
142921
  ).run(id, input.projectId);
142922
+ const snapshotFileRows = this.db.prepare(`
142923
+ SELECT id, projectId, path, contentHash FROM code_files WHERE projectId = ? ORDER BY path
142924
+ `).all(input.projectId);
142925
+ const insertSnapshotFile = this.db.prepare(`
142926
+ INSERT INTO code_state_snapshot_files (snapshotId, projectId, fileId, path, contentHash)
142927
+ VALUES (?, ?, ?, ?, ?)
142928
+ `);
142929
+ for (const file of snapshotFileRows) {
142930
+ insertSnapshotFile.run(id, file.projectId, file.id, file.path, file.contentHash);
142931
+ }
142714
142932
  snapshot = {
142715
142933
  ...input,
142716
142934
  id,
@@ -142876,8 +143094,8 @@ __export(lite_provider_exports, {
142876
143094
  import { createHash as createHash7 } from "crypto";
142877
143095
  import { readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
142878
143096
  import { join as join4, relative } from "path";
142879
- function hashText(text) {
142880
- return createHash7("sha256").update(text).digest("hex");
143097
+ function hashText(text2) {
143098
+ return createHash7("sha256").update(text2).digest("hex");
142881
143099
  }
142882
143100
  function extension2(path19) {
142883
143101
  const index = path19.lastIndexOf(".");
@@ -142921,19 +143139,19 @@ function walk(root, exclude, maxFiles) {
142921
143139
  visit(root);
142922
143140
  return out;
142923
143141
  }
142924
- function lineOf(text, index) {
142925
- return text.slice(0, index).split(/\r?\n/).length;
143142
+ function lineOf(text2, index) {
143143
+ return text2.slice(0, index).split(/\r?\n/).length;
142926
143144
  }
142927
- function extractSymbols(projectId, file, text, indexedAt) {
143145
+ function extractSymbols(projectId, file, text2, indexedAt) {
142928
143146
  const symbols = [];
142929
143147
  const profile = file.language ? languageProfiles[file.language] : void 0;
142930
143148
  const patterns = profile?.symbols ?? [];
142931
143149
  for (const { kind, re } of patterns) {
142932
143150
  re.lastIndex = 0;
142933
- for (const match2 of text.matchAll(re)) {
143151
+ for (const match2 of text2.matchAll(re)) {
142934
143152
  const name = match2[1];
142935
143153
  if (!name || name.includes("\n")) continue;
142936
- const startLine = lineOf(text, match2.index ?? 0);
143154
+ const startLine = lineOf(text2, match2.index ?? 0);
142937
143155
  const id = makeCodeSymbolId({ projectId, path: file.path, qualifiedName: name, kind });
142938
143156
  symbols.push({
142939
143157
  id,
@@ -142953,13 +143171,13 @@ function extractSymbols(projectId, file, text, indexedAt) {
142953
143171
  }
142954
143172
  return symbols;
142955
143173
  }
142956
- function extractImportEdges(projectId, file, text, indexedAt) {
143174
+ function extractImportEdges(projectId, file, text2, indexedAt) {
142957
143175
  const edges = [];
142958
143176
  const profile = file.language ? languageProfiles[file.language] : void 0;
142959
143177
  const imports = profile?.imports ?? [];
142960
143178
  for (const re of imports) {
142961
143179
  re.lastIndex = 0;
142962
- for (const match2 of text.matchAll(re)) {
143180
+ for (const match2 of text2.matchAll(re)) {
142963
143181
  const target = (match2[1] ?? "").trim();
142964
143182
  if (!target) continue;
142965
143183
  if (target.includes("\n")) {
@@ -142994,14 +143212,14 @@ function extractImportEdges(projectId, file, text, indexedAt) {
142994
143212
  function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
142995
143213
  const rel = normalizeCodePath(relative(projectRoot, abs));
142996
143214
  try {
142997
- const text = readFileSync6(abs, "utf-8");
143215
+ const text2 = readFileSync6(abs, "utf-8");
142998
143216
  const stat4 = fileStat ?? statSync3(abs);
142999
143217
  const file = {
143000
143218
  id: makeCodeFileId(projectId, rel),
143001
143219
  projectId,
143002
143220
  path: rel,
143003
143221
  language: languageForPath(rel),
143004
- contentHash: hashText(text),
143222
+ contentHash: hashText(text2),
143005
143223
  // Preserve filesystem precision. Rounding here made same-size edits in
143006
143224
  // a short Windows timestamp window indistinguishable to refresh.
143007
143225
  mtimeMs: Number(stat4.mtimeMs),
@@ -143011,8 +143229,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
143011
143229
  return {
143012
143230
  delta: {
143013
143231
  file,
143014
- symbols: extractSymbols(projectId, file, text, indexedAt),
143015
- edges: extractImportEdges(projectId, file, text, indexedAt)
143232
+ symbols: extractSymbols(projectId, file, text2, indexedAt),
143233
+ edges: extractImportEdges(projectId, file, text2, indexedAt)
143016
143234
  },
143017
143235
  unreadable: false
143018
143236
  };
@@ -143291,18 +143509,18 @@ __export(binder_exports, {
143291
143509
  function observationText(obs) {
143292
143510
  return [obs.title, obs.narrative, ...obs.facts ?? []].join("\n");
143293
143511
  }
143294
- function mentionsSymbol(text, symbol) {
143512
+ function mentionsSymbol(text2, symbol) {
143295
143513
  const name = symbol.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
143296
- return new RegExp(`(^|[^\\w$])${name}([^\\w$]|$)`).test(text);
143514
+ return new RegExp(`(^|[^\\w$])${name}([^\\w$]|$)`).test(text2);
143297
143515
  }
143298
- function identifierCandidates(text) {
143299
- return [...new Set(text.match(/[A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?/g) ?? [])];
143516
+ function identifierCandidates(text2) {
143517
+ return [...new Set(text2.match(/[A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?/g) ?? [])];
143300
143518
  }
143301
- function codeIdentifierCandidates(text) {
143519
+ function codeIdentifierCandidates(text2) {
143302
143520
  const explicit = /* @__PURE__ */ new Set();
143303
- for (const match2 of text.matchAll(/`([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)`/g)) explicit.add(match2[1]);
143304
- for (const match2 of text.matchAll(/\b([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)\s*\(/g)) explicit.add(match2[1]);
143305
- return identifierCandidates(text).filter((candidate) => explicit.has(candidate) || candidate.includes("::") || /[!?=]$/.test(candidate) || /^[A-Z]/.test(candidate) || /[_$]/.test(candidate) || /[a-z0-9][A-Z]/.test(candidate) || /[A-Z]{2}/.test(candidate));
143521
+ for (const match2 of text2.matchAll(/`([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)`/g)) explicit.add(match2[1]);
143522
+ for (const match2 of text2.matchAll(/\b([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)\s*\(/g)) explicit.add(match2[1]);
143523
+ return identifierCandidates(text2).filter((candidate) => explicit.has(candidate) || candidate.includes("::") || /[!?=]$/.test(candidate) || /^[A-Z]/.test(candidate) || /[_$]/.test(candidate) || /[a-z0-9][A-Z]/.test(candidate) || /[A-Z]{2}/.test(candidate));
143306
143524
  }
143307
143525
  function fileRef(projectId, obs, file) {
143308
143526
  return {
@@ -143332,7 +143550,7 @@ function symbolRef(projectId, obs, file, symbol) {
143332
143550
  }
143333
143551
  function resolveObservationCodeRefs(obs, lookup2) {
143334
143552
  const refs = /* @__PURE__ */ new Map();
143335
- const text = observationText(obs);
143553
+ const text2 = observationText(obs);
143336
143554
  const candidateFiles = /* @__PURE__ */ new Map();
143337
143555
  for (const rawPath of obs.filesModified ?? []) {
143338
143556
  const file = lookup2.findFile(normalizeCodePath(rawPath));
@@ -143342,7 +143560,7 @@ function resolveObservationCodeRefs(obs, lookup2) {
143342
143560
  refs.set(ref.id, ref);
143343
143561
  }
143344
143562
  const hintedFileIds = new Set(candidateFiles.keys());
143345
- const symbolNames = hintedFileIds.size > 0 ? identifierCandidates(text) : codeIdentifierCandidates(text);
143563
+ const symbolNames = hintedFileIds.size > 0 ? identifierCandidates(text2) : codeIdentifierCandidates(text2);
143346
143564
  const symbols = lookup2.findSymbols(symbolNames, [...hintedFileIds]);
143347
143565
  const symbolsByName = /* @__PURE__ */ new Map();
143348
143566
  for (const symbol of symbols) {
@@ -143353,7 +143571,7 @@ function resolveObservationCodeRefs(obs, lookup2) {
143353
143571
  for (const group of symbolsByName.values()) {
143354
143572
  const candidates = group.length === 1 ? group : [];
143355
143573
  for (const symbol of candidates) {
143356
- if (!mentionsSymbol(text, symbol)) continue;
143574
+ if (!mentionsSymbol(text2, symbol)) continue;
143357
143575
  const file = candidateFiles.get(symbol.fileId) ?? lookup2.findFile(symbol.path);
143358
143576
  if (!file) continue;
143359
143577
  candidateFiles.set(file.id, file);
@@ -144081,8 +144299,8 @@ function taskTokens(task) {
144081
144299
  return normalizedTokens(task);
144082
144300
  }
144083
144301
  function scoreClaim(claim, tokens) {
144084
- const text = [claim.subject, claim.predicate, claim.objectValue].join(" ").toLocaleLowerCase("en-US");
144085
- const words = normalizedTokens(text);
144302
+ const text2 = [claim.subject, claim.predicate, claim.objectValue].join(" ").toLocaleLowerCase("en-US");
144303
+ const words = normalizedTokens(text2);
144086
144304
  let matches = 0;
144087
144305
  for (const token of tokens) {
144088
144306
  if (words.has(token)) matches += 1;
@@ -144978,29 +145196,141 @@ var init_workflow_store = __esm({
144978
145196
  }
144979
145197
  });
144980
145198
 
145199
+ // src/knowledge/outcome-types.ts
145200
+ var MEMORY_OUTCOME_CANDIDATE_KINDS, MEMORY_OUTCOME_SIGNAL_KINDS;
145201
+ var init_outcome_types = __esm({
145202
+ "src/knowledge/outcome-types.ts"() {
145203
+ "use strict";
145204
+ init_esm_shims();
145205
+ MEMORY_OUTCOME_CANDIDATE_KINDS = ["claim", "durable-memory", "workflow"];
145206
+ MEMORY_OUTCOME_SIGNAL_KINDS = [
145207
+ "verification-passed",
145208
+ "verification-failed",
145209
+ "verified-reuse",
145210
+ "user-pin",
145211
+ "user-correction",
145212
+ "source-changed",
145213
+ "conflict-confirmed",
145214
+ "manual-review"
145215
+ ];
145216
+ }
145217
+ });
145218
+
145219
+ // src/knowledge/outcome-store.ts
145220
+ import { randomUUID as randomUUID10 } from "crypto";
145221
+ function optionalText4(value) {
145222
+ return typeof value === "string" && value.trim() ? value : void 0;
145223
+ }
145224
+ function text(value, field, max = 1e3) {
145225
+ const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
145226
+ if (!safe) throw new Error("Outcome signal " + field + " is required.");
145227
+ if (safe.length > max) throw new Error("Outcome signal " + field + " is too long.");
145228
+ return safe;
145229
+ }
145230
+ function oneOf(value, values, field) {
145231
+ if (typeof value !== "string" || !values.includes(value)) {
145232
+ throw new Error("Outcome signal " + field + " is invalid.");
145233
+ }
145234
+ return value;
145235
+ }
145236
+ function rowToSignal(row) {
145237
+ return {
145238
+ id: row.id,
145239
+ projectId: row.projectId,
145240
+ candidateKind: row.candidateKind,
145241
+ candidateId: row.candidateId,
145242
+ kind: row.kind,
145243
+ sourceRef: row.sourceRef,
145244
+ ...optionalText4(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
145245
+ ...optionalText4(row.detail) ? { detail: row.detail } : {},
145246
+ observedAt: row.observedAt
145247
+ };
145248
+ }
145249
+ var OutcomeStore;
145250
+ var init_outcome_store = __esm({
145251
+ "src/knowledge/outcome-store.ts"() {
145252
+ "use strict";
145253
+ init_esm_shims();
145254
+ init_secret_filter();
145255
+ init_sqlite_db();
145256
+ init_outcome_types();
145257
+ OutcomeStore = class {
145258
+ db = null;
145259
+ async init(dataDir) {
145260
+ this.db = getDatabase(dataDir);
145261
+ }
145262
+ record(input) {
145263
+ const signal = {
145264
+ id: randomUUID10(),
145265
+ projectId: text(input.projectId, "project id"),
145266
+ candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
145267
+ candidateId: text(input.candidateId, "candidate id"),
145268
+ kind: oneOf(input.kind, MEMORY_OUTCOME_SIGNAL_KINDS, "kind"),
145269
+ sourceRef: text(input.sourceRef, "source reference"),
145270
+ ...input.snapshotId ? { snapshotId: text(input.snapshotId, "snapshot id") } : {},
145271
+ ...input.detail ? { detail: text(input.detail, "detail") } : {},
145272
+ observedAt: input.observedAt ?? (/* @__PURE__ */ new Date()).toISOString()
145273
+ };
145274
+ this.db.prepare(`
145275
+ INSERT INTO memory_outcome_signals (
145276
+ id, projectId, candidateKind, candidateId, kind, sourceRef, snapshotId, detail, observedAt
145277
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
145278
+ `).run(
145279
+ signal.id,
145280
+ signal.projectId,
145281
+ signal.candidateKind,
145282
+ signal.candidateId,
145283
+ signal.kind,
145284
+ signal.sourceRef,
145285
+ signal.snapshotId ?? null,
145286
+ signal.detail ?? null,
145287
+ signal.observedAt
145288
+ );
145289
+ return signal;
145290
+ }
145291
+ latestForCandidates(projectId, candidateKind, candidateIds) {
145292
+ const ids = [...new Set(candidateIds.filter(Boolean))];
145293
+ if (ids.length === 0) return /* @__PURE__ */ new Map();
145294
+ const placeholders = ids.map(() => "?").join(", ");
145295
+ const rows = this.db.prepare(`
145296
+ SELECT * FROM memory_outcome_signals
145297
+ WHERE projectId = ? AND candidateKind = ? AND candidateId IN (${placeholders})
145298
+ ORDER BY observedAt DESC, id DESC
145299
+ `).all(projectId, candidateKind, ...ids);
145300
+ const latest = /* @__PURE__ */ new Map();
145301
+ for (const row of rows) {
145302
+ const signal = rowToSignal(row);
145303
+ if (!latest.has(signal.candidateId)) latest.set(signal.candidateId, signal);
145304
+ }
145305
+ return latest;
145306
+ }
145307
+ };
145308
+ }
145309
+ });
145310
+
144981
145311
  // src/codegraph/task-lens.ts
144982
- function containsTaskKeyword(text, keyword) {
145312
+ function containsTaskKeyword(text2, keyword) {
144983
145313
  const matcher = /^[a-z0-9_-]+$/i.test(keyword) ? new RegExp(`(^|[^a-z0-9_-])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?=[^a-z0-9_-]|$)`, "gi") : new RegExp(keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
144984
145314
  let match2;
144985
- while ((match2 = matcher.exec(text)) !== null) {
145315
+ while ((match2 = matcher.exec(text2)) !== null) {
144986
145316
  const keywordIndex = match2.index + (match2[1]?.length ?? 0);
144987
- if (!isNegatedTaskKeyword(text, keywordIndex)) return true;
145317
+ if (!isNegatedTaskKeyword(text2, keywordIndex)) return true;
144988
145318
  }
144989
145319
  return false;
144990
145320
  }
144991
- function isNegatedTaskKeyword(text, keywordIndex) {
145321
+ function isNegatedTaskKeyword(text2, keywordIndex) {
144992
145322
  const boundary = Math.max(
144993
- text.lastIndexOf(".", keywordIndex - 1),
144994
- text.lastIndexOf("!", keywordIndex - 1),
144995
- text.lastIndexOf("?", keywordIndex - 1),
144996
- text.lastIndexOf(";", keywordIndex - 1),
144997
- text.lastIndexOf("\n", keywordIndex - 1),
144998
- text.lastIndexOf("\u3002", keywordIndex - 1),
144999
- text.lastIndexOf("\uFF01", keywordIndex - 1),
145000
- text.lastIndexOf("\uFF1F", keywordIndex - 1),
145001
- text.lastIndexOf("\uFF1B", keywordIndex - 1)
145323
+ text2.lastIndexOf(".", keywordIndex - 1),
145324
+ text2.lastIndexOf("!", keywordIndex - 1),
145325
+ text2.lastIndexOf("?", keywordIndex - 1),
145326
+ text2.lastIndexOf(";", keywordIndex - 1),
145327
+ text2.lastIndexOf("\n", keywordIndex - 1),
145328
+ text2.lastIndexOf("\u3002", keywordIndex - 1),
145329
+ text2.lastIndexOf("\uFF01", keywordIndex - 1),
145330
+ text2.lastIndexOf("\uFF1F", keywordIndex - 1),
145331
+ text2.lastIndexOf("\uFF1B", keywordIndex - 1)
145002
145332
  );
145003
- let prefix = text.slice(boundary + 1, keywordIndex).toLowerCase();
145333
+ let prefix = text2.slice(boundary + 1, keywordIndex).toLowerCase();
145004
145334
  const contrast = /(?:,|\uff0c)\s*(?:but|however|instead|yet|\u4f46\u662f|\u4f46|\u800c\u662f)\s*/gi;
145005
145335
  let contrastMatch;
145006
145336
  let contrastEnd = -1;
@@ -145059,7 +145389,7 @@ function requiredText(data, key) {
145059
145389
  }
145060
145390
  return value.trim();
145061
145391
  }
145062
- function optionalText4(data, key) {
145392
+ function optionalText5(data, key) {
145063
145393
  const value = data[key];
145064
145394
  if (value === void 0 || value === null || value === "") return void 0;
145065
145395
  if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
@@ -145102,11 +145432,11 @@ function phaseFromValue(value, index) {
145102
145432
  }
145103
145433
  const data = value;
145104
145434
  const title = requiredText(data, "title");
145105
- const phaseId = optionalText4(data, "id") ?? slug(title) + "-" + (index + 1);
145435
+ const phaseId = optionalText5(data, "id") ?? slug(title) + "-" + (index + 1);
145106
145436
  return {
145107
145437
  id: phaseId,
145108
145438
  title,
145109
- instructions: optionalText4(data, "instructions") ?? "",
145439
+ instructions: optionalText5(data, "instructions") ?? "",
145110
145440
  branches: optionalStringArray(data, "branches"),
145111
145441
  expectedOutputs: optionalStringArray(data, "expectedOutputs"),
145112
145442
  verificationGates: optionalStringArray(data, "verificationGates")
@@ -145215,7 +145545,7 @@ function parsedWorkflow(data, body, input) {
145215
145545
  id: requiredText(data, "id"),
145216
145546
  workspaceId: input.workspaceId,
145217
145547
  title: requiredText(data, "title"),
145218
- description: optionalText4(data, "description") ?? "",
145548
+ description: optionalText5(data, "description") ?? "",
145219
145549
  status: optionalStatus(data),
145220
145550
  version: optionalNumber(data, "version", 1),
145221
145551
  taskLenses: optionalStringArray(data, "taskLenses"),
@@ -145234,9 +145564,9 @@ function parsedWorkflow(data, body, input) {
145234
145564
  sourcePath: normalizeSourcePath(input.sourcePath),
145235
145565
  sourceHash: "",
145236
145566
  contentHash: input.contentHash,
145237
- createdAt: optionalText4(data, "createdAt") ?? now2(),
145238
- updatedAt: optionalText4(data, "updatedAt") ?? now2(),
145239
- ...optionalText4(data, "importedFrom") ? { importedFrom: optionalText4(data, "importedFrom") } : {}
145567
+ createdAt: optionalText5(data, "createdAt") ?? now2(),
145568
+ updatedAt: optionalText5(data, "updatedAt") ?? now2(),
145569
+ ...optionalText5(data, "importedFrom") ? { importedFrom: optionalText5(data, "importedFrom") } : {}
145240
145570
  };
145241
145571
  if (candidate.phases.length === 0) {
145242
145572
  throw new Error("workflow must contain at least one phase or a non-empty body");
@@ -145335,11 +145665,11 @@ async function syncCanonicalWorkflows(workspace) {
145335
145665
  return { workflows, errors };
145336
145666
  }
145337
145667
  function inferTaskLenses(value) {
145338
- const text = value.toLowerCase();
145339
- return Object.entries(LENS_TERMS).filter(([, terms]) => terms.some((term) => text.includes(term))).map(([lens]) => lens);
145668
+ const text2 = value.toLowerCase();
145669
+ return Object.entries(LENS_TERMS).filter(([, terms]) => terms.some((term) => text2.includes(term))).map(([lens]) => lens);
145340
145670
  }
145341
145671
  function taskScore(workflow, task) {
145342
- const text = task.toLowerCase();
145672
+ const text2 = task.toLowerCase();
145343
145673
  let score = 0;
145344
145674
  const reasons = [];
145345
145675
  const matchedLenses = /* @__PURE__ */ new Set();
@@ -145606,7 +145936,31 @@ async function recordWorkflowRun(input) {
145606
145936
  throw new Error("Workflow was not found for this workspace");
145607
145937
  }
145608
145938
  if (!input.run.task.trim()) throw new Error("Workflow run task is required");
145609
- return store.recordRun(input.run);
145939
+ const run = store.recordRun(input.run);
145940
+ const kind = run.outcome === "passed" && run.verificationVerdict === "passed" ? "verification-passed" : run.outcome === "failed" || run.verificationVerdict === "failed" ? "verification-failed" : void 0;
145941
+ if (!kind) return run;
145942
+ const evidence = /* @__PURE__ */ new Map();
145943
+ evidence.set(workflow.id, "workflow");
145944
+ for (const reference of run.selectedEvidence) {
145945
+ const match2 = /^(claim|durable):(.+)$/.exec(reference);
145946
+ if (!match2) continue;
145947
+ evidence.set(match2[2], match2[1] === "claim" ? "claim" : "durable-memory");
145948
+ }
145949
+ const outcomes = new OutcomeStore();
145950
+ await outcomes.init(workflowStoreDataDir(input.workspace));
145951
+ for (const [candidateId, candidateKind] of evidence) {
145952
+ outcomes.record({
145953
+ projectId: run.projectId,
145954
+ candidateKind,
145955
+ candidateId,
145956
+ kind,
145957
+ sourceRef: "workflow-run:" + run.id,
145958
+ ...run.startingSnapshotId ? { snapshotId: run.startingSnapshotId } : {},
145959
+ ...run.failureReason ? { detail: run.failureReason } : {},
145960
+ observedAt: run.completedAt ?? run.startedAt
145961
+ });
145962
+ }
145963
+ return run;
145610
145964
  }
145611
145965
  async function selectWorkspaceWorkflows(input) {
145612
145966
  const synced = await syncCanonicalWorkflows(input.workspace);
@@ -145631,6 +145985,7 @@ var init_workflows2 = __esm({
145631
145985
  init_workflow_sync();
145632
145986
  init_workspace();
145633
145987
  init_workflow_store();
145988
+ init_outcome_store();
145634
145989
  init_task_lens();
145635
145990
  KNOWN_AGENTS = [
145636
145991
  "windsurf",