@moldea.ai/core 3.1.0 → 4.0.1

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 (39) hide show
  1. package/README.md +8 -4
  2. package/dist/adapter-repository/index.d.ts +19 -1
  3. package/dist/adapter-repository/index.d.ts.map +1 -1
  4. package/dist/adapter-retained-memory/index.d.ts +8 -0
  5. package/dist/adapter-retained-memory/index.d.ts.map +1 -0
  6. package/dist/adapter-validation/index.d.ts.map +1 -1
  7. package/dist/adapter.js +27 -5
  8. package/dist/adapter.js.map +1 -1
  9. package/dist/canonical-content-page/index.d.ts.map +1 -1
  10. package/dist/chunks/{exceptions-DWsZTpqs.js → exceptions-HS9ofVy2.js} +21 -2
  11. package/dist/chunks/exceptions-HS9ofVy2.js.map +1 -0
  12. package/dist/constants/index.d.ts.map +1 -1
  13. package/dist/contracts/index.d.ts +31 -5
  14. package/dist/contracts/index.d.ts.map +1 -1
  15. package/dist/diagnostic-utilities/index.d.ts.map +1 -1
  16. package/dist/exceptions/index.d.ts +20 -4
  17. package/dist/exceptions/index.d.ts.map +1 -1
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +289 -113
  21. package/dist/index.js.map +1 -1
  22. package/dist/options/index.d.ts.map +1 -1
  23. package/dist/project-agent-assignments/index.d.ts +2 -2
  24. package/dist/project-agent-assignments/index.d.ts.map +1 -1
  25. package/dist/project-inspection/index.d.ts +5 -0
  26. package/dist/project-inspection/index.d.ts.map +1 -0
  27. package/dist/project-metadata/index.d.ts +2 -2
  28. package/dist/project-metadata/index.d.ts.map +1 -1
  29. package/dist/project-validation/index.d.ts +2 -0
  30. package/dist/project-validation/index.d.ts.map +1 -1
  31. package/dist/repository-inspection-session/index.d.ts +8 -0
  32. package/dist/repository-inspection-session/index.d.ts.map +1 -1
  33. package/dist/scope-matching/matching.d.ts.map +1 -1
  34. package/dist/scope-matching/validations.d.ts.map +1 -1
  35. package/dist/text/index.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/dist/chunks/exceptions-DWsZTpqs.js.map +0 -1
  38. package/dist/project-inspection-page/index.d.ts +0 -5
  39. 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, r as registerRuntimeAdapterRetainedByteReservation, t as CoreConfigurationException } from "./chunks/exceptions-HS9ofVy2.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
  /**
@@ -2301,11 +2312,13 @@ var parseManifestDocument = async (input, options, operation = "parse-manifest")
2301
2312
  };
2302
2313
  //#endregion
2303
2314
  //#region src/project-agent-assignments/index.ts
2304
- /** Collects deterministic content-free agent-to-runtime assignments from a validated project. */
2305
- var collectProjectAgentAssignments = (project) => project.agents.map((agent) => ({
2306
- agentId: agent.id,
2307
- runtimeId: agent.declaration.runtime.id
2308
- })).sort((left, right) => compareExactStrings(left.agentId, right.agentId) || compareExactStrings(left.runtimeId, right.runtimeId));
2315
+ /** Iterates content-free agent-to-runtime assignments without retaining an intermediate array. */
2316
+ var iterateProjectAgentAssignments = function* (project) {
2317
+ for (const agent of project.agents) yield {
2318
+ agentId: agent.id,
2319
+ runtimeId: agent.declaration.runtime.id
2320
+ };
2321
+ };
2309
2322
  //#endregion
2310
2323
  //#region src/project-metadata/index.ts
