@moldea.ai/core 3.1.0 → 4.0.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.
Files changed (32) hide show
  1. package/README.md +8 -4
  2. package/dist/adapter-repository/index.d.ts.map +1 -1
  3. package/dist/adapter-validation/index.d.ts.map +1 -1
  4. package/dist/adapter.js +7 -4
  5. package/dist/adapter.js.map +1 -1
  6. package/dist/canonical-content-page/index.d.ts.map +1 -1
  7. package/dist/chunks/{exceptions-DWsZTpqs.js → exceptions-C_KfCIjE.js} +7 -1
  8. package/dist/chunks/exceptions-C_KfCIjE.js.map +1 -0
  9. package/dist/constants/index.d.ts.map +1 -1
  10. package/dist/contracts/index.d.ts +31 -5
  11. package/dist/contracts/index.d.ts.map +1 -1
  12. package/dist/diagnostic-utilities/index.d.ts.map +1 -1
  13. package/dist/exceptions/index.d.ts +20 -4
  14. package/dist/exceptions/index.d.ts.map +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +223 -73
  18. package/dist/index.js.map +1 -1
  19. package/dist/options/index.d.ts.map +1 -1
  20. package/dist/project-inspection/index.d.ts +5 -0
  21. package/dist/project-inspection/index.d.ts.map +1 -0
  22. package/dist/project-validation/index.d.ts +2 -0
  23. package/dist/project-validation/index.d.ts.map +1 -1
  24. package/dist/repository-inspection-session/index.d.ts +8 -0
  25. package/dist/repository-inspection-session/index.d.ts.map +1 -1
  26. package/dist/scope-matching/matching.d.ts.map +1 -1
  27. package/dist/scope-matching/validations.d.ts.map +1 -1
  28. package/dist/text/index.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/dist/chunks/exceptions-DWsZTpqs.js.map +0 -1
  31. package/dist/project-inspection-page/index.d.ts +0 -5
  32. package/dist/project-inspection-page/index.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as CoreOperationException, t as CoreConfigurationException } from "./chunks/exceptions-DWsZTpqs.js";
1
+ import { n as CoreOperationException, t as CoreConfigurationException } from "./chunks/exceptions-C_KfCIjE.js";
2
2
  import { REPOSITORY_ROOT, RepositoryPathException, RepositorySourceException, compareRepositoryPaths, createRepositoryIdentity, isRepositoryPath, parseRepositoryPath } from "@moldea.ai/repository";
3
3
  import { Parser, isAlias, isMap, isScalar, isSeq, parseAllDocuments } from "yaml";
4
4
  //#region src/constants/index.ts
@@ -22,6 +22,7 @@ var DEFAULT_CORE_RESOURCE_LIMITS = Object.freeze({
22
22
  maxEvidence: 1e4,
23
23
  maxFileBytes: 8388608,
24
24
  maxManifestBytes: 2097152,
25
+ maxRetainedBytes: 536870912,
25
26
  maxTotalBytesRead: 134217728
26
27
  });
27
28
  //#endregion
@@ -142,6 +143,9 @@ var readCanonicalContentPage = async (input, options) => {
142
143
  if (entry.byteLength > options.limits.maxFileBytes) throw new CoreOperationException({
143
144
  code: "RESOURCE_LIMIT_EXCEEDED",
144
145
  limit: "maxFileBytes",
146
+ limitMaximum: options.limits.maxFileBytes,
147
+ nextAction: "reduce-input-or-increase-limit",
148
+ observedUsage: entry.byteLength,
145
149
  operation: "read-canonical-content-page"
146
150
  });
147
151
  if (input.offset > entry.byteLength) return invalidArgument$4();
@@ -443,6 +447,9 @@ var registerCoreDiagnosticCandidates = (limits, amount, operation, fallbackCount
443
447
  ...adapterId === void 0 ? {} : { adapterId },
444
448
  code: "RESOURCE_LIMIT_EXCEEDED",
445
449
  limit: "maxDiagnostics",
450
+ limitMaximum: limits.maxDiagnostics,
451
+ nextAction: "reduce-input-or-increase-limit",
452
+ observedUsage: currentCount + amount,
446
453
  operation
447
454
  });
448
455
  if (budget !== void 0) budget.count += amount;
@@ -788,6 +795,7 @@ var RESOURCE_LIMIT_KEYS = [
788
795
  "maxTotalBytesRead",
789
796
  "maxFileBytes",
790
797
  "maxManifestBytes",
798
+ "maxRetainedBytes",
791
799
  "maxDiagnostics",
792
800
  "maxEvidence"
793
801
  ];
@@ -964,7 +972,7 @@ var createSourceLocator = (value) => {
964
972
  };
965
973
  //#endregion
966
974
  //#region src/text/index.ts
967
- var encoder$1 = new TextEncoder();
975
+ var encoder$2 = new TextEncoder();
968
976
  var decoder = new TextDecoder("utf-8", {
969
977
  fatal: true,
970
978
  ignoreBOM: true
@@ -1053,6 +1061,9 @@ var enforceFileLimit = (byteLength, limits, operation, limit) => {
1053
1061
  throw new CoreOperationException({
1054
1062
  code: "RESOURCE_LIMIT_EXCEEDED",
1055
1063
  limit,
1064
+ limitMaximum: limits[limit],
1065
+ nextAction: "reduce-input-or-increase-limit",
1066
+ observedUsage: byteLength,
1056
1067
  operation
1057
1068
  });
1058
1069
  };
@@ -1135,7 +1146,7 @@ var normalizeTextDocument = (input, limits, operation, limit = "maxFileBytes") =
1135
1146
  diagnostics: [],
1136
1147
  text: freezeRecursively({
1137
1148
  scalarLength: countUnicodeScalars(value),
1138
- utf8ByteLength: encoder$1.encode(value).byteLength,
1149
+ utf8ByteLength: encoder$2.encode(value).byteLength,
1139
1150
  value
1140
1151
  }),
1141
1152
  valid: true
@@ -1152,7 +1163,7 @@ var toLowercaseHex = (bytes) => {
1152
1163
  * @returns A promise resolving to the canonical lowercase SHA-256 digest.
1153
1164
  */
1154
1165
  var calculateNormalizedTextDigest = async (text) => {
1155
- const bytes = encoder$1.encode(text.value);
1166
+ const bytes = encoder$2.encode(text.value);
1156
1167
  return `sha256:${toLowercaseHex(new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", bytes)))}`;
1157
1168
  };
1158
1169
  /**
@@ -2472,6 +2483,9 @@ var registerRawOutput = (outputCounts, key, amount, context) => {
2472
2483
  adapterId: context.adapterId,
2473
2484
  code: "RESOURCE_LIMIT_EXCEEDED",
2474
2485
  limit,
2486
+ limitMaximum: context.limits[limit],
2487
+ nextAction: "reduce-input-or-increase-limit",
2488
+ observedUsage: outputCounts[key] + amount,
2475
2489
  operation: "validate-adapter"
2476
2490
  });
2477
2491
  outputCounts[key] += amount;
@@ -2837,6 +2851,10 @@ var inspectRuntimeAdapters = async (project, session, options, signal) => {
2837
2851
  var MANIFEST_PATH$3 = parseRepositoryPath("/moldea/moldea.yaml");
2838
2852
  var SOURCE_PAGE_ENTRIES = 256;
2839
2853
  var SOURCE_PAGE_BYTES = 65536;
2854
+ var CANONICAL_BYTE_RETENTION_MULTIPLIER = 4;
2855
+ var RETAINED_PATH_FIXED_BYTES = 64;
2856
+ var RETAINED_PATH_BYTE_MULTIPLIER = 2;
2857
+ var encoder$1 = new TextEncoder();
2840
2858
  var invalidSourceData$3 = (operation, path) => {
2841
2859
  throw new RepositorySourceException({
2842
2860
  code: "INVALID_SOURCE_DATA",
@@ -2869,10 +2887,13 @@ var isStrictDescendant = (path, prefix) => {
2869
2887
  const descendantPrefix = prefix === REPOSITORY_ROOT ? REPOSITORY_ROOT : `${prefix}/`;
2870
2888
  return path.startsWith(descendantPrefix);
2871
2889
  };
2872
- var throwResourceLimitExceeded = (limit) => {
2890
+ var throwResourceLimitExceeded = (limit, limitMaximum, observedUsage) => {
2873
2891
  throw new CoreOperationException({
2874
2892
  code: "RESOURCE_LIMIT_EXCEEDED",
2875
2893
  limit,
2894
+ limitMaximum,
2895
+ nextAction: "reduce-input-or-increase-limit",
2896
+ observedUsage,
2876
2897
  operation: "validate-project"
2877
2898
  });
2878
2899
  };
@@ -2891,6 +2912,9 @@ var createSourceOptions = (sessionSignal, operationSignal) => {
2891
2912
  */
2892
2913
  var createRepositoryInspectionSession = (repository, limits, signal) => {
2893
2914
  const seenPaths = /* @__PURE__ */ new Set();
2915
+ let canonicalBytes = 0;
2916
+ let peakRetainedBytes = 0;
2917
+ let retainedBytes = 0;
2894
2918
  let totalBytesRead = 0;
2895
2919
  const adapterLimits = Object.freeze({
2896
2920
  maxEntries: limits.maxEntries,
@@ -2906,9 +2930,19 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2906
2930
  operation: "validate-project"
2907
2931
  });
2908
2932
  };
2933
+ const reserveRetainedBytes = (byteLength) => {
2934
+ if (!Number.isSafeInteger(byteLength) || byteLength < 0 || byteLength > limits.maxRetainedBytes - retainedBytes) return throwResourceLimitExceeded("maxRetainedBytes", limits.maxRetainedBytes, retainedBytes + byteLength);
2935
+ retainedBytes += byteLength;
2936
+ peakRetainedBytes = Math.max(peakRetainedBytes, retainedBytes);
2937
+ };
2938
+ const observeTransientBytes = (byteLength) => {
2939
+ if (!Number.isSafeInteger(byteLength) || byteLength < 0 || byteLength > limits.maxRetainedBytes - retainedBytes) return throwResourceLimitExceeded("maxRetainedBytes", limits.maxRetainedBytes, retainedBytes + byteLength);
2940
+ peakRetainedBytes = Math.max(peakRetainedBytes, retainedBytes + byteLength);
2941
+ };
2909
2942
  const registerPath = (path) => {
2910
2943
  if (path === REPOSITORY_ROOT || seenPaths.has(path)) return;
2911
- if (seenPaths.size >= limits.maxEntries) return throwResourceLimitExceeded("maxEntries");
2944
+ if (seenPaths.size >= limits.maxEntries) return throwResourceLimitExceeded("maxEntries", limits.maxEntries, seenPaths.size + 1);
2945
+ reserveRetainedBytes(RETAINED_PATH_FIXED_BYTES + encoder$1.encode(path).byteLength * RETAINED_PATH_BYTE_MULTIPLIER);
2912
2946
  seenPaths.add(path);
2913
2947
  };
2914
2948
  const throwIfSignalAborted = (operationSignal) => {
@@ -2917,7 +2951,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2917
2951
  };
2918
2952
  throwIfSignalAborted();
2919
2953
  const reserveReadBytes = (byteLength) => {
2920
- if (byteLength > limits.maxTotalBytesRead - totalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead");
2954
+ if (byteLength > limits.maxTotalBytesRead - totalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytesRead + byteLength);
2921
2955
  totalBytesRead += byteLength;
2922
2956
  };
2923
2957
  const getEntry = async (path, options) => {
@@ -2964,15 +2998,29 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2964
2998
  };
2965
2999
  const loadFile = async (path, operationSignal) => {
2966
3000
  const fileLimit = path === MANIFEST_PATH$3 ? "maxManifestBytes" : "maxFileBytes";
3001
+ const availableRetainedBytes = limits.maxRetainedBytes - retainedBytes;
3002
+ if (availableRetainedBytes < 1) return throwResourceLimitExceeded("maxRetainedBytes", limits.maxRetainedBytes, retainedBytes + 1);
3003
+ const firstPageMaximumBytes = Math.min(SOURCE_PAGE_BYTES, Math.max(1, limits[fileLimit]), availableRetainedBytes);
3004
+ observeTransientBytes(firstPageMaximumBytes);
2967
3005
  const firstPage = await repository.readFilePage(path, {
2968
- maxBytes: Math.min(SOURCE_PAGE_BYTES, Math.max(1, limits[fileLimit])),
3006
+ maxBytes: firstPageMaximumBytes,
2969
3007
  offset: 0,
2970
3008
  ...createSourceOptions(signal, operationSignal) ?? {}
2971
3009
  });
2972
3010
  throwIfSignalAborted(operationSignal);
2973
3011
  if (firstPage.snapshot.id !== repository.snapshot.id || firstPage.offset !== 0 || !(firstPage.bytes instanceof Uint8Array) || !Number.isSafeInteger(firstPage.totalBytes) || firstPage.totalBytes < 0 || firstPage.bytes.byteLength > SOURCE_PAGE_BYTES || firstPage.bytes.byteLength > firstPage.totalBytes || firstPage.bytes.byteLength === 0 && !firstPage.isComplete) return invalidSourceData$3("read-file-page", path);
2974
- if (firstPage.totalBytes > limits[fileLimit]) return throwResourceLimitExceeded(fileLimit);
3012
+ if (firstPage.totalBytes > limits[fileLimit]) return throwResourceLimitExceeded(fileLimit, limits[fileLimit], firstPage.totalBytes);
3013
+ const firstPageBytes = firstPage.bytes.byteLength;
3014
+ const retainedBytesAvailableForCanonicalContent = limits.maxRetainedBytes - retainedBytes - firstPageBytes;
3015
+ if (retainedBytesAvailableForCanonicalContent < 0 || firstPage.totalBytes > Math.floor(retainedBytesAvailableForCanonicalContent / CANONICAL_BYTE_RETENTION_MULTIPLIER)) {
3016
+ const projectedUsage = retainedBytes + firstPageBytes + firstPage.totalBytes * CANONICAL_BYTE_RETENTION_MULTIPLIER;
3017
+ return throwResourceLimitExceeded("maxRetainedBytes", limits.maxRetainedBytes, Number.isSafeInteger(projectedUsage) ? projectedUsage : Number.MAX_SAFE_INTEGER);
3018
+ }
2975
3019
  reserveReadBytes(firstPage.totalBytes);
3020
+ const canonicalRetainedBytes = firstPage.totalBytes * CANONICAL_BYTE_RETENTION_MULTIPLIER;
3021
+ observeTransientBytes(canonicalRetainedBytes + firstPageBytes);
3022
+ reserveRetainedBytes(canonicalRetainedBytes);
3023
+ canonicalBytes += firstPage.totalBytes;
2976
3024
  const content = new Uint8Array(firstPage.totalBytes);
2977
3025
  let offset = 0;
2978
3026
  let page = firstPage;
@@ -3033,7 +3081,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
3033
3081
  const listAdapterEntriesPage = async (options) => {
3034
3082
  const prefix = options.prefix === void 0 ? REPOSITORY_ROOT : parseRepositoryPath(options.prefix);
3035
3083
  registerPath(prefix);
3036
- if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1 || options.maxEntries > adapterLimits.maxPageEntries) return throwResourceLimitExceeded("maxEntries");
3084
+ if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1 || options.maxEntries > adapterLimits.maxPageEntries) return throwResourceLimitExceeded("maxEntries", adapterLimits.maxPageEntries, options.maxEntries);
3037
3085
  throwIfSignalAborted(options.signal);
3038
3086
  const page = await repository.listEntriesPage({
3039
3087
  ...options.cursor === void 0 ? {} : { cursor: options.cursor },
@@ -3064,7 +3112,8 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
3064
3112
  throwIfSignalAborted(options.signal);
3065
3113
  registerPath(parsedPath);
3066
3114
  const reservedBytes = Math.min(options.maxBytes, limits.maxTotalBytesRead - totalBytesRead);
3067
- if (reservedBytes < 1) return throwResourceLimitExceeded("maxTotalBytesRead");
3115
+ if (reservedBytes < 1) return throwResourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytesRead + 1);
3116
+ observeTransientBytes(reservedBytes);
3068
3117
  reserveReadBytes(reservedBytes);
3069
3118
  let page;
3070
3119
  try {
@@ -3101,6 +3150,12 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
3101
3150
  });
3102
3151
  return Object.freeze({
3103
3152
  adapterRepository,
3153
+ getResourceUsage: () => Object.freeze({
3154
+ canonicalBytes,
3155
+ peakRetainedBytes,
3156
+ retainedBytes,
3157
+ totalBytesRead
3158
+ }),
3104
3159
  reader: Object.freeze({
3105
3160
  getEntry,
3106
3161
  iterateEntries,
@@ -4846,6 +4901,7 @@ var validateProjectState = async (input, options) => {
4846
4901
  }, operationOptions);
4847
4902
  if (universal.project === null) return freezeRecursively({
4848
4903
  project: null,
4904
+ resourceUsage: session.getResourceUsage(),
4849
4905
  result: {
4850
4906
  diagnostics: universal.diagnostics,
4851
4907
  evidence: [],
@@ -4859,6 +4915,7 @@ var validateProjectState = async (input, options) => {
4859
4915
  const availabilityDiagnostics = validateRuntimeAdapterAvailability(universal.runtimeLocations, universal.project.formatVersion, operationOptions);
4860
4916
  if (availabilityDiagnostics.length > 0) return freezeRecursively({
4861
4917
  project: universal.project,
4918
+ resourceUsage: session.getResourceUsage(),
4862
4919
  result: {
4863
4920
  diagnostics: availabilityDiagnostics,
4864
4921
  evidence: [],
@@ -4872,6 +4929,7 @@ var validateProjectState = async (input, options) => {
4872
4929
  const valid = adapterInspection.diagnostics.length === 0;
4873
4930
  return freezeRecursively({
4874
4931
  project: universal.project,
4932
+ resourceUsage: session.getResourceUsage(),
4875
4933
  result: {
4876
4934
  diagnostics: adapterInspection.diagnostics,
4877
4935
  evidence: adapterInspection.evidence,
@@ -4885,16 +4943,30 @@ var validateProjectState = async (input, options) => {
4885
4943
  /** Validates one project without returning canonical document bodies. */
4886
4944
  var validateProject = async (input, options) => (await validateProjectState(input, options)).result;
4887
4945
  //#endregion
4888
- //#region src/project-inspection-page/index.ts
4889
- var CURSOR_PREFIX = "core3";
4946
+ //#region src/project-inspection/index.ts
4947
+ var CURSOR_PREFIX = "core4";
4948
+ var PREPARED_FIXED_BYTES = 512;
4949
+ var PREPARED_ITEM_FIXED_BYTES = 64;
4950
+ var PREPARED_TEXT_BYTE_MULTIPLIER = 2;
4951
+ var PREPARED_VIEW_REFERENCE_BYTES = 8;
4890
4952
  var encoder = new TextEncoder();
4891
4953
  var invalidArgument$1 = () => {
4892
4954
  throw new CoreOperationException({
4893
4955
  code: "INVALID_ARGUMENT",
4894
- operation: "inspect-project-page"
4956
+ operation: "create-project-inspection"
4957
+ });
4958
+ };
4959
+ var resourceLimitExceeded$1 = (limitMaximum, observedUsage) => {
4960
+ throw new CoreOperationException({
4961
+ code: "RESOURCE_LIMIT_EXCEEDED",
4962
+ limit: "maxRetainedBytes",
4963
+ limitMaximum,
4964
+ nextAction: "reduce-input-or-increase-limit",
4965
+ observedUsage,
4966
+ operation: "create-project-inspection"
4895
4967
  });
4896
4968
  };
4897
- /** Serializes the closed inspection records with recursively sorted object keys. */
4969
+ /** Serializes content-free inspection state with recursively sorted object keys. */
4898
4970
  var serializeDeterministically = (candidate) => {
4899
4971
  if (candidate === null || typeof candidate !== "object") {
4900
4972
  const serialized = JSON.stringify(candidate);
@@ -4933,7 +5005,7 @@ var createItemBaseKey = (item) => {
4933
5005
  createIdentity([serializeDeterministically(item.evidence)])
4934
5006
  ].join(":");
4935
5007
  };
4936
- /** Assigns stable unique keys without relying on collection offsets. */
5008
+ /** Assigns stable unique keys once without relying on collection offsets. */
4937
5009
  var createKeyedItems = (items) => {
4938
5010
  const sorted = items.map((item) => ({
4939
5011
  baseKey: createItemBaseKey(item),
@@ -4964,7 +5036,7 @@ var encodeCursor = (inspectionDigest, view, lastKey) => [
4964
5036
  ].join(":");
4965
5037
  var decodeCursor = (cursor, inspectionDigest, view) => {
4966
5038
  if (cursor === void 0) return null;
4967
- const match = /^core3:(all|diagnostics|evidence|metadata):([^:]+):(sha256:[0-9a-f]{64}):(sha256:[0-9a-f]{64})$/u.exec(cursor);
5039
+ const match = /^core4:(all|diagnostics|evidence|metadata):([^:]+):(sha256:[0-9a-f]{64}):(sha256:[0-9a-f]{64})$/u.exec(cursor);
4968
5040
  if (match === null || match[1] !== view || match[3] !== inspectionDigest) return invalidArgument$1();
4969
5041
  let lastKey;
4970
5042
  try {
@@ -4975,20 +5047,112 @@ var decodeCursor = (cursor, inspectionDigest, view) => {
4975
5047
  if (lastKey.length === 0 || match[4] !== createCursorChecksum(inspectionDigest, lastKey, view)) return invalidArgument$1();
4976
5048
  return lastKey;
4977
5049
  };
4978
- var selectViewItems = (items, view) => view === "all" ? items : items.filter(({ item }) => {
4979
- if (view === "diagnostics") return item.kind === "diagnostic";
4980
- return view === "metadata" ? item.kind === "metadata" || item.kind === "agent" : item.kind === view;
5050
+ var createViews = (items) => freezeRecursively({
5051
+ all: items,
5052
+ diagnostics: items.filter(({ item }) => item.kind === "diagnostic"),
5053
+ evidence: items.filter(({ item }) => item.kind === "evidence"),
5054
+ metadata: items.filter(({ item }) => item.kind === "metadata" || item.kind === "agent")
4981
5055
  });
4982
- /** Returns one bounded content-free view over a completely validated project snapshot. */
4983
- var inspectProjectPage = async (input, options) => {
4984
- if (typeof input !== "object" || input === null || Reflect.ownKeys(input).some((key) => key !== "cursor" && key !== "maxItems" && key !== "repository" && key !== "signal" && key !== "view") || !Number.isSafeInteger(input.maxItems) || input.maxItems < 1 || input.maxItems > options.limits.maxEntries || input.cursor !== void 0 && typeof input.cursor !== "string" || input.view !== "all" && input.view !== "diagnostics" && input.view !== "evidence" && input.view !== "metadata") return invalidArgument$1();
4985
- const state = await validateProjectState({
4986
- repository: input.repository,
4987
- ...input.signal === void 0 ? {} : { signal: input.signal }
4988
- }, options);
5056
+ var addRetainedBytes = (current, additional, retainedBeforePreparation, maximum) => {
5057
+ const projectedUsage = retainedBeforePreparation + current + additional;
5058
+ if (!Number.isSafeInteger(additional) || additional < 0 || !Number.isSafeInteger(projectedUsage) || projectedUsage > maximum) return resourceLimitExceeded$1(maximum, Number.isSafeInteger(projectedUsage) ? projectedUsage : Number.MAX_SAFE_INTEGER);
5059
+ return current + additional;
5060
+ };
5061
+ /** Calculates deterministic logical bytes retained by prepared records and view indexes. */
5062
+ var calculatePreparedBytes = (items, views, header, retainedBeforePreparation, maximum) => {
5063
+ let bytes = addRetainedBytes(0, PREPARED_FIXED_BYTES, retainedBeforePreparation, maximum);
5064
+ for (const { item, key } of items) {
5065
+ const encodedBytes = encoder.encode(key).byteLength + encoder.encode(serializeDeterministically(item)).byteLength;
5066
+ bytes = addRetainedBytes(bytes, PREPARED_ITEM_FIXED_BYTES + encodedBytes * PREPARED_TEXT_BYTE_MULTIPLIER, retainedBeforePreparation, maximum);
5067
+ }
5068
+ bytes = addRetainedBytes(bytes, encoder.encode(serializeDeterministically(header)).byteLength * PREPARED_TEXT_BYTE_MULTIPLIER, retainedBeforePreparation, maximum);
5069
+ bytes = addRetainedBytes(bytes, (views.diagnostics.length + views.evidence.length + views.metadata.length) * PREPARED_VIEW_REFERENCE_BYTES, retainedBeforePreparation, maximum);
5070
+ return bytes;
5071
+ };
5072
+ var findStartIndex = (items, lastKey) => {
5073
+ if (lastKey === null) return 0;
5074
+ let lower = 0;
5075
+ let upper = items.length - 1;
5076
+ while (lower <= upper) {
5077
+ const middle = lower + Math.floor((upper - lower) / 2);
5078
+ const candidate = items[middle];
5079
+ if (candidate === void 0) return invalidArgument$1();
5080
+ if (candidate.key === lastKey) return middle + 1;
5081
+ if (candidate.key < lastKey) lower = middle + 1;
5082
+ else upper = middle - 1;
5083
+ }
5084
+ return invalidArgument$1();
5085
+ };
5086
+ /** Creates the returned inspection from content-free state only. */
5087
+ var createPreparedProjectInspection = (input) => {
5088
+ const { counts, formatVersion, items, maxEntries, maxRetainedBytes, resourceUsage: validationUsage, source, summary, valid } = input;
5089
+ const views = createViews(items);
5090
+ const inspectionDigest = createIdentity([serializeDeterministically({
5091
+ counts,
5092
+ formatVersion,
5093
+ itemKeys: items.map(({ key }) => key),
5094
+ summary,
5095
+ valid
5096
+ })]);
5097
+ const preparedBytes = calculatePreparedBytes(items, views, {
5098
+ counts,
5099
+ formatVersion,
5100
+ inspectionDigest,
5101
+ source,
5102
+ summary,
5103
+ valid
5104
+ }, validationUsage.retainedBytes, maxRetainedBytes);
5105
+ const resourceUsage = freezeRecursively({
5106
+ canonicalBytes: validationUsage.canonicalBytes,
5107
+ peakRetainedBytes: Math.max(validationUsage.peakRetainedBytes, validationUsage.retainedBytes + preparedBytes),
5108
+ preparedBytes,
5109
+ retainedBytes: preparedBytes,
5110
+ totalBytesRead: validationUsage.totalBytesRead
5111
+ });
5112
+ const readPage = (pageInput) => {
5113
+ if (typeof pageInput !== "object" || pageInput === null || Reflect.ownKeys(pageInput).some((key) => key !== "cursor" && key !== "maxItems" && key !== "view") || !Number.isSafeInteger(pageInput.maxItems) || pageInput.maxItems < 1 || pageInput.maxItems > maxEntries || pageInput.cursor !== void 0 && typeof pageInput.cursor !== "string" || pageInput.view !== "all" && pageInput.view !== "diagnostics" && pageInput.view !== "evidence" && pageInput.view !== "metadata") return invalidArgument$1();
5114
+ const viewItems = views[pageInput.view];
5115
+ const startIndex = findStartIndex(viewItems, decodeCursor(pageInput.cursor, inspectionDigest, pageInput.view));
5116
+ const records = viewItems.slice(startIndex, startIndex + pageInput.maxItems).map(({ item, key }, index) => {
5117
+ return {
5118
+ item,
5119
+ nextCursor: startIndex + index + 1 >= viewItems.length ? null : encodeCursor(inspectionDigest, pageInput.view, key)
5120
+ };
5121
+ });
5122
+ const nextCursor = records.at(-1)?.nextCursor ?? null;
5123
+ return freezeRecursively({
5124
+ counts,
5125
+ formatVersion,
5126
+ inspectionDigest,
5127
+ page: {
5128
+ isComplete: nextCursor === null,
5129
+ nextCursor,
5130
+ records,
5131
+ totalItems: viewItems.length
5132
+ },
5133
+ source,
5134
+ summary,
5135
+ valid,
5136
+ view: pageInput.view
5137
+ });
5138
+ };
5139
+ return freezeRecursively({
5140
+ counts,
5141
+ formatVersion,
5142
+ inspectionDigest,
5143
+ readPage,
5144
+ resourceUsage,
5145
+ source,
5146
+ summary,
5147
+ valid
5148
+ });
5149
+ };
5150
+ /** Prepares one immutable content-free inspection for synchronous bounded page reads. */
5151
+ var createProjectInspection = async (input, options) => {
5152
+ const state = await validateProjectState(input, options);
4989
5153
  const metadata = state.project === null ? [] : collectProjectMetadata(state.project);
4990
5154
  const agentAssignments = state.project === null ? [] : collectProjectAgentAssignments(state.project);
4991
- const allItems = createKeyedItems([
5155
+ const items = freezeRecursively(createKeyedItems([
4992
5156
  ...metadata.map((metadataItem) => ({
4993
5157
  kind: "metadata",
4994
5158
  metadata: metadataItem
@@ -5005,50 +5169,27 @@ var inspectProjectPage = async (input, options) => {
5005
5169
  agent,
5006
5170
  kind: "agent"
5007
5171
  }))
5008
- ]);
5009
- const counts = {
5010
- agents: state.result.summary?.counts.agents ?? 0,
5011
- context: state.result.summary?.counts.context ?? 0,
5012
- decisions: state.result.summary?.counts.decisions ?? 0,
5013
- diagnostics: state.result.diagnostics.length,
5014
- evidence: state.result.evidence.length,
5015
- metadata: metadata.length,
5016
- mirrors: state.result.summary?.counts.mirrors ?? 0,
5017
- runtimes: state.result.summary?.counts.runtimes ?? 0,
5018
- unresolved: state.result.summary?.counts.unresolved ?? 0
5019
- };
5020
- const inspectionDigest = createIdentity([serializeDeterministically({
5021
- counts,
5022
- formatVersion: state.result.formatVersion,
5023
- itemKeys: allItems.map(({ key }) => key),
5024
- summary: state.result.summary,
5025
- valid: state.result.valid
5026
- })]);
5027
- const items = selectViewItems(allItems, input.view);
5028
- const lastKey = decodeCursor(input.cursor, inspectionDigest, input.view);
5029
- const startIndex = lastKey === null ? 0 : items.findIndex(({ key }) => key === lastKey) + 1;
5030
- if (lastKey !== null && startIndex === 0) return invalidArgument$1();
5031
- const records = items.slice(startIndex, startIndex + input.maxItems).map(({ item, key }, index) => {
5032
- return {
5033
- item,
5034
- nextCursor: startIndex + index + 1 >= items.length ? null : encodeCursor(inspectionDigest, input.view, key)
5035
- };
5036
- });
5037
- const nextCursor = records.at(-1)?.nextCursor ?? null;
5038
- return freezeRecursively({
5039
- counts,
5172
+ ]));
5173
+ return createPreparedProjectInspection({
5174
+ counts: freezeRecursively({
5175
+ agents: state.result.summary?.counts.agents ?? 0,
5176
+ context: state.result.summary?.counts.context ?? 0,
5177
+ decisions: state.result.summary?.counts.decisions ?? 0,
5178
+ diagnostics: state.result.diagnostics.length,
5179
+ evidence: state.result.evidence.length,
5180
+ metadata: metadata.length,
5181
+ mirrors: state.result.summary?.counts.mirrors ?? 0,
5182
+ runtimes: state.result.summary?.counts.runtimes ?? 0,
5183
+ unresolved: state.result.summary?.counts.unresolved ?? 0
5184
+ }),
5040
5185
  formatVersion: state.result.formatVersion,
5041
- inspectionDigest,
5042
- page: {
5043
- isComplete: nextCursor === null,
5044
- nextCursor,
5045
- records,
5046
- totalItems: items.length
5047
- },
5186
+ items,
5187
+ maxEntries: options.limits.maxEntries,
5188
+ maxRetainedBytes: options.limits.maxRetainedBytes,
5189
+ resourceUsage: state.resourceUsage,
5048
5190
  source: state.result.source,
5049
5191
  summary: state.result.summary,
5050
- valid: state.result.valid,
5051
- view: input.view
5192
+ valid: state.result.valid
5052
5193
  });
5053
5194
  };
5054
5195
  //#endregion
@@ -5064,10 +5205,13 @@ var invalidArgument = () => {
5064
5205
  });
5065
5206
  };
5066
5207
  /** Throws the stable resource failure for an exhausted scope-input budget. */
5067
- var resourceLimitExceeded = (limit) => {
5208
+ var resourceLimitExceeded = (limit, limitMaximum, observedUsage) => {
5068
5209
  throw new CoreOperationException({
5069
5210
  code: "RESOURCE_LIMIT_EXCEEDED",
5070
5211
  limit,
5212
+ limitMaximum,
5213
+ nextAction: "reduce-input-or-increase-limit",
5214
+ observedUsage,
5071
5215
  operation: "match-manifest-scope"
5072
5216
  });
5073
5217
  };
@@ -5095,14 +5239,14 @@ var normalizeManifestScopeInput = (input, limits) => {
5095
5239
  const manifest = input["manifest"];
5096
5240
  const pathCandidates = input["paths"];
5097
5241
  if (!isRecord(manifest) || !Array.isArray(pathCandidates)) return invalidArgument();
5098
- if (pathCandidates.length > limits.maxEntries) return resourceLimitExceeded("maxEntries");
5242
+ if (pathCandidates.length > limits.maxEntries) return resourceLimitExceeded("maxEntries", limits.maxEntries, pathCandidates.length);
5099
5243
  const uniquePaths = /* @__PURE__ */ new Set();
5100
5244
  let totalBytes = 0;
5101
5245
  for (const [index, candidate] of pathCandidates.entries()) {
5102
5246
  if (typeof candidate !== "string") return invalidArgument();
5103
5247
  const path = parseRepositoryPath(candidate);
5104
5248
  totalBytes += measureUtf8ByteLength(path) + (index === 0 ? 0 : 1);
5105
- if (totalBytes > limits.maxTotalBytesRead) return resourceLimitExceeded("maxTotalBytesRead");
5249
+ if (totalBytes > limits.maxTotalBytesRead) return resourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytes);
5106
5250
  uniquePaths.add(path);
5107
5251
  }
5108
5252
  return {
@@ -5293,6 +5437,9 @@ var compileManifestScope = (manifest, limits) => {
5293
5437
  if (declarations.length > limits.maxEntries) throw new CoreOperationException({
5294
5438
  code: "RESOURCE_LIMIT_EXCEEDED",
5295
5439
  limit: "maxEntries",
5440
+ limitMaximum: limits.maxEntries,
5441
+ nextAction: "reduce-input-or-increase-limit",
5442
+ observedUsage: declarations.length,
5296
5443
  operation: "match-manifest-scope"
5297
5444
  });
5298
5445
  const exact = /* @__PURE__ */ new Map();
@@ -5340,6 +5487,9 @@ var addMatch = (matches, inputPath, relationship, limits) => {
5340
5487
  if (matches.length >= limits.maxEntries) throw new CoreOperationException({
5341
5488
  code: "RESOURCE_LIMIT_EXCEEDED",
5342
5489
  limit: "maxEntries",
5490
+ limitMaximum: limits.maxEntries,
5491
+ nextAction: "reduce-input-or-increase-limit",
5492
+ observedUsage: matches.length + 1,
5343
5493
  operation: "match-manifest-scope"
5344
5494
  });
5345
5495
  matches.push(createMatch(inputPath, relationship));
@@ -5454,7 +5604,7 @@ var createCore = (options) => {
5454
5604
  calculateContentDigest: (input) => {
5455
5605
  return calculateContentDigest(input, createCoreOperationOptionsSnapshot(snapshot).limits);
5456
5606
  },
5457
- inspectProjectPage: (input) => inspectProjectPage(input, createCoreOperationOptionsSnapshot(snapshot)),
5607
+ createProjectInspection: (input) => createProjectInspection(input, createCoreOperationOptionsSnapshot(snapshot)),
5458
5608
  validateProject: (input) => validateProject(input, createCoreOperationOptionsSnapshot(snapshot)),
5459
5609
  matchManifestScope: (input) => matchManifestScope(input, createCoreOperationOptionsSnapshot(snapshot)),
5460
5610
  normalizeText: (input) => {