@moldea.ai/core 3.0.1 → 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 (34) 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 +38 -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 +240 -73
  18. package/dist/index.js.map +1 -1
  19. package/dist/options/index.d.ts.map +1 -1
  20. package/dist/project-agent-assignments/index.d.ts +4 -0
  21. package/dist/project-agent-assignments/index.d.ts.map +1 -0
  22. package/dist/project-inspection/index.d.ts +5 -0
  23. package/dist/project-inspection/index.d.ts.map +1 -0
  24. package/dist/project-validation/index.d.ts +2 -0
  25. package/dist/project-validation/index.d.ts.map +1 -1
  26. package/dist/repository-inspection-session/index.d.ts +8 -0
  27. package/dist/repository-inspection-session/index.d.ts.map +1 -1
  28. package/dist/scope-matching/matching.d.ts.map +1 -1
  29. package/dist/scope-matching/validations.d.ts.map +1 -1
  30. package/dist/text/index.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/dist/chunks/exceptions-DWsZTpqs.js.map +0 -1
  33. package/dist/project-inspection-page/index.d.ts +0 -5
  34. 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
  /**
@@ -2300,6 +2311,13 @@ var parseManifestDocument = async (input, options, operation = "parse-manifest")
2300
2311
  });
2301
2312
  };
2302
2313
  //#endregion
2314
+ //#region src/project-agent-assignments/index.ts
2315
+ /** Collects deterministic content-free agent-to-runtime assignments from a validated project. */
2316
+ var collectProjectAgentAssignments = (project) => project.agents.map((agent) => ({
2317
+ agentId: agent.id,
2318
+ runtimeId: agent.declaration.runtime.id
2319
+ })).sort((left, right) => compareExactStrings(left.agentId, right.agentId) || compareExactStrings(left.runtimeId, right.runtimeId));
2320
+ //#endregion
2303
2321
  //#region src/project-metadata/index.ts