2311
2324
  var createAssetItem = (asset, kind, agentId = null, decisionId = null) => ({
@@ -2318,18 +2331,18 @@ var createAssetItem = (asset, kind, agentId = null, decisionId = null) => ({
2318
2331
  path: asset.path,
2319
2332
  scalarLength: asset.scalarLength
2320
2333
  });
2321
- var compareMetadataItems = (left, right) => compareExactStrings(left.path, right.path) || compareExactStrings(left.kind, right.kind) || compareExactStrings(left.agentId ?? "", right.agentId ?? "");
2322
- /** Collects deterministic content-free asset metadata from one validated project. */
2323
- var collectProjectMetadata = (project) => {
2324
- const items = [createAssetItem(project.manifest.asset, "manifest"), createAssetItem(project.project, "project")];
2325
- for (const context of project.context) items.push(createAssetItem(context.asset, "context"));
2326
- for (const { decision } of project.decisions) items.push(createAssetItem(decision.asset, "decision", null, decision.id));
2327
- for (const runtime of project.runtimes) items.push(createAssetItem(runtime.asset, "runtime-guidance"));
2334
+ /** Iterates content-free asset metadata without retaining a second project-sized collection. */
2335
+ var iterateProjectMetadata = function* (project) {
2336
+ yield createAssetItem(project.manifest.asset, "manifest");
2337
+ yield createAssetItem(project.project, "project");
2338
+ for (const context of project.context) yield createAssetItem(context.asset, "context");
2339
+ for (const { decision } of project.decisions) yield createAssetItem(decision.asset, "decision", null, decision.id);
2340
+ for (const runtime of project.runtimes) yield createAssetItem(runtime.asset, "runtime-guidance");
2328
2341
  for (const agent of project.agents) {
2329
- items.push(createAssetItem(agent.description.asset, "agent-description", agent.id));
2330
- items.push(createAssetItem(agent.instruction, "agent-instruction", agent.id));
2331
- if (agent.handoffDescription !== null) items.push(createAssetItem(agent.handoffDescription.asset, "agent-handoff-description", agent.id));
2332
- for (const mirror of agent.mirrors) items.push({
2342
+ yield createAssetItem(agent.description.asset, "agent-description", agent.id);
2343
+ yield createAssetItem(agent.instruction, "agent-instruction", agent.id);
2344
+ if (agent.handoffDescription !== null) yield createAssetItem(agent.handoffDescription.asset, "agent-handoff-description", agent.id);
2345
+ for (const mirror of agent.mirrors) yield {
2333
2346
  agentId: agent.id,
2334
2347
  byteLength: mirror.byteLength,
2335
2348
  canonicalDigest: mirror.canonicalDigest,
@@ -2338,9 +2351,8 @@ var collectProjectMetadata = (project) => {
2338
2351
  kind: "mirror",
2339
2352
  path: mirror.path,
2340
2353
  scalarLength: mirror.scalarLength
2341
- });
2354
+ };
2342
2355
  }
2343
- return items.sort(compareMetadataItems);
2344
2356
  };
2345
2357
  /** Creates the content-free summary for one validated project snapshot. */
2346
2358
  var createProjectSummary = (project) => ({
@@ -2472,6 +2484,9 @@ var registerRawOutput = (outputCounts, key, amount, context) => {
2472
2484
  adapterId: context.adapterId,
2473
2485
  code: "RESOURCE_LIMIT_EXCEEDED",
2474
2486
  limit,
2487
+ limitMaximum: context.limits[limit],
2488
+ nextAction: "reduce-input-or-increase-limit",
2489
+ observedUsage: outputCounts[key] + amount,
2475
2490
  operation: "validate-adapter"
2476
2491
  });
2477
2492
  outputCounts[key] += amount;
@@ -2837,6 +2852,10 @@ var inspectRuntimeAdapters = async (project, session, options, signal) => {
2837
2852
  var MANIFEST_PATH$3 = parseRepositoryPath("/moldea/moldea.yaml");
2838
2853
  var SOURCE_PAGE_ENTRIES = 256;
2839
2854
  var SOURCE_PAGE_BYTES = 65536;
2855
+ var CANONICAL_BYTE_RETENTION_MULTIPLIER = 4;
2856
+ var RETAINED_PATH_FIXED_BYTES = 64;
2857
+ var RETAINED_PATH_BYTE_MULTIPLIER = 2;
2858
+ var encoder$1 = new TextEncoder();
2840
2859
  var invalidSourceData$3 = (operation, path) => {
2841
2860
  throw new RepositorySourceException({
2842
2861
  code: "INVALID_SOURCE_DATA",
@@ -2869,10 +2888,13 @@ var isStrictDescendant = (path, prefix) => {
2869
2888
  const descendantPrefix = prefix === REPOSITORY_ROOT ? REPOSITORY_ROOT : `${prefix}/`;
2870
2889
  return path.startsWith(descendantPrefix);
2871
2890
  };
2872
- var throwResourceLimitExceeded = (limit) => {
2891
+ var throwResourceLimitExceeded = (limit, limitMaximum, observedUsage) => {
2873
2892
  throw new CoreOperationException({
2874
2893
  code: "RESOURCE_LIMIT_EXCEEDED",
2875
2894
  limit,
2895
+ limitMaximum,
2896
+ nextAction: "reduce-input-or-increase-limit",
2897
+ observedUsage,
2876
2898
  operation: "validate-project"
2877
2899
  });
2878
2900
  };
@@ -2891,6 +2913,9 @@ var createSourceOptions = (sessionSignal, operationSignal) => {
2891
2913
  */
2892
2914
  var createRepositoryInspectionSession = (repository, limits, signal) => {
2893
2915
  const seenPaths = /* @__PURE__ */ new Set();
2916
+ let canonicalBytes = 0;
2917
+ let peakRetainedBytes = 0;
2918
+ let retainedBytes = 0;
2894
2919
  let totalBytesRead = 0;
2895
2920
  const adapterLimits = Object.freeze({
2896
2921
  maxEntries: limits.maxEntries,
@@ -2906,9 +2931,19 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2906
2931
  operation: "validate-project"
2907
2932
  });
2908
2933
  };
2934
+ const reserveRetainedBytes = (byteLength) => {
2935
+ if (!Number.isSafeInteger(byteLength) || byteLength < 0 || byteLength > limits.maxRetainedBytes - retainedBytes) return throwResourceLimitExceeded("maxRetainedBytes", limits.maxRetainedBytes, retainedBytes + byteLength);
2936
+ retainedBytes += byteLength;
2937
+ peakRetainedBytes = Math.max(peakRetainedBytes, retainedBytes);
2938
+ };
2939
+ const observeTransientBytes = (byteLength) => {
2940
+ if (!Number.isSafeInteger(byteLength) || byteLength < 0 || byteLength > limits.maxRetainedBytes - retainedBytes) return throwResourceLimitExceeded("maxRetainedBytes", limits.maxRetainedBytes, retainedBytes + byteLength);
2941
+ peakRetainedBytes = Math.max(peakRetainedBytes, retainedBytes + byteLength);
2942
+ };
2909
2943
  const registerPath = (path) => {
2910
2944
  if (path === REPOSITORY_ROOT || seenPaths.has(path)) return;
2911
- if (seenPaths.size >= limits.maxEntries) return throwResourceLimitExceeded("maxEntries");
2945
+ if (seenPaths.size >= limits.maxEntries) return throwResourceLimitExceeded("maxEntries", limits.maxEntries, seenPaths.size + 1);
2946
+ reserveRetainedBytes(RETAINED_PATH_FIXED_BYTES + encoder$1.encode(path).byteLength * RETAINED_PATH_BYTE_MULTIPLIER);
2912
2947
  seenPaths.add(path);
2913
2948
  };
2914
2949
  const throwIfSignalAborted = (operationSignal) => {
@@ -2917,7 +2952,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2917
2952
  };
2918
2953
  throwIfSignalAborted();
2919
2954
  const reserveReadBytes = (byteLength) => {
2920
- if (byteLength > limits.maxTotalBytesRead - totalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead");
2955
+ if (byteLength > limits.maxTotalBytesRead - totalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytesRead + byteLength);
2921
2956
  totalBytesRead += byteLength;
2922
2957
  };
2923
2958
  const getEntry = async (path, options) => {
@@ -2964,15 +2999,29 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2964
2999
  };
2965
3000
  const loadFile = async (path, operationSignal) => {
2966
3001
  const fileLimit = path === MANIFEST_PATH$3 ? "maxManifestBytes" : "maxFileBytes";
3002
+ const availableRetainedBytes = limits.maxRetainedBytes - retainedBytes;
3003
+ if (availableRetainedBytes < 1) return throwResourceLimitExceeded("maxRetainedBytes", limits.maxRetainedBytes, retainedBytes + 1);
3004
+ const firstPageMaximumBytes = Math.min(SOURCE_PAGE_BYTES, Math.max(1, limits[fileLimit]), availableRetainedBytes);
3005
+ observeTransientBytes(firstPageMaximumBytes);
2967
3006
  const firstPage = await repository.readFilePage(path, {
2968
- maxBytes: Math.min(SOURCE_PAGE_BYTES, Math.max(1, limits[fileLimit])),
3007
+ maxBytes: firstPageMaximumBytes,
2969
3008
  offset: 0,
2970
3009
  ...createSourceOptions(signal, operationSignal) ?? {}
2971
3010
  });
2972
3011
  throwIfSignalAborted(operationSignal);
2973
3012
  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);
3013
+ if (firstPage.totalBytes > limits[fileLimit]) return throwResourceLimitExceeded(fileLimit, limits[fileLimit], firstPage.totalBytes);
3014
+ const firstPageBytes = firstPage.bytes.byteLength;
3015
+ const retainedBytesAvailableForCanonicalContent = limits.maxRetainedBytes - retainedBytes - firstPageBytes;
3016
+ if (retainedBytesAvailableForCanonicalContent < 0 || firstPage.totalBytes > Math.floor(retainedBytesAvailableForCanonicalContent / CANONICAL_BYTE_RETENTION_MULTIPLIER)) {
3017
+ const projectedUsage = retainedBytes + firstPageBytes + firstPage.totalBytes * CANONICAL_BYTE_RETENTION_MULTIPLIER;
3018
+ return throwResourceLimitExceeded("maxRetainedBytes", limits.maxRetainedBytes, Number.isSafeInteger(projectedUsage) ? projectedUsage : Number.MAX_SAFE_INTEGER);
3019
+ }
2975
3020
  reserveReadBytes(firstPage.totalBytes);
3021
+ const canonicalRetainedBytes = firstPage.totalBytes * CANONICAL_BYTE_RETENTION_MULTIPLIER;
3022
+ observeTransientBytes(canonicalRetainedBytes + firstPageBytes);
3023
+ reserveRetainedBytes(canonicalRetainedBytes);
3024
+ canonicalBytes += firstPage.totalBytes;
2976
3025
  const content = new Uint8Array(firstPage.totalBytes);
2977
3026
  let offset = 0;
2978
3027
  let page = firstPage;
@@ -3033,7 +3082,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
3033
3082
  const listAdapterEntriesPage = async (options) => {
3034
3083
  const prefix = options.prefix === void 0 ? REPOSITORY_ROOT : parseRepositoryPath(options.prefix);
3035
3084
  registerPath(prefix);
3036
- if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1 || options.maxEntries > adapterLimits.maxPageEntries) return throwResourceLimitExceeded("maxEntries");
3085
+ if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1 || options.maxEntries > adapterLimits.maxPageEntries) return throwResourceLimitExceeded("maxEntries", adapterLimits.maxPageEntries, options.maxEntries);
3037
3086
  throwIfSignalAborted(options.signal);
3038
3087
  const page = await repository.listEntriesPage({
3039
3088
  ...options.cursor === void 0 ? {} : { cursor: options.cursor },
@@ -3064,7 +3113,8 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
3064
3113
  throwIfSignalAborted(options.signal);
3065
3114
  registerPath(parsedPath);
3066
3115
  const reservedBytes = Math.min(options.maxBytes, limits.maxTotalBytesRead - totalBytesRead);
3067
- if (reservedBytes < 1) return throwResourceLimitExceeded("maxTotalBytesRead");
3116
+ if (reservedBytes < 1) return throwResourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytesRead + 1);
3117
+ observeTransientBytes(reservedBytes);
3068
3118
  reserveReadBytes(reservedBytes);
3069
3119
  let page;
3070
3120
  try {
@@ -3099,8 +3149,15 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
3099
3149
  readFilePage: readAdapterFilePage,
3100
3150
  snapshot: repository.snapshot
3101
3151
  });
3152
+ registerRuntimeAdapterRetainedByteReservation(adapterRepository, reserveRetainedBytes);
3102
3153
  return Object.freeze({
3103
3154
  adapterRepository,
3155
+ getResourceUsage: () => Object.freeze({
3156
+ canonicalBytes,
3157
+ peakRetainedBytes,
3158
+ retainedBytes,
3159
+ totalBytesRead
3160
+ }),
3104
3161
  reader: Object.freeze({
3105
3162
  getEntry,
3106
3163
  iterateEntries,
@@ -4846,6 +4903,7 @@ var validateProjectState = async (input, options) => {
4846
4903
  }, operationOptions);
4847
4904
  if (universal.project === null) return freezeRecursively({
4848
4905
  project: null,
4906
+ resourceUsage: session.getResourceUsage(),
4849
4907
  result: {
4850
4908
  diagnostics: universal.diagnostics,
4851
4909
  evidence: [],
@@ -4859,6 +4917,7 @@ var validateProjectState = async (input, options) => {
4859
4917
  const availabilityDiagnostics = validateRuntimeAdapterAvailability(universal.runtimeLocations, universal.project.formatVersion, operationOptions);
4860
4918
  if (availabilityDiagnostics.length > 0) return freezeRecursively({
4861
4919
  project: universal.project,
4920
+ resourceUsage: session.getResourceUsage(),
4862
4921
  result: {
4863
4922
  diagnostics: availabilityDiagnostics,
4864
4923
  evidence: [],
@@ -4872,6 +4931,7 @@ var validateProjectState = async (input, options) => {
4872
4931
  const valid = adapterInspection.diagnostics.length === 0;
4873
4932
  return freezeRecursively({
4874
4933
  project: universal.project,
4934
+ resourceUsage: session.getResourceUsage(),
4875
4935
  result: {
4876
4936
  diagnostics: adapterInspection.diagnostics,
4877
4937
  evidence: adapterInspection.evidence,
@@ -4885,16 +4945,30 @@ var validateProjectState = async (input, options) => {
4885
4945
  /** Validates one project without returning canonical document bodies. */
4886
4946
  var validateProject = async (input, options) => (await validateProjectState(input, options)).result;
4887
4947
  //#endregion
4888
- //#region src/project-inspection-page/index.ts
4889
- var CURSOR_PREFIX = "core3";
4948
+ //#region src/project-inspection/index.ts
4949
+ var CURSOR_PREFIX = "core4";
4950
+ var PREPARED_FIXED_BYTES = 512;
4951
+ var PREPARED_ITEM_FIXED_BYTES = 64;
4952
+ var PREPARED_TEXT_BYTE_MULTIPLIER = 2;
4953
+ var PREPARED_VIEW_REFERENCE_BYTES = 8;
4890
4954
  var encoder = new TextEncoder();
4891
4955
  var invalidArgument$1 = () => {
4892
4956
  throw new CoreOperationException({
4893
4957
  code: "INVALID_ARGUMENT",
4894
- operation: "inspect-project-page"
4958
+ operation: "create-project-inspection"
4959
+ });
4960
+ };
4961
+ var resourceLimitExceeded$1 = (limitMaximum, observedUsage) => {
4962
+ throw new CoreOperationException({
4963
+ code: "RESOURCE_LIMIT_EXCEEDED",
4964
+ limit: "maxRetainedBytes",
4965
+ limitMaximum,
4966
+ nextAction: "reduce-input-or-increase-limit",
4967
+ observedUsage,
4968
+ operation: "create-project-inspection"
4895
4969
  });
4896
4970
  };
4897
- /** Serializes the closed inspection records with recursively sorted object keys. */
4971
+ /** Serializes content-free inspection state with recursively sorted object keys. */
4898
4972
  var serializeDeterministically = (candidate) => {
4899
4973
  if (candidate === null || typeof candidate !== "object") {
4900
4974
  const serialized = JSON.stringify(candidate);
@@ -4903,7 +4977,9 @@ var serializeDeterministically = (candidate) => {
4903
4977
  if (Array.isArray(candidate)) return `[${candidate.map((item) => serializeDeterministically(item)).join(",")}]`;
4904
4978
  return `{${Object.entries(candidate).filter(([, value]) => value !== void 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, value]) => `${JSON.stringify(key)}:${serializeDeterministically(value)}`).join(",")}}`;
4905
4979
  };
4906
- var createIdentity = (parts) => createRepositoryIdentity(parts.map((part) => encoder.encode(part)));
4980
+ var createIdentity = (parts) => createRepositoryIdentity((function* () {
4981
+ for (const part of parts) yield encoder.encode(part);
4982
+ })());
4907
4983
  var createItemBaseKey = (item) => {
4908
4984
  if (item.kind === "agent") return [
4909
4985
  "3",
@@ -4933,21 +5009,21 @@ var createItemBaseKey = (item) => {
4933
5009
  createIdentity([serializeDeterministically(item.evidence)])
4934
5010
  ].join(":");
4935
5011
  };
4936
- /** Assigns stable unique keys without relying on collection offsets. */
5012
+ /** Assigns stable unique keys once without relying on collection offsets. */
4937
5013
  var createKeyedItems = (items) => {
4938
- const sorted = items.map((item) => ({
4939
- baseKey: createItemBaseKey(item),
4940
- item
4941
- })).sort((left, right) => left.baseKey < right.baseKey ? -1 : left.baseKey > right.baseKey ? 1 : 0);
4942
- const occurrences = /* @__PURE__ */ new Map();
4943
- return sorted.map(({ baseKey, item }) => {
4944
- const occurrence = occurrences.get(baseKey) ?? 0;
4945
- occurrences.set(baseKey, occurrence + 1);
4946
- return {
4947
- item,
4948
- key: `${baseKey}:${String(occurrence).padStart(10, "0")}`
4949
- };
4950
- });
5014
+ const sorted = Array.from(items, (item) => ({
5015
+ item,
5016
+ key: createItemBaseKey(item)
5017
+ })).sort((left, right) => left.key < right.key ? -1 : left.key > right.key ? 1 : 0);
5018
+ let previousBaseKey = null;
5019
+ let occurrence = 0;
5020
+ for (const item of sorted) {
5021
+ const baseKey = item.key;
5022
+ occurrence = baseKey === previousBaseKey ? occurrence + 1 : 0;
5023
+ previousBaseKey = baseKey;
5024
+ item.key = `${baseKey}:${String(occurrence).padStart(10, "0")}`;
5025
+ }
5026
+ return sorted;
4951
5027
  };
4952
5028
  var createCursorChecksum = (inspectionDigest, lastKey, view) => createIdentity([
4953
5029
  CURSOR_PREFIX,
@@ -4964,7 +5040,7 @@ var encodeCursor = (inspectionDigest, view, lastKey) => [
4964
5040
  ].join(":");
4965
5041
  var decodeCursor = (cursor, inspectionDigest, view) => {
4966
5042
  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);
5043
+ const match = /^core4:(all|diagnostics|evidence|metadata):([^:]+):(sha256:[0-9a-f]{64}):(sha256:[0-9a-f]{64})$/u.exec(cursor);
4968
5044
  if (match === null || match[1] !== view || match[3] !== inspectionDigest) return invalidArgument$1();
4969
5045
  let lastKey;
4970
5046
  try {
@@ -4975,80 +5051,171 @@ var decodeCursor = (cursor, inspectionDigest, view) => {
4975
5051
  if (lastKey.length === 0 || match[4] !== createCursorChecksum(inspectionDigest, lastKey, view)) return invalidArgument$1();
4976
5052
  return lastKey;
4977
5053
  };
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;
5054
+ var createViews = (items) => freezeRecursively({
5055
+ all: items,
5056
+ diagnostics: items.filter(({ item }) => item.kind === "diagnostic"),
5057
+ evidence: items.filter(({ item }) => item.kind === "evidence"),
5058
+ metadata: items.filter(({ item }) => item.kind === "metadata" || item.kind === "agent")
4981
5059
  });
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);
4989
- const metadata = state.project === null ? [] : collectProjectMetadata(state.project);
4990
- const agentAssignments = state.project === null ? [] : collectProjectAgentAssignments(state.project);
4991
- const allItems = createKeyedItems([
4992
- ...metadata.map((metadataItem) => ({
4993
- kind: "metadata",
4994
- metadata: metadataItem
4995
- })),
4996
- ...state.result.diagnostics.map((diagnostic) => ({
4997
- diagnostic,
4998
- kind: "diagnostic"
4999
- })),
5000
- ...state.result.evidence.map((evidence) => ({
5001
- evidence,
5002
- kind: "evidence"
5003
- })),
5004
- ...agentAssignments.map((agent) => ({
5005
- agent,
5006
- kind: "agent"
5007
- }))
5008
- ]);
5060
+ var addRetainedBytes = (current, additional, retainedBeforePreparation, maximum) => {
5061
+ const projectedUsage = retainedBeforePreparation + current + additional;
5062
+ if (!Number.isSafeInteger(additional) || additional < 0 || !Number.isSafeInteger(projectedUsage) || projectedUsage > maximum) return resourceLimitExceeded$1(maximum, Number.isSafeInteger(projectedUsage) ? projectedUsage : Number.MAX_SAFE_INTEGER);
5063
+ return current + additional;
5064
+ };
5065
+ /** Iterates every content-free inspection item without retaining preparation state. */
5066
+ var iterateProjectInspectionItems = function* (state) {
5067
+ if (state.project !== null) for (const metadata of iterateProjectMetadata(state.project)) yield {
5068
+ kind: "metadata",
5069
+ metadata
5070
+ };
5071
+ for (const diagnostic of state.result.diagnostics) yield {
5072
+ diagnostic,
5073
+ kind: "diagnostic"
5074
+ };
5075
+ for (const evidence of state.result.evidence) yield {
5076
+ evidence,
5077
+ kind: "evidence"
5078
+ };
5079
+ if (state.project !== null) for (const agent of iterateProjectAgentAssignments(state.project)) yield {
5080
+ agent,
5081
+ kind: "agent"
5082
+ };
5083
+ };
5084
+ /** Preflights the complete logical prepared-state cost before retaining its collections. */
5085
+ var estimatePreparedProjectInspection = (state, baseCounts, maximum) => {
5086
+ const retainedBeforePreparation = state.resourceUsage.retainedBytes;
5087
+ let bytes = addRetainedBytes(0, PREPARED_FIXED_BYTES, retainedBeforePreparation, maximum);
5088
+ let metadataCount = 0;
5089
+ for (const item of iterateProjectInspectionItems(state)) {
5090
+ const key = `${createItemBaseKey(item)}:0000000000`;
5091
+ const encodedBytes = encoder.encode(key).byteLength + encoder.encode(serializeDeterministically(item)).byteLength;
5092
+ bytes = addRetainedBytes(bytes, PREPARED_ITEM_FIXED_BYTES + encodedBytes * PREPARED_TEXT_BYTE_MULTIPLIER + PREPARED_VIEW_REFERENCE_BYTES, retainedBeforePreparation, maximum);
5093
+ if (item.kind === "metadata") metadataCount += 1;
5094
+ }
5009
5095
  const counts = {
5096
+ agents: baseCounts.agents,
5097
+ context: baseCounts.context,
5098
+ decisions: baseCounts.decisions,
5099
+ diagnostics: baseCounts.diagnostics,
5100
+ evidence: baseCounts.evidence,
5101
+ metadata: metadataCount,
5102
+ mirrors: baseCounts.mirrors,
5103
+ runtimes: baseCounts.runtimes,
5104
+ unresolved: baseCounts.unresolved
5105
+ };
5106
+ bytes = addRetainedBytes(bytes, encoder.encode(serializeDeterministically({
5107
+ counts,
5108
+ formatVersion: state.result.formatVersion,
5109
+ inspectionDigest: "sha256:0000000000000000000000000000000000000000000000000000000000000000",
5110
+ source: state.result.source,
5111
+ summary: state.result.summary,
5112
+ valid: state.result.valid
5113
+ })).byteLength * PREPARED_TEXT_BYTE_MULTIPLIER, retainedBeforePreparation, maximum);
5114
+ return {
5115
+ counts,
5116
+ preparedBytes: bytes
5117
+ };
5118
+ };
5119
+ /** Creates the inspection digest without materializing a project-sized key projection. */
5120
+ var createInspectionDigest = (counts, formatVersion, items, summary, valid) => createIdentity((function* () {
5121
+ yield "core4-prepared-inspection";
5122
+ yield serializeDeterministically({
5123
+ counts,
5124
+ formatVersion,
5125
+ summary,
5126
+ valid
5127
+ });
5128
+ for (const { key } of items) yield key;
5129
+ })());
5130
+ var findStartIndex = (items, lastKey) => {
5131
+ if (lastKey === null) return 0;
5132
+ let lower = 0;
5133
+ let upper = items.length - 1;
5134
+ while (lower <= upper) {
5135
+ const middle = lower + Math.floor((upper - lower) / 2);
5136
+ const candidate = items[middle];
5137
+ if (candidate === void 0) return invalidArgument$1();
5138
+ if (candidate.key === lastKey) return middle + 1;
5139
+ if (candidate.key < lastKey) lower = middle + 1;
5140
+ else upper = middle - 1;
5141
+ }
5142
+ return invalidArgument$1();
5143
+ };
5144
+ /** Creates the returned inspection from content-free state only. */
5145
+ var createPreparedProjectInspection = (input) => {
5146
+ const { counts, formatVersion, items, maxEntries, preparedBytes, resourceUsage: validationUsage, source, summary, valid } = input;
5147
+ const views = createViews(items);
5148
+ const inspectionDigest = createInspectionDigest(counts, formatVersion, items, summary, valid);
5149
+ const resourceUsage = freezeRecursively({
5150
+ canonicalBytes: validationUsage.canonicalBytes,
5151
+ peakRetainedBytes: Math.max(validationUsage.peakRetainedBytes, validationUsage.retainedBytes + preparedBytes),
5152
+ preparedBytes,
5153
+ retainedBytes: preparedBytes,
5154
+ totalBytesRead: validationUsage.totalBytesRead
5155
+ });
5156
+ const readPage = (pageInput) => {
5157
+ 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();
5158
+ const viewItems = views[pageInput.view];
5159
+ const startIndex = findStartIndex(viewItems, decodeCursor(pageInput.cursor, inspectionDigest, pageInput.view));
5160
+ const records = viewItems.slice(startIndex, startIndex + pageInput.maxItems).map(({ item, key }, index) => {
5161
+ return {
5162
+ item,
5163
+ nextCursor: startIndex + index + 1 >= viewItems.length ? null : encodeCursor(inspectionDigest, pageInput.view, key)
5164
+ };
5165
+ });
5166
+ const nextCursor = records.at(-1)?.nextCursor ?? null;
5167
+ return freezeRecursively({
5168
+ counts,
5169
+ formatVersion,
5170
+ inspectionDigest,
5171
+ page: {
5172
+ isComplete: nextCursor === null,
5173
+ nextCursor,
5174
+ records,
5175
+ totalItems: viewItems.length
5176
+ },
5177
+ source,
5178
+ summary,
5179
+ valid,
5180
+ view: pageInput.view
5181
+ });
5182
+ };
5183
+ return freezeRecursively({
5184
+ counts,
5185
+ formatVersion,
5186
+ inspectionDigest,
5187
+ readPage,
5188
+ resourceUsage,
5189
+ source,
5190
+ summary,
5191
+ valid
5192
+ });
5193
+ };
5194
+ /** Prepares one immutable content-free inspection for synchronous bounded page reads. */
5195
+ var createProjectInspection = async (input, options) => {
5196
+ const state = await validateProjectState(input, options);
5197
+ const estimate = estimatePreparedProjectInspection(state, {
5010
5198
  agents: state.result.summary?.counts.agents ?? 0,
5011
5199
  context: state.result.summary?.counts.context ?? 0,
5012
5200
  decisions: state.result.summary?.counts.decisions ?? 0,
5013
5201
  diagnostics: state.result.diagnostics.length,
5014
5202
  evidence: state.result.evidence.length,
5015
- metadata: metadata.length,
5016
5203
  mirrors: state.result.summary?.counts.mirrors ?? 0,
5017
5204
  runtimes: state.result.summary?.counts.runtimes ?? 0,
5018
5205
  unresolved: state.result.summary?.counts.unresolved ?? 0
5019
- };
5020
- const inspectionDigest = createIdentity([serializeDeterministically({
5206
+ }, options.limits.maxRetainedBytes);
5207
+ const counts = freezeRecursively(estimate.counts);
5208
+ const items = freezeRecursively(createKeyedItems(iterateProjectInspectionItems(state)));
5209
+ return createPreparedProjectInspection({
5021
5210
  counts,
5022
5211
  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,
5040
- formatVersion: state.result.formatVersion,
5041
- inspectionDigest,
5042
- page: {
5043
- isComplete: nextCursor === null,
5044
- nextCursor,
5045
- records,
5046
- totalItems: items.length
5047
- },
5212
+ items,
5213
+ maxEntries: options.limits.maxEntries,
5214
+ preparedBytes: estimate.preparedBytes,
5215
+ resourceUsage: state.resourceUsage,
5048
5216
  source: state.result.source,
5049
5217
  summary: state.result.summary,
5050
- valid: state.result.valid,
5051
- view: input.view
5218
+ valid: state.result.valid
5052
5219
  });
5053
5220
  };
5054
5221
  //#endregion
@@ -5064,10 +5231,13 @@ var invalidArgument = () => {
5064
5231
  });
5065
5232
  };
5066
5233
  /** Throws the stable resource failure for an exhausted scope-input budget. */
5067
- var resourceLimitExceeded = (limit) => {
5234
+ var resourceLimitExceeded = (limit, limitMaximum, observedUsage) => {
5068
5235
  throw new CoreOperationException({
5069
5236
  code: "RESOURCE_LIMIT_EXCEEDED",
5070
5237
  limit,
5238
+ limitMaximum,
5239
+ nextAction: "reduce-input-or-increase-limit",
5240
+ observedUsage,
5071
5241
  operation: "match-manifest-scope"
5072
5242
  });
5073
5243
  };
@@ -5095,14 +5265,14 @@ var normalizeManifestScopeInput = (input, limits) => {
5095
5265
  const manifest = input["manifest"];
5096
5266
  const pathCandidates = input["paths"];
5097
5267
  if (!isRecord(manifest) || !Array.isArray(pathCandidates)) return invalidArgument();
5098
- if (pathCandidates.length > limits.maxEntries) return resourceLimitExceeded("maxEntries");
5268
+ if (pathCandidates.length > limits.maxEntries) return resourceLimitExceeded("maxEntries", limits.maxEntries, pathCandidates.length);
5099
5269
  const uniquePaths = /* @__PURE__ */ new Set();
5100
5270
  let totalBytes = 0;
5101
5271
  for (const [index, candidate] of pathCandidates.entries()) {
5102
5272
  if (typeof candidate !== "string") return invalidArgument();
5103
5273
  const path = parseRepositoryPath(candidate);
5104
5274
  totalBytes += measureUtf8ByteLength(path) + (index === 0 ? 0 : 1);
5105
- if (totalBytes > limits.maxTotalBytesRead) return resourceLimitExceeded("maxTotalBytesRead");
5275
+ if (totalBytes > limits.maxTotalBytesRead) return resourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytes);
5106
5276
  uniquePaths.add(path);
5107
5277
  }
5108
5278
  return {
@@ -5293,6 +5463,9 @@ var compileManifestScope = (manifest, limits) => {
5293
5463
  if (declarations.length > limits.maxEntries) throw new CoreOperationException({
5294
5464
  code: "RESOURCE_LIMIT_EXCEEDED",
5295
5465
  limit: "maxEntries",
5466
+ limitMaximum: limits.maxEntries,
5467
+ nextAction: "reduce-input-or-increase-limit",
5468
+ observedUsage: declarations.length,
5296
5469
  operation: "match-manifest-scope"
5297
5470
  });
5298
5471
  const exact = /* @__PURE__ */ new Map();
@@ -5340,6 +5513,9 @@ var addMatch = (matches, inputPath, relationship, limits) => {
5340
5513
  if (matches.length >= limits.maxEntries) throw new CoreOperationException({
5341
5514
  code: "RESOURCE_LIMIT_EXCEEDED",
5342
5515
  limit: "maxEntries",
5516
+ limitMaximum: limits.maxEntries,
5517
+ nextAction: "reduce-input-or-increase-limit",
5518
+ observedUsage: matches.length + 1,
5343
5519
  operation: "match-manifest-scope"
5344
5520
  });
5345
5521
  matches.push(createMatch(inputPath, relationship));
@@ -5454,7 +5630,7 @@ var createCore = (options) => {
5454
5630
  calculateContentDigest: (input) => {
5455
5631
  return calculateContentDigest(input, createCoreOperationOptionsSnapshot(snapshot).limits);
5456
5632
  },
5457
- inspectProjectPage: (input) => inspectProjectPage(input, createCoreOperationOptionsSnapshot(snapshot)),
5633
+ createProjectInspection: (input) => createProjectInspection(input, createCoreOperationOptionsSnapshot(snapshot)),
5458
5634
  validateProject: (input) => validateProject(input, createCoreOperationOptionsSnapshot(snapshot)),
5459
5635
  matchManifestScope: (input) => matchManifestScope(input, createCoreOperationOptionsSnapshot(snapshot)),
5460
5636
  normalizeText: (input) => {