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;
@@ -6926,6 +6964,7 @@ __export(store_exports, {
6926
6964
  CodeGraphStore: () => CodeGraphStore
6927
6965
  });
6928
6966
  import { randomUUID as randomUUID3 } from "crypto";
6967
+ import { posix as pathPosix } from "path";
6929
6968
  function rowToFile(row) {
6930
6969
  return {
6931
6970
  id: row.id,
@@ -6976,6 +7015,26 @@ function rowToEdge(row) {
6976
7015
  ...row.sourceEpoch != null ? { sourceEpoch: Number(row.sourceEpoch) } : {}
6977
7016
  };
6978
7017
  }
7018
+ function resolveRelativeImportTarget(sourcePath, evidence, fileByPath) {
7019
+ const target = evidence?.trim().replace(/\\/g, "/");
7020
+ if (!target?.startsWith(".")) return void 0;
7021
+ const base = normalizeCodePath(pathPosix.normalize(pathPosix.join(pathPosix.dirname(sourcePath), target)));
7022
+ if (!base || base === ".." || base.startsWith("../")) return void 0;
7023
+ const candidates = [base];
7024
+ const extensionMatch = base.match(/(\.[A-Za-z0-9]+)$/);
7025
+ const hasExtension = Boolean(extensionMatch);
7026
+ if (!hasExtension) {
7027
+ const extensions = [...new Set([...fileByPath.keys()].map((path18) => path18.match(/(\.[A-Za-z0-9]+)$/)?.[1]).filter((extension3) => Boolean(extension3)))];
7028
+ for (const extension3 of extensions) {
7029
+ candidates.push(`${base}${extension3}`, `${base}/index${extension3}`);
7030
+ }
7031
+ } else {
7032
+ const sourceStem = base.slice(0, -extensionMatch[1].length);
7033
+ const sourceExtensions = extensionMatch[1] === ".js" ? [".ts", ".tsx", ".js", ".jsx"] : extensionMatch[1] === ".mjs" ? [".mts", ".mjs"] : extensionMatch[1] === ".cjs" ? [".cts", ".cjs"] : [];
7034
+ for (const extension3 of sourceExtensions) candidates.push(`${sourceStem}${extension3}`);
7035
+ }
7036
+ return candidates.map((candidate) => fileByPath.get(candidate)).find((file) => Boolean(file));
7037
+ }
6979
7038
  function rowToRef(row) {
6980
7039
  return {
6981
7040
  id: row.id,
@@ -7036,6 +7095,18 @@ function rowToSnapshot(row) {
7036
7095
  ...row.previousSnapshotId ? { previousSnapshotId: row.previousSnapshotId } : {}
7037
7096
  };
7038
7097
  }
7098
+ function rowToSnapshotFile(row) {
7099
+ return {
7100
+ snapshotId: row.snapshotId,
7101
+ projectId: row.projectId,
7102
+ fileId: row.fileId,
7103
+ path: row.path,
7104
+ contentHash: row.contentHash
7105
+ };
7106
+ }
7107
+ function snapshotIsIncomplete(snapshot) {
7108
+ return snapshot.completeness.skippedOversizedFiles > 0 || (snapshot.completeness.unreadableFiles ?? 0) > 0 || snapshot.completeness.removalScanDeferred;
7109
+ }
7039
7110
  var CodeGraphStore;
7040
7111
  var init_store = __esm({
7041
7112
  "src/codegraph/store.ts"() {
@@ -7207,6 +7278,7 @@ var init_store = __esm({
7207
7278
  }
7208
7279
  });
7209
7280
  tx();
7281
+ this.reconcileLocalImportTargets(projectId);
7210
7282
  }
7211
7283
  /**
7212
7284
  * Reconcile only files whose source changed plus files that disappeared.
@@ -7216,7 +7288,10 @@ var init_store = __esm({
7216
7288
  const changed = input.changed.filter((delta) => delta.file.projectId === projectId);
7217
7289
  const metadataOnly = (input.metadataOnly ?? []).filter((file) => file.projectId === projectId);
7218
7290
  const removedFileIds = input.removedFileIds ?? [];
7219
- if (changed.length === 0 && metadataOnly.length === 0 && removedFileIds.length === 0) return;
7291
+ if (changed.length === 0 && metadataOnly.length === 0 && removedFileIds.length === 0) {
7292
+ this.reconcileLocalImportTargets(projectId);
7293
+ return;
7294
+ }
7220
7295
  const staleRefsForFile = this.db.prepare(`
7221
7296
  UPDATE observation_code_refs
7222
7297
  SET status = 'stale', updatedAt = ?
@@ -7226,9 +7301,18 @@ var init_store = __esm({
7226
7301
  )
7227
7302
  )
7228
7303
  `);
7229
- const deleteEdgesForFile = this.db.prepare(`
7304
+ const deleteEdgesFromFile = this.db.prepare(`
7305
+ DELETE FROM code_edges
7306
+ WHERE projectId = ? AND fromFileId = ?
7307
+ `);
7308
+ const clearImportTargetsForFile = this.db.prepare(`
7309
+ UPDATE code_edges
7310
+ SET toFileId = NULL
7311
+ WHERE projectId = ? AND toFileId = ? AND type = 'imports'
7312
+ `);
7313
+ const deleteOtherIncomingEdgesForFile = this.db.prepare(`
7230
7314
  DELETE FROM code_edges
7231
- WHERE projectId = ? AND (fromFileId = ? OR toFileId = ?)
7315
+ WHERE projectId = ? AND toFileId = ? AND type != 'imports'
7232
7316
  `);
7233
7317
  const deleteSymbolsForFile = this.db.prepare(`DELETE FROM code_symbols WHERE projectId = ? AND fileId = ?`);
7234
7318
  const deleteFile = this.db.prepare(`DELETE FROM code_files WHERE projectId = ? AND id = ?`);
@@ -7265,14 +7349,18 @@ var init_store = __esm({
7265
7349
  const staleAt = (/* @__PURE__ */ new Date()).toISOString();
7266
7350
  for (const fileId of removedFileIds) {
7267
7351
  staleRefsForFile.run(staleAt, projectId, fileId, projectId, fileId);
7268
- deleteEdgesForFile.run(projectId, fileId, fileId);
7352
+ deleteEdgesFromFile.run(projectId, fileId);
7353
+ clearImportTargetsForFile.run(projectId, fileId);
7354
+ deleteOtherIncomingEdgesForFile.run(projectId, fileId);
7269
7355
  deleteSymbolsForFile.run(projectId, fileId);
7270
7356
  deleteFile.run(projectId, fileId);
7271
7357
  }
7272
7358
  for (const delta of changed) {
7273
7359
  const fileId = delta.file.id;
7274
7360
  staleRefsForFile.run(staleAt, projectId, fileId, projectId, fileId);
7275
- deleteEdgesForFile.run(projectId, fileId, fileId);
7361
+ deleteEdgesFromFile.run(projectId, fileId);
7362
+ clearImportTargetsForFile.run(projectId, fileId);
7363
+ deleteOtherIncomingEdgesForFile.run(projectId, fileId);
7276
7364
  deleteSymbolsForFile.run(projectId, fileId);
7277
7365
  writeFile5(delta.file);
7278
7366
  for (const symbol of delta.symbols) {
@@ -7310,6 +7398,7 @@ var init_store = __esm({
7310
7398
  for (const file of metadataOnly) writeFile5(file);
7311
7399
  });
7312
7400
  tx();
7401
+ this.reconcileLocalImportTargets(projectId);
7313
7402
  }
7314
7403
  upsertObservationRefs(refs) {
7315
7404
  if (refs.length === 0) return;
@@ -7371,6 +7460,25 @@ var init_store = __esm({
7371
7460
  });
7372
7461
  tx();
7373
7462
  }
7463
+ /**
7464
+ * Lite import extraction records the original import text first. Resolve only
7465
+ * local relative imports against the current index after each refresh, so an
7466
+ * added or removed target cannot leave a fabricated file-to-file relation.
7467
+ */
7468
+ reconcileLocalImportTargets(projectId) {
7469
+ const fileById = new Map(this.listFiles(projectId).map((file) => [file.id, file]));
7470
+ const fileByPath = new Map([...fileById.values()].map((file) => [file.path, file]));
7471
+ const update2 = this.db.prepare("UPDATE code_edges SET toFileId = ? WHERE id = ?");
7472
+ const tx = this.db.transaction(() => {
7473
+ for (const edge of this.listEdges(projectId)) {
7474
+ if (edge.type !== "imports" || !edge.fromFileId) continue;
7475
+ const source = fileById.get(edge.fromFileId);
7476
+ const target = source ? resolveRelativeImportTarget(source.path, edge.evidence, fileByPath) : void 0;
7477
+ update2.run(target?.id ?? null, edge.id);
7478
+ }
7479
+ });
7480
+ tx();
7481
+ }
7374
7482
  getFile(projectId, path18) {
7375
7483
  const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(path18));
7376
7484
  return row ? rowToFile(row) : null;
@@ -7445,6 +7553,18 @@ var init_store = __esm({
7445
7553
  ORDER BY observationId, status, id
7446
7554
  `).all(projectId).map(rowToRef);
7447
7555
  }
7556
+ /** Count distinct observations whose binding to these exact files is stale. */
7557
+ countStaleObservationRefsForFiles(projectId, fileIds) {
7558
+ const ids = [...new Set(fileIds.filter(Boolean))];
7559
+ if (ids.length === 0) return 0;
7560
+ const placeholders = ids.map(() => "?").join(", ");
7561
+ const row = this.db.prepare(`
7562
+ SELECT COUNT(DISTINCT observationId) AS count
7563
+ FROM observation_code_refs
7564
+ WHERE projectId = ? AND status = 'stale' AND fileId IN (${placeholders})
7565
+ `).get(projectId, ...ids);
7566
+ return Number(row?.count ?? 0);
7567
+ }
7448
7568
  listReferencedSymbols(projectId) {
7449
7569
  return this.db.prepare(`
7450
7570
  SELECT DISTINCT symbols.*
@@ -7466,6 +7586,94 @@ var init_store = __esm({
7466
7586
  "SELECT * FROM code_state_snapshots WHERE projectId = ? ORDER BY sourceEpoch DESC LIMIT ?"
7467
7587
  ).all(projectId, safeLimit).map(rowToSnapshot);
7468
7588
  }
7589
+ listSnapshotFiles(snapshotId) {
7590
+ return this.db.prepare(`
7591
+ SELECT snapshotId, projectId, fileId, path, contentHash
7592
+ FROM code_state_snapshot_files
7593
+ WHERE snapshotId = ?
7594
+ ORDER BY path
7595
+ `).all(snapshotId).map(rowToSnapshotFile);
7596
+ }
7597
+ diffSnapshots(projectId, fromSnapshotId, toSnapshotId) {
7598
+ const snapshots = this.db.prepare(`
7599
+ SELECT * FROM code_state_snapshots WHERE id IN (?, ?)
7600
+ `).all(fromSnapshotId, toSnapshotId).map(rowToSnapshot);
7601
+ if (snapshots.length !== 2) {
7602
+ return { projectId, fromSnapshotId, toSnapshotId, available: false, reason: "missing-snapshot", changes: [] };
7603
+ }
7604
+ if (snapshots.some((snapshot) => snapshot.projectId !== projectId)) {
7605
+ return { projectId, fromSnapshotId, toSnapshotId, available: false, reason: "project-mismatch", changes: [] };
7606
+ }
7607
+ if (snapshots.some(snapshotIsIncomplete)) {
7608
+ return { projectId, fromSnapshotId, toSnapshotId, available: false, reason: "incomplete-snapshot", changes: [] };
7609
+ }
7610
+ const before = this.listSnapshotFiles(fromSnapshotId);
7611
+ const after = this.listSnapshotFiles(toSnapshotId);
7612
+ if (before.length === 0 || after.length === 0) {
7613
+ return {
7614
+ projectId,
7615
+ fromSnapshotId,
7616
+ toSnapshotId,
7617
+ available: false,
7618
+ reason: "legacy-snapshot-without-manifest",
7619
+ changes: []
7620
+ };
7621
+ }
7622
+ const beforeByPath = new Map(before.map((file) => [file.path, file]));
7623
+ const afterByPath = new Map(after.map((file) => [file.path, file]));
7624
+ const paths = [.../* @__PURE__ */ new Set([...beforeByPath.keys(), ...afterByPath.keys()])].sort();
7625
+ const changes = [];
7626
+ for (const path18 of paths) {
7627
+ const oldFile = beforeByPath.get(path18);
7628
+ const newFile = afterByPath.get(path18);
7629
+ if (!oldFile && newFile) {
7630
+ changes.push({ path: path18, kind: "added", afterHash: newFile.contentHash });
7631
+ } else if (oldFile && !newFile) {
7632
+ changes.push({ path: path18, kind: "removed", beforeHash: oldFile.contentHash });
7633
+ } else if (oldFile && newFile && oldFile.contentHash !== newFile.contentHash) {
7634
+ changes.push({ path: path18, kind: "modified", beforeHash: oldFile.contentHash, afterHash: newFile.contentHash });
7635
+ }
7636
+ }
7637
+ return { projectId, fromSnapshotId, toSnapshotId, available: true, changes };
7638
+ }
7639
+ impactSlice(projectId, changedPaths, maxRelations = 24) {
7640
+ const safeLimit = Number.isFinite(maxRelations) ? Math.max(1, Math.min(100, Math.floor(maxRelations))) : 24;
7641
+ const normalizedPaths = [...new Set(changedPaths.map(normalizeCodePath).filter(Boolean))].sort();
7642
+ const files = this.listFiles(projectId);
7643
+ const fileById = new Map(files.map((file) => [file.id, file.path]));
7644
+ const fileIdBySymbolId = new Map(this.listSymbols(projectId).map((symbol) => [symbol.id, symbol.fileId]));
7645
+ const changedIds = new Set(files.filter((file) => normalizedPaths.includes(file.path)).map((file) => file.id));
7646
+ const direct = /* @__PURE__ */ new Set();
7647
+ let relationCount = 0;
7648
+ let truncated = false;
7649
+ for (const edge of this.listEdges(projectId)) {
7650
+ const endpoints = [
7651
+ edge.fromFileId,
7652
+ edge.toFileId,
7653
+ edge.fromSymbolId ? fileIdBySymbolId.get(edge.fromSymbolId) : void 0,
7654
+ edge.toSymbolId ? fileIdBySymbolId.get(edge.toSymbolId) : void 0
7655
+ ].filter((id) => Boolean(id));
7656
+ if (!endpoints.some((id) => changedIds.has(id))) continue;
7657
+ const relatedPaths = endpoints.filter((id) => !changedIds.has(id)).map((id) => fileById.get(id)).filter((path18) => Boolean(path18));
7658
+ if (relatedPaths.length === 0) continue;
7659
+ if (relationCount >= safeLimit) {
7660
+ truncated = true;
7661
+ break;
7662
+ }
7663
+ relationCount += 1;
7664
+ for (const path18 of relatedPaths) direct.add(path18);
7665
+ }
7666
+ const snapshot = this.latestSnapshot(projectId);
7667
+ return {
7668
+ projectId,
7669
+ ...snapshot ? { snapshotId: snapshot.id } : {},
7670
+ changedPaths: normalizedPaths,
7671
+ directlyConnectedPaths: [...direct].sort(),
7672
+ relationCount,
7673
+ truncated,
7674
+ provider: snapshot?.provider ?? "lite"
7675
+ };
7676
+ }
7469
7677
  /**
7470
7678
  * Record a completed scan and mark all current structural facts with its
7471
7679
  * epoch. The snapshot is written only after refresh reconciliation succeeds,
@@ -7507,6 +7715,16 @@ var init_store = __esm({
7507
7715
  this.db.prepare(
7508
7716
  "UPDATE observation_code_refs SET snapshotId = ? WHERE projectId = ? AND status = 'current'"
7509
7717
  ).run(id, input.projectId);
7718
+ const snapshotFileRows = this.db.prepare(`
7719
+ SELECT id, projectId, path, contentHash FROM code_files WHERE projectId = ? ORDER BY path
7720
+ `).all(input.projectId);
7721
+ const insertSnapshotFile = this.db.prepare(`
7722
+ INSERT INTO code_state_snapshot_files (snapshotId, projectId, fileId, path, contentHash)
7723
+ VALUES (?, ?, ?, ?, ?)
7724
+ `);
7725
+ for (const file of snapshotFileRows) {
7726
+ insertSnapshotFile.run(id, file.projectId, file.id, file.path, file.contentHash);
7727
+ }
7510
7728
  snapshot = {
7511
7729
  ...input,
7512
7730
  id,
@@ -7547,18 +7765,18 @@ __export(binder_exports, {
7547
7765
  function observationText(obs) {
7548
7766
  return [obs.title, obs.narrative, ...obs.facts ?? []].join("\n");
7549
7767
  }
7550
- function mentionsSymbol(text, symbol) {
7768
+ function mentionsSymbol(text2, symbol) {
7551
7769
  const name = symbol.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7552
- return new RegExp(`(^|[^\\w$])${name}([^\\w$]|$)`).test(text);
7770
+ return new RegExp(`(^|[^\\w$])${name}([^\\w$]|$)`).test(text2);
7553
7771
  }
7554
- function identifierCandidates(text) {
7555
- return [...new Set(text.match(/[A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?/g) ?? [])];
7772
+ function identifierCandidates(text2) {
7773
+ return [...new Set(text2.match(/[A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?/g) ?? [])];
7556
7774
  }
7557
- function codeIdentifierCandidates(text) {
7775
+ function codeIdentifierCandidates(text2) {
7558
7776
  const explicit = /* @__PURE__ */ new Set();
7559
- for (const match2 of text.matchAll(/`([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)`/g)) explicit.add(match2[1]);
7560
- for (const match2 of text.matchAll(/\b([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)\s*\(/g)) explicit.add(match2[1]);
7561
- 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));
7777
+ for (const match2 of text2.matchAll(/`([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)`/g)) explicit.add(match2[1]);
7778
+ for (const match2 of text2.matchAll(/\b([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)\s*\(/g)) explicit.add(match2[1]);
7779
+ 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));
7562
7780
  }
7563
7781
  function fileRef(projectId, obs, file) {
7564
7782
  return {
@@ -7588,7 +7806,7 @@ function symbolRef(projectId, obs, file, symbol) {
7588
7806
  }
7589
7807
  function resolveObservationCodeRefs(obs, lookup2) {
7590
7808
  const refs = /* @__PURE__ */ new Map();
7591
- const text = observationText(obs);
7809
+ const text2 = observationText(obs);
7592
7810
  const candidateFiles = /* @__PURE__ */ new Map();
7593
7811
  for (const rawPath of obs.filesModified ?? []) {
7594
7812
  const file = lookup2.findFile(normalizeCodePath(rawPath));
@@ -7598,7 +7816,7 @@ function resolveObservationCodeRefs(obs, lookup2) {
7598
7816
  refs.set(ref.id, ref);
7599
7817
  }
7600
7818
  const hintedFileIds = new Set(candidateFiles.keys());
7601
- const symbolNames = hintedFileIds.size > 0 ? identifierCandidates(text) : codeIdentifierCandidates(text);
7819
+ const symbolNames = hintedFileIds.size > 0 ? identifierCandidates(text2) : codeIdentifierCandidates(text2);
7602
7820
  const symbols = lookup2.findSymbols(symbolNames, [...hintedFileIds]);
7603
7821
  const symbolsByName = /* @__PURE__ */ new Map();
7604
7822
  for (const symbol of symbols) {
@@ -7609,7 +7827,7 @@ function resolveObservationCodeRefs(obs, lookup2) {
7609
7827
  for (const group of symbolsByName.values()) {
7610
7828
  const candidates = group.length === 1 ? group : [];
7611
7829
  for (const symbol of candidates) {
7612
- if (!mentionsSymbol(text, symbol)) continue;
7830
+ if (!mentionsSymbol(text2, symbol)) continue;
7613
7831
  const file = candidateFiles.get(symbol.fileId) ?? lookup2.findFile(symbol.path);
7614
7832
  if (!file) continue;
7615
7833
  candidateFiles.set(file.id, file);
@@ -9111,9 +9329,9 @@ __export(consolidation_exports, {
9111
9329
  executeConsolidation: () => executeConsolidation,
9112
9330
  findConsolidationCandidates: () => findConsolidationCandidates
9113
9331
  });
9114
- function tokenize(text) {
9332
+ function tokenize(text2) {
9115
9333
  return new Set(
9116
- text.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff\s-]/g, " ").split(/\s+/).filter((t2) => t2.length > 1)
9334
+ text2.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff\s-]/g, " ").split(/\s+/).filter((t2) => t2.length > 1)
9117
9335
  );
9118
9336
  }
9119
9337
  function jaccardSimilarity(a, b) {
@@ -18071,8 +18289,8 @@ var init_headers = __esm({
18071
18289
  });
18072
18290
 
18073
18291
  // packages/ai/dist/utils/sanitize-unicode.js
18074
- function sanitizeSurrogates(text) {
18075
- return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
18292
+ function sanitizeSurrogates(text2) {
18293
+ return text2.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
18076
18294
  }
18077
18295
  var init_sanitize_unicode = __esm({
18078
18296
  "packages/ai/dist/utils/sanitize-unicode.js"() {
@@ -18554,9 +18772,9 @@ var init_values = __esm({
18554
18772
  }
18555
18773
  return n;
18556
18774
  };
18557
- safeJSON = (text) => {
18775
+ safeJSON = (text2) => {
18558
18776
  try {
18559
- return JSON.parse(text);
18777
+ return JSON.parse(text2);
18560
18778
  } catch (err) {
18561
18779
  return void 0;
18562
18780
  }
@@ -19719,8 +19937,8 @@ async function defaultParseResponse(client, props) {
19719
19937
  const json2 = await response.json();
19720
19938
  return addRequestID(json2, response);
19721
19939
  }
19722
- const text = await response.text();
19723
- return text;
19940
+ const text2 = await response.text();
19941
+ return text2;
19724
19942
  })();
19725
19943
  loggerFor(client).debug(`[${requestLogID}] response parsed`, formatRequestDetails({
19726
19944
  retryOfRequestLogID,
@@ -45327,9 +45545,9 @@ var init_values2 = __esm({
45327
45545
  }
45328
45546
  return n;
45329
45547
  };
45330
- safeJSON2 = (text) => {
45548
+ safeJSON2 = (text2) => {
45331
45549
  try {
45332
- return JSON.parse(text);
45550
+ return JSON.parse(text2);
45333
45551
  } catch (err) {
45334
45552
  return void 0;
45335
45553
  }
@@ -46103,8 +46321,8 @@ async function defaultParseResponse2(client, props) {
46103
46321
  const json2 = await response.json();
46104
46322
  return addRequestID2(json2, response);
46105
46323
  }
46106
- const text = await response.text();
46107
- return text;
46324
+ const text2 = await response.text();
46325
+ return text2;
46108
46326
  })();
46109
46327
  loggerFor2(client).debug(`[${requestLogID}] response parsed`, formatRequestDetails2({
46110
46328
  retryOfRequestLogID,
@@ -52697,9 +52915,9 @@ function decodeSseLine(line, state) {
52697
52915
  }
52698
52916
  return null;
52699
52917
  }
52700
- function nextLineBreakIndex(text) {
52701
- const carriageReturnIndex = text.indexOf("\r");
52702
- const newlineIndex = text.indexOf("\n");
52918
+ function nextLineBreakIndex(text2) {
52919
+ const carriageReturnIndex = text2.indexOf("\r");
52920
+ const newlineIndex = text2.indexOf("\n");
52703
52921
  if (carriageReturnIndex === -1) {
52704
52922
  return newlineIndex;
52705
52923
  }
@@ -52708,18 +52926,18 @@ function nextLineBreakIndex(text) {
52708
52926
  }
52709
52927
  return Math.min(carriageReturnIndex, newlineIndex);
52710
52928
  }
52711
- function consumeLine(text) {
52712
- const lineBreakIndex = nextLineBreakIndex(text);
52929
+ function consumeLine(text2) {
52930
+ const lineBreakIndex = nextLineBreakIndex(text2);
52713
52931
  if (lineBreakIndex === -1) {
52714
52932
  return null;
52715
52933
  }
52716
52934
  let nextIndex = lineBreakIndex + 1;
52717
- if (text[lineBreakIndex] === "\r" && text[nextIndex] === "\n") {
52935
+ if (text2[lineBreakIndex] === "\r" && text2[nextIndex] === "\n") {
52718
52936
  nextIndex += 1;
52719
52937
  }
52720
52938
  return {
52721
- line: text.slice(0, lineBreakIndex),
52722
- rest: text.slice(nextIndex)
52939
+ line: text2.slice(0, lineBreakIndex),
52940
+ rest: text2.slice(nextIndex)
52723
52941
  };
52724
52942
  }
52725
52943
  async function* iterateSseMessages(body, signal) {
@@ -54886,8 +55104,8 @@ var require_common = __commonJS({
54886
55104
  }
54887
55105
  function redactString(obj, key) {
54888
55106
  if (typeof obj === "object" && obj !== null && typeof obj[key] === "string") {
54889
- const text = obj[key];
54890
- if (/grant_type=/i.test(text) || /assertion=/i.test(text) || /secret/i.test(text)) {
55107
+ const text2 = obj[key];
55108
+ if (/grant_type=/i.test(text2) || /assertion=/i.test(text2) || /secret/i.test(text2)) {
54891
55109
  obj[key] = REDACT;
54892
55110
  }
54893
55111
  }
@@ -61715,8 +61933,8 @@ var init_body = __esm({
61715
61933
  * @return Promise
61716
61934
  */
61717
61935
  async json() {
61718
- const text = await this.text();
61719
- return JSON.parse(text);
61936
+ const text2 = await this.text();
61937
+ return JSON.parse(text2);
61720
61938
  }
61721
61939
  /**
61722
61940
  * Decode response as text
@@ -62868,7 +63086,7 @@ var require_gaxios = __commonJS({
62868
63086
  var retry_js_1 = require_retry3();
62869
63087
  var stream_1 = __require("stream");
62870
63088
  var interceptor_js_1 = require_interceptor();
62871
- var randomUUID10 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
63089
+ var randomUUID11 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
62872
63090
  var HTTP_STATUS_NO_CONTENT = 204;
62873
63091
  var Gaxios = class {
62874
63092
  agentCache = /* @__PURE__ */ new Map();
@@ -63141,7 +63359,7 @@ var require_gaxios = __commonJS({
63141
63359
  */
63142
63360
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
63143
63361
  if (opts.multipart?.length) {
63144
- const boundary = await randomUUID10();
63362
+ const boundary = await randomUUID11();
63145
63363
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
63146
63364
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
63147
63365
  } else if (shouldDirectlyPassData) {
@@ -64892,18 +65110,18 @@ var require_parse = __commonJS({
64892
65110
  n: "\n",
64893
65111
  r: "\r",
64894
65112
  t: " "
64895
- }, text, error = function(m2) {
65113
+ }, text2, error = function(m2) {
64896
65114
  throw {
64897
65115
  name: "SyntaxError",
64898
65116
  message: m2,
64899
65117
  at,
64900
- text
65118
+ text: text2
64901
65119
  };
64902
65120
  }, next = function(c) {
64903
65121
  if (c && c !== ch) {
64904
65122
  error("Expected '" + c + "' instead of '" + ch + "'");
64905
65123
  }
64906
- ch = text.charAt(at);
65124
+ ch = text2.charAt(at);
64907
65125
  at += 1;
64908
65126
  return ch;
64909
65127
  }, number44 = function() {
@@ -64950,12 +65168,12 @@ var require_parse = __commonJS({
64950
65168
  var startAt = at;
64951
65169
  while (next()) {
64952
65170
  if (ch === '"') {
64953
- if (at - 1 > startAt) string474 += text.substring(startAt, at - 1);
65171
+ if (at - 1 > startAt) string474 += text2.substring(startAt, at - 1);
64954
65172
  next();
64955
65173
  return string474;
64956
65174
  }
64957
65175
  if (ch === "\\") {
64958
- if (at - 1 > startAt) string474 += text.substring(startAt, at - 1);
65176
+ if (at - 1 > startAt) string474 += text2.substring(startAt, at - 1);
64959
65177
  next();
64960
65178
  if (ch === "u") {
64961
65179
  uffff = 0;
@@ -65088,7 +65306,7 @@ var require_parse = __commonJS({
65088
65306
  };
65089
65307
  return function(source, reviver) {
65090
65308
  var result;
65091
- text = source + "";
65309
+ text2 = source + "";
65092
65310
  at = 0;
65093
65311
  ch = " ";
65094
65312
  result = value();
@@ -66000,8 +66218,8 @@ var require_crypto = __commonJS({
66000
66218
  const result = new TextDecoder().decode(uint8array);
66001
66219
  return result;
66002
66220
  }
66003
- encodeBase64StringUtf8(text) {
66004
- const uint8array = new TextEncoder().encode(text);
66221
+ encodeBase64StringUtf8(text2) {
66222
+ const uint8array = new TextEncoder().encode(text2);
66005
66223
  const result = base64js.fromByteArray(uint8array);
66006
66224
  return result;
66007
66225
  }
@@ -66070,8 +66288,8 @@ var require_crypto2 = __commonJS({
66070
66288
  decodeBase64StringUtf8(base64) {
66071
66289
  return Buffer.from(base64, "base64").toString("utf-8");
66072
66290
  }
66073
- encodeBase64StringUtf8(text) {
66074
- return Buffer.from(text, "utf-8").toString("base64");
66291
+ encodeBase64StringUtf8(text2) {
66292
+ return Buffer.from(text2, "utf-8").toString("base64");
66075
66293
  }
66076
66294
  /**
66077
66295
  * Computes the SHA-256 hash of the provided string.
@@ -86516,8 +86734,8 @@ async function defaultParseResponse3(client, props) {
86516
86734
  const json2 = await response.json();
86517
86735
  return json2;
86518
86736
  }
86519
- const text = await response.text();
86520
- return text;
86737
+ const text2 = await response.text();
86738
+ return text2;
86521
86739
  })();
86522
86740
  loggerFor3(client).debug(`[${requestLogID}] response parsed`, formatRequestDetails3({
86523
86741
  retryOfRequestLogID,
@@ -88094,7 +88312,7 @@ var init_node = __esm({
88094
88312
  if (this.candidates && this.candidates.length > 1) {
88095
88313
  console.warn("there are multiple candidates in the response, returning text from the first one.");
88096
88314
  }
88097
- let text = "";
88315
+ let text2 = "";
88098
88316
  let anyTextPartText = false;
88099
88317
  const nonTextParts = [];
88100
88318
  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 : []) {
@@ -88108,13 +88326,13 @@ var init_node = __esm({
88108
88326
  continue;
88109
88327
  }
88110
88328
  anyTextPartText = true;
88111
- text += part.text;
88329
+ text2 += part.text;
88112
88330
  }
88113
88331
  }
88114
88332
  if (nonTextParts.length > 0) {
88115
88333
  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.`);
88116
88334
  }
88117
- return anyTextPartText ? text : void 0;
88335
+ return anyTextPartText ? text2 : void 0;
88118
88336
  }
88119
88337
  /**
88120
88338
  * Returns the concatenation of all inline data parts from the first candidate
@@ -88367,7 +88585,7 @@ var init_node = __esm({
88367
88585
  */
88368
88586
  get text() {
88369
88587
  var _a5, _b, _c;
88370
- let text = "";
88588
+ let text2 = "";
88371
88589
  let anyTextPartFound = false;
88372
88590
  const nonTextParts = [];
88373
88591
  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 : []) {
@@ -88381,13 +88599,13 @@ var init_node = __esm({
88381
88599
  continue;
88382
88600
  }
88383
88601
  anyTextPartFound = true;
88384
- text += part.text;
88602
+ text2 += part.text;
88385
88603
  }
88386
88604
  }
88387
88605
  if (nonTextParts.length > 0) {
88388
88606
  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.`);
88389
88607
  }
88390
- return anyTextPartFound ? text : void 0;
88608
+ return anyTextPartFound ? text2 : void 0;
88391
88609
  }
88392
88610
  /**
88393
88611
  * Returns the concatenation of all inline data parts from the server content if present.
@@ -93059,9 +93277,9 @@ var init_node = __esm({
93059
93277
  }
93060
93278
  return n;
93061
93279
  };
93062
- safeJSON3 = (text) => {
93280
+ safeJSON3 = (text2) => {
93063
93281
  try {
93064
- return JSON.parse(text);
93282
+ return JSON.parse(text2);
93065
93283
  } catch (err) {
93066
93284
  return void 0;
93067
93285
  }
@@ -110924,8 +111142,8 @@ function findBoundary(buf) {
110924
111142
  return null;
110925
111143
  }
110926
111144
  function parseMessage2(chunk, parse2, state, dataRequired) {
110927
- const text = new TextDecoder().decode(chunk);
110928
- const lines = text.split(/\r\n|\r|\n/);
111145
+ const text2 = new TextDecoder().decode(chunk);
111146
+ const lines = text2.split(/\r\n|\r|\n/);
110929
111147
  const dataLines = [];
110930
111148
  const ret = {};
110931
111149
  let ignore = true;
@@ -133799,10 +134017,10 @@ function formatMistralError(error) {
133799
134017
  }
133800
134018
  return safeJsonStringify(error);
133801
134019
  }
133802
- function truncateErrorText(text, maxChars) {
133803
- if (text.length <= maxChars)
133804
- return text;
133805
- return `${text.slice(0, maxChars)}... [truncated ${text.length - maxChars} chars]`;
134020
+ function truncateErrorText(text2, maxChars) {
134021
+ if (text2.length <= maxChars)
134022
+ return text2;
134023
+ return `${text2.slice(0, maxChars)}... [truncated ${text2.length - maxChars} chars]`;
133806
134024
  }
133807
134025
  function safeJsonStringify(value) {
133808
134026
  try {
@@ -133922,7 +134140,7 @@ async function consumeChatStream(model, output, stream2, mistralStream) {
133922
134140
  continue;
133923
134141
  }
133924
134142
  if (item.type === "thinking") {
133925
- const deltaText = item.thinking.map((part) => "text" in part ? part.text : "").filter((text) => text.length > 0).join("");
134143
+ const deltaText = item.thinking.map((part) => "text" in part ? part.text : "").filter((text2) => text2.length > 0).join("");
133926
134144
  const thinkingDelta = sanitizeSurrogates(deltaText);
133927
134145
  if (!thinkingDelta)
133928
134146
  continue;
@@ -134119,8 +134337,8 @@ function toChatMessages(messages, supportsImages) {
134119
134337
  }
134120
134338
  return result;
134121
134339
  }
134122
- function buildToolResultText(text, hasImages, supportsImages, isError) {
134123
- const trimmed = text.trim();
134340
+ function buildToolResultText(text2, hasImages, supportsImages, isError) {
134341
+ const trimmed = text2.trim();
134124
134342
  const errorPrefix = isError ? "[tool error] " : "";
134125
134343
  if (trimmed.length > 0) {
134126
134344
  const imageSuffix = hasImages && !supportsImages ? "\n[tool image omitted: model does not support images]" : "";
@@ -134921,14 +135139,14 @@ async function* parseWebSocket(socket, signal, idleTimeoutMs) {
134921
135139
  };
134922
135140
  const onMessage = (event) => {
134923
135141
  void (async () => {
134924
- let text = null;
135142
+ let text2 = null;
134925
135143
  try {
134926
135144
  if (!event || typeof event !== "object" || !("data" in event))
134927
135145
  return;
134928
- text = await decodeWebSocketData(event.data);
134929
- if (!text)
135146
+ text2 = await decodeWebSocketData(event.data);
135147
+ if (!text2)
134930
135148
  return;
134931
- const parsed = JSON.parse(text);
135149
+ const parsed = JSON.parse(text2);
134932
135150
  const type = typeof parsed.type === "string" ? parsed.type : "";
134933
135151
  if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
134934
135152
  sawCompletion = true;
@@ -134939,7 +135157,7 @@ async function* parseWebSocket(socket, signal, idleTimeoutMs) {
134939
135157
  } catch (cause) {
134940
135158
  failed = new CodexProtocolError(`Invalid Codex WebSocket JSON: ${formatThrownValue(cause)}`, {
134941
135159
  cause,
134942
- payload: text
135160
+ payload: text2
134943
135161
  });
134944
135162
  done = true;
134945
135163
  wake();
@@ -143103,11 +143321,11 @@ function videoRequestConfig(input) {
143103
143321
  };
143104
143322
  }
143105
143323
  async function readMiniMaxJson(response) {
143106
- const text = await response.text();
143324
+ const text2 = await response.text();
143107
143325
  let body = {};
143108
- if (text) {
143326
+ if (text2) {
143109
143327
  try {
143110
- const parsed = JSON.parse(text);
143328
+ const parsed = JSON.parse(text2);
143111
143329
  if (isRecord(parsed)) body = parsed;
143112
143330
  } catch {
143113
143331
  throw new Error("MiniMax video API returned invalid JSON");
@@ -144023,8 +144241,8 @@ __export(lite_provider_exports, {
144023
144241
  import { createHash as createHash7 } from "crypto";
144024
144242
  import { readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
144025
144243
  import { join as join4, relative } from "path";
144026
- function hashText(text) {
144027
- return createHash7("sha256").update(text).digest("hex");
144244
+ function hashText(text2) {
144245
+ return createHash7("sha256").update(text2).digest("hex");
144028
144246
  }
144029
144247
  function extension2(path18) {
144030
144248
  const index = path18.lastIndexOf(".");
@@ -144068,19 +144286,19 @@ function walk(root, exclude, maxFiles) {
144068
144286
  visit(root);
144069
144287
  return out;
144070
144288
  }
144071
- function lineOf(text, index) {
144072
- return text.slice(0, index).split(/\r?\n/).length;
144289
+ function lineOf(text2, index) {
144290
+ return text2.slice(0, index).split(/\r?\n/).length;
144073
144291
  }
144074
- function extractSymbols(projectId, file, text, indexedAt) {
144292
+ function extractSymbols(projectId, file, text2, indexedAt) {
144075
144293
  const symbols = [];
144076
144294
  const profile = file.language ? languageProfiles[file.language] : void 0;
144077
144295
  const patterns = profile?.symbols ?? [];
144078
144296
  for (const { kind, re } of patterns) {
144079
144297
  re.lastIndex = 0;
144080
- for (const match2 of text.matchAll(re)) {
144298
+ for (const match2 of text2.matchAll(re)) {
144081
144299
  const name = match2[1];
144082
144300
  if (!name || name.includes("\n")) continue;
144083
- const startLine = lineOf(text, match2.index ?? 0);
144301
+ const startLine = lineOf(text2, match2.index ?? 0);
144084
144302
  const id = makeCodeSymbolId({ projectId, path: file.path, qualifiedName: name, kind });
144085
144303
  symbols.push({
144086
144304
  id,
@@ -144100,13 +144318,13 @@ function extractSymbols(projectId, file, text, indexedAt) {
144100
144318
  }
144101
144319
  return symbols;
144102
144320
  }
144103
- function extractImportEdges(projectId, file, text, indexedAt) {
144321
+ function extractImportEdges(projectId, file, text2, indexedAt) {
144104
144322
  const edges = [];
144105
144323
  const profile = file.language ? languageProfiles[file.language] : void 0;
144106
144324
  const imports = profile?.imports ?? [];
144107
144325
  for (const re of imports) {
144108
144326
  re.lastIndex = 0;
144109
- for (const match2 of text.matchAll(re)) {
144327
+ for (const match2 of text2.matchAll(re)) {
144110
144328
  const target = (match2[1] ?? "").trim();
144111
144329
  if (!target) continue;
144112
144330
  if (target.includes("\n")) {
@@ -144141,14 +144359,14 @@ function extractImportEdges(projectId, file, text, indexedAt) {
144141
144359
  function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
144142
144360
  const rel = normalizeCodePath(relative(projectRoot, abs));
144143
144361
  try {
144144
- const text = readFileSync6(abs, "utf-8");
144362
+ const text2 = readFileSync6(abs, "utf-8");
144145
144363
  const stat4 = fileStat ?? statSync3(abs);
144146
144364
  const file = {
144147
144365
  id: makeCodeFileId(projectId, rel),
144148
144366
  projectId,
144149
144367
  path: rel,
144150
144368
  language: languageForPath(rel),
144151
- contentHash: hashText(text),
144369
+ contentHash: hashText(text2),
144152
144370
  // Preserve filesystem precision. Rounding here made same-size edits in
144153
144371
  // a short Windows timestamp window indistinguishable to refresh.
144154
144372
  mtimeMs: Number(stat4.mtimeMs),
@@ -144158,8 +144376,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
144158
144376
  return {
144159
144377
  delta: {
144160
144378
  file,
144161
- symbols: extractSymbols(projectId, file, text, indexedAt),
144162
- edges: extractImportEdges(projectId, file, text, indexedAt)
144379
+ symbols: extractSymbols(projectId, file, text2, indexedAt),
144380
+ edges: extractImportEdges(projectId, file, text2, indexedAt)
144163
144381
  },
144164
144382
  unreadable: false
144165
144383
  };
@@ -145071,8 +145289,8 @@ function taskTokens(task) {
145071
145289
  return normalizedTokens(task);
145072
145290
  }
145073
145291
  function scoreClaim(claim, tokens) {
145074
- const text = [claim.subject, claim.predicate, claim.objectValue].join(" ").toLocaleLowerCase("en-US");
145075
- const words = normalizedTokens(text);
145292
+ const text2 = [claim.subject, claim.predicate, claim.objectValue].join(" ").toLocaleLowerCase("en-US");
145293
+ const words = normalizedTokens(text2);
145076
145294
  let matches = 0;
145077
145295
  for (const token of tokens) {
145078
145296
  if (words.has(token)) matches += 1;
@@ -145968,29 +146186,141 @@ var init_workflow_store = __esm({
145968
146186
  }
145969
146187
  });
145970
146188
 
146189
+ // src/knowledge/outcome-types.ts
146190
+ var MEMORY_OUTCOME_CANDIDATE_KINDS, MEMORY_OUTCOME_SIGNAL_KINDS;
146191
+ var init_outcome_types = __esm({
146192
+ "src/knowledge/outcome-types.ts"() {
146193
+ "use strict";
146194
+ init_esm_shims();
146195
+ MEMORY_OUTCOME_CANDIDATE_KINDS = ["claim", "durable-memory", "workflow"];
146196
+ MEMORY_OUTCOME_SIGNAL_KINDS = [
146197
+ "verification-passed",
146198
+ "verification-failed",
146199
+ "verified-reuse",
146200
+ "user-pin",
146201
+ "user-correction",
146202
+ "source-changed",
146203
+ "conflict-confirmed",
146204
+ "manual-review"
146205
+ ];
146206
+ }
146207
+ });
146208
+
146209
+ // src/knowledge/outcome-store.ts
146210
+ import { randomUUID as randomUUID10 } from "crypto";
146211
+ function optionalText4(value) {
146212
+ return typeof value === "string" && value.trim() ? value : void 0;
146213
+ }
146214
+ function text(value, field, max = 1e3) {
146215
+ const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
146216
+ if (!safe) throw new Error("Outcome signal " + field + " is required.");
146217
+ if (safe.length > max) throw new Error("Outcome signal " + field + " is too long.");
146218
+ return safe;
146219
+ }
146220
+ function oneOf(value, values, field) {
146221
+ if (typeof value !== "string" || !values.includes(value)) {
146222
+ throw new Error("Outcome signal " + field + " is invalid.");
146223
+ }
146224
+ return value;
146225
+ }
146226
+ function rowToSignal(row) {
146227
+ return {
146228
+ id: row.id,
146229
+ projectId: row.projectId,
146230
+ candidateKind: row.candidateKind,
146231
+ candidateId: row.candidateId,
146232
+ kind: row.kind,
146233
+ sourceRef: row.sourceRef,
146234
+ ...optionalText4(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
146235
+ ...optionalText4(row.detail) ? { detail: row.detail } : {},
146236
+ observedAt: row.observedAt
146237
+ };
146238
+ }
146239
+ var OutcomeStore;
146240
+ var init_outcome_store = __esm({
146241
+ "src/knowledge/outcome-store.ts"() {
146242
+ "use strict";
146243
+ init_esm_shims();
146244
+ init_secret_filter();
146245
+ init_sqlite_db();
146246
+ init_outcome_types();
146247
+ OutcomeStore = class {
146248
+ db = null;
146249
+ async init(dataDir) {
146250
+ this.db = getDatabase(dataDir);
146251
+ }
146252
+ record(input) {
146253
+ const signal = {
146254
+ id: randomUUID10(),
146255
+ projectId: text(input.projectId, "project id"),
146256
+ candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
146257
+ candidateId: text(input.candidateId, "candidate id"),
146258
+ kind: oneOf(input.kind, MEMORY_OUTCOME_SIGNAL_KINDS, "kind"),
146259
+ sourceRef: text(input.sourceRef, "source reference"),
146260
+ ...input.snapshotId ? { snapshotId: text(input.snapshotId, "snapshot id") } : {},
146261
+ ...input.detail ? { detail: text(input.detail, "detail") } : {},
146262
+ observedAt: input.observedAt ?? (/* @__PURE__ */ new Date()).toISOString()
146263
+ };
146264
+ this.db.prepare(`
146265
+ INSERT INTO memory_outcome_signals (
146266
+ id, projectId, candidateKind, candidateId, kind, sourceRef, snapshotId, detail, observedAt
146267
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
146268
+ `).run(
146269
+ signal.id,
146270
+ signal.projectId,
146271
+ signal.candidateKind,
146272
+ signal.candidateId,
146273
+ signal.kind,
146274
+ signal.sourceRef,
146275
+ signal.snapshotId ?? null,
146276
+ signal.detail ?? null,
146277
+ signal.observedAt
146278
+ );
146279
+ return signal;
146280
+ }
146281
+ latestForCandidates(projectId, candidateKind, candidateIds) {
146282
+ const ids = [...new Set(candidateIds.filter(Boolean))];
146283
+ if (ids.length === 0) return /* @__PURE__ */ new Map();
146284
+ const placeholders = ids.map(() => "?").join(", ");
146285
+ const rows = this.db.prepare(`
146286
+ SELECT * FROM memory_outcome_signals
146287
+ WHERE projectId = ? AND candidateKind = ? AND candidateId IN (${placeholders})
146288
+ ORDER BY observedAt DESC, id DESC
146289
+ `).all(projectId, candidateKind, ...ids);
146290
+ const latest = /* @__PURE__ */ new Map();
146291
+ for (const row of rows) {
146292
+ const signal = rowToSignal(row);
146293
+ if (!latest.has(signal.candidateId)) latest.set(signal.candidateId, signal);
146294
+ }
146295
+ return latest;
146296
+ }
146297
+ };
146298
+ }
146299
+ });
146300
+
145971
146301
  // src/codegraph/task-lens.ts
145972
- function containsTaskKeyword(text, keyword) {
146302
+ function containsTaskKeyword(text2, keyword) {
145973
146303
  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");
145974
146304
  let match2;
145975
- while ((match2 = matcher.exec(text)) !== null) {
146305
+ while ((match2 = matcher.exec(text2)) !== null) {
145976
146306
  const keywordIndex = match2.index + (match2[1]?.length ?? 0);
145977
- if (!isNegatedTaskKeyword(text, keywordIndex)) return true;
146307
+ if (!isNegatedTaskKeyword(text2, keywordIndex)) return true;
145978
146308
  }
145979
146309
  return false;
145980
146310
  }
145981
- function isNegatedTaskKeyword(text, keywordIndex) {
146311
+ function isNegatedTaskKeyword(text2, keywordIndex) {
145982
146312
  const boundary = Math.max(
145983
- text.lastIndexOf(".", keywordIndex - 1),
145984
- text.lastIndexOf("!", keywordIndex - 1),
145985
- text.lastIndexOf("?", keywordIndex - 1),
145986
- text.lastIndexOf(";", keywordIndex - 1),
145987
- text.lastIndexOf("\n", keywordIndex - 1),
145988
- text.lastIndexOf("\u3002", keywordIndex - 1),
145989
- text.lastIndexOf("\uFF01", keywordIndex - 1),
145990
- text.lastIndexOf("\uFF1F", keywordIndex - 1),
145991
- text.lastIndexOf("\uFF1B", keywordIndex - 1)
146313
+ text2.lastIndexOf(".", keywordIndex - 1),
146314
+ text2.lastIndexOf("!", keywordIndex - 1),
146315
+ text2.lastIndexOf("?", keywordIndex - 1),
146316
+ text2.lastIndexOf(";", keywordIndex - 1),
146317
+ text2.lastIndexOf("\n", keywordIndex - 1),
146318
+ text2.lastIndexOf("\u3002", keywordIndex - 1),
146319
+ text2.lastIndexOf("\uFF01", keywordIndex - 1),
146320
+ text2.lastIndexOf("\uFF1F", keywordIndex - 1),
146321
+ text2.lastIndexOf("\uFF1B", keywordIndex - 1)
145992
146322
  );
145993
- let prefix = text.slice(boundary + 1, keywordIndex).toLowerCase();
146323
+ let prefix = text2.slice(boundary + 1, keywordIndex).toLowerCase();
145994
146324
  const contrast = /(?:,|\uff0c)\s*(?:but|however|instead|yet|\u4f46\u662f|\u4f46|\u800c\u662f)\s*/gi;
145995
146325
  let contrastMatch;
145996
146326
  let contrastEnd = -1;
@@ -146049,7 +146379,7 @@ function requiredText(data, key) {
146049
146379
  }
146050
146380
  return value.trim();
146051
146381
  }
146052
- function optionalText4(data, key) {
146382
+ function optionalText5(data, key) {
146053
146383
  const value = data[key];
146054
146384
  if (value === void 0 || value === null || value === "") return void 0;
146055
146385
  if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
@@ -146092,11 +146422,11 @@ function phaseFromValue(value, index) {
146092
146422
  }
146093
146423
  const data = value;
146094
146424
  const title = requiredText(data, "title");
146095
- const phaseId = optionalText4(data, "id") ?? slug(title) + "-" + (index + 1);
146425
+ const phaseId = optionalText5(data, "id") ?? slug(title) + "-" + (index + 1);
146096
146426
  return {
146097
146427
  id: phaseId,
146098
146428
  title,
146099
- instructions: optionalText4(data, "instructions") ?? "",
146429
+ instructions: optionalText5(data, "instructions") ?? "",
146100
146430
  branches: optionalStringArray(data, "branches"),
146101
146431
  expectedOutputs: optionalStringArray(data, "expectedOutputs"),
146102
146432
  verificationGates: optionalStringArray(data, "verificationGates")
@@ -146205,7 +146535,7 @@ function parsedWorkflow(data, body, input) {
146205
146535
  id: requiredText(data, "id"),
146206
146536
  workspaceId: input.workspaceId,
146207
146537
  title: requiredText(data, "title"),
146208
- description: optionalText4(data, "description") ?? "",
146538
+ description: optionalText5(data, "description") ?? "",
146209
146539
  status: optionalStatus(data),
146210
146540
  version: optionalNumber(data, "version", 1),
146211
146541
  taskLenses: optionalStringArray(data, "taskLenses"),
@@ -146224,9 +146554,9 @@ function parsedWorkflow(data, body, input) {
146224
146554
  sourcePath: normalizeSourcePath(input.sourcePath),
146225
146555
  sourceHash: "",
146226
146556
  contentHash: input.contentHash,
146227
- createdAt: optionalText4(data, "createdAt") ?? now2(),
146228
- updatedAt: optionalText4(data, "updatedAt") ?? now2(),
146229
- ...optionalText4(data, "importedFrom") ? { importedFrom: optionalText4(data, "importedFrom") } : {}
146557
+ createdAt: optionalText5(data, "createdAt") ?? now2(),
146558
+ updatedAt: optionalText5(data, "updatedAt") ?? now2(),
146559
+ ...optionalText5(data, "importedFrom") ? { importedFrom: optionalText5(data, "importedFrom") } : {}
146230
146560
  };
146231
146561
  if (candidate.phases.length === 0) {
146232
146562
  throw new Error("workflow must contain at least one phase or a non-empty body");
@@ -146325,11 +146655,11 @@ async function syncCanonicalWorkflows(workspace) {
146325
146655
  return { workflows, errors };
146326
146656
  }
146327
146657
  function inferTaskLenses(value) {
146328
- const text = value.toLowerCase();
146329
- return Object.entries(LENS_TERMS).filter(([, terms]) => terms.some((term) => text.includes(term))).map(([lens]) => lens);
146658
+ const text2 = value.toLowerCase();
146659
+ return Object.entries(LENS_TERMS).filter(([, terms]) => terms.some((term) => text2.includes(term))).map(([lens]) => lens);
146330
146660
  }
146331
146661
  function taskScore(workflow, task) {
146332
- const text = task.toLowerCase();
146662
+ const text2 = task.toLowerCase();
146333
146663
  let score = 0;
146334
146664
  const reasons = [];
146335
146665
  const matchedLenses = /* @__PURE__ */ new Set();
@@ -146596,7 +146926,31 @@ async function recordWorkflowRun(input) {
146596
146926
  throw new Error("Workflow was not found for this workspace");
146597
146927
  }
146598
146928
  if (!input.run.task.trim()) throw new Error("Workflow run task is required");
146599
- return store.recordRun(input.run);
146929
+ const run = store.recordRun(input.run);
146930
+ const kind = run.outcome === "passed" && run.verificationVerdict === "passed" ? "verification-passed" : run.outcome === "failed" || run.verificationVerdict === "failed" ? "verification-failed" : void 0;
146931
+ if (!kind) return run;
146932
+ const evidence = /* @__PURE__ */ new Map();
146933
+ evidence.set(workflow.id, "workflow");
146934
+ for (const reference of run.selectedEvidence) {
146935
+ const match2 = /^(claim|durable):(.+)$/.exec(reference);
146936
+ if (!match2) continue;
146937
+ evidence.set(match2[2], match2[1] === "claim" ? "claim" : "durable-memory");
146938
+ }
146939
+ const outcomes = new OutcomeStore();
146940
+ await outcomes.init(workflowStoreDataDir(input.workspace));
146941
+ for (const [candidateId, candidateKind] of evidence) {
146942
+ outcomes.record({
146943
+ projectId: run.projectId,
146944
+ candidateKind,
146945
+ candidateId,
146946
+ kind,
146947
+ sourceRef: "workflow-run:" + run.id,
146948
+ ...run.startingSnapshotId ? { snapshotId: run.startingSnapshotId } : {},
146949
+ ...run.failureReason ? { detail: run.failureReason } : {},
146950
+ observedAt: run.completedAt ?? run.startedAt
146951
+ });
146952
+ }
146953
+ return run;
146600
146954
  }
146601
146955
  async function selectWorkspaceWorkflows(input) {
146602
146956
  const synced = await syncCanonicalWorkflows(input.workspace);
@@ -146621,6 +146975,7 @@ var init_workflows2 = __esm({
146621
146975
  init_workflow_sync();
146622
146976
  init_workspace();
146623
146977
  init_workflow_store();
146978
+ init_outcome_store();
146624
146979
  init_task_lens();
146625
146980
  KNOWN_AGENTS = [
146626
146981
  "windsurf",