@cerefox/memory 0.10.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7184,7 +7184,7 @@ var exports_meta = {};
7184
7184
  __export(exports_meta, {
7185
7185
  PKG_VERSION: () => PKG_VERSION
7186
7186
  });
7187
- var PKG_VERSION = "0.10.3";
7187
+ var PKG_VERSION = "0.11.0";
7188
7188
  var init_meta = () => {};
7189
7189
 
7190
7190
  // ../../node_modules/.bun/tslib@2.8.1/node_modules/tslib/tslib.js
@@ -7792,7 +7792,7 @@ var require_tslib = __commonJS((exports, module) => {
7792
7792
  });
7793
7793
  });
7794
7794
 
7795
- // ../../node_modules/.bun/@supabase+functions-js@2.107.0/node_modules/@supabase/functions-js/dist/main/helper.js
7795
+ // ../../node_modules/.bun/@supabase+functions-js@2.108.1/node_modules/@supabase/functions-js/dist/main/helper.js
7796
7796
  var require_helper = __commonJS((exports) => {
7797
7797
  Object.defineProperty(exports, "__esModule", { value: true });
7798
7798
  exports.resolveFetch = undefined;
@@ -7805,7 +7805,7 @@ var require_helper = __commonJS((exports) => {
7805
7805
  exports.resolveFetch = resolveFetch;
7806
7806
  });
7807
7807
 
7808
- // ../../node_modules/.bun/@supabase+functions-js@2.107.0/node_modules/@supabase/functions-js/dist/main/types.js
7808
+ // ../../node_modules/.bun/@supabase+functions-js@2.108.1/node_modules/@supabase/functions-js/dist/main/types.js
7809
7809
  var require_types = __commonJS((exports) => {
7810
7810
  Object.defineProperty(exports, "__esModule", { value: true });
7811
7811
  exports.FunctionRegion = exports.FunctionsHttpError = exports.FunctionsRelayError = exports.FunctionsFetchError = exports.FunctionsError = undefined;
@@ -7866,7 +7866,7 @@ var require_types = __commonJS((exports) => {
7866
7866
  })(FunctionRegion || (exports.FunctionRegion = FunctionRegion = {}));
7867
7867
  });
7868
7868
 
7869
- // ../../node_modules/.bun/@supabase+functions-js@2.107.0/node_modules/@supabase/functions-js/dist/main/FunctionsClient.js
7869
+ // ../../node_modules/.bun/@supabase+functions-js@2.108.1/node_modules/@supabase/functions-js/dist/main/FunctionsClient.js
7870
7870
  var require_FunctionsClient = __commonJS((exports) => {
7871
7871
  Object.defineProperty(exports, "__esModule", { value: true });
7872
7872
  exports.FunctionsClient = undefined;
@@ -7979,7 +7979,7 @@ var require_FunctionsClient = __commonJS((exports) => {
7979
7979
  exports.FunctionsClient = FunctionsClient;
7980
7980
  });
7981
7981
 
7982
- // ../../node_modules/.bun/@supabase+functions-js@2.107.0/node_modules/@supabase/functions-js/dist/main/index.js
7982
+ // ../../node_modules/.bun/@supabase+functions-js@2.108.1/node_modules/@supabase/functions-js/dist/main/index.js
7983
7983
  var require_main = __commonJS((exports) => {
7984
7984
  Object.defineProperty(exports, "__esModule", { value: true });
7985
7985
  exports.FunctionRegion = exports.FunctionsRelayError = exports.FunctionsHttpError = exports.FunctionsFetchError = exports.FunctionsError = exports.FunctionsClient = undefined;
@@ -8005,7 +8005,7 @@ var require_main = __commonJS((exports) => {
8005
8005
  } });
8006
8006
  });
8007
8007
 
8008
- // ../../node_modules/.bun/@supabase+postgrest-js@2.107.0/node_modules/@supabase/postgrest-js/dist/index.mjs
8008
+ // ../../node_modules/.bun/@supabase+postgrest-js@2.108.1/node_modules/@supabase/postgrest-js/dist/index.mjs
8009
8009
  function sleep(ms, signal) {
8010
8010
  return new Promise((resolve) => {
8011
8011
  if (signal === null || signal === undefined ? undefined : signal.aborted) {
@@ -8144,14 +8144,17 @@ var DEFAULT_MAX_RETRIES = 3, getRetryDelay = (attemptIndex) => Math.min(1000 * 2
8144
8144
  const executeWithRetry = async () => {
8145
8145
  let attemptCount = 0;
8146
8146
  while (true) {
8147
- const requestHeaders = new Headers(_this.headers);
8147
+ const headers = {};
8148
+ _this.headers.forEach((value, key) => {
8149
+ headers[key] = value;
8150
+ });
8148
8151
  if (attemptCount > 0)
8149
- requestHeaders.set("X-Retry-Count", String(attemptCount));
8152
+ headers["X-Retry-Count"] = String(attemptCount);
8150
8153
  let res$1;
8151
8154
  try {
8152
8155
  res$1 = await _fetch(_this.url.toString(), {
8153
8156
  method: _this.method,
8154
- headers: requestHeaders,
8157
+ headers,
8155
8158
  body: JSON.stringify(_this.body, (_, value) => typeof value === "bigint" ? value.toString() : value),
8156
8159
  signal: _this.signal
8157
8160
  });
@@ -8562,6 +8565,9 @@ var init_dist = __esm(() => {
8562
8565
  }
8563
8566
  };
8564
8567
  PostgrestTransformBuilder = class extends PostgrestBuilder {
8568
+ throwOnError() {
8569
+ return super.throwOnError();
8570
+ }
8565
8571
  select(columns) {
8566
8572
  let quoted = false;
8567
8573
  const cleanedColumns = (columns !== null && columns !== undefined ? columns : "*").split("").map((c2) => {
@@ -8581,9 +8587,9 @@ var init_dist = __esm(() => {
8581
8587
  this.url.searchParams.set(key, `${existingOrder ? `${existingOrder},` : ""}${column}.${ascending ? "asc" : "desc"}${nullsFirst === undefined ? "" : nullsFirst ? ".nullsfirst" : ".nullslast"}`);
8582
8588
  return this;
8583
8589
  }
8584
- limit(count, { foreignTable, referencedTable = foreignTable } = {}) {
8590
+ limit(rows, { foreignTable, referencedTable = foreignTable } = {}) {
8585
8591
  const key = typeof referencedTable === "undefined" ? "limit" : `${referencedTable}.limit`;
8586
- this.url.searchParams.set(key, `${count}`);
8592
+ this.url.searchParams.set(key, `${rows}`);
8587
8593
  return this;
8588
8594
  }
8589
8595
  range(from, to, { foreignTable, referencedTable = foreignTable } = {}) {
@@ -8636,14 +8642,17 @@ var init_dist = __esm(() => {
8636
8642
  returns() {
8637
8643
  return this;
8638
8644
  }
8639
- maxAffected(value) {
8645
+ maxAffected(rows) {
8640
8646
  this.headers.append("Prefer", "handling=strict");
8641
- this.headers.append("Prefer", `max-affected=${value}`);
8647
+ this.headers.append("Prefer", `max-affected=${rows}`);
8642
8648
  return this;
8643
8649
  }
8644
8650
  };
8645
8651
  PostgrestReservedCharsRegexp = /* @__PURE__ */ new RegExp("[,()]");
8646
8652
  PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
8653
+ throwOnError() {
8654
+ return super.throwOnError();
8655
+ }
8647
8656
  eq(column, value) {
8648
8657
  this.url.searchParams.append(column, `eq.${value}`);
8649
8658
  return this;
@@ -8807,7 +8816,7 @@ var init_dist = __esm(() => {
8807
8816
  };
8808
8817
  });
8809
8818
 
8810
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/lib/websocket-factory.js
8819
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/lib/websocket-factory.js
8811
8820
  var require_websocket_factory = __commonJS((exports) => {
8812
8821
  Object.defineProperty(exports, "__esModule", { value: true });
8813
8822
  exports.WebSocketFactory = undefined;
@@ -8898,14 +8907,14 @@ Suggested solution: ${env.workaround}`;
8898
8907
  exports.default = WebSocketFactory;
8899
8908
  });
8900
8909
 
8901
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/lib/version.js
8910
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/lib/version.js
8902
8911
  var require_version = __commonJS((exports) => {
8903
8912
  Object.defineProperty(exports, "__esModule", { value: true });
8904
8913
  exports.version = undefined;
8905
- exports.version = "2.107.0";
8914
+ exports.version = "2.108.1";
8906
8915
  });
8907
8916
 
8908
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/lib/constants.js
8917
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/lib/constants.js
8909
8918
  var require_constants = __commonJS((exports) => {
8910
8919
  Object.defineProperty(exports, "__esModule", { value: true });
8911
8920
  exports.CONNECTION_STATE = exports.TRANSPORTS = exports.CHANNEL_EVENTS = exports.CHANNEL_STATES = exports.SOCKET_STATES = exports.MAX_PUSH_BUFFER_SIZE = exports.WS_CLOSE_NORMAL = exports.DEFAULT_TIMEOUT = exports.VERSION = exports.DEFAULT_VSN = exports.VSN_2_0_0 = exports.VSN_1_0_0 = exports.DEFAULT_VERSION = undefined;
@@ -8950,7 +8959,7 @@ var require_constants = __commonJS((exports) => {
8950
8959
  };
8951
8960
  });
8952
8961
 
8953
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/lib/serializer.js
8962
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/lib/serializer.js
8954
8963
  var require_serializer = __commonJS((exports) => {
8955
8964
  Object.defineProperty(exports, "__esModule", { value: true });
8956
8965
 
@@ -9095,7 +9104,7 @@ var require_serializer = __commonJS((exports) => {
9095
9104
  exports.default = Serializer;
9096
9105
  });
9097
9106
 
9098
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/lib/transformers.js
9107
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/lib/transformers.js
9099
9108
  var require_transformers = __commonJS((exports) => {
9100
9109
  Object.defineProperty(exports, "__esModule", { value: true });
9101
9110
  exports.httpEndpointURL = exports.toTimestampString = exports.toArray = exports.toJson = exports.toNumber = exports.toBoolean = exports.convertCell = exports.convertColumn = exports.convertChangeData = exports.PostgresTypes = undefined;
@@ -10642,7 +10651,7 @@ var require_phoenix_cjs = __commonJS((exports, module) => {
10642
10651
  };
10643
10652
  });
10644
10653
 
10645
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/phoenix/presenceAdapter.js
10654
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/phoenix/presenceAdapter.js
10646
10655
  var require_presenceAdapter = __commonJS((exports) => {
10647
10656
  Object.defineProperty(exports, "__esModule", { value: true });
10648
10657
  var phoenix_1 = require_phoenix_cjs();
@@ -10715,7 +10724,7 @@ var require_presenceAdapter = __commonJS((exports) => {
10715
10724
  }
10716
10725
  });
10717
10726
 
10718
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/RealtimePresence.js
10727
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/RealtimePresence.js
10719
10728
  var require_RealtimePresence = __commonJS((exports) => {
10720
10729
  Object.defineProperty(exports, "__esModule", { value: true });
10721
10730
  exports.REALTIME_PRESENCE_LISTEN_EVENTS = undefined;
@@ -10740,7 +10749,7 @@ var require_RealtimePresence = __commonJS((exports) => {
10740
10749
  exports.default = RealtimePresence;
10741
10750
  });
10742
10751
 
10743
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/lib/normalizeChannelError.js
10752
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/lib/normalizeChannelError.js
10744
10753
  var require_normalizeChannelError = __commonJS((exports) => {
10745
10754
  Object.defineProperty(exports, "__esModule", { value: true });
10746
10755
  exports.normalizeChannelError = normalizeChannelError;
@@ -10763,7 +10772,7 @@ var require_normalizeChannelError = __commonJS((exports) => {
10763
10772
  }
10764
10773
  });
10765
10774
 
10766
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/phoenix/channelAdapter.js
10775
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/phoenix/channelAdapter.js
10767
10776
  var require_channelAdapter = __commonJS((exports) => {
10768
10777
  Object.defineProperty(exports, "__esModule", { value: true });
10769
10778
  var constants_1 = require_constants();
@@ -10865,7 +10874,7 @@ var require_channelAdapter = __commonJS((exports) => {
10865
10874
  }
10866
10875
  });
10867
10876
 
10868
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/RealtimeChannel.js
10877
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/RealtimeChannel.js
10869
10878
  var require_RealtimeChannel = __commonJS((exports) => {
10870
10879
  Object.defineProperty(exports, "__esModule", { value: true });
10871
10880
  exports.REALTIME_CHANNEL_STATES = exports.REALTIME_SUBSCRIBE_STATES = exports.REALTIME_LISTEN_TYPES = exports.REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = undefined;
@@ -11240,7 +11249,7 @@ var require_RealtimeChannel = __commonJS((exports) => {
11240
11249
  exports.default = RealtimeChannel;
11241
11250
  });
11242
11251
 
11243
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/phoenix/socketAdapter.js
11252
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/phoenix/socketAdapter.js
11244
11253
  var require_socketAdapter = __commonJS((exports) => {
11245
11254
  Object.defineProperty(exports, "__esModule", { value: true });
11246
11255
  var phoenix_1 = require_phoenix_cjs();
@@ -11353,7 +11362,7 @@ var require_socketAdapter = __commonJS((exports) => {
11353
11362
  exports.default = SocketAdapter;
11354
11363
  });
11355
11364
 
11356
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/RealtimeClient.js
11365
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/RealtimeClient.js
11357
11366
  var require_RealtimeClient = __commonJS((exports) => {
11358
11367
  Object.defineProperty(exports, "__esModule", { value: true });
11359
11368
  var tslib_1 = require_tslib();
@@ -11820,7 +11829,7 @@ var require_RealtimeClient = __commonJS((exports) => {
11820
11829
  exports.default = RealtimeClient;
11821
11830
  });
11822
11831
 
11823
- // ../../node_modules/.bun/@supabase+realtime-js@2.107.0/node_modules/@supabase/realtime-js/dist/main/index.js
11832
+ // ../../node_modules/.bun/@supabase+realtime-js@2.108.1/node_modules/@supabase/realtime-js/dist/main/index.js
11824
11833
  var require_main2 = __commonJS((exports) => {
11825
11834
  Object.defineProperty(exports, "__esModule", { value: true });
11826
11835
  exports.WebSocketFactory = exports.REALTIME_CHANNEL_STATES = exports.REALTIME_SUBSCRIBE_STATES = exports.REALTIME_PRESENCE_LISTEN_EVENTS = exports.REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = exports.REALTIME_LISTEN_TYPES = exports.RealtimeClient = exports.RealtimeChannel = exports.RealtimePresence = undefined;
@@ -12150,7 +12159,7 @@ var init_dist2 = __esm(() => {
12150
12159
  };
12151
12160
  });
12152
12161
 
12153
- // ../../node_modules/.bun/@supabase+storage-js@2.107.0/node_modules/@supabase/storage-js/dist/index.mjs
12162
+ // ../../node_modules/.bun/@supabase+storage-js@2.108.1/node_modules/@supabase/storage-js/dist/index.mjs
12154
12163
  function _typeof2(o) {
12155
12164
  "@babel/helpers - typeof";
12156
12165
  return _typeof2 = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(o$1) {
@@ -12458,7 +12467,7 @@ var StorageError, StorageApiError, StorageUnknownError, resolveFetch = (customFe
12458
12467
  throw error;
12459
12468
  }
12460
12469
  }
12461
- }, DEFAULT_SEARCH_OPTIONS, DEFAULT_FILE_OPTIONS, StorageFileApi, version = "2.107.0", DEFAULT_HEADERS, StorageBucketApi, StorageAnalyticsClient, VectorIndexApi, VectorDataApi, VectorBucketApi, StorageVectorsClient, VectorBucketScope, VectorIndexScope, StorageClient;
12470
+ }, DEFAULT_SEARCH_OPTIONS, DEFAULT_FILE_OPTIONS, StorageFileApi, version = "2.108.1", DEFAULT_HEADERS, StorageBucketApi, StorageAnalyticsClient, VectorIndexApi, VectorDataApi, VectorBucketApi, StorageVectorsClient, VectorBucketScope, VectorIndexScope, StorageClient;
12462
12471
  var init_dist3 = __esm(() => {
12463
12472
  init_dist2();
12464
12473
  StorageError = class extends Error {
@@ -13166,14 +13175,14 @@ var init_dist3 = __esm(() => {
13166
13175
  };
13167
13176
  });
13168
13177
 
13169
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/version.js
13178
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/version.js
13170
13179
  var require_version2 = __commonJS((exports) => {
13171
13180
  Object.defineProperty(exports, "__esModule", { value: true });
13172
13181
  exports.version = undefined;
13173
- exports.version = "2.107.0";
13182
+ exports.version = "2.108.1";
13174
13183
  });
13175
13184
 
13176
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/constants.js
13185
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/constants.js
13177
13186
  var require_constants2 = __commonJS((exports) => {
13178
13187
  Object.defineProperty(exports, "__esModule", { value: true });
13179
13188
  exports.JWKS_TTL = exports.BASE64URL_REGEX = exports.API_VERSIONS = exports.API_VERSION_HEADER_NAME = exports.NETWORK_FAILURE = exports.DEFAULT_HEADERS = exports.AUDIENCE = exports.STORAGE_KEY = exports.GOTRUE_URL = exports.EXPIRY_MARGIN_MS = exports.AUTO_REFRESH_TICK_THRESHOLD = exports.AUTO_REFRESH_TICK_DURATION_MS = undefined;
@@ -13200,7 +13209,7 @@ var require_constants2 = __commonJS((exports) => {
13200
13209
  exports.JWKS_TTL = 10 * 60 * 1000;
13201
13210
  });
13202
13211
 
13203
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/errors.js
13212
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/errors.js
13204
13213
  var require_errors = __commonJS((exports) => {
13205
13214
  Object.defineProperty(exports, "__esModule", { value: true });
13206
13215
  exports.AuthInvalidJwtError = exports.AuthWeakPasswordError = exports.AuthRefreshDiscardedError = exports.AuthRetryableFetchError = exports.AuthPKCECodeVerifierMissingError = exports.AuthPKCEGrantCodeExchangeError = exports.AuthImplicitGrantRedirectError = exports.AuthInvalidCredentialsError = exports.AuthInvalidTokenResponseError = exports.AuthSessionMissingError = exports.CustomAuthError = exports.AuthUnknownError = exports.AuthApiError = exports.AuthError = undefined;
@@ -13369,7 +13378,7 @@ var require_errors = __commonJS((exports) => {
13369
13378
  exports.AuthInvalidJwtError = AuthInvalidJwtError;
13370
13379
  });
13371
13380
 
13372
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/base64url.js
13381
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/base64url.js
13373
13382
  var require_base64url = __commonJS((exports) => {
13374
13383
  Object.defineProperty(exports, "__esModule", { value: true });
13375
13384
  exports.byteToBase64URL = byteToBase64URL;
@@ -13557,7 +13566,7 @@ var require_base64url = __commonJS((exports) => {
13557
13566
  }
13558
13567
  });
13559
13568
 
13560
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/helpers.js
13569
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/helpers.js
13561
13570
  var require_helpers = __commonJS((exports) => {
13562
13571
  Object.defineProperty(exports, "__esModule", { value: true });
13563
13572
  exports.Deferred = exports.removeItemAsync = exports.getItemAsync = exports.setItemAsync = exports.looksLikeFetchResponse = exports.resolveFetch = exports.supportsLocalStorage = exports.isBrowser = undefined;
@@ -13872,7 +13881,7 @@ var require_helpers = __commonJS((exports) => {
13872
13881
  }
13873
13882
  });
13874
13883
 
13875
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/fetch.js
13884
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/fetch.js
13876
13885
  var require_fetch = __commonJS((exports) => {
13877
13886
  Object.defineProperty(exports, "__esModule", { value: true });
13878
13887
  exports.handleError = handleError2;
@@ -14037,14 +14046,14 @@ var require_fetch = __commonJS((exports) => {
14037
14046
  }
14038
14047
  });
14039
14048
 
14040
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/types.js
14049
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/types.js
14041
14050
  var require_types2 = __commonJS((exports) => {
14042
14051
  Object.defineProperty(exports, "__esModule", { value: true });
14043
14052
  exports.SIGN_OUT_SCOPES = undefined;
14044
14053
  exports.SIGN_OUT_SCOPES = ["global", "local", "others"];
14045
14054
  });
14046
14055
 
14047
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/GoTrueAdminApi.js
14056
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/GoTrueAdminApi.js
14048
14057
  var require_GoTrueAdminApi = __commonJS((exports) => {
14049
14058
  Object.defineProperty(exports, "__esModule", { value: true });
14050
14059
  var tslib_1 = require_tslib();
@@ -14491,7 +14500,7 @@ var require_GoTrueAdminApi = __commonJS((exports) => {
14491
14500
  exports.default = GoTrueAdminApi;
14492
14501
  });
14493
14502
 
14494
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/local-storage.js
14503
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/local-storage.js
14495
14504
  var require_local_storage = __commonJS((exports) => {
14496
14505
  Object.defineProperty(exports, "__esModule", { value: true });
14497
14506
  exports.memoryLocalStorageAdapter = memoryLocalStorageAdapter;
@@ -14510,7 +14519,7 @@ var require_local_storage = __commonJS((exports) => {
14510
14519
  }
14511
14520
  });
14512
14521
 
14513
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/locks.js
14522
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/locks.js
14514
14523
  var require_locks = __commonJS((exports) => {
14515
14524
  Object.defineProperty(exports, "__esModule", { value: true });
14516
14525
  exports.ProcessLockAcquireTimeoutError = exports.NavigatorLockAcquireTimeoutError = exports.LockAcquireTimeoutError = exports.internals = undefined;
@@ -14684,7 +14693,7 @@ var require_locks = __commonJS((exports) => {
14684
14693
  }
14685
14694
  });
14686
14695
 
14687
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/polyfills.js
14696
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/polyfills.js
14688
14697
  var require_polyfills = __commonJS((exports) => {
14689
14698
  Object.defineProperty(exports, "__esModule", { value: true });
14690
14699
  exports.polyfillGlobalThis = polyfillGlobalThis;
@@ -14708,7 +14717,7 @@ var require_polyfills = __commonJS((exports) => {
14708
14717
  }
14709
14718
  });
14710
14719
 
14711
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/web3/ethereum.js
14720
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/web3/ethereum.js
14712
14721
  var require_ethereum = __commonJS((exports) => {
14713
14722
  Object.defineProperty(exports, "__esModule", { value: true });
14714
14723
  exports.getAddress = getAddress;
@@ -14785,7 +14794,7 @@ ${suffix}`;
14785
14794
  }
14786
14795
  });
14787
14796
 
14788
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/webauthn.errors.js
14797
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/webauthn.errors.js
14789
14798
  var require_webauthn_errors = __commonJS((exports) => {
14790
14799
  Object.defineProperty(exports, "__esModule", { value: true });
14791
14800
  exports.WebAuthnUnknownError = exports.WebAuthnError = undefined;
@@ -14972,7 +14981,7 @@ var require_webauthn_errors = __commonJS((exports) => {
14972
14981
  }
14973
14982
  });
14974
14983
 
14975
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/lib/webauthn.js
14984
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/lib/webauthn.js
14976
14985
  var require_webauthn = __commonJS((exports) => {
14977
14986
  Object.defineProperty(exports, "__esModule", { value: true });
14978
14987
  exports.WebAuthnApi = exports.DEFAULT_REQUEST_OPTIONS = exports.DEFAULT_CREATION_OPTIONS = exports.webAuthnAbortService = exports.WebAuthnAbortService = exports.identifyAuthenticationError = exports.identifyRegistrationError = exports.isWebAuthnError = exports.WebAuthnError = undefined;
@@ -15426,7 +15435,7 @@ var require_webauthn = __commonJS((exports) => {
15426
15435
  exports.WebAuthnApi = WebAuthnApi;
15427
15436
  });
15428
15437
 
15429
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/GoTrueClient.js
15438
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/GoTrueClient.js
15430
15439
  var require_GoTrueClient = __commonJS((exports) => {
15431
15440
  Object.defineProperty(exports, "__esModule", { value: true });
15432
15441
  var tslib_1 = require_tslib();
@@ -16261,15 +16270,25 @@ var require_GoTrueClient = __commonJS((exports) => {
16261
16270
  const endpoint = `${this.url}/resend`;
16262
16271
  if ("email" in credentials) {
16263
16272
  const { email, type, options } = credentials;
16273
+ let codeChallenge = null;
16274
+ let codeChallengeMethod = null;
16275
+ if (this.flowType === "pkce") {
16276
+ [codeChallenge, codeChallengeMethod] = await (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey);
16277
+ }
16264
16278
  const { error } = await (0, fetch_1._request)(this.fetch, "POST", endpoint, {
16265
16279
  headers: this.headers,
16266
16280
  body: {
16267
16281
  email,
16268
16282
  type,
16269
- gotrue_meta_security: { captcha_token: options === null || options === undefined ? undefined : options.captchaToken }
16283
+ gotrue_meta_security: { captcha_token: options === null || options === undefined ? undefined : options.captchaToken },
16284
+ code_challenge: codeChallenge,
16285
+ code_challenge_method: codeChallengeMethod
16270
16286
  },
16271
16287
  redirectTo: options === null || options === undefined ? undefined : options.emailRedirectTo
16272
16288
  });
16289
+ if (error) {
16290
+ await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
16291
+ }
16273
16292
  return this._returnResult({ data: { user: null, session: null }, error });
16274
16293
  } else if ("phone" in credentials) {
16275
16294
  const { phone, type, options } = credentials;
@@ -16288,6 +16307,7 @@ var require_GoTrueClient = __commonJS((exports) => {
16288
16307
  }
16289
16308
  throw new errors_1.AuthInvalidCredentialsError("You must provide either an email or phone number and a type");
16290
16309
  } catch (error) {
16310
+ await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
16291
16311
  if ((0, errors_1.isAuthError)(error)) {
16292
16312
  return this._returnResult({ data: { user: null, session: null }, error });
16293
16313
  }
@@ -17002,7 +17022,7 @@ var require_GoTrueClient = __commonJS((exports) => {
17002
17022
  if ((0, errors_1.isAuthRefreshDiscardedError)(error)) {
17003
17023
  this._debug(debugName, "refresh discarded by commit guard", error);
17004
17024
  } else {
17005
- console.error(error);
17025
+ this._debug(debugName, "refresh failed", error);
17006
17026
  if (!(0, errors_1.isAuthRetryableFetchError)(error)) {
17007
17027
  this._debug(debugName, "refresh failed with a non-retryable error, removing the session", error);
17008
17028
  await this._removeSession();
@@ -18068,7 +18088,7 @@ var require_GoTrueClient = __commonJS((exports) => {
18068
18088
  exports.default = GoTrueClient;
18069
18089
  });
18070
18090
 
18071
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/AuthAdminApi.js
18091
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/AuthAdminApi.js
18072
18092
  var require_AuthAdminApi = __commonJS((exports) => {
18073
18093
  Object.defineProperty(exports, "__esModule", { value: true });
18074
18094
  var tslib_1 = require_tslib();
@@ -18077,7 +18097,7 @@ var require_AuthAdminApi = __commonJS((exports) => {
18077
18097
  exports.default = AuthAdminApi;
18078
18098
  });
18079
18099
 
18080
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/AuthClient.js
18100
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/AuthClient.js
18081
18101
  var require_AuthClient = __commonJS((exports) => {
18082
18102
  Object.defineProperty(exports, "__esModule", { value: true });
18083
18103
  var tslib_1 = require_tslib();
@@ -18086,7 +18106,7 @@ var require_AuthClient = __commonJS((exports) => {
18086
18106
  exports.default = AuthClient;
18087
18107
  });
18088
18108
 
18089
- // ../../node_modules/.bun/@supabase+auth-js@2.107.0/node_modules/@supabase/auth-js/dist/main/index.js
18109
+ // ../../node_modules/.bun/@supabase+auth-js@2.108.1/node_modules/@supabase/auth-js/dist/main/index.js
18090
18110
  var require_main3 = __commonJS((exports) => {
18091
18111
  Object.defineProperty(exports, "__esModule", { value: true });
18092
18112
  exports.processLock = exports.lockInternals = exports.NavigatorLockAcquireTimeoutError = exports.navigatorLock = exports.AuthClient = exports.AuthAdminApi = exports.GoTrueClient = exports.GoTrueAdminApi = undefined;
@@ -18116,7 +18136,7 @@ var require_main3 = __commonJS((exports) => {
18116
18136
  } });
18117
18137
  });
18118
18138
 
18119
- // ../../node_modules/.bun/@supabase+supabase-js@2.107.0/node_modules/@supabase/supabase-js/dist/index.mjs
18139
+ // ../../node_modules/.bun/@supabase+supabase-js@2.108.1/node_modules/@supabase/supabase-js/dist/index.mjs
18120
18140
  var exports_dist = {};
18121
18141
  __export(exports_dist, {
18122
18142
  createClient: () => createClient,
@@ -18377,7 +18397,7 @@ function shouldShowDeprecationWarning() {
18377
18397
  return false;
18378
18398
  return parseInt(versionMatch[1], 10) <= 18;
18379
18399
  }
18380
- var import_functions_js, import_realtime_js, import_auth_js, version2 = "2.107.0", JS_ENV = "", JS_RUNTIME_VERSION, _Deno$version, _process$version, _runtimeMeta, DEFAULT_HEADERS2, DEFAULT_GLOBAL_OPTIONS, DEFAULT_DB_OPTIONS, DEFAULT_AUTH_OPTIONS, DEFAULT_REALTIME_OPTIONS, DEFAULT_TRACE_PROPAGATION_OPTIONS, otelModulePromise = null, OTEL_PKG = "@opentelemetry/api", resolveFetch2 = (customFetch) => {
18400
+ var import_functions_js, import_realtime_js, import_auth_js, version2 = "2.108.1", JS_ENV = "", JS_RUNTIME_VERSION, _Deno$version, _process$version, _runtimeMeta, DEFAULT_HEADERS2, DEFAULT_GLOBAL_OPTIONS, DEFAULT_DB_OPTIONS, DEFAULT_AUTH_OPTIONS, DEFAULT_REALTIME_OPTIONS, DEFAULT_TRACE_PROPAGATION_OPTIONS, otelModulePromise = null, OTEL_PKG = "@opentelemetry/api", resolveFetch2 = (customFetch) => {
18381
18401
  if (customFetch)
18382
18402
  return (...args) => customFetch(...args);
18383
18403
  return (...args) => fetch(...args);
@@ -24758,6 +24778,157 @@ var init_src = __esm(() => {
24758
24778
  src_default = Postgres;
24759
24779
  });
24760
24780
 
24781
+ // ../../_shared/mcp-tools/_utils.ts
24782
+ function getMaxResponseBytes() {
24783
+ const raw = globalThis.process?.env?.CEREFOX_MAX_RESPONSE_BYTES;
24784
+ if (raw === undefined || raw === "")
24785
+ return MAX_RESPONSE_BYTES;
24786
+ const n = Number.parseInt(raw, 10);
24787
+ return Number.isNaN(n) || n <= 0 ? MAX_RESPONSE_BYTES : n;
24788
+ }
24789
+ function getMinSearchScore() {
24790
+ const raw = globalThis.process?.env?.CEREFOX_MIN_SEARCH_SCORE;
24791
+ if (raw === undefined || raw === "")
24792
+ return DEFAULT_MIN_SEARCH_SCORE;
24793
+ const n = Number.parseFloat(raw);
24794
+ return Number.isNaN(n) || n < 0 || n > 1 ? DEFAULT_MIN_SEARCH_SCORE : n;
24795
+ }
24796
+ function applyByteBudget(rows, maxBytes) {
24797
+ const accepted = [];
24798
+ let usedBytes = 0;
24799
+ let truncated = false;
24800
+ for (const row of rows) {
24801
+ const rowBytes = new TextEncoder().encode(JSON.stringify(row)).length;
24802
+ if (usedBytes + rowBytes > maxBytes) {
24803
+ truncated = true;
24804
+ break;
24805
+ }
24806
+ accepted.push(row);
24807
+ usedBytes += rowBytes;
24808
+ }
24809
+ return { accepted, truncated, usedBytes };
24810
+ }
24811
+ function logUsage(supabase, params) {
24812
+ Promise.resolve(supabase.rpc("cerefox_log_usage", {
24813
+ p_operation: params.operation,
24814
+ p_access_path: params.accessPath,
24815
+ p_requestor: params.requestor ?? "mcp-agent",
24816
+ p_document_id: params.document_id ?? null,
24817
+ p_project_id: params.project_id ?? null,
24818
+ p_query_text: params.query_text ?? null,
24819
+ p_result_count: params.result_count ?? null,
24820
+ p_extra: params.extra ?? {}
24821
+ })).catch(() => {});
24822
+ }
24823
+ var MAX_RESPONSE_BYTES = 200000, DEFAULT_MIN_SEARCH_SCORE = 0.5;
24824
+
24825
+ // ../../_shared/mcp-tools/_projects.ts
24826
+ async function ensureDocumentInProject(supabase, documentId, projectName) {
24827
+ let projectId = null;
24828
+ const { data: proj } = await supabase.from("cerefox_projects").select("id").ilike("name", projectName).limit(1);
24829
+ if (proj?.length) {
24830
+ projectId = proj[0].id;
24831
+ } else {
24832
+ const { data: newProj } = await supabase.from("cerefox_projects").insert({ name: projectName }).select("id");
24833
+ projectId = newProj?.[0]?.id ?? null;
24834
+ }
24835
+ if (!projectId)
24836
+ return null;
24837
+ const { data: existing } = await supabase.from("cerefox_document_projects").select("document_id").eq("document_id", documentId).eq("project_id", projectId).limit(1);
24838
+ if (existing?.length)
24839
+ return projectId;
24840
+ const { error: insertErr } = await supabase.from("cerefox_document_projects").insert({ document_id: documentId, project_id: projectId });
24841
+ if (insertErr && !String(insertErr.message ?? "").includes("duplicate key")) {
24842
+ console.warn("ensureDocumentInProject: insert failed", insertErr);
24843
+ }
24844
+ return projectId;
24845
+ }
24846
+ async function setDocumentProjectsByName(supabase, documentId, projectNames) {
24847
+ const projectIds = [];
24848
+ for (const name of projectNames) {
24849
+ if (!name)
24850
+ continue;
24851
+ const { data: proj } = await supabase.from("cerefox_projects").select("id").ilike("name", name).limit(1);
24852
+ if (proj?.length) {
24853
+ projectIds.push(proj[0].id);
24854
+ } else {
24855
+ const { data: newProj } = await supabase.from("cerefox_projects").insert({ name }).select("id");
24856
+ if (newProj?.[0]?.id)
24857
+ projectIds.push(newProj[0].id);
24858
+ }
24859
+ }
24860
+ await supabase.from("cerefox_document_projects").delete().eq("document_id", documentId);
24861
+ if (projectIds.length > 0) {
24862
+ const rows = projectIds.map((pid) => ({ document_id: documentId, project_id: pid }));
24863
+ await supabase.from("cerefox_document_projects").insert(rows);
24864
+ }
24865
+ return projectIds;
24866
+ }
24867
+ async function replaceDocumentProjects(supabase, opts) {
24868
+ const { documentId, projectNames, author, authorType, accessPath } = opts;
24869
+ const seenLower = new Set;
24870
+ const cleanNames = [];
24871
+ for (const n of projectNames) {
24872
+ const stripped = (n ?? "").trim();
24873
+ if (!stripped)
24874
+ continue;
24875
+ const key = stripped.toLowerCase();
24876
+ if (seenLower.has(key))
24877
+ continue;
24878
+ seenLower.add(key);
24879
+ cleanNames.push(stripped);
24880
+ }
24881
+ const { data: doc } = await supabase.from("cerefox_documents").select("id, title").eq("id", documentId).is("deleted_at", null).limit(1);
24882
+ if (!doc?.length) {
24883
+ throw new Error(`Document not found (or soft-deleted): ${documentId}`);
24884
+ }
24885
+ const projectIds = [];
24886
+ for (const name of cleanNames) {
24887
+ const { data: proj } = await supabase.from("cerefox_projects").select("id").ilike("name", name).limit(1);
24888
+ if (proj?.length) {
24889
+ projectIds.push(proj[0].id);
24890
+ } else {
24891
+ const { data: newProj } = await supabase.from("cerefox_projects").insert({ name }).select("id");
24892
+ if (newProj?.[0]?.id)
24893
+ projectIds.push(newProj[0].id);
24894
+ }
24895
+ }
24896
+ await supabase.from("cerefox_document_projects").delete().eq("document_id", documentId);
24897
+ if (projectIds.length > 0) {
24898
+ const rows = projectIds.map((pid) => ({ document_id: documentId, project_id: pid }));
24899
+ await supabase.from("cerefox_document_projects").insert(rows);
24900
+ }
24901
+ try {
24902
+ await supabase.rpc("cerefox_create_audit_entry", {
24903
+ p_document_id: documentId,
24904
+ p_version_id: null,
24905
+ p_operation: "update-metadata",
24906
+ p_author: author,
24907
+ p_author_type: authorType,
24908
+ p_size_before: null,
24909
+ p_size_after: null,
24910
+ p_description: cleanNames.length > 0 ? `Set document projects to [${cleanNames.join(", ")}]` : "Cleared all project memberships"
24911
+ });
24912
+ } catch (err) {
24913
+ console.warn("replaceDocumentProjects: audit entry failed", err);
24914
+ }
24915
+ logUsage(supabase, {
24916
+ operation: "set-document-projects",
24917
+ accessPath,
24918
+ requestor: author,
24919
+ document_id: documentId,
24920
+ result_count: projectIds.length
24921
+ });
24922
+ return { documentTitle: doc[0].title, cleanNames, projectIds };
24923
+ }
24924
+ async function lookupProjectId(supabase, projectName) {
24925
+ const { data, error } = await supabase.from("cerefox_projects").select("id").ilike("name", projectName).limit(1);
24926
+ if (error || !data?.length)
24927
+ return null;
24928
+ return data[0].id;
24929
+ }
24930
+ var init__projects = () => {};
24931
+
24761
24932
  // src/cli/util/bundled-docs.ts
24762
24933
  import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync } from "node:fs";
24763
24934
  import { dirname as dirname3, join as join7, resolve as resolve2 } from "node:path";
@@ -39828,10 +39999,10 @@ var require_flate = __commonJS((exports) => {
39828
39999
  var GenericWorker = require_GenericWorker();
39829
40000
  var ARRAY_TYPE = USE_TYPEDARRAY ? "uint8array" : "array";
39830
40001
  exports.magic = "\b\x00";
39831
- function FlateWorker(action17, options) {
39832
- GenericWorker.call(this, "FlateWorker/" + action17);
40002
+ function FlateWorker(action18, options) {
40003
+ GenericWorker.call(this, "FlateWorker/" + action18);
39833
40004
  this._pako = null;
39834
- this._pakoAction = action17;
40005
+ this._pakoAction = action18;
39835
40006
  this._pakoOptions = options;
39836
40007
  this.meta = {};
39837
40008
  }
@@ -53732,50 +53903,6 @@ var require_cli_progress = __commonJS((exports, module) => {
53732
53903
  };
53733
53904
  });
53734
53905
 
53735
- // ../../_shared/mcp-tools/_utils.ts
53736
- function getMaxResponseBytes() {
53737
- const raw = globalThis.process?.env?.CEREFOX_MAX_RESPONSE_BYTES;
53738
- if (raw === undefined || raw === "")
53739
- return MAX_RESPONSE_BYTES;
53740
- const n = Number.parseInt(raw, 10);
53741
- return Number.isNaN(n) || n <= 0 ? MAX_RESPONSE_BYTES : n;
53742
- }
53743
- function getMinSearchScore() {
53744
- const raw = globalThis.process?.env?.CEREFOX_MIN_SEARCH_SCORE;
53745
- if (raw === undefined || raw === "")
53746
- return DEFAULT_MIN_SEARCH_SCORE;
53747
- const n = Number.parseFloat(raw);
53748
- return Number.isNaN(n) || n < 0 || n > 1 ? DEFAULT_MIN_SEARCH_SCORE : n;
53749
- }
53750
- function applyByteBudget(rows, maxBytes) {
53751
- const accepted = [];
53752
- let usedBytes = 0;
53753
- let truncated = false;
53754
- for (const row of rows) {
53755
- const rowBytes = new TextEncoder().encode(JSON.stringify(row)).length;
53756
- if (usedBytes + rowBytes > maxBytes) {
53757
- truncated = true;
53758
- break;
53759
- }
53760
- accepted.push(row);
53761
- usedBytes += rowBytes;
53762
- }
53763
- return { accepted, truncated, usedBytes };
53764
- }
53765
- function logUsage(supabase, params) {
53766
- Promise.resolve(supabase.rpc("cerefox_log_usage", {
53767
- p_operation: params.operation,
53768
- p_access_path: params.accessPath,
53769
- p_requestor: params.requestor ?? "mcp-agent",
53770
- p_document_id: params.document_id ?? null,
53771
- p_project_id: params.project_id ?? null,
53772
- p_query_text: params.query_text ?? null,
53773
- p_result_count: params.result_count ?? null,
53774
- p_extra: params.extra ?? {}
53775
- })).catch(() => {});
53776
- }
53777
- var MAX_RESPONSE_BYTES = 200000, DEFAULT_MIN_SEARCH_SCORE = 0.5;
53778
-
53779
53906
  // ../../_shared/mcp-tools/audit-log.ts
53780
53907
  async function handler(supabase, args, ctx) {
53781
53908
  const params = {};
@@ -53880,16 +54007,18 @@ async function handler2(supabase, args, ctx) {
53880
54007
  result_count: 1
53881
54008
  });
53882
54009
  const label = version_id !== null ? " (archived version)" : " (current)";
53883
- return `# ${row.doc_title ?? "Untitled"}${label}
54010
+ const hashLine = row.content_hash ? `content_hash: ${row.content_hash}
53884
54011
 
53885
- ${row.full_content ?? ""}`;
54012
+ ` : "";
54013
+ return `# ${row.doc_title ?? "Untitled"}${label}
54014
+ ${hashLine}${row.full_content ?? ""}`;
53886
54015
  }
53887
54016
  var getDocumentTool;
53888
54017
  var init_get_document = __esm(() => {
53889
54018
  init_types3();
53890
54019
  getDocumentTool = {
53891
54020
  name: "cerefox_get_document",
53892
- description: "Retrieve the full reconstructed content of a document. Pass version_id to retrieve an archived version; omit it (or pass null) for the current version. Version UUIDs are returned by cerefox_list_versions.",
54021
+ description: "Retrieve the full reconstructed content of a document. Pass version_id to retrieve an archived version; omit it (or pass null) for the current version. Version UUIDs are returned by cerefox_list_versions. The response header includes the document's current content_hash — pass it back as expected_content_hash when updating via cerefox_ingest (optimistic concurrency).",
53893
54022
  inputSchema: {
53894
54023
  type: "object",
53895
54024
  required: ["document_id"],
@@ -53910,15 +54039,23 @@ var init_get_document = __esm(() => {
53910
54039
  });
53911
54040
 
53912
54041
  // ../../_shared/mcp-tools/get-help-content.ts
53913
- var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **10 MCP tools** (9 of them have CLI equivalents — `cerefox_get_help` is MCP-only). For the full guide, search Cerefox for "How AI Agents Use Cerefox" or call `cerefox_get_help` to retrieve this content over MCP.\n\n## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata`, `author` |\n| `cerefox_get_document` | Get full document by ID | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find docs by metadata (no text query) | `metadata_filter` (required), `include_content`, `updated_since` |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc\'s project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.\n\n## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> modify ->\ningest(title="Same Title", content="...", document_id="abc123", author="my-agent")\n```\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true, author="my-agent")\n```\n\n## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```\n\n## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …). The legacy Python `uv run cerefox` is now a frozen husk as of v0.9 — only `uv run cerefox mcp` still works.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` |\n| `cerefox_set_document_projects` | _MCP-only; a CLI command will be added in a future release. Until then, run via MCP if available._ |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
54042
+ var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **10 MCP tools** (9 of them have CLI equivalents — `cerefox_get_help` is MCP-only). For the full guide, search Cerefox for "How AI Agents Use Cerefox" or call `cerefox_get_help` to retrieve this content over MCP.\n\n## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata`, `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token) | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project\'s docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_since |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc\'s project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you read (shown by `cerefox_get_document`, `cerefox_search`, and `cerefox_metadata_search`) when updating a document. If it\'s stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer\'s work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.\n10. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.\n\n## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> note its content_hash -> modify ->\ningest(title="Same Title", content="...", document_id="abc123",\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\nOn a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\n## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```\n\n## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …). The legacy Python `uv run cerefox` is now a frozen husk as of v0.9 — only `uv run cerefox mcp` still works.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
53914
54043
  var init_get_help_content = __esm(() => {
53915
54044
  HELP_SECTIONS = {
53916
- Tools: "## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata`, `author` |\n| `cerefox_get_document` | Get full document by ID | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find docs by metadata (no text query) | `metadata_filter` (required), `include_content`, `updated_since` |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc's project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |",
53917
- "Essential Rules": '## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.',
53918
- "Update Workflow (ID-based -- preferred)": '## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> modify ->\ningest(title="Same Title", content="...", document_id="abc123", author="my-agent")\n```',
53919
- "Update Workflow (title-based -- fallback)": '## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true, author="my-agent")\n```',
54045
+ Tools: "## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata`, `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token) | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project's docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_since |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc's project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |",
54046
+ "Essential Rules": '## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you read (shown by `cerefox_get_document`, `cerefox_search`, and `cerefox_metadata_search`) when updating a document. If it\'s stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer\'s work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.\n10. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.',
54047
+ "Update Workflow (ID-based -- preferred)": `## Update Workflow (ID-based -- preferred)
54048
+
54049
+ \`\`\`
54050
+ search("topic") -> find doc [id: abc123] -> get_document(abc123) -> note its content_hash -> modify ->
54051
+ ingest(title="Same Title", content="...", document_id="abc123",
54052
+ expected_content_hash="<the hash you read>", author="my-agent")
54053
+ \`\`\`
54054
+
54055
+ On a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.`,
54056
+ "Update Workflow (title-based -- fallback)": '## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", author="my-agent")\n```',
53920
54057
  "Catch-Up Workflow": '## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```',
53921
- "CLI fallback (when MCP is unavailable)": '## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …). The legacy Python `uv run cerefox` is now a frozen husk as of v0.9 — only `uv run cerefox mcp` still works.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` |\n| `cerefox_set_document_projects` | _MCP-only; a CLI command will be added in a future release. Until then, run via MCP if available._ |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.'
54058
+ "CLI fallback (when MCP is unavailable)": '## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …). The legacy Python `uv run cerefox` is now a frozen husk as of v0.9 — only `uv run cerefox mcp` still works.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.'
53922
54059
  };
53923
54060
  HELP_SECTION_HEADINGS = ["Tools", "Essential Rules", "Update Workflow (ID-based -- preferred)", "Update Workflow (title-based -- fallback)", "Catch-Up Workflow", "CLI fallback (when MCP is unavailable)"];
53924
54061
  });
@@ -54137,56 +54274,22 @@ async function sha256hex(text) {
54137
54274
  }
54138
54275
  var MAX_CHUNK_CHARS = 4000;
54139
54276
 
54140
- // ../../_shared/mcp-tools/_projects.ts
54141
- async function ensureDocumentInProject(supabase, documentId, projectName) {
54142
- let projectId = null;
54143
- const { data: proj } = await supabase.from("cerefox_projects").select("id").ilike("name", projectName).limit(1);
54144
- if (proj?.length) {
54145
- projectId = proj[0].id;
54146
- } else {
54147
- const { data: newProj } = await supabase.from("cerefox_projects").insert({ name: projectName }).select("id");
54148
- projectId = newProj?.[0]?.id ?? null;
54149
- }
54150
- if (!projectId)
54151
- return null;
54152
- const { data: existing } = await supabase.from("cerefox_document_projects").select("document_id").eq("document_id", documentId).eq("project_id", projectId).limit(1);
54153
- if (existing?.length)
54154
- return projectId;
54155
- const { error: insertErr } = await supabase.from("cerefox_document_projects").insert({ document_id: documentId, project_id: projectId });
54156
- if (insertErr && !String(insertErr.message ?? "").includes("duplicate key")) {
54157
- console.warn("ensureDocumentInProject: insert failed", insertErr);
54158
- }
54159
- return projectId;
54277
+ // ../../_shared/mcp-tools/ingest.ts
54278
+ function conflictError(documentId, expectedHash, currentHash) {
54279
+ return new Error(`Conflict: document ${documentId} changed since you read it ` + `(your base hash: ${expectedHash}, current hash: ${currentHash}). ` + `To resolve: (1) cerefox_get_document("${documentId}") to fetch the latest content ` + `and its content_hash, (2) merge your changes into it, (3) retry cerefox_ingest ` + `with expected_content_hash set to the new hash. Do not overwrite blindly — ` + `the current content may include another writer's work.`);
54160
54280
  }
54161
- async function setDocumentProjectsByName(supabase, documentId, projectNames) {
54162
- const projectIds = [];
54163
- for (const name of projectNames) {
54164
- if (!name)
54165
- continue;
54166
- const { data: proj } = await supabase.from("cerefox_projects").select("id").ilike("name", name).limit(1);
54167
- if (proj?.length) {
54168
- projectIds.push(proj[0].id);
54169
- } else {
54170
- const { data: newProj } = await supabase.from("cerefox_projects").insert({ name }).select("id");
54171
- if (newProj?.[0]?.id)
54172
- projectIds.push(newProj[0].id);
54173
- }
54281
+ function mapIngestRpcError(message, documentId) {
54282
+ if (message.includes("CEREFOX_CONFLICT")) {
54283
+ const current = message.match(/current hash ([0-9a-f]{64})/)?.[1] ?? "unknown";
54284
+ const expected = message.match(/expected hash ([0-9a-f]{64})/)?.[1] ?? "unknown";
54285
+ return conflictError(documentId, expected, current);
54174
54286
  }
54175
- await supabase.from("cerefox_document_projects").delete().eq("document_id", documentId);
54176
- if (projectIds.length > 0) {
54177
- const rows = projectIds.map((pid) => ({ document_id: documentId, project_id: pid }));
54178
- await supabase.from("cerefox_document_projects").insert(rows);
54287
+ if (message.includes("CEREFOX_TOKEN_REQUIRED")) {
54288
+ const current = message.match(/Current hash: ([0-9a-f]{64})/)?.[1];
54289
+ return new Error(`Concurrency token required: content updates need expected_content_hash — ` + `the content_hash of the version you based your edit on (returned by ` + `cerefox_get_document, cerefox_search, and cerefox_metadata_search).` + (current ? ` The document's current hash is ${current}; pass it ONLY if your edit was based on the current content.` : "") + ` If you have not read the document, read it first. To deliberately overwrite ` + `regardless of concurrent changes, pass last_write_wins=true.`);
54179
54290
  }
54180
- return projectIds;
54181
- }
54182
- async function lookupProjectId(supabase, projectName) {
54183
- const { data, error: error2 } = await supabase.from("cerefox_projects").select("id").ilike("name", projectName).limit(1);
54184
- if (error2 || !data?.length)
54185
- return null;
54186
- return data[0].id;
54291
+ return new Error(`Ingest RPC failed: ${message}`);
54187
54292
  }
54188
-
54189
- // ../../_shared/mcp-tools/ingest.ts
54190
54293
  async function handler4(supabase, args, ctx) {
54191
54294
  const title = args.title?.trim();
54192
54295
  const content = args.content;
@@ -54198,6 +54301,8 @@ async function handler4(supabase, args, ctx) {
54198
54301
  const update_if_exists = args.update_if_exists ?? false;
54199
54302
  const author = args.author ?? "mcp-agent";
54200
54303
  const author_type = "agent";
54304
+ const expected_content_hash = args.expected_content_hash?.trim() || null;
54305
+ const last_write_wins = args.last_write_wins ?? false;
54201
54306
  if (!title || !content?.trim()) {
54202
54307
  throw new McpInvalidParams("title and content are required");
54203
54308
  }
@@ -54218,7 +54323,10 @@ async function handler4(supabase, args, ctx) {
54218
54323
  const existingDoc = existing[0];
54219
54324
  if (existingDoc.content_hash === contentHash2) {
54220
54325
  const note2 = update_if_exists ? "" : " Note: update_if_exists flag was overridden by document_id.";
54221
- return `Document already up-to-date: "${existingDoc.title}" (id: ${existingDoc.id}). Content hash unchanged.${note2}`;
54326
+ return `Document already up-to-date: "${existingDoc.title}" (id: ${existingDoc.id}). Content hash unchanged (${contentHash2}).${note2}`;
54327
+ }
54328
+ if (!last_write_wins && expected_content_hash && expected_content_hash !== existingDoc.content_hash) {
54329
+ throw conflictError(existingDoc.id, expected_content_hash, existingDoc.content_hash);
54222
54330
  }
54223
54331
  const chunks2 = chunkMarkdown2(content);
54224
54332
  if (chunks2.length === 0)
@@ -54247,10 +54355,12 @@ ${c2.content}`);
54247
54355
  p_chunks: chunkData2,
54248
54356
  p_author: author,
54249
54357
  p_author_type: author_type,
54250
- p_source_label: source
54358
+ p_source_label: source,
54359
+ p_expected_content_hash: expected_content_hash,
54360
+ p_last_write_wins: last_write_wins
54251
54361
  });
54252
54362
  if (ingestErr2)
54253
- throw new Error(`Ingest RPC failed: ${ingestErr2.message}`);
54363
+ throw mapIngestRpcError(ingestErr2.message, existingDoc.id);
54254
54364
  logUsage(supabase, {
54255
54365
  operation: "ingest",
54256
54366
  accessPath: ctx.accessPath,
@@ -54264,14 +54374,17 @@ ${c2.content}`);
54264
54374
  await ensureDocumentInProject(supabase, existingDoc.id, project_name);
54265
54375
  }
54266
54376
  const note = update_if_exists ? "" : " Note: update_if_exists flag was overridden by document_id.";
54267
- return `Document updated: "${title}" (id: ${existingDoc.id}), ${chunks2.length} chunk(s), ${totalChars2} chars.${note}`;
54377
+ return `Document updated: "${title}" (id: ${existingDoc.id}), ${chunks2.length} chunk(s), ${totalChars2} chars. New content_hash: ${contentHash2}.${note}`;
54268
54378
  }
54269
54379
  if (update_if_exists) {
54270
54380
  const { data: existing } = await supabase.from("cerefox_documents").select("id, title, content_hash").eq("title", title).order("updated_at", { ascending: false }).limit(1);
54271
54381
  if (existing?.length) {
54272
54382
  const existingDoc = existing[0];
54273
54383
  if (existingDoc.content_hash === contentHash2) {
54274
- return `Document already up-to-date: "${existingDoc.title}" (id: ${existingDoc.id}). Content hash unchanged.`;
54384
+ return `Document already up-to-date: "${existingDoc.title}" (id: ${existingDoc.id}). Content hash unchanged (${contentHash2}).`;
54385
+ }
54386
+ if (!last_write_wins && expected_content_hash && expected_content_hash !== existingDoc.content_hash) {
54387
+ throw conflictError(existingDoc.id, expected_content_hash, existingDoc.content_hash);
54275
54388
  }
54276
54389
  const chunks2 = chunkMarkdown2(content);
54277
54390
  if (chunks2.length === 0)
@@ -54300,10 +54413,12 @@ ${c2.content}`);
54300
54413
  p_chunks: chunkData2,
54301
54414
  p_author: author,
54302
54415
  p_author_type: author_type,
54303
- p_source_label: source
54416
+ p_source_label: source,
54417
+ p_expected_content_hash: expected_content_hash,
54418
+ p_last_write_wins: last_write_wins
54304
54419
  });
54305
54420
  if (ingestErr2)
54306
- throw new Error(`Ingest RPC failed: ${ingestErr2.message}`);
54421
+ throw mapIngestRpcError(ingestErr2.message, existingDoc.id);
54307
54422
  logUsage(supabase, {
54308
54423
  operation: "ingest",
54309
54424
  accessPath: ctx.accessPath,
@@ -54316,7 +54431,7 @@ ${c2.content}`);
54316
54431
  } else if (project_name) {
54317
54432
  await ensureDocumentInProject(supabase, existingDoc.id, project_name);
54318
54433
  }
54319
- return `Document updated: "${existingDoc.title}" (id: ${existingDoc.id}), ${chunks2.length} chunk(s), ${totalChars2} chars.`;
54434
+ return `Document updated: "${existingDoc.title}" (id: ${existingDoc.id}), ${chunks2.length} chunk(s), ${totalChars2} chars. New content_hash: ${contentHash2}.`;
54320
54435
  }
54321
54436
  }
54322
54437
  const { data: hashMatch } = await supabase.from("cerefox_documents").select("id, title").eq("content_hash", contentHash2).limit(1);
@@ -54372,6 +54487,7 @@ ${c2.content}`);
54372
54487
  }
54373
54488
  var ingestTool;
54374
54489
  var init_ingest = __esm(() => {
54490
+ init__projects();
54375
54491
  init_types3();
54376
54492
  ingestTool = {
54377
54493
  name: "cerefox_ingest",
@@ -54400,6 +54516,14 @@ var init_ingest = __esm(() => {
54400
54516
  type: "boolean",
54401
54517
  description: "When true, update an existing document with the same title instead of creating a new one (default: false). Ignored when document_id is provided."
54402
54518
  },
54519
+ expected_content_hash: {
54520
+ type: "string",
54521
+ description: "REQUIRED on content updates (optimistic concurrency): the content_hash of the document version you based your edit on, as returned by cerefox_get_document / cerefox_search / cerefox_metadata_search. If the document changed since you read it, the update fails with a conflict — re-read, merge, retry with the new hash. Not needed when creating a new document."
54522
+ },
54523
+ last_write_wins: {
54524
+ type: "boolean",
54525
+ description: "Explicitly skip the concurrency check and overwrite regardless of concurrent changes (default: false). Use ONLY when an external source of truth makes conflicts meaningless (e.g. re-syncing from files). Recorded in the audit log."
54526
+ },
54403
54527
  metadata: { type: "object", description: "Arbitrary JSON metadata (optional)" },
54404
54528
  author: {
54405
54529
  type: "string",
@@ -54545,11 +54669,12 @@ async function handler8(supabase, args, ctx) {
54545
54669
  const limit = args.limit ?? 10;
54546
54670
  const include_content = args.include_content ?? false;
54547
54671
  const requested_max_bytes = args.max_bytes;
54548
- if (!metadata_filter || typeof metadata_filter !== "object" || Array.isArray(metadata_filter)) {
54549
- throw new McpInvalidParams("metadata_filter is required and must be a JSON object");
54672
+ if (metadata_filter !== undefined && (typeof metadata_filter !== "object" || Array.isArray(metadata_filter))) {
54673
+ throw new McpInvalidParams("metadata_filter must be a JSON object when provided");
54550
54674
  }
54551
- if (Object.keys(metadata_filter).length === 0) {
54552
- throw new McpInvalidParams("metadata_filter must contain at least one key-value pair");
54675
+ const has_metadata = !!metadata_filter && Object.keys(metadata_filter).length > 0;
54676
+ if (!has_metadata && !project_name && !updated_since && !created_since) {
54677
+ throw new McpInvalidParams("Provide at least one of: metadata_filter, project_name, updated_since, or created_since.");
54553
54678
  }
54554
54679
  let projectId = null;
54555
54680
  if (project_name) {
@@ -54560,7 +54685,7 @@ async function handler8(supabase, args, ctx) {
54560
54685
  const ceiling = getMaxResponseBytes();
54561
54686
  const max_bytes = include_content ? Math.min(requested_max_bytes ?? ceiling, ceiling) : null;
54562
54687
  const params = {
54563
- p_metadata_filter: metadata_filter,
54688
+ p_metadata_filter: metadata_filter ?? {},
54564
54689
  p_project_id: projectId,
54565
54690
  p_updated_since: updated_since ?? null,
54566
54691
  p_created_since: created_since ?? null,
@@ -54577,17 +54702,19 @@ async function handler8(supabase, args, ctx) {
54577
54702
  operation: "metadata_search",
54578
54703
  accessPath: ctx.accessPath,
54579
54704
  requestor: args.requestor,
54580
- query_text: JSON.stringify(metadata_filter),
54705
+ query_text: JSON.stringify(metadata_filter ?? {}),
54581
54706
  project_id: projectId,
54582
54707
  result_count: rows.length
54583
54708
  });
54584
54709
  if (rows.length === 0)
54585
- return "No documents match the metadata filter.";
54710
+ return "No documents match the given criteria.";
54586
54711
  const parts = rows.map((row) => {
54587
54712
  const projects = row.project_names?.length ? ` | projects: ${row.project_names.join(", ")}` : "";
54588
54713
  const meta = Object.entries(row.doc_metadata ?? {}).map(([k, v]) => `${k}=${v}`).join(", ");
54714
+ const hash = row.content_hash ? `
54715
+ hash: ${row.content_hash}` : "";
54589
54716
  const header = `## ${row.title} [id: ${row.document_id}]
54590
- ` + `${meta}${projects} | ${row.total_chars} chars | ${row.review_status} | updated ${row.updated_at?.slice(0, 10) ?? "?"}`;
54717
+ ` + `${meta}${projects} | ${row.total_chars} chars | ${row.review_status} | updated ${row.updated_at?.slice(0, 10) ?? "?"}${hash}`;
54591
54718
  if (include_content && row.content) {
54592
54719
  return `${header}
54593
54720
 
@@ -54603,20 +54730,20 @@ ${row.content}`;
54603
54730
  }
54604
54731
  var metadataSearchTool;
54605
54732
  var init_metadata_search = __esm(() => {
54733
+ init__projects();
54606
54734
  init_types3();
54607
54735
  metadataSearchTool = {
54608
54736
  name: "cerefox_metadata_search",
54609
- description: "Find documents by metadata key-value criteria without a text search term. Use to discover documents tagged with specific attributes, browse by taxonomy, or retrieve messages/tasks by type and status.",
54737
+ description: "Find or list documents by metadata key-value criteria without a text search term. Use to discover documents tagged with specific attributes, browse by taxonomy, retrieve messages/tasks by type and status, or list all documents in a project (pass project_name alone). At least one of metadata_filter, project_name, updated_since, or created_since must be supplied; results are ordered newest-updated first.",
54610
54738
  inputSchema: {
54611
54739
  type: "object",
54612
- required: ["metadata_filter"],
54613
54740
  properties: {
54614
54741
  metadata_filter: {
54615
54742
  type: "object",
54616
- description: 'Key-value pairs; ALL must match (AND semantics). Example: {"type": "decision", "status": "active"}. Call cerefox_list_metadata_keys first to discover available keys.',
54743
+ description: 'Key-value pairs; ALL must match (AND semantics). Example: {"type": "decision", "status": "active"}. Call cerefox_list_metadata_keys first to discover available keys. Optional — omit (or pass {}) to list by project_name / time range alone.',
54617
54744
  additionalProperties: { type: "string" }
54618
54745
  },
54619
- project_name: { type: "string", description: "Restrict to a project by name (optional)" },
54746
+ project_name: { type: "string", description: "Restrict to a project by name. Sufficient on its own to list that project's documents (optional)." },
54620
54747
  updated_since: {
54621
54748
  type: "string",
54622
54749
  description: "ISO-8601 timestamp; only docs updated on/after (optional)"
@@ -54729,7 +54856,9 @@ async function handler9(supabase, args, ctx) {
54729
54856
  const docId = row.document_id ? ` [id: ${row.document_id}]` : "";
54730
54857
  const score = row.best_score != null ? ` (score: ${row.best_score.toFixed(3)})` : "";
54731
54858
  const partial = row.is_partial ? ` -- partial (${row.chunk_count} of ${(row.total_chars ?? 0).toLocaleString()} chars)` : "";
54732
- return `## ${title}${docId}${score}${partial}
54859
+ const hash = row.content_hash ? `
54860
+ hash: ${row.content_hash}` : "";
54861
+ return `## ${title}${docId}${score}${partial}${hash}
54733
54862
 
54734
54863
  ${row.full_content ?? ""}`;
54735
54864
  });
@@ -54747,6 +54876,7 @@ ${row.full_content ?? ""}`;
54747
54876
  }
54748
54877
  var searchTool;
54749
54878
  var init_search = __esm(() => {
54879
+ init__projects();
54750
54880
  init_types3();
54751
54881
  searchTool = {
54752
54882
  name: "cerefox_search",
@@ -54797,58 +54927,12 @@ async function handler10(supabase, args, ctx) {
54797
54927
  if (!project_names_raw.every((n) => typeof n === "string")) {
54798
54928
  throw new McpInvalidParams("project_names must contain only strings.");
54799
54929
  }
54800
- const seenLower = new Set;
54801
- const cleanNames = [];
54802
- for (const n of project_names_raw) {
54803
- const stripped = n.trim();
54804
- if (!stripped)
54805
- continue;
54806
- const key = stripped.toLowerCase();
54807
- if (seenLower.has(key))
54808
- continue;
54809
- seenLower.add(key);
54810
- cleanNames.push(stripped);
54811
- }
54812
- const { data: doc } = await supabase.from("cerefox_documents").select("id, title").eq("id", document_id).is("deleted_at", null).limit(1);
54813
- if (!doc?.length) {
54814
- throw new Error(`Document not found (or soft-deleted): ${document_id}`);
54815
- }
54816
- const projectIds = [];
54817
- for (const name of cleanNames) {
54818
- const { data: proj } = await supabase.from("cerefox_projects").select("id").ilike("name", name).limit(1);
54819
- if (proj?.length) {
54820
- projectIds.push(proj[0].id);
54821
- } else {
54822
- const { data: newProj } = await supabase.from("cerefox_projects").insert({ name }).select("id");
54823
- if (newProj?.[0]?.id)
54824
- projectIds.push(newProj[0].id);
54825
- }
54826
- }
54827
- await supabase.from("cerefox_document_projects").delete().eq("document_id", document_id);
54828
- if (projectIds.length > 0) {
54829
- const rows = projectIds.map((pid) => ({ document_id, project_id: pid }));
54830
- await supabase.from("cerefox_document_projects").insert(rows);
54831
- }
54832
- try {
54833
- await supabase.rpc("cerefox_create_audit_entry", {
54834
- p_document_id: document_id,
54835
- p_version_id: null,
54836
- p_operation: "update-metadata",
54837
- p_author: author,
54838
- p_author_type: "agent",
54839
- p_size_before: null,
54840
- p_size_after: null,
54841
- p_description: cleanNames.length > 0 ? `Set document projects to [${cleanNames.join(", ")}]` : "Cleared all project memberships"
54842
- });
54843
- } catch (err) {
54844
- console.warn("set-document-projects: audit entry failed", err);
54845
- }
54846
- logUsage(supabase, {
54847
- operation: "set-document-projects",
54848
- accessPath: ctx.accessPath,
54849
- requestor: author,
54850
- document_id,
54851
- result_count: projectIds.length
54930
+ const { cleanNames, projectIds } = await replaceDocumentProjects(supabase, {
54931
+ documentId: document_id,
54932
+ projectNames: project_names_raw,
54933
+ author,
54934
+ authorType: "agent",
54935
+ accessPath: ctx.accessPath
54852
54936
  });
54853
54937
  if (cleanNames.length === 0) {
54854
54938
  return `Cleared all project memberships for document ${document_id}. ` + "The document no longer belongs to any project.";
@@ -54860,6 +54944,7 @@ async function handler10(supabase, args, ctx) {
54860
54944
  }
54861
54945
  var setDocumentProjectsTool;
54862
54946
  var init_set_document_projects = __esm(() => {
54947
+ init__projects();
54863
54948
  init_types3();
54864
54949
  setDocumentProjectsTool = {
54865
54950
  name: "cerefox_set_document_projects",
@@ -54965,6 +55050,7 @@ async function runSyncSelfDocs(options = {}) {
54965
55050
  topic: doc.topic
54966
55051
  },
54967
55052
  update_if_exists: true,
55053
+ last_write_wins: true,
54968
55054
  project_name: project,
54969
55055
  author,
54970
55056
  author_type: authorType
@@ -54983,11 +55069,11 @@ async function runSyncSelfDocs(options = {}) {
54983
55069
  printTable(outcomes.filter((o) => o.status === "error").map((o) => ({ topic: o.topic, error: o.detail.slice(0, 100) })));
54984
55070
  }
54985
55071
  }
54986
- async function action19(options) {
55072
+ async function action20(options) {
54987
55073
  await runSyncSelfDocs(options);
54988
55074
  }
54989
55075
  function registerSyncSelfDocs(program2) {
54990
- program2.command("sync-self-docs").description("Ingest bundled Cerefox docs under the _cerefox-self-docs project.").option("--dry-run", "List what would be ingested without writing.").option("--project <name>", "Override the target project name.", "_cerefox-self-docs").action(action19);
55076
+ program2.command("sync-self-docs").description("Ingest bundled Cerefox docs under the _cerefox-self-docs project.").option("--dry-run", "List what would be ingested without writing.").option("--project <name>", "Override the target project name.", "_cerefox-self-docs").action(action20);
54991
55077
  }
54992
55078
  var init_sync_self_docs = __esm(() => {
54993
55079
  init_cli_core();
@@ -70458,6 +70544,43 @@ function registerDocumentRestore(parent) {
70458
70544
  parent.command("restore").description("Restore a soft-deleted document from the trash (inverse of `document delete`).").argument("<document-id>", "UUID of the soft-deleted document.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").action(action11);
70459
70545
  }
70460
70546
 
70547
+ // src/cli/commands/document-set-projects.ts
70548
+ init_cli_core();
70549
+ init__projects();
70550
+ init_client();
70551
+ async function action12(documentId, projectNames, options) {
70552
+ const names = projectNames ?? [];
70553
+ if (options.clear && names.length > 0) {
70554
+ throw userError("Pass either project names or --clear, not both.", "Use --clear on its own to remove the document from all projects.");
70555
+ }
70556
+ if (!options.clear && names.length === 0) {
70557
+ throw userError("No project names given.", "Pass one or more project names, or --clear to remove all memberships.");
70558
+ }
70559
+ const author = resolveAuthor(options.author);
70560
+ const authorType = resolveAuthorType(options.authorType);
70561
+ if (author === "unknown") {
70562
+ warn("No --author / CEREFOX_AUTHOR_NAME set — audit log will record this as 'unknown'.");
70563
+ }
70564
+ const client = getClient();
70565
+ const { documentTitle, cleanNames } = await replaceDocumentProjects(client.raw, {
70566
+ documentId,
70567
+ projectNames: names,
70568
+ author,
70569
+ authorType,
70570
+ accessPath: "cli"
70571
+ });
70572
+ if (cleanNames.length === 0) {
70573
+ println(c.green(`✓ Cleared all project memberships for "${documentTitle}" (id: ${documentId}).`));
70574
+ return;
70575
+ }
70576
+ println(c.green(`✓ Set ${cleanNames.length} project membership(s) for "${documentTitle}" (id: ${documentId}).`));
70577
+ println(c.dim(` Projects: ${cleanNames.join(", ")}`));
70578
+ println(c.dim(" This REPLACED the previous set — any project not listed is no longer associated."));
70579
+ }
70580
+ function registerDocumentSetProjects(parent) {
70581
+ parent.command("set-projects").description("Replace a document's project memberships with exactly the given set (or --clear to remove all).").argument("<document-id>", "UUID of the document.").argument("[project-names...]", "Project names to set (created if missing). Omit and pass --clear to remove all.").option("--clear", "Remove the document from all projects.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").action(action12);
70582
+ }
70583
+
70461
70584
  // src/cli/commands/guides.ts
70462
70585
  init_cli_core();
70463
70586
  init_bundled_docs();
@@ -70505,7 +70628,7 @@ function registerGuides(parent) {
70505
70628
  // src/cli/commands/project-create.ts
70506
70629
  init_cli_core();
70507
70630
  init_client();
70508
- async function action12(name, options) {
70631
+ async function action13(name, options) {
70509
70632
  const trimmed = name.trim();
70510
70633
  if (!trimmed)
70511
70634
  throw userError("Project name is required.");
@@ -70517,14 +70640,14 @@ async function action12(name, options) {
70517
70640
  println(c.green(`✓ Created project "${data.name}" (id: ${data.id}).`));
70518
70641
  }
70519
70642
  function registerProjectCreate(parent) {
70520
- parent.command("create").description("Create a new (empty) project.").argument("<name>", "Project name (must be unique).").option("--description <text>", "Optional project description.").action(action12);
70643
+ parent.command("create").description("Create a new (empty) project.").argument("<name>", "Project name (must be unique).").option("--description <text>", "Optional project description.").action(action13);
70521
70644
  }
70522
70645
 
70523
70646
  // src/cli/commands/project-edit.ts
70524
70647
  init_cli_core();
70525
70648
  init_client();
70526
70649
  var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
70527
- async function action13(target, options) {
70650
+ async function action14(target, options) {
70528
70651
  const update = {};
70529
70652
  if (options.name !== undefined) {
70530
70653
  const n = options.name.trim();
@@ -70551,7 +70674,7 @@ async function action13(target, options) {
70551
70674
  println(c.green(`✓ Updated project "${data.name}" (id: ${data.id}).`));
70552
70675
  }
70553
70676
  function registerProjectEdit(parent) {
70554
- parent.command("edit").description("Rename a project and/or change its description.").argument("<name-or-id>", "Project name (exact match) or UUID.").option("--name <new-name>", "New project name.").option("--description <text>", "New project description.").action(action13);
70677
+ parent.command("edit").description("Rename a project and/or change its description.").argument("<name-or-id>", "Project name (exact match) or UUID.").option("--name <new-name>", "New project name.").option("--description <text>", "New project description.").action(action14);
70555
70678
  }
70556
70679
 
70557
70680
  // src/cli/commands/version-archive.ts
@@ -73974,7 +74097,7 @@ import { homedir as homedir5 } from "node:os";
73974
74097
  import { join as join8 } from "node:path";
73975
74098
 
73976
74099
  // ../../_shared/ef-meta/index.ts
73977
- var EF_VERSION = "0.10.1";
74100
+ var EF_VERSION = "0.11.0";
73978
74101
 
73979
74102
  // src/cli/util/checks.ts
73980
74103
  init_config();
@@ -74539,7 +74662,7 @@ function symbol(status) {
74539
74662
  return cErr.dim("ℹ");
74540
74663
  }
74541
74664
  }
74542
- async function action14(options) {
74665
+ async function action15(options) {
74543
74666
  const useSpinner = !options.json && process.stderr.isTTY;
74544
74667
  const spinner = useSpinner ? ora({ text: "Starting checks…", spinner: "dots", stream: process.stderr }).start() : null;
74545
74668
  const results = await runAllChecks({
@@ -74596,13 +74719,13 @@ async function action14(options) {
74596
74719
  }
74597
74720
  }
74598
74721
  function registerDoctor(program2) {
74599
- program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").action(action14);
74722
+ program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").action(action15);
74600
74723
  }
74601
74724
 
74602
74725
  // src/cli/commands/get-audit-log.ts
74603
74726
  init_cli_core();
74604
74727
  init_client();
74605
- async function action15(options) {
74728
+ async function action16(options) {
74606
74729
  const limit = parsePositiveInt(options.limit, "--limit", 50);
74607
74730
  const client = getClient();
74608
74731
  const data = await client.rpc("cerefox_list_audit_entries", {
@@ -74640,14 +74763,14 @@ async function action15(options) {
74640
74763
  })));
74641
74764
  }
74642
74765
  function registerGetAuditLog(program2) {
74643
- program2.command("get-audit-log").description("Query the audit log with optional filters.").option("-d, --document-id <uuid>", "Filter by document.").option("-a, --author <name>", "Filter by author.").option("-o, --operation <type>", "Filter by operation: create, update-content, update-metadata, delete, restore.").option("--since <iso>", "Lower-bound ISO timestamp.").option("--until <iso>", "Upper-bound ISO timestamp.").option("-l, --limit <n>", "Maximum entries (max 200).", "50").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action15);
74766
+ program2.command("get-audit-log").description("Query the audit log with optional filters.").option("-d, --document-id <uuid>", "Filter by document.").option("-a, --author <name>", "Filter by author.").option("-o, --operation <type>", "Filter by operation: create, update-content, update-metadata, delete, restore.").option("--since <iso>", "Lower-bound ISO timestamp.").option("--until <iso>", "Upper-bound ISO timestamp.").option("-l, --limit <n>", "Maximum entries (max 200).", "50").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action16);
74644
74767
  }
74645
74768
 
74646
74769
  // src/cli/commands/get-doc.ts
74647
74770
  init_cli_core();
74648
74771
  init_cli_core();
74649
74772
  init_client();
74650
- async function action16(documentId, options) {
74773
+ async function action17(documentId, options) {
74651
74774
  const client = getClient();
74652
74775
  const rows = await client.rpc("cerefox_get_document", {
74653
74776
  p_document_id: documentId,
@@ -74673,11 +74796,14 @@ async function action16(documentId, options) {
74673
74796
  }
74674
74797
  println(c.bold(`# ${doc.doc_title}`));
74675
74798
  println(c.dim(`[${doc.document_id}] · chunks: ${doc.chunk_count} · chars: ${doc.total_chars}` + (doc.is_archived ? " · archived" : "") + (doc.version_id ? ` · version: ${doc.version_id}` : "")));
74799
+ if (doc.content_hash) {
74800
+ println(c.dim(`content_hash: ${doc.content_hash}`));
74801
+ }
74676
74802
  println("");
74677
74803
  println(doc.full_content);
74678
74804
  }
74679
74805
  function registerGetDoc(program2) {
74680
- program2.command("get-doc").description("Retrieve the full content of a document by ID.").argument("<document-id>", "UUID of the document.").option("--version-id <uuid>", "Specific archived version (default: current).").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action16);
74806
+ program2.command("get-doc").description("Retrieve the full content of a document by ID.").argument("<document-id>", "UUID of the document.").option("--version-id <uuid>", "Specific archived version (default: current).").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action17);
74681
74807
  }
74682
74808
 
74683
74809
  // src/cli/commands/ingest.ts
@@ -74932,6 +75058,47 @@ async function resolveProjectIds(input, getOrCreateProject) {
74932
75058
  }
74933
75059
  return [];
74934
75060
  }
75061
+ // src/ingestion/types.ts
75062
+ class ConcurrencyConflictError extends Error {
75063
+ documentId;
75064
+ currentHash;
75065
+ constructor(documentId, currentHash, message) {
75066
+ super(message);
75067
+ this.name = "ConcurrencyConflictError";
75068
+ this.documentId = documentId;
75069
+ this.currentHash = currentHash;
75070
+ }
75071
+ }
75072
+
75073
+ class ConcurrencyTokenRequiredError extends Error {
75074
+ constructor(message) {
75075
+ super(message);
75076
+ this.name = "ConcurrencyTokenRequiredError";
75077
+ }
75078
+ }
75079
+ var DEFAULT_PIPELINE_SETTINGS = {
75080
+ maxChunkChars: 4000,
75081
+ minChunkChars: 100,
75082
+ versionRetentionHours: 48,
75083
+ versionCleanupEnabled: true
75084
+ };
75085
+ function loadPipelineSettings() {
75086
+ const env4 = globalThis.process?.env ?? {};
75087
+ const intMin = (raw, def, min) => {
75088
+ if (raw === undefined || raw === "")
75089
+ return def;
75090
+ const n = Number.parseInt(raw, 10);
75091
+ return Number.isNaN(n) || n < min ? def : n;
75092
+ };
75093
+ const bool = (raw, def) => raw === undefined || raw === "" ? def : !/^(false|0|no|off)$/i.test(raw.trim());
75094
+ return {
75095
+ maxChunkChars: intMin(env4.CEREFOX_MAX_CHUNK_CHARS, DEFAULT_PIPELINE_SETTINGS.maxChunkChars, 1),
75096
+ minChunkChars: intMin(env4.CEREFOX_MIN_CHUNK_CHARS, DEFAULT_PIPELINE_SETTINGS.minChunkChars, 0),
75097
+ versionRetentionHours: intMin(env4.CEREFOX_VERSION_RETENTION_HOURS, DEFAULT_PIPELINE_SETTINGS.versionRetentionHours, 0),
75098
+ versionCleanupEnabled: bool(env4.CEREFOX_VERSION_CLEANUP_ENABLED, DEFAULT_PIPELINE_SETTINGS.versionCleanupEnabled)
75099
+ };
75100
+ }
75101
+
74935
75102
  // src/ingestion/client-bridge.ts
74936
75103
  class IngestionDbBridge {
74937
75104
  supabase;
@@ -74984,9 +75151,22 @@ class IngestionDbBridge {
74984
75151
  params.p_retention_hours = args.retentionHours;
74985
75152
  if (args.cleanupEnabled !== undefined)
74986
75153
  params.p_cleanup_enabled = args.cleanupEnabled;
75154
+ if (args.documentId !== null) {
75155
+ params.p_expected_content_hash = args.expectedContentHash ?? null;
75156
+ params.p_last_write_wins = args.lastWriteWins ?? false;
75157
+ }
74987
75158
  const { data, error: error2 } = await this.supabase.rpc("cerefox_ingest_document", params);
74988
- if (error2)
74989
- throw new Error(error2.message ?? JSON.stringify(error2));
75159
+ if (error2) {
75160
+ const msg = error2.message ?? JSON.stringify(error2);
75161
+ if (msg.includes("CEREFOX_CONFLICT")) {
75162
+ const current = msg.match(/current hash ([0-9a-f]{64})/)?.[1] ?? null;
75163
+ throw new ConcurrencyConflictError(args.documentId ?? "", current, msg);
75164
+ }
75165
+ if (msg.includes("CEREFOX_TOKEN_REQUIRED")) {
75166
+ throw new ConcurrencyTokenRequiredError(msg);
75167
+ }
75168
+ throw new Error(msg);
75169
+ }
74990
75170
  if (Array.isArray(data) && data.length > 0) {
74991
75171
  return data[0];
74992
75172
  }
@@ -75091,30 +75271,6 @@ async function fileToMarkdown(filename, data) {
75091
75271
  return data.toString("utf8");
75092
75272
  }
75093
75273
 
75094
- // src/ingestion/types.ts
75095
- var DEFAULT_PIPELINE_SETTINGS = {
75096
- maxChunkChars: 4000,
75097
- minChunkChars: 100,
75098
- versionRetentionHours: 48,
75099
- versionCleanupEnabled: true
75100
- };
75101
- function loadPipelineSettings() {
75102
- const env4 = globalThis.process?.env ?? {};
75103
- const intMin = (raw, def, min) => {
75104
- if (raw === undefined || raw === "")
75105
- return def;
75106
- const n = Number.parseInt(raw, 10);
75107
- return Number.isNaN(n) || n < min ? def : n;
75108
- };
75109
- const bool = (raw, def) => raw === undefined || raw === "" ? def : !/^(false|0|no|off)$/i.test(raw.trim());
75110
- return {
75111
- maxChunkChars: intMin(env4.CEREFOX_MAX_CHUNK_CHARS, DEFAULT_PIPELINE_SETTINGS.maxChunkChars, 1),
75112
- minChunkChars: intMin(env4.CEREFOX_MIN_CHUNK_CHARS, DEFAULT_PIPELINE_SETTINGS.minChunkChars, 0),
75113
- versionRetentionHours: intMin(env4.CEREFOX_VERSION_RETENTION_HOURS, DEFAULT_PIPELINE_SETTINGS.versionRetentionHours, 0),
75114
- versionCleanupEnabled: bool(env4.CEREFOX_VERSION_CLEANUP_ENABLED, DEFAULT_PIPELINE_SETTINGS.versionCleanupEnabled)
75115
- };
75116
- }
75117
-
75118
75274
  // src/ingestion/pipeline.ts
75119
75275
  class IngestionPipeline {
75120
75276
  db;
@@ -75141,7 +75297,9 @@ class IngestionPipeline {
75141
75297
  updateExisting = false,
75142
75298
  documentId,
75143
75299
  author = "unknown",
75144
- authorType = "user"
75300
+ authorType = "user",
75301
+ expectedContentHash,
75302
+ lastWriteWins = false
75145
75303
  } = opts;
75146
75304
  const listFormProvided = projectIds !== undefined && projectIds !== null || projectNames !== undefined && projectNames !== null;
75147
75305
  const getOrCreate = (name) => this.db.getOrCreateProject(name);
@@ -75162,7 +75320,9 @@ class IngestionPipeline {
75162
75320
  projectIds: fullSetResolved,
75163
75321
  metadata,
75164
75322
  author,
75165
- authorType
75323
+ authorType,
75324
+ expectedContentHash,
75325
+ lastWriteWins
75166
75326
  });
75167
75327
  if (!listFormProvided && (projectId || projectName)) {
75168
75328
  const singular = await resolveProjectIds({ projectId, projectName }, getOrCreate);
@@ -75197,7 +75357,9 @@ class IngestionPipeline {
75197
75357
  projectIds: fullSetResolved,
75198
75358
  metadata,
75199
75359
  author,
75200
- authorType
75360
+ authorType,
75361
+ expectedContentHash,
75362
+ lastWriteWins
75201
75363
  });
75202
75364
  if (!listFormProvided && (projectId || projectName)) {
75203
75365
  const singular = await resolveProjectIds({ projectId, projectName }, getOrCreate);
@@ -75283,7 +75445,9 @@ ${c2.content}`);
75283
75445
  projectIds,
75284
75446
  metadata,
75285
75447
  author = "unknown",
75286
- authorType = "user"
75448
+ authorType = "user",
75449
+ expectedContentHash,
75450
+ lastWriteWins = false
75287
75451
  } = opts;
75288
75452
  const existing = await this.db.getDocumentById(documentId);
75289
75453
  if (!existing) {
@@ -75292,6 +75456,9 @@ ${c2.content}`);
75292
75456
  const newHash = contentHash(text);
75293
75457
  const contentUnchanged = newHash === existing.content_hash;
75294
75458
  if (!contentUnchanged) {
75459
+ if (!lastWriteWins && expectedContentHash && expectedContentHash !== existing.content_hash) {
75460
+ throw new ConcurrencyConflictError(documentId, existing.content_hash, `CEREFOX_CONFLICT: document ${documentId} changed since it was read ` + `(expected hash ${expectedContentHash}, current hash ${existing.content_hash}). ` + `Re-read the document, merge your changes, and retry with the new hash.`);
75461
+ }
75295
75462
  const collision = await this.db.getDocumentByHash(newHash);
75296
75463
  if (collision && collision.id !== documentId) {
75297
75464
  throw new Error(`Identical content already exists as document ${JSON.stringify(collision.title)}. ` + "Edit that document or change the content before saving.");
@@ -75392,7 +75559,9 @@ ${c2.content}`);
75392
75559
  authorType,
75393
75560
  sourceLabel: source,
75394
75561
  retentionHours: this.settings.versionRetentionHours,
75395
- cleanupEnabled: this.settings.versionCleanupEnabled
75562
+ cleanupEnabled: this.settings.versionCleanupEnabled,
75563
+ expectedContentHash: expectedContentHash ?? null,
75564
+ lastWriteWins
75396
75565
  });
75397
75566
  let finalProjectIds;
75398
75567
  if (newProjectIds !== null) {
@@ -75455,7 +75624,7 @@ async function readContent(path, paste) {
75455
75624
  const titleFromPath = basename2(path, extname3(path));
75456
75625
  return { content, titleFromPath };
75457
75626
  }
75458
- async function action17(path, options) {
75627
+ async function action18(path, options) {
75459
75628
  const { content, titleFromPath } = await readContent(path, Boolean(options.paste));
75460
75629
  const title = options.title ?? titleFromPath;
75461
75630
  if (!title || title.trim() === "") {
@@ -75497,7 +75666,9 @@ async function action17(path, options) {
75497
75666
  updateExisting: Boolean(options.updateIfExists),
75498
75667
  documentId: options.documentId ?? null,
75499
75668
  author,
75500
- authorType
75669
+ authorType,
75670
+ expectedContentHash: options.expectedContentHash ?? null,
75671
+ lastWriteWins: Boolean(options.lastWriteWins)
75501
75672
  }) : await pipeline.ingestText({
75502
75673
  text: content,
75503
75674
  title,
@@ -75508,7 +75679,9 @@ async function action17(path, options) {
75508
75679
  updateExisting: Boolean(options.updateIfExists),
75509
75680
  documentId: options.documentId ?? null,
75510
75681
  author,
75511
- authorType
75682
+ authorType,
75683
+ expectedContentHash: options.expectedContentHash ?? null,
75684
+ lastWriteWins: Boolean(options.lastWriteWins)
75512
75685
  });
75513
75686
  let verb;
75514
75687
  if (result.action === "created") {
@@ -75527,7 +75700,7 @@ async function action17(path, options) {
75527
75700
  }
75528
75701
  }
75529
75702
  function registerIngest(program2) {
75530
- program2.command("ingest").description("Ingest a file (or stdin paste) into the knowledge base.").argument("[path]", "Path to the file to ingest. Omit when using --paste.").option("--paste", "Read content from stdin instead of a file.").option("-t, --title <title>", "Document title (required with --paste; defaults to filename without extension).").option("-p, --project-name <name>", "Single project membership (non-destructive on update).").option("-P, --project-names <names>", "Comma-separated full project membership set (destructive replace on update).").option("-m, --metadata <json>", "JSON metadata object.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-i, --document-id <uuid>", "Update a specific document by UUID (overrides --update-if-exists).").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").action(action17);
75703
+ program2.command("ingest").description("Ingest a file (or stdin paste) into the knowledge base.").argument("[path]", "Path to the file to ingest. Omit when using --paste.").option("--paste", "Read content from stdin instead of a file.").option("-t, --title <title>", "Document title (required with --paste; defaults to filename without extension).").option("-p, --project-name <name>", "Single project membership (non-destructive on update).").option("-P, --project-names <names>", "Comma-separated full project membership set (destructive replace on update).").option("-m, --metadata <json>", "JSON metadata object.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-i, --document-id <uuid>", "Update a specific document by UUID (overrides --update-if-exists).").option("--expected-content-hash <sha256>", "Optimistic-concurrency token: the content_hash of the version this edit is based on (shown by `document get` / `search`). Required on content updates unless --last-write-wins.").option("--last-write-wins", "Skip the concurrency check and overwrite regardless of concurrent changes (recorded in the audit log).").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").action(action18);
75531
75704
  }
75532
75705
 
75533
75706
  // src/cli/commands/ingest-dir.ts
@@ -75563,7 +75736,7 @@ function walk(dir, extensions) {
75563
75736
  }
75564
75737
  return files;
75565
75738
  }
75566
- async function action18(dir, options) {
75739
+ async function action19(dir, options) {
75567
75740
  const extensions = new Set((options.extensions ?? ".md,.txt").split(",").map((e) => e.trim().toLowerCase()).map((e) => e.startsWith(".") ? e : "." + e).filter((e) => e.length > 0));
75568
75741
  const files = walk(dir, extensions);
75569
75742
  if (files.length === 0) {
@@ -75609,7 +75782,8 @@ async function action18(dir, options) {
75609
75782
  metadata,
75610
75783
  updateExisting: Boolean(options.updateIfExists),
75611
75784
  author,
75612
- authorType
75785
+ authorType,
75786
+ lastWriteWins: true
75613
75787
  });
75614
75788
  outcomes.push({
75615
75789
  file,
@@ -75636,7 +75810,7 @@ async function action18(dir, options) {
75636
75810
  }
75637
75811
  }
75638
75812
  function registerIngestDir(program2) {
75639
- program2.command("ingest-dir").description("Recursively ingest a directory of markdown / text files.").argument("<dir>", "Root directory to walk.").option("-p, --project-name <name>", "Project membership for all ingested docs.").option("-m, --metadata <json>", "JSON metadata applied to every doc.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").option("-e, --extensions <list>", "Comma-separated file extensions to ingest.", ".md,.txt").action(action18);
75813
+ program2.command("ingest-dir").description("Recursively ingest a directory of markdown / text files.").argument("<dir>", "Root directory to walk.").option("-p, --project-name <name>", "Project membership for all ingested docs.").option("-m, --metadata <json>", "JSON metadata applied to every doc.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").option("-e, --extensions <list>", "Comma-separated file extensions to ingest.", ".md,.txt").action(action19);
75640
75814
  }
75641
75815
 
75642
75816
  // src/cli/commands/init.ts
@@ -75966,7 +76140,7 @@ function writeAnswersTo(target, answers) {
75966
76140
  }
75967
76141
  }
75968
76142
  }
75969
- async function action20(options) {
76143
+ async function action21(options) {
75970
76144
  const homeEnv = join10(homedir6(), USER_STATE_DIR_NAME, ".env");
75971
76145
  const cwdEnv = join10(process.cwd(), ".env");
75972
76146
  const explicitDir = (process.env.CEREFOX_CONFIG_DIR ?? "").trim();
@@ -76075,13 +76249,13 @@ async function action20(options) {
76075
76249
  await postWriteLifecycle(target, options);
76076
76250
  }
76077
76251
  function registerInit(program2) {
76078
- program2.command("init").description("Interactive first-run setup (config, schema deploy stub, optional MCP wiring).").option("-c, --config <file>", "Non-interactive mode: read answers from a JSON file.").option("--force", "Overwrite existing configuration without prompting.").option("--skip-schema", "Skip the schema deploy step.").option("--skip-self-docs", "Skip the bundled self-doc ingest.").option("--skip-agent-config", "Skip the optional MCP agent wiring.").action(action20);
76252
+ program2.command("init").description("Interactive first-run setup (config, schema deploy stub, optional MCP wiring).").option("-c, --config <file>", "Non-interactive mode: read answers from a JSON file.").option("--force", "Overwrite existing configuration without prompting.").option("--skip-schema", "Skip the schema deploy step.").option("--skip-self-docs", "Skip the bundled self-doc ingest.").option("--skip-agent-config", "Skip the optional MCP agent wiring.").action(action21);
76079
76253
  }
76080
76254
 
76081
76255
  // src/cli/commands/list-docs.ts
76082
76256
  init_cli_core();
76083
76257
  init_client();
76084
- async function action21(options) {
76258
+ async function action22(options) {
76085
76259
  const deleted = !!options.deleted;
76086
76260
  const limit = parsePositiveInt(options.limit, "--limit", 100);
76087
76261
  const client = getClient();
@@ -76141,13 +76315,13 @@ async function action21(options) {
76141
76315
  }));
76142
76316
  }
76143
76317
  function registerListDocs(program2) {
76144
- program2.command("list-docs").description("List documents in the knowledge base.").option("-p, --project <name>", "Filter to a specific project.").option("-l, --limit <n>", "Maximum docs to return.", "100").option("--deleted", "List soft-deleted (trashed) documents instead of active ones.").option("--json", "Emit machine-readable JSON.").action(action21);
76318
+ program2.command("list-docs").description("List documents in the knowledge base.").option("-p, --project <name>", "Filter to a specific project.").option("-l, --limit <n>", "Maximum docs to return.", "100").option("--deleted", "List soft-deleted (trashed) documents instead of active ones.").option("--json", "Emit machine-readable JSON.").action(action22);
76145
76319
  }
76146
76320
 
76147
76321
  // src/cli/commands/list-metadata-keys.ts
76148
76322
  init_cli_core();
76149
76323
  init_client();
76150
- async function action22(options) {
76324
+ async function action23(options) {
76151
76325
  const client = getClient();
76152
76326
  const data = await client.rpc("cerefox_list_metadata_keys");
76153
76327
  if (data === null) {
@@ -76175,13 +76349,13 @@ async function action22(options) {
76175
76349
  })));
76176
76350
  }
76177
76351
  function registerListMetadataKeys(program2) {
76178
- program2.command("list-metadata-keys").description("List all metadata keys with document counts and example values.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action22);
76352
+ program2.command("list-metadata-keys").description("List all metadata keys with document counts and example values.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action23);
76179
76353
  }
76180
76354
 
76181
76355
  // src/cli/commands/list-projects.ts
76182
76356
  init_cli_core();
76183
76357
  init_client();
76184
- async function action23(options) {
76358
+ async function action24(options) {
76185
76359
  const client = getClient();
76186
76360
  const { data, error: error2 } = await client.raw.from("cerefox_projects").select("id, name, description, created_at").order("name", { ascending: true });
76187
76361
  if (error2) {
@@ -76210,13 +76384,13 @@ async function action23(options) {
76210
76384
  })), "(no projects)");
76211
76385
  }
76212
76386
  function registerListProjects(program2) {
76213
- program2.command("list-projects").description("List all projects in the knowledge base.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action23);
76387
+ program2.command("list-projects").description("List all projects in the knowledge base.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action24);
76214
76388
  }
76215
76389
 
76216
76390
  // src/cli/commands/list-versions.ts
76217
76391
  init_cli_core();
76218
76392
  init_client();
76219
- async function action24(documentId, options) {
76393
+ async function action25(documentId, options) {
76220
76394
  const client = getClient();
76221
76395
  const data = await client.rpc("cerefox_list_document_versions", {
76222
76396
  p_document_id: documentId
@@ -76257,7 +76431,7 @@ async function action24(documentId, options) {
76257
76431
  })));
76258
76432
  }
76259
76433
  function registerListVersions(program2) {
76260
- program2.command("list-versions").description("List archived versions of a document.").argument("<document-id>", "UUID of the document.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action24);
76434
+ program2.command("list-versions").description("List archived versions of a document.").argument("<document-id>", "UUID of the document.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action25);
76261
76435
  }
76262
76436
 
76263
76437
  // src/cli/commands/mcp.ts
@@ -76272,7 +76446,7 @@ function registerMcp(program2) {
76272
76446
  // src/cli/commands/metadata-search.ts
76273
76447
  init_cli_core();
76274
76448
  init_client();
76275
- async function action25(options) {
76449
+ async function action26(options) {
76276
76450
  const metadataFilter = parseJsonObjectArg(options.metadataFilter, "--metadata-filter");
76277
76451
  if (!metadataFilter || Object.keys(metadataFilter).length === 0) {
76278
76452
  throw userError("--metadata-filter is required and must be a non-empty JSON object.", `Example: --metadata-filter '{"type":"decision-log"}'.`);
@@ -76335,7 +76509,7 @@ async function action25(options) {
76335
76509
  }
76336
76510
  }
76337
76511
  function registerMetadataSearch(program2) {
76338
- program2.command("metadata-search").description("Find documents by metadata criteria (no text query).").requiredOption("-f, --metadata-filter <json>", "JSON object; only docs whose metadata contains ALL pairs are returned.").option("-p, --project-name <name>", "Filter to a specific project.").option("--updated-since <iso>", "Only docs updated on/after this ISO timestamp.").option("--created-since <iso>", "Only docs created on/after this ISO timestamp.").option("--include-content", "Include full document text in results.").option("-l, --limit <n>", "Maximum docs to return.", "10").option("--max-bytes <n>", "Response size budget in bytes (with --include-content).", "200000").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action25);
76512
+ program2.command("metadata-search").description("Find documents by metadata criteria (no text query).").requiredOption("-f, --metadata-filter <json>", "JSON object; only docs whose metadata contains ALL pairs are returned.").option("-p, --project-name <name>", "Filter to a specific project.").option("--updated-since <iso>", "Only docs updated on/after this ISO timestamp.").option("--created-since <iso>", "Only docs created on/after this ISO timestamp.").option("--include-content", "Include full document text in results.").option("-l, --limit <n>", "Maximum docs to return.", "10").option("--max-bytes <n>", "Response size budget in bytes (with --include-content).", "200000").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action26);
76339
76513
  }
76340
76514
 
76341
76515
  // src/cli/commands/reindex.ts
@@ -76343,7 +76517,7 @@ init_dist4();
76343
76517
  init_cli_core();
76344
76518
  init_config();
76345
76519
  var DEFAULT_MODEL = "text-embedding-3-small";
76346
- async function action26(options) {
76520
+ async function action27(options) {
76347
76521
  const settings = loadSettings();
76348
76522
  if (!settings.supabaseUrl || !settings.supabaseKey) {
76349
76523
  throw userError("Supabase credentials not configured — run `cerefox init` first.");
@@ -76422,7 +76596,7 @@ ${c2.content}`;
76422
76596
  }
76423
76597
  }
76424
76598
  function registerReindex(program2) {
76425
- program2.command("reindex").description("Re-embed existing document chunks (v0.7+).").option("--all", "Reindex every chunk regardless of embedder.").option("--batch <n>", "Chunks per OpenAI batch call. Capped at 96 internally.", "32").option("--dry-run", "Show counts without re-embedding.").option("-i, --document-id <uuid>", "Limit reindex to a single document.").action(action26);
76599
+ program2.command("reindex").description("Re-embed existing document chunks (v0.7+).").option("--all", "Reindex every chunk regardless of embedder.").option("--batch <n>", "Chunks per OpenAI batch call. Capped at 96 internally.", "32").option("--dry-run", "Show counts without re-embedding.").option("-i, --document-id <uuid>", "Limit reindex to a single document.").action(action27);
76426
76600
  }
76427
76601
 
76428
76602
  // src/cli/commands/restore.ts
@@ -76452,7 +76626,7 @@ function resolveBackupFile(target) {
76452
76626
  }
76453
76627
  return join11(path, candidates[0].name);
76454
76628
  }
76455
- async function action27(target, options) {
76629
+ async function action28(target, options) {
76456
76630
  const file = resolveBackupFile(target);
76457
76631
  let payload;
76458
76632
  try {
@@ -76508,7 +76682,7 @@ async function action27(target, options) {
76508
76682
  }
76509
76683
  }
76510
76684
  function registerRestore(program2) {
76511
- program2.command("restore").description("Restore a JSON-snapshot backup into the knowledge base.").argument("<snapshot>", "Backup file (or directory; most recent is picked) produced by `cerefox backup`.").option("--dry-run", "Print what would be restored without writing.").option("-p, --project-name <name>", "Reserved for future use; currently ignored (project memberships ride along with each doc's metadata).").action(action27);
76685
+ program2.command("restore").description("Restore a JSON-snapshot backup into the knowledge base.").argument("<snapshot>", "Backup file (or directory; most recent is picked) produced by `cerefox backup`.").option("--dry-run", "Print what would be restored without writing.").option("-p, --project-name <name>", "Reserved for future use; currently ignored (project memberships ride along with each doc's metadata).").action(action28);
76512
76686
  }
76513
76687
 
76514
76688
  // src/cli/commands/search.ts
@@ -76533,7 +76707,7 @@ async function embedQuery(query) {
76533
76707
  }
76534
76708
 
76535
76709
  // src/cli/commands/search.ts
76536
- async function action28(query, options) {
76710
+ async function action29(query, options) {
76537
76711
  if (!query || query.trim() === "") {
76538
76712
  throw userError("Empty query.");
76539
76713
  }
@@ -76655,10 +76829,12 @@ async function action28(query, options) {
76655
76829
  println(c.bold(`## ${title}${docId}${score}${counts}${kind}`));
76656
76830
  const bestMatch = doc2.best_chunk_heading_path?.length ? doc2.best_chunk_heading_path.join(" › ") : null;
76657
76831
  const updated = doc2.doc_updated_at ? doc2.doc_updated_at.slice(0, 10) : null;
76658
- if (bestMatch || updated) {
76832
+ const hash = doc2.content_hash ? `hash: ${doc2.content_hash}` : null;
76833
+ if (bestMatch || updated || hash) {
76659
76834
  const bits = [
76660
76835
  bestMatch ? `best match: ${bestMatch}` : null,
76661
- updated ? `updated ${updated}` : null
76836
+ updated ? `updated ${updated}` : null,
76837
+ hash
76662
76838
  ].filter(Boolean);
76663
76839
  println(c.dim(` ${bits.join(" · ")}`));
76664
76840
  }
@@ -76685,7 +76861,7 @@ async function action28(query, options) {
76685
76861
  }
76686
76862
  }
76687
76863
  function registerSearch(program2) {
76688
- program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE or 0.5).").option("--max-bytes <n>", "Response size budget in bytes (default: CEREFOX_MAX_RESPONSE_BYTES or 200000).").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").option("--only-metadata", "List matching docs (id, score, chunks, chars, partial/full) WITHOUT their content — like the web UI's collapsed result list. Grab a [id:…] then `cerefox document get <id>`.").action(action28);
76864
+ program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE or 0.5).").option("--max-bytes <n>", "Response size budget in bytes (default: CEREFOX_MAX_RESPONSE_BYTES or 200000).").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").option("--only-metadata", "List matching docs (id, score, chunks, chars, partial/full) WITHOUT their content — like the web UI's collapsed result list. Grab a [id:…] then `cerefox document get <id>`.").action(action29);
76689
76865
  }
76690
76866
 
76691
76867
  // src/cli/commands/self-update.ts
@@ -76732,7 +76908,7 @@ async function fetchLatestVersion() {
76732
76908
  }
76733
76909
  return body.version;
76734
76910
  }
76735
- async function action29(options) {
76911
+ async function action30(options) {
76736
76912
  let target;
76737
76913
  try {
76738
76914
  target = options.version ?? await fetchLatestVersion();
@@ -76784,7 +76960,7 @@ async function action29(options) {
76784
76960
  }
76785
76961
  function registerSelfUpdate(program2) {
76786
76962
  const desc = "Upgrade Cerefox in place. Alias: `cerefox upgrade`.";
76787
- const declaration = (cmd) => cmd.description(desc).option("--check", "Print current vs latest; do nothing.").option("--yes", "Non-interactive (skip confirmation).").option("--version <version>", "Pin a specific version (e.g. 0.5.1 or 0.6.0-rc.1).").action(action29);
76963
+ const declaration = (cmd) => cmd.description(desc).option("--check", "Print current vs latest; do nothing.").option("--yes", "Non-interactive (skip confirmation).").option("--version <version>", "Pin a specific version (e.g. 0.5.1 or 0.6.0-rc.1).").action(action30);
76788
76964
  declaration(program2.command("self-update"));
76789
76965
  declaration(program2.command("upgrade"));
76790
76966
  }
@@ -76803,7 +76979,7 @@ function symbol2(status) {
76803
76979
  return cErr.dim("ℹ");
76804
76980
  }
76805
76981
  }
76806
- async function action30(options) {
76982
+ async function action31(options) {
76807
76983
  const useSpinner = !options.json && process.stderr.isTTY;
76808
76984
  const spinner = useSpinner ? ora({ text: "Starting checks…", spinner: "dots", stream: process.stderr }).start() : null;
76809
76985
  const results = await runFastChecks({
@@ -76824,7 +77000,7 @@ async function action30(options) {
76824
77000
  }
76825
77001
  }
76826
77002
  function registerStatus(program2) {
76827
- program2.command("status").description("Quick sanity check (fast subset of `cerefox doctor`).").option("--json", "Emit machine-readable JSON.").action(action30);
77003
+ program2.command("status").description("Quick sanity check (fast subset of `cerefox doctor`).").option("--json", "Emit machine-readable JSON.").action(action31);
76828
77004
  }
76829
77005
 
76830
77006
  // src/cli/program.ts
@@ -76833,15 +77009,15 @@ init_sync_self_docs();
76833
77009
  // src/cli/commands/web.ts
76834
77010
  init_cli_core();
76835
77011
 
76836
- // ../../node_modules/.bun/@hono+node-server@2.0.4+1bbe96acb4c5ebf1/node_modules/@hono/node-server/dist/constants-BLSFu_RU.mjs
77012
+ // ../../node_modules/.bun/@hono+node-server@2.0.4+8bc17dcb8a4189a6/node_modules/@hono/node-server/dist/constants-BLSFu_RU.mjs
76837
77013
  var X_ALREADY_SENT = "x-hono-already-sent";
76838
77014
 
76839
- // ../../node_modules/.bun/@hono+node-server@2.0.4+1bbe96acb4c5ebf1/node_modules/@hono/node-server/dist/index.mjs
77015
+ // ../../node_modules/.bun/@hono+node-server@2.0.4+8bc17dcb8a4189a6/node_modules/@hono/node-server/dist/index.mjs
76840
77016
  import { STATUS_CODES, createServer } from "node:http";
76841
77017
  import { Http2ServerRequest, constants } from "node:http2";
76842
77018
  import { Readable } from "node:stream";
76843
77019
 
76844
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/helper/websocket/index.js
77020
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/helper/websocket/index.js
76845
77021
  var WSContext = class {
76846
77022
  #init;
76847
77023
  constructor(init) {
@@ -76889,7 +77065,7 @@ var defineWebSocketHelper = (handler11) => {
76889
77065
  };
76890
77066
  };
76891
77067
 
76892
- // ../../node_modules/.bun/@hono+node-server@2.0.4+1bbe96acb4c5ebf1/node_modules/@hono/node-server/dist/index.mjs
77068
+ // ../../node_modules/.bun/@hono+node-server@2.0.4+8bc17dcb8a4189a6/node_modules/@hono/node-server/dist/index.mjs
76893
77069
  var RequestError = class extends Error {
76894
77070
  constructor(message, options) {
76895
77071
  super(message, options);
@@ -78023,7 +78199,7 @@ var serve = (options, listeningListener) => {
78023
78199
  return server;
78024
78200
  };
78025
78201
 
78026
- // ../../node_modules/.bun/@hono+node-server@2.0.4+1bbe96acb4c5ebf1/node_modules/@hono/node-server/dist/utils/stream.mjs
78202
+ // ../../node_modules/.bun/@hono+node-server@2.0.4+8bc17dcb8a4189a6/node_modules/@hono/node-server/dist/utils/stream.mjs
78027
78203
  import { Readable as Readable2 } from "node:stream";
78028
78204
  import { versions as versions2 } from "node:process";
78029
78205
  var pr54206Applied = () => {
@@ -78089,7 +78265,7 @@ var createStreamBody = (stream, useNativeReadableToWeb = useReadableToWeb) => {
78089
78265
  });
78090
78266
  };
78091
78267
 
78092
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/utils/mime.js
78268
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/mime.js
78093
78269
  var getMimeType = (filename, mimes = baseMimes) => {
78094
78270
  const regexp = /\.([a-zA-Z0-9]+?)$/;
78095
78271
  const match = filename.match(regexp);
@@ -78158,7 +78334,7 @@ var _baseMimes = {
78158
78334
  };
78159
78335
  var baseMimes = _baseMimes;
78160
78336
 
78161
- // ../../node_modules/.bun/@hono+node-server@2.0.4+1bbe96acb4c5ebf1/node_modules/@hono/node-server/dist/serve-static.mjs
78337
+ // ../../node_modules/.bun/@hono+node-server@2.0.4+8bc17dcb8a4189a6/node_modules/@hono/node-server/dist/serve-static.mjs
78162
78338
  import { createReadStream, existsSync as existsSync12, statSync as statSync5 } from "node:fs";
78163
78339
  import { join as join12 } from "node:path";
78164
78340
  var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
@@ -78274,7 +78450,7 @@ import { existsSync as existsSync16 } from "node:fs";
78274
78450
  import { readFileSync as readFileSync15 } from "node:fs";
78275
78451
  import { join as join16 } from "node:path";
78276
78452
 
78277
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/compose.js
78453
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/compose.js
78278
78454
  var compose = (middleware, onError, onNotFound) => {
78279
78455
  return (context, next) => {
78280
78456
  let index = -1;
@@ -78318,10 +78494,10 @@ var compose = (middleware, onError, onNotFound) => {
78318
78494
  };
78319
78495
  };
78320
78496
 
78321
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/request/constants.js
78497
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/request/constants.js
78322
78498
  var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
78323
78499
 
78324
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/utils/body.js
78500
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/body.js
78325
78501
  var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
78326
78502
  const { all = false, dot = false } = options;
78327
78503
  const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
@@ -78392,7 +78568,7 @@ var handleParsingNestedValues = (form, key, value) => {
78392
78568
  });
78393
78569
  };
78394
78570
 
78395
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/utils/url.js
78571
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/url.js
78396
78572
  var splitPath = (path) => {
78397
78573
  const paths = path.split("/");
78398
78574
  if (paths[0] === "") {
@@ -78592,7 +78768,7 @@ var getQueryParams = (url, key) => {
78592
78768
  };
78593
78769
  var decodeURIComponent_ = decodeURIComponent;
78594
78770
 
78595
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/request.js
78771
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/request.js
78596
78772
  var tryDecodeURIComponent = (str) => tryDecode2(str, decodeURIComponent_);
78597
78773
  var HonoRequest = class {
78598
78774
  raw;
@@ -78706,7 +78882,7 @@ var HonoRequest = class {
78706
78882
  }
78707
78883
  };
78708
78884
 
78709
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/utils/html.js
78885
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/html.js
78710
78886
  var HtmlEscapedCallbackPhase = {
78711
78887
  Stringify: 1,
78712
78888
  BeforeStream: 2,
@@ -78744,7 +78920,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer2) =>
78744
78920
  }
78745
78921
  };
78746
78922
 
78747
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/context.js
78923
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/context.js
78748
78924
  var TEXT_PLAIN = "text/plain; charset=UTF-8";
78749
78925
  var setDefaultContentType = (contentType2, headers) => {
78750
78926
  return {
@@ -78911,7 +79087,7 @@ var Context = class {
78911
79087
  };
78912
79088
  };
78913
79089
 
78914
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/router.js
79090
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router.js
78915
79091
  var METHOD_NAME_ALL = "ALL";
78916
79092
  var METHOD_NAME_ALL_LOWERCASE = "all";
78917
79093
  var METHODS = ["get", "post", "put", "delete", "options", "patch"];
@@ -78919,10 +79095,10 @@ var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is
78919
79095
  var UnsupportedPathError = class extends Error {
78920
79096
  };
78921
79097
 
78922
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/utils/constants.js
79098
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/constants.js
78923
79099
  var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
78924
79100
 
78925
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/hono-base.js
79101
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/hono-base.js
78926
79102
  var notFoundHandler = (c2) => {
78927
79103
  return c2.text("404 Not Found", 404);
78928
79104
  };
@@ -79146,7 +79322,7 @@ var Hono = class _Hono {
79146
79322
  };
79147
79323
  };
79148
79324
 
79149
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/router/reg-exp-router/matcher.js
79325
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/matcher.js
79150
79326
  var emptyParam = [];
79151
79327
  function match(method, path) {
79152
79328
  const matchers = this.buildAllMatchers();
@@ -79167,7 +79343,7 @@ function match(method, path) {
79167
79343
  return match2(method, path);
79168
79344
  }
79169
79345
 
79170
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/router/reg-exp-router/node.js
79346
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/node.js
79171
79347
  var LABEL_REG_EXP_STR = "[^/]+";
79172
79348
  var ONLY_WILDCARD_REG_EXP_STR = ".*";
79173
79349
  var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
@@ -79271,7 +79447,7 @@ var Node = class _Node {
79271
79447
  }
79272
79448
  };
79273
79449
 
79274
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/router/reg-exp-router/trie.js
79450
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/trie.js
79275
79451
  var Trie = class {
79276
79452
  #context = { varIndex: 0 };
79277
79453
  #root = new Node;
@@ -79327,7 +79503,7 @@ var Trie = class {
79327
79503
  }
79328
79504
  };
79329
79505
 
79330
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/router/reg-exp-router/router.js
79506
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/router.js
79331
79507
  var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
79332
79508
  var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
79333
79509
  function buildWildcardRegExp(path) {
@@ -79492,7 +79668,7 @@ var RegExpRouter = class {
79492
79668
  }
79493
79669
  };
79494
79670
 
79495
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/router/reg-exp-router/prepared-router.js
79671
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/prepared-router.js
79496
79672
  var PreparedRegExpRouter = class {
79497
79673
  name = "PreparedRegExpRouter";
79498
79674
  #matchers;
@@ -79564,7 +79740,7 @@ var PreparedRegExpRouter = class {
79564
79740
  match = match;
79565
79741
  };
79566
79742
 
79567
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/router/smart-router/router.js
79743
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/smart-router/router.js
79568
79744
  var SmartRouter = class {
79569
79745
  name = "SmartRouter";
79570
79746
  #routers = [];
@@ -79619,7 +79795,7 @@ var SmartRouter = class {
79619
79795
  }
79620
79796
  };
79621
79797
 
79622
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/router/trie-router/node.js
79798
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/trie-router/node.js
79623
79799
  var emptyParams = /* @__PURE__ */ Object.create(null);
79624
79800
  var hasChildren = (children) => {
79625
79801
  for (const _ in children) {
@@ -79788,7 +79964,7 @@ var Node2 = class _Node2 {
79788
79964
  }
79789
79965
  };
79790
79966
 
79791
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/router/trie-router/router.js
79967
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/trie-router/router.js
79792
79968
  var TrieRouter = class {
79793
79969
  name = "TrieRouter";
79794
79970
  #node;
@@ -79810,7 +79986,7 @@ var TrieRouter = class {
79810
79986
  }
79811
79987
  };
79812
79988
 
79813
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/hono.js
79989
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/hono.js
79814
79990
  var Hono2 = class extends Hono {
79815
79991
  constructor(options = {}) {
79816
79992
  super(options);
@@ -79820,7 +79996,7 @@ var Hono2 = class extends Hono {
79820
79996
  }
79821
79997
  };
79822
79998
 
79823
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/utils/color.js
79999
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/color.js
79824
80000
  function getColorEnabled() {
79825
80001
  const { process: process10, Deno: Deno2 } = globalThis;
79826
80002
  const isNoColor = typeof Deno2?.noColor === "boolean" ? Deno2.noColor : process10 !== undefined ? "NO_COLOR" in process10?.env : false;
@@ -79839,7 +80015,7 @@ async function getColorEnabledAsync() {
79839
80015
  return !isNoColor;
79840
80016
  }
79841
80017
 
79842
- // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/middleware/logger/index.js
80018
+ // ../../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/middleware/logger/index.js
79843
80019
  var humanize = (times) => {
79844
80020
  const [delimiter, separator] = [",", "."];
79845
80021
  const orderTimes = times.map((v) => v.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1" + delimiter));
@@ -80728,6 +80904,7 @@ function registerDocumentReadRoutes(app, ctx) {
80728
80904
  created_at: meta ? meta.created_at ?? null : null,
80729
80905
  updated_at: meta ? meta.updated_at ?? null : null,
80730
80906
  deleted_at: meta ? meta.deleted_at ?? null : null,
80907
+ content_hash: meta ? meta.content_hash ?? null : null,
80731
80908
  versions: versions3.map((v) => ({
80732
80909
  version_id: v.version_id,
80733
80910
  version_number: v.version_number,
@@ -80890,10 +81067,26 @@ function registerDocumentWriteRoutes(app, ctx) {
80890
81067
  projectIds: Array.isArray(body.project_ids) ? body.project_ids : undefined,
80891
81068
  metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
80892
81069
  author: "web-ui",
80893
- authorType: "user"
81070
+ authorType: "user",
81071
+ expectedContentHash: typeof body.expected_content_hash === "string" ? body.expected_content_hash : null
80894
81072
  });
80895
81073
  return c2.json({ success: true, reindexed: result.reindexed });
80896
81074
  } catch (err) {
81075
+ if (err instanceof ConcurrencyConflictError) {
81076
+ return c2.json({
81077
+ success: false,
81078
+ error: "conflict",
81079
+ message: "This document changed while you were editing it (another writer saved a newer version). Open it again in a new tab, merge your changes, and save from there.",
81080
+ current_hash: err.currentHash
81081
+ }, 409);
81082
+ }
81083
+ if (err instanceof ConcurrencyTokenRequiredError) {
81084
+ return c2.json({
81085
+ success: false,
81086
+ error: "expected_content_hash required",
81087
+ message: err.message
81088
+ }, 400);
81089
+ }
80897
81090
  return c2.json({
80898
81091
  success: false,
80899
81092
  error: err instanceof Error ? err.message : String(err)
@@ -82048,6 +82241,7 @@ Learn more:
82048
82241
  moveInto(document2, registerDeleteDoc, "delete");
82049
82242
  registerDocumentRestore(document2);
82050
82243
  registerDocumentEdit(document2);
82244
+ registerDocumentSetProjects(document2);
82051
82245
  moveInto(document2, registerIngest, "ingest");
82052
82246
  moveInto(document2, registerIngestDir, "ingest-dir");
82053
82247
  const documentVersion = document2.command("version").description("Document versions: list, archive, unarchive.");