2304
2322
  var createAssetItem = (asset, kind, agentId = null, decisionId = null) => ({
2305
2323
  agentId,
@@ -2465,6 +2483,9 @@ var registerRawOutput = (outputCounts, key, amount, context) => {
2465
2483
  adapterId: context.adapterId,
2466
2484
  code: "RESOURCE_LIMIT_EXCEEDED",
2467
2485
  limit,
2486
+ limitMaximum: context.limits[limit],
2487
+ nextAction: "reduce-input-or-increase-limit",
2488
+ observedUsage: outputCounts[key] + amount,
2468
2489
  operation: "validate-adapter"
2469
2490
  });
2470
2491
  outputCounts[key] += amount;
@@ -2830,6 +2851,10 @@ var inspectRuntimeAdapters = async (project, session, options, signal) => {
2830
2851
  var MANIFEST_PATH$3 = parseRepositoryPath("/moldea/moldea.yaml");
2831
2852
  var SOURCE_PAGE_ENTRIES = 256;
2832
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();
2833
2858
  var invalidSourceData$3 = (operation, path) => {
2834
2859
  throw new RepositorySourceException({
2835
2860
  code: "INVALID_SOURCE_DATA",
@@ -2862,10 +2887,13 @@ var isStrictDescendant = (path, prefix) => {
2862
2887
  const descendantPrefix = prefix === REPOSITORY_ROOT ? REPOSITORY_ROOT : `${prefix}/`;
2863
2888
  return path.startsWith(descendantPrefix);
2864
2889
  };
2865
- var throwResourceLimitExceeded = (limit) => {
2890
+ var throwResourceLimitExceeded = (limit, limitMaximum, observedUsage) => {
2866
2891
  throw new CoreOperationException({
2867
2892
  code: "RESOURCE_LIMIT_EXCEEDED",
2868
2893
  limit,
2894
+ limitMaximum,
2895
+ nextAction: "reduce-input-or-increase-limit",
2896
+ observedUsage,
2869
2897
  operation: "validate-project"
2870
2898
  });
2871
2899
  };
@@ -2884,6 +2912,9 @@ var createSourceOptions = (sessionSignal, operationSignal) => {
2884
2912
  */
2885
2913
  var createRepositoryInspectionSession = (repository, limits, signal) => {
2886
2914
  const seenPaths = /* @__PURE__ */ new Set();
2915
+ let canonicalBytes = 0;
2916
+ let peakRetainedBytes = 0;
2917
+ let retainedBytes = 0;
2887
2918
  let totalBytesRead = 0;
2888
2919
  const adapterLimits = Object.freeze({
2889
2920
  maxEntries: limits.maxEntries,
@@ -2899,9 +2930,19 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2899
2930
  operation: "validate-project"
2900
2931
  });
2901
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
+ };
2902
2942
  const registerPath = (path) => {
2903
2943
  if (path === REPOSITORY_ROOT || seenPaths.has(path)) return;
2904
- 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);
2905
2946
  seenPaths.add(path);
2906
2947
  };
2907
2948
  const throwIfSignalAborted = (operationSignal) => {
@@ -2910,7 +2951,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2910
2951
  };
2911
2952
  throwIfSignalAborted();
2912
2953
  const reserveReadBytes = (byteLength) => {
2913
- if (byteLength > limits.maxTotalBytesRead - totalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead");
2954
+ if (byteLength > limits.maxTotalBytesRead - totalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytesRead + byteLength);
2914
2955
  totalBytesRead += byteLength;
2915
2956
  };
2916
2957
  const getEntry = async (path, options) => {
@@ -2957,15 +2998,29 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2957
2998
  };
2958
2999
  const loadFile = async (path, operationSignal) => {
2959
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);
2960
3005
  const firstPage = await repository.readFilePage(path, {
2961
- maxBytes: Math.min(SOURCE_PAGE_BYTES, Math.max(1, limits[fileLimit])),
3006
+ maxBytes: firstPageMaximumBytes,
2962
3007
  offset: 0,
2963
3008
  ...createSourceOptions(signal, operationSignal) ?? {}
2964
3009
  });
2965
3010
  throwIfSignalAborted(operationSignal);
2966
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);
2967
- 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
+ }
2968
3019
  reserveReadBytes(firstPage.totalBytes);
3020
+ const canonicalRetainedBytes = firstPage.totalBytes * CANONICAL_BYTE_RETENTION_MULTIPLIER;
3021
+ observeTransientBytes(canonicalRetainedBytes + firstPageBytes);
3022
+ reserveRetainedBytes(canonicalRetainedBytes);
3023
+ canonicalBytes += firstPage.totalBytes;
2969
3024
  const content = new Uint8Array(firstPage.totalBytes);
2970
3025
  let offset = 0;
2971
3026
  let page = firstPage;
@@ -3026,7 +3081,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
3026
3081
  const listAdapterEntriesPage = async (options) => {
3027
3082
  const prefix = options.prefix === void 0 ? REPOSITORY_ROOT : parseRepositoryPath(options.prefix);
3028
3083
  registerPath(prefix);
3029
- 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);
3030
3085
  throwIfSignalAborted(options.signal);
3031
3086
  const page = await repository.listEntriesPage({
3032
3087
  ...options.cursor === void 0 ? {} : { cursor: options.cursor },
@@ -3057,7 +3112,8 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
3057
3112
  throwIfSignalAborted(options.signal);
3058
3113
  registerPath(parsedPath);
3059
3114
  const reservedBytes = Math.min(options.maxBytes, limits.maxTotalBytesRead - totalBytesRead);
3060
- if (reservedBytes < 1) return throwResourceLimitExceeded("maxTotalBytesRead");
3115
+ if (reservedBytes < 1) return throwResourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytesRead + 1);
3116
+ observeTransientBytes(reservedBytes);
3061
3117
  reserveReadBytes(reservedBytes);
3062
3118
  let page;
3063
3119
  try {
@@ -3094,6 +3150,12 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
3094
3150
  });
3095
3151
  return Object.freeze({
3096
3152
  adapterRepository,
3153
+ getResourceUsage: () => Object.freeze({
3154
+ canonicalBytes,
3155
+ peakRetainedBytes,
3156
+ retainedBytes,
3157
+ totalBytesRead
3158
+ }),
3097
3159
  reader: Object.freeze({
3098
3160
  getEntry,
3099
3161
  iterateEntries,
@@ -4839,6 +4901,7 @@ var validateProjectState = async (input, options) => {
4839
4901
  }, operationOptions);
4840
4902
  if (universal.project === null) return freezeRecursively({
4841
4903
  project: null,
4904
+ resourceUsage: session.getResourceUsage(),
4842
4905
  result: {
4843
4906
  diagnostics: universal.diagnostics,
4844
4907
  evidence: [],
@@ -4852,6 +4915,7 @@ var validateProjectState = async (input, options) => {
4852
4915
  const availabilityDiagnostics = validateRuntimeAdapterAvailability(universal.runtimeLocations, universal.project.formatVersion, operationOptions);
4853
4916
  if (availabilityDiagnostics.length > 0) return freezeRecursively({
4854
4917
  project: universal.project,
4918
+ resourceUsage: session.getResourceUsage(),
4855
4919
  result: {
4856
4920
  diagnostics: availabilityDiagnostics,
4857
4921
  evidence: [],
@@ -4865,6 +4929,7 @@ var validateProjectState = async (input, options) => {
4865
4929
  const valid = adapterInspection.diagnostics.length === 0;
4866
4930
  return freezeRecursively({
4867
4931
  project: universal.project,
4932
+ resourceUsage: session.getResourceUsage(),
4868
4933
  result: {
4869
4934
  diagnostics: adapterInspection.diagnostics,
4870
4935
  evidence: adapterInspection.evidence,
@@ -4878,16 +4943,30 @@ var validateProjectState = async (input, options) => {
4878
4943
  /** Validates one project without returning canonical document bodies. */
4879
4944
  var validateProject = async (input, options) => (await validateProjectState(input, options)).result;
4880
4945
  //#endregion
4881
- //#region src/project-inspection-page/index.ts
4882
- 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;
4883
4952
  var encoder = new TextEncoder();
4884
4953
  var invalidArgument$1 = () => {
4885
4954
  throw new CoreOperationException({
4886
4955
  code: "INVALID_ARGUMENT",
4887
- operation: "inspect-project-page"
4956
+ operation: "create-project-inspection"
4888
4957
  });
4889
4958
  };
4890
- /** Serializes the closed inspection records with recursively sorted object keys. */
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"
4967
+ });
4968
+ };
4969
+ /** Serializes content-free inspection state with recursively sorted object keys. */
4891
4970
  var serializeDeterministically = (candidate) => {
4892
4971
  if (candidate === null || typeof candidate !== "object") {
4893
4972
  const serialized = JSON.stringify(candidate);
@@ -4898,6 +4977,11 @@ var serializeDeterministically = (candidate) => {
4898
4977
  };
4899
4978
  var createIdentity = (parts) => createRepositoryIdentity(parts.map((part) => encoder.encode(part)));
4900
4979
  var createItemBaseKey = (item) => {
4980
+ if (item.kind === "agent") return [
4981
+ "3",
4982
+ item.agent.agentId,
4983
+ item.agent.runtimeId
4984
+ ].join("\0");
4901
4985
  if (item.kind === "metadata") return [
4902
4986
  "0",
4903
4987
  item.metadata.path,
@@ -4921,7 +5005,7 @@ var createItemBaseKey = (item) => {
4921
5005
  createIdentity([serializeDeterministically(item.evidence)])
4922
5006
  ].join(":");
4923
5007
  };
4924
- /** Assigns stable unique keys without relying on collection offsets. */
5008
+ /** Assigns stable unique keys once without relying on collection offsets. */
4925
5009
  var createKeyedItems = (items) => {
4926
5010
  const sorted = items.map((item) => ({
4927
5011
  baseKey: createItemBaseKey(item),
@@ -4952,7 +5036,7 @@ var encodeCursor = (inspectionDigest, view, lastKey) => [
4952
5036
  ].join(":");
4953
5037
  var decodeCursor = (cursor, inspectionDigest, view) => {
4954
5038
  if (cursor === void 0) return null;
4955
- 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);
4956
5040
  if (match === null || match[1] !== view || match[3] !== inspectionDigest) return invalidArgument$1();
4957
5041
  let lastKey;
4958
5042
  try {
@@ -4963,19 +5047,112 @@ var decodeCursor = (cursor, inspectionDigest, view) => {
4963
5047
  if (lastKey.length === 0 || match[4] !== createCursorChecksum(inspectionDigest, lastKey, view)) return invalidArgument$1();
4964
5048
  return lastKey;
4965
5049
  };
4966
- var selectViewItems = (items, view) => view === "all" ? items : items.filter(({ item }) => {
4967
- if (view === "diagnostics") return item.kind === "diagnostic";
4968
- return 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")
4969
5055
  });
4970
- /** Returns one bounded content-free view over a completely validated project snapshot. */
4971
- var inspectProjectPage = async (input, options) => {
4972
- 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();
4973
- const state = await validateProjectState({
4974
- repository: input.repository,
4975
- ...input.signal === void 0 ? {} : { signal: input.signal }
4976
- }, 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);
4977
5153
  const metadata = state.project === null ? [] : collectProjectMetadata(state.project);
4978
- const allItems = createKeyedItems([
5154
+ const agentAssignments = state.project === null ? [] : collectProjectAgentAssignments(state.project);
5155
+ const items = freezeRecursively(createKeyedItems([
4979
5156
  ...metadata.map((metadataItem) => ({
4980
5157
  kind: "metadata",
4981
5158
  metadata: metadataItem
@@ -4987,51 +5164,32 @@ var inspectProjectPage = async (input, options) => {
4987
5164
  ...state.result.evidence.map((evidence) => ({
4988
5165
  evidence,
4989
5166
  kind: "evidence"
5167
+ })),
5168
+ ...agentAssignments.map((agent) => ({
5169
+ agent,
5170
+ kind: "agent"
4990
5171
  }))
4991
- ]);
4992
- const counts = {
4993
- agents: state.result.summary?.counts.agents ?? 0,
4994
- context: state.result.summary?.counts.context ?? 0,
4995
- decisions: state.result.summary?.counts.decisions ?? 0,
4996
- diagnostics: state.result.diagnostics.length,
4997
- evidence: state.result.evidence.length,
4998
- metadata: metadata.length,
4999
- mirrors: state.result.summary?.counts.mirrors ?? 0,
5000
- runtimes: state.result.summary?.counts.runtimes ?? 0,
5001
- unresolved: state.result.summary?.counts.unresolved ?? 0
5002
- };
5003
- const inspectionDigest = createIdentity([serializeDeterministically({
5004
- counts,
5005
- formatVersion: state.result.formatVersion,
5006
- itemKeys: allItems.map(({ key }) => key),
5007
- summary: state.result.summary,
5008
- valid: state.result.valid
5009
- })]);
5010
- const items = selectViewItems(allItems, input.view);
5011
- const lastKey = decodeCursor(input.cursor, inspectionDigest, input.view);
5012
- const startIndex = lastKey === null ? 0 : items.findIndex(({ key }) => key === lastKey) + 1;
5013
- if (lastKey !== null && startIndex === 0) return invalidArgument$1();
5014
- const records = items.slice(startIndex, startIndex + input.maxItems).map(({ item, key }, index) => {
5015
- return {
5016
- item,
5017
- nextCursor: startIndex + index + 1 >= items.length ? null : encodeCursor(inspectionDigest, input.view, key)
5018
- };
5019
- });
5020
- const nextCursor = records.at(-1)?.nextCursor ?? null;
5021
- return freezeRecursively({
5022
- 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
+ }),
5023
5185
  formatVersion: state.result.formatVersion,
5024
- inspectionDigest,
5025
- page: {
5026
- isComplete: nextCursor === null,
5027
- nextCursor,
5028
- records,
5029
- totalItems: items.length
5030
- },
5186
+ items,
5187
+ maxEntries: options.limits.maxEntries,
5188
+ maxRetainedBytes: options.limits.maxRetainedBytes,
5189
+ resourceUsage: state.resourceUsage,
5031
5190
  source: state.result.source,
5032
5191
  summary: state.result.summary,
5033
- valid: state.result.valid,
5034
- view: input.view
5192
+ valid: state.result.valid
5035
5193
  });
5036
5194
  };
5037
5195
  //#endregion
@@ -5047,10 +5205,13 @@ var invalidArgument = () => {
5047
5205
  });
5048
5206
  };
5049
5207
  /** Throws the stable resource failure for an exhausted scope-input budget. */
5050
- var resourceLimitExceeded = (limit) => {
5208
+ var resourceLimitExceeded = (limit, limitMaximum, observedUsage) => {
5051
5209
  throw new CoreOperationException({
5052
5210
  code: "RESOURCE_LIMIT_EXCEEDED",
5053
5211
  limit,
5212
+ limitMaximum,
5213
+ nextAction: "reduce-input-or-increase-limit",
5214
+ observedUsage,
5054
5215
  operation: "match-manifest-scope"
5055
5216
  });
5056
5217
  };
@@ -5078,14 +5239,14 @@ var normalizeManifestScopeInput = (input, limits) => {
5078
5239
  const manifest = input["manifest"];
5079
5240
  const pathCandidates = input["paths"];
5080
5241
  if (!isRecord(manifest) || !Array.isArray(pathCandidates)) return invalidArgument();
5081
- if (pathCandidates.length > limits.maxEntries) return resourceLimitExceeded("maxEntries");
5242
+ if (pathCandidates.length > limits.maxEntries) return resourceLimitExceeded("maxEntries", limits.maxEntries, pathCandidates.length);
5082
5243
  const uniquePaths = /* @__PURE__ */ new Set();
5083
5244
  let totalBytes = 0;
5084
5245
  for (const [index, candidate] of pathCandidates.entries()) {
5085
5246
  if (typeof candidate !== "string") return invalidArgument();
5086
5247
  const path = parseRepositoryPath(candidate);
5087
5248
  totalBytes += measureUtf8ByteLength(path) + (index === 0 ? 0 : 1);
5088
- if (totalBytes > limits.maxTotalBytesRead) return resourceLimitExceeded("maxTotalBytesRead");
5249
+ if (totalBytes > limits.maxTotalBytesRead) return resourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytes);
5089
5250
  uniquePaths.add(path);
5090
5251
  }
5091
5252
  return {
@@ -5276,6 +5437,9 @@ var compileManifestScope = (manifest, limits) => {
5276
5437
  if (declarations.length > limits.maxEntries) throw new CoreOperationException({
5277
5438
  code: "RESOURCE_LIMIT_EXCEEDED",
5278
5439
  limit: "maxEntries",
5440
+ limitMaximum: limits.maxEntries,
5441
+ nextAction: "reduce-input-or-increase-limit",
5442
+ observedUsage: declarations.length,
5279
5443
  operation: "match-manifest-scope"
5280
5444
  });
5281
5445
  const exact = /* @__PURE__ */ new Map();
@@ -5323,6 +5487,9 @@ var addMatch = (matches, inputPath, relationship, limits) => {
5323
5487
  if (matches.length >= limits.maxEntries) throw new CoreOperationException({
5324
5488
  code: "RESOURCE_LIMIT_EXCEEDED",
5325
5489
  limit: "maxEntries",
5490
+ limitMaximum: limits.maxEntries,
5491
+ nextAction: "reduce-input-or-increase-limit",
5492
+ observedUsage: matches.length + 1,
5326
5493
  operation: "match-manifest-scope"
5327
5494
  });
5328
5495
  matches.push(createMatch(inputPath, relationship));
@@ -5437,7 +5604,7 @@ var createCore = (options) => {
5437
5604
  calculateContentDigest: (input) => {
5438
5605
  return calculateContentDigest(input, createCoreOperationOptionsSnapshot(snapshot).limits);
5439
5606
  },
5440
- inspectProjectPage: (input) => inspectProjectPage(input, createCoreOperationOptionsSnapshot(snapshot)),
5607
+ createProjectInspection: (input) => createProjectInspection(input, createCoreOperationOptionsSnapshot(snapshot)),
5441
5608
  validateProject: (input) => validateProject(input, createCoreOperationOptionsSnapshot(snapshot)),
5442
5609
  matchManifestScope: (input) => matchManifestScope(input, createCoreOperationOptionsSnapshot(snapshot)),
5443
5610
  normalizeText: (input) => {