@moldea.ai/core 2.1.0 → 3.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 (53) hide show
  1. package/README.md +37 -99
  2. package/dist/adapter/index.d.ts +97 -14
  3. package/dist/adapter/index.d.ts.map +1 -1
  4. package/dist/adapter-execution/index.d.ts.map +1 -1
  5. package/dist/adapter-repository/index.d.ts +9 -0
  6. package/dist/adapter-repository/index.d.ts.map +1 -0
  7. package/dist/adapter-validation/index.d.ts +2 -3
  8. package/dist/adapter-validation/index.d.ts.map +1 -1
  9. package/dist/adapter.js +93 -0
  10. package/dist/adapter.js.map +1 -0
  11. package/dist/agent-assets/index.d.ts +2 -2
  12. package/dist/agent-assets/index.d.ts.map +1 -1
  13. package/dist/canonical-content-page/index.d.ts +5 -0
  14. package/dist/canonical-content-page/index.d.ts.map +1 -0
  15. package/dist/canonical-discovery/index.d.ts +3 -2
  16. package/dist/canonical-discovery/index.d.ts.map +1 -1
  17. package/dist/chunks/exceptions-DWsZTpqs.js +71 -0
  18. package/dist/chunks/exceptions-DWsZTpqs.js.map +1 -0
  19. package/dist/contracts/index.d.ts +129 -6
  20. package/dist/contracts/index.d.ts.map +1 -1
  21. package/dist/core/index.d.ts.map +1 -1
  22. package/dist/decision/index.d.ts +1 -1
  23. package/dist/decision/index.d.ts.map +1 -1
  24. package/dist/decision-graph/index.d.ts +3 -2
  25. package/dist/decision-graph/index.d.ts.map +1 -1
  26. package/dist/exceptions/index.d.ts +2 -2
  27. package/dist/exceptions/index.d.ts.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +688 -228
  31. package/dist/index.js.map +1 -1
  32. package/dist/manifest/index.d.ts +1 -1
  33. package/dist/manifest/index.d.ts.map +1 -1
  34. package/dist/mirrors/index.d.ts +3 -2
  35. package/dist/mirrors/index.d.ts.map +1 -1
  36. package/dist/project-assets/index.d.ts +4 -3
  37. package/dist/project-assets/index.d.ts.map +1 -1
  38. package/dist/project-inspection-page/index.d.ts +5 -0
  39. package/dist/project-inspection-page/index.d.ts.map +1 -0
  40. package/dist/project-metadata/index.d.ts +6 -0
  41. package/dist/project-metadata/index.d.ts.map +1 -0
  42. package/dist/{project-inspection → project-validation}/index.d.ts +8 -2
  43. package/dist/project-validation/index.d.ts.map +1 -0
  44. package/dist/repository-inspection-session/index.d.ts +13 -3
  45. package/dist/repository-inspection-session/index.d.ts.map +1 -1
  46. package/dist/repository-reference-validation/index.d.ts +3 -2
  47. package/dist/repository-reference-validation/index.d.ts.map +1 -1
  48. package/dist/repository-text/index.d.ts +3 -2
  49. package/dist/repository-text/index.d.ts.map +1 -1
  50. package/dist/runtime-guidance/index.d.ts +3 -2
  51. package/dist/runtime-guidance/index.d.ts.map +1 -1
  52. package/package.json +2 -2
  53. package/dist/project-inspection/index.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { REPOSITORY_ROOT, RepositoryPathException, RepositorySourceException, isRepositoryPath, parseRepositoryPath } from "@moldea.ai/repository";
2
- import { Exception } from "error-message-utils";
1
+ import { n as CoreOperationException, t as CoreConfigurationException } from "./chunks/exceptions-DWsZTpqs.js";
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
5
5
  var RECOGNIZED_RUNTIME_ADAPTER_IDS = Object.freeze([
@@ -25,69 +25,154 @@ var DEFAULT_CORE_RESOURCE_LIMITS = Object.freeze({
25
25
  maxTotalBytesRead: 134217728
26
26
  });
27
27
  //#endregion
28
- //#region src/exceptions/index.ts
29
- var CONFIGURATION_ERROR_MESSAGES = {
30
- DUPLICATE_ADAPTER_ID: "A runtime adapter ID is registered more than once.",
31
- INVALID_ADAPTER_DEFINITION: "A runtime adapter definition is invalid.",
32
- INVALID_RESOURCE_LIMIT: "A Core resource limit is invalid.",
33
- RESERVED_ADAPTER_ID: "A reserved runtime adapter ID was supplied."
34
- };
35
- var OPERATION_ERROR_MESSAGES = {
36
- ABORTED: "The Core operation was aborted.",
37
- ADAPTER_EXECUTION_FAILED: "A runtime adapter failed during inspection.",
38
- INVALID_ARGUMENT: "The Core operation received an invalid argument.",
39
- RESOURCE_LIMIT_EXCEEDED: "A Core resource limit was exceeded."
40
- };
41
- var OPERATION_ERROR_RETRYABILITY = {
42
- ABORTED: true,
43
- ADAPTER_EXECUTION_FAILED: false,
44
- INVALID_ARGUMENT: false,
45
- RESOURCE_LIMIT_EXCEEDED: false
46
- };
47
- var attachCause = (exception, cause) => {
48
- if (cause === void 0) return;
49
- Object.defineProperty(exception, "cause", {
50
- configurable: true,
51
- enumerable: false,
52
- value: cause,
53
- writable: false
28
+ //#region src/immutable/index.ts
29
+ /**
30
+ * Recursively freezes a Core-owned value without cloning it.
31
+ * @param value The detached acyclic value whose already-frozen objects are deeply frozen.
32
+ * @returns The same value with every mutable reachable object frozen.
33
+ */
34
+ var freezeRecursively = (value) => {
35
+ if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value;
36
+ for (const key of Reflect.ownKeys(value)) {
37
+ const child = value[key];
38
+ freezeRecursively(child);
39
+ }
40
+ return Object.freeze(value);
41
+ };
42
+ /**
43
+ * Creates a record that cannot collide with prototype-chain properties.
44
+ * @param entries The own string-keyed entries to assign.
45
+ * @returns A mutable null-prototype record for later normalization and freezing.
46
+ */
47
+ var createNullPrototypeRecord = (entries) => {
48
+ const record = Object.create(null);
49
+ for (const [key, value] of entries) record[key] = value;
50
+ return record;
51
+ };
52
+ //#endregion
53
+ //#region src/canonical-content-page/index.ts
54
+ var decoder$1 = new TextDecoder("utf-8", {
55
+ fatal: true,
56
+ ignoreBOM: true
57
+ });
58
+ var invalidArgument$4 = () => {
59
+ throw new CoreOperationException({
60
+ code: "INVALID_ARGUMENT",
61
+ operation: "read-canonical-content-page"
54
62
  });
55
63
  };
56
- /** Represents invalid immutable Core configuration. */
57
- var CoreConfigurationException = class extends Exception {
58
- code;
59
- operation;
60
- adapterId;
61
- /** Creates a configuration exception with safe adapter metadata. */
62
- constructor(options) {
63
- super(CONFIGURATION_ERROR_MESSAGES[options.code], options.code);
64
- this.code = options.code;
65
- this.name = "CoreConfigurationException";
66
- this.operation = options.operation;
67
- this.adapterId = options.adapterId ?? null;
68
- attachCause(this, options.cause);
69
- }
70
- };
71
- /** Represents an operational failure that prevented Core from completing. */
72
- var CoreOperationException = class extends Exception {
73
- code;
74
- operation;
75
- retryable;
76
- adapterId;
77
- agentId;
78
- limit;
79
- /** Creates an operation exception with derived retry and safe scope metadata. */
80
- constructor(options) {
81
- super(OPERATION_ERROR_MESSAGES[options.code], options.code);
82
- this.code = options.code;
83
- this.name = "CoreOperationException";
84
- this.operation = options.operation;
85
- this.retryable = OPERATION_ERROR_RETRYABILITY[options.code];
86
- this.adapterId = options.adapterId ?? null;
87
- this.agentId = options.agentId ?? null;
88
- this.limit = options.limit ?? null;
89
- attachCause(this, options.cause);
64
+ var invalidContent = () => {
65
+ throw new CoreOperationException({
66
+ code: "CONTENT_INVALID",
67
+ operation: "read-canonical-content-page"
68
+ });
69
+ };
70
+ var isRepositoryReader$1 = (candidate) => typeof candidate === "object" && candidate !== null && typeof candidate.getEntry === "function" && typeof candidate.readFilePage === "function";
71
+ var readBytes = async (repository, path, offset, maxBytes, signal) => {
72
+ const chunks = [];
73
+ let byteLength = 0;
74
+ let nextOffset = offset;
75
+ let lastPage = null;
76
+ let totalBytes = null;
77
+ while (byteLength < maxBytes) {
78
+ const page = await repository.readFilePage(path, {
79
+ maxBytes: maxBytes - byteLength,
80
+ offset: nextOffset,
81
+ ...signal === void 0 ? {} : { signal }
82
+ });
83
+ if (page.snapshot.id !== repository.snapshot.id || page.offset !== nextOffset || page.bytes.byteLength > maxBytes - byteLength || page.totalBytes < page.offset + page.bytes.byteLength || totalBytes !== null && page.totalBytes !== totalBytes || page.bytes.byteLength === 0 && !page.isComplete) throw new RepositorySourceException({
84
+ code: "INVALID_SOURCE_DATA",
85
+ operation: "read-file-page",
86
+ path,
87
+ retryable: false
88
+ });
89
+ chunks.push(page.bytes);
90
+ byteLength += page.bytes.byteLength;
91
+ totalBytes = page.totalBytes;
92
+ lastPage = page;
93
+ if (page.isComplete || byteLength === maxBytes) break;
94
+ if (page.nextOffset !== page.offset + page.bytes.byteLength) throw new RepositorySourceException({
95
+ code: "INVALID_SOURCE_DATA",
96
+ operation: "read-file-page",
97
+ path,
98
+ retryable: false
99
+ });
100
+ nextOffset = page.nextOffset;
90
101
  }
102
+ if (lastPage === null) return invalidArgument$4();
103
+ const bytes = new Uint8Array(byteLength);
104
+ let writeOffset = 0;
105
+ for (const chunk of chunks) {
106
+ bytes.set(chunk, writeOffset);
107
+ writeOffset += chunk.byteLength;
108
+ }
109
+ return {
110
+ bytes,
111
+ lastPage
112
+ };
113
+ };
114
+ var decodeSafePrefix = (bytes, maximumBytes) => {
115
+ const minimumCandidate = Math.max(0, maximumBytes - 3);
116
+ for (let end = maximumBytes; end >= minimumCandidate; end -= 1) try {
117
+ const content = decoder$1.decode(bytes.subarray(0, end));
118
+ if (end === 0 && bytes.byteLength > 0) continue;
119
+ return [content, end];
120
+ } catch {}
121
+ return invalidContent();
122
+ };
123
+ /** Reads one explicit canonical file range without materializing the complete file. */
124
+ var readCanonicalContentPage = async (input, options) => {
125
+ if (typeof input !== "object" || input === null || Reflect.ownKeys(input).some((key) => key !== "maxBytes" && key !== "offset" && key !== "path" && key !== "repository" && key !== "signal") || !isRepositoryReader$1(input.repository) || !Number.isSafeInteger(input.maxBytes) || input.maxBytes < 1 || input.maxBytes > options.limits.maxFileBytes || !Number.isSafeInteger(input.offset) || input.offset < 0) return invalidArgument$4();
126
+ const path = parseRepositoryPath(input.path);
127
+ if (!path.startsWith("/moldea/")) return invalidArgument$4();
128
+ const operationOptions = input.signal === void 0 ? void 0 : { signal: input.signal };
129
+ const entry = await input.repository.getEntry(path, operationOptions);
130
+ if (entry === null) throw new RepositorySourceException({
131
+ code: "ENTRY_NOT_FOUND",
132
+ operation: "read-file-page",
133
+ path,
134
+ retryable: false
135
+ });
136
+ if (entry.type !== "file" || entry.byteLength === null) throw new RepositorySourceException({
137
+ code: "ENTRY_NOT_FILE",
138
+ operation: "read-file-page",
139
+ path,
140
+ retryable: false
141
+ });
142
+ if (entry.byteLength > options.limits.maxFileBytes) throw new CoreOperationException({
143
+ code: "RESOURCE_LIMIT_EXCEEDED",
144
+ limit: "maxFileBytes",
145
+ operation: "read-canonical-content-page"
146
+ });
147
+ if (input.offset > entry.byteLength) return invalidArgument$4();
148
+ if (input.offset === entry.byteLength) return freezeRecursively({
149
+ byteEnd: input.offset,
150
+ byteStart: input.offset,
151
+ content: "",
152
+ contentIdentity: entry.contentIdentity,
153
+ isComplete: true,
154
+ nextOffset: null,
155
+ path,
156
+ source: input.repository.snapshot,
157
+ totalBytes: entry.byteLength
158
+ });
159
+ const lookaheadBytes = Math.min(entry.byteLength - input.offset, input.maxBytes + 3);
160
+ const { bytes, lastPage } = await readBytes(input.repository, path, input.offset, lookaheadBytes, input.signal);
161
+ if (lastPage.totalBytes !== entry.byteLength || (bytes[0] & 192) === 128) return invalidArgument$4();
162
+ const [content, consumedBytes] = decodeSafePrefix(bytes, Math.min(input.maxBytes, bytes.byteLength));
163
+ const byteEnd = input.offset + consumedBytes;
164
+ const isComplete = byteEnd === entry.byteLength;
165
+ return freezeRecursively({
166
+ byteEnd,
167
+ byteStart: input.offset,
168
+ content,
169
+ contentIdentity: entry.contentIdentity,
170
+ isComplete,
171
+ nextOffset: isComplete ? null : byteEnd,
172
+ path,
173
+ source: input.repository.snapshot,
174
+ totalBytes: entry.byteLength
175
+ });
91
176
  };
92
177
  //#endregion
93
178
  //#region src/format-validation/index.ts
@@ -248,31 +333,6 @@ var sortRepositoryReferences = (references) => {
248
333
  });
249
334
  };
250
335
  //#endregion
251
- //#region src/immutable/index.ts
252
- /**
253
- * Recursively freezes a Core-owned value without cloning it.
254
- * @param value The detached acyclic value whose already-frozen objects are deeply frozen.
255
- * @returns The same value with every mutable reachable object frozen.
256
- */
257
- var freezeRecursively = (value) => {
258
- if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value;
259
- for (const key of Reflect.ownKeys(value)) {
260
- const child = value[key];
261
- freezeRecursively(child);
262
- }
263
- return Object.freeze(value);
264
- };
265
- /**
266
- * Creates a record that cannot collide with prototype-chain properties.
267
- * @param entries The own string-keyed entries to assign.
268
- * @returns A mutable null-prototype record for later normalization and freezing.
269
- */
270
- var createNullPrototypeRecord = (entries) => {
271
- const record = Object.create(null);
272
- for (const [key, value] of entries) record[key] = value;
273
- return record;
274
- };
275
- //#endregion
276
336
  //#region src/diagnostic-utilities/index.ts
277
337
  var CORE_DIAGNOSTIC_MESSAGES = {
278
338
  MOLDEA_AGENT_DESCRIPTION_INVALID: "The agent description is invalid.",
@@ -904,7 +964,7 @@ var createSourceLocator = (value) => {
904
964
  };
905
965
  //#endregion
906
966
  //#region src/text/index.ts
907
- var encoder = new TextEncoder();
967
+ var encoder$1 = new TextEncoder();
908
968
  var decoder = new TextDecoder("utf-8", {
909
969
  fatal: true,
910
970
  ignoreBOM: true
@@ -982,7 +1042,7 @@ var collectNulDiagnostics = (value, path, diagnostics) => {
982
1042
  offset += 1;
983
1043
  }
984
1044
  };
985
- var invalidArgument$2 = (operation) => {
1045
+ var invalidArgument$3 = (operation) => {
986
1046
  throw new CoreOperationException({
987
1047
  code: "INVALID_ARGUMENT",
988
1048
  operation
@@ -1017,10 +1077,10 @@ var invalidResult = (diagnostics) => {
1017
1077
  * - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
1018
1078
  */
1019
1079
  var readInput = (input, limits, operation, limit, diagnostics) => {
1020
- if (!isRecord$3(input)) return invalidArgument$2(operation);
1080
+ if (!isRecord$3(input)) return invalidArgument$3(operation);
1021
1081
  const pathCandidate = input["path"];
1022
1082
  const content = input["content"];
1023
- if (typeof pathCandidate !== "string" || typeof content !== "string" && !(content instanceof Uint8Array)) return invalidArgument$2(operation);
1083
+ if (typeof pathCandidate !== "string" || typeof content !== "string" && !(content instanceof Uint8Array)) return invalidArgument$3(operation);
1024
1084
  const path = parseRepositoryPath(pathCandidate);
1025
1085
  if (typeof content === "string") {
1026
1086
  const byteLength = measureScalarUtf8ByteLength(content, limits[limit]);
@@ -1075,7 +1135,7 @@ var normalizeTextDocument = (input, limits, operation, limit = "maxFileBytes") =
1075
1135
  diagnostics: [],
1076
1136
  text: freezeRecursively({
1077
1137
  scalarLength: countUnicodeScalars(value),
1078
- utf8ByteLength: encoder.encode(value).byteLength,
1138
+ utf8ByteLength: encoder$1.encode(value).byteLength,
1079
1139
  value
1080
1140
  }),
1081
1141
  valid: true
@@ -1092,7 +1152,7 @@ var toLowercaseHex = (bytes) => {
1092
1152
  * @returns A promise resolving to the canonical lowercase SHA-256 digest.
1093
1153
  */
1094
1154
  var calculateNormalizedTextDigest = async (text) => {
1095
- const bytes = encoder.encode(text.value);
1155
+ const bytes = encoder$1.encode(text.value);
1096
1156
  return `sha256:${toLowercaseHex(new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", bytes)))}`;
1097
1157
  };
1098
1158
  /**
@@ -2240,6 +2300,57 @@ var parseManifestDocument = async (input, options, operation = "parse-manifest")
2240
2300
  });
2241
2301
  };
2242
2302
  //#endregion
2303
+ //#region src/project-metadata/index.ts
2304
+ var createAssetItem = (asset, kind, agentId = null, decisionId = null) => ({
2305
+ agentId,
2306
+ byteLength: asset.utf8ByteLength,
2307
+ canonicalDigest: null,
2308
+ decisionId,
2309
+ digest: asset.digest,
2310
+ kind,
2311
+ path: asset.path,
2312
+ scalarLength: asset.scalarLength
2313
+ });
2314
+ var compareMetadataItems = (left, right) => compareExactStrings(left.path, right.path) || compareExactStrings(left.kind, right.kind) || compareExactStrings(left.agentId ?? "", right.agentId ?? "");
2315
+ /** Collects deterministic content-free asset metadata from one validated project. */
2316
+ var collectProjectMetadata = (project) => {
2317
+ const items = [createAssetItem(project.manifest.asset, "manifest"), createAssetItem(project.project, "project")];
2318
+ for (const context of project.context) items.push(createAssetItem(context.asset, "context"));
2319
+ for (const { decision } of project.decisions) items.push(createAssetItem(decision.asset, "decision", null, decision.id));
2320
+ for (const runtime of project.runtimes) items.push(createAssetItem(runtime.asset, "runtime-guidance"));
2321
+ for (const agent of project.agents) {
2322
+ items.push(createAssetItem(agent.description.asset, "agent-description", agent.id));
2323
+ items.push(createAssetItem(agent.instruction, "agent-instruction", agent.id));
2324
+ if (agent.handoffDescription !== null) items.push(createAssetItem(agent.handoffDescription.asset, "agent-handoff-description", agent.id));
2325
+ for (const mirror of agent.mirrors) items.push({
2326
+ agentId: agent.id,
2327
+ byteLength: mirror.byteLength,
2328
+ canonicalDigest: mirror.canonicalDigest,
2329
+ decisionId: null,
2330
+ digest: mirror.digest,
2331
+ kind: "mirror",
2332
+ path: mirror.path,
2333
+ scalarLength: mirror.scalarLength
2334
+ });
2335
+ }
2336
+ return items.sort(compareMetadataItems);
2337
+ };
2338
+ /** Creates the content-free summary for one validated project snapshot. */
2339
+ var createProjectSummary = (project) => ({
2340
+ counts: {
2341
+ agents: project.agents.length,
2342
+ context: project.context.length,
2343
+ decisions: project.decisions.length,
2344
+ mirrors: project.agents.reduce((count, agent) => count + agent.mirrors.length, 0),
2345
+ runtimes: project.runtimes.length,
2346
+ unresolved: Object.keys(project.unresolved).length
2347
+ },
2348
+ manifestDigest: project.manifest.asset.digest,
2349
+ manifestPath: project.manifest.asset.path,
2350
+ projectDigest: project.project.digest,
2351
+ projectPath: project.project.path
2352
+ });
2353
+ //#endregion
2243
2354
  //#region src/adapter-availability/index.ts
2244
2355
  /**
2245
2356
  * Validates adapter availability and format support before any runtime adapter runs.
@@ -2251,7 +2362,7 @@ var parseManifestDocument = async (input, options, operation = "parse-manifest")
2251
2362
  * - RESOURCE_LIMIT_EXCEEDED: The raw Core diagnostic limit was exceeded.
2252
2363
  */
2253
2364
  var validateRuntimeAdapterAvailability = (locations, formatVersion, options) => {
2254
- const diagnostics = createCoreDiagnosticCollector(options.limits, "inspect-project");
2365
+ const diagnostics = createCoreDiagnosticCollector(options.limits, "validate-project");
2255
2366
  const adapters = new Map(options.adapters.map((adapter) => [adapter.id, adapter]));
2256
2367
  for (const location of locations) {
2257
2368
  if (location.adapterId === "custom") continue;
@@ -2589,18 +2700,58 @@ var validateRuntimeAdapterResult = async (candidate, context, outputCounts) => {
2589
2700
  //#endregion
2590
2701
  //#region src/adapter-execution/index.ts
2591
2702
  var isInspectionBoundaryFailure = (error) => {
2592
- return error instanceof RepositoryPathException || error instanceof RepositorySourceException || error instanceof CoreOperationException && error.operation === "inspect-project" && (error.code === "ABORTED" || error.code === "RESOURCE_LIMIT_EXCEEDED");
2593
- };
2594
- var invokeAdapter = async (adapter, project, session, options, outputCounts, signal) => {
2595
- const agents = freezeRecursively(project.agents.filter(({ declaration }) => declaration.runtime.id === adapter.id));
2596
- if (agents.length === 0) return freezeRecursively({
2597
- diagnostics: [],
2598
- evidence: []
2599
- });
2703
+ return error instanceof RepositoryPathException || error instanceof RepositorySourceException || error instanceof CoreOperationException && error.operation === "validate-project" && (error.code === "ABORTED" || error.code === "RESOURCE_LIMIT_EXCEEDED");
2704
+ };
2705
+ var getRuntimeBindingKey = (reference) => JSON.stringify([reference.path, reference.symbol ?? null]);
2706
+ /** Builds one compact exact-binding index for a single configured runtime. */
2707
+ var createRuntimeAgentBindingIndex = (agents) => {
2708
+ const candidates = /* @__PURE__ */ new Map();
2709
+ for (const agent of agents) {
2710
+ const runtimeAgent = agent.declaration.bindings?.runtimeAgent;
2711
+ if (runtimeAgent === void 0) continue;
2712
+ const key = getRuntimeBindingKey(runtimeAgent);
2713
+ const existing = candidates.get(key);
2714
+ if (existing !== void 0) {
2715
+ candidates.set(key, {
2716
+ candidateCount: existing.kind === "matched" ? 2 : existing.candidateCount + 1,
2717
+ kind: "ambiguous"
2718
+ });
2719
+ continue;
2720
+ }
2721
+ candidates.set(key, {
2722
+ agent,
2723
+ kind: "matched",
2724
+ resolved: freezeRecursively({
2725
+ declaration: agent.declaration,
2726
+ description: agent.description,
2727
+ handoffDescription: agent.handoffDescription,
2728
+ id: agent.id
2729
+ })
2730
+ });
2731
+ }
2732
+ return candidates;
2733
+ };
2734
+ var invokeAdapter = async (adapter, agent, agentBindings, project, session, options, outputCounts, signal) => {
2735
+ const scopedAgent = freezeRecursively(agent);
2736
+ const resolvedAgents = /* @__PURE__ */ new Map([[scopedAgent.id, scopedAgent]]);
2737
+ const repository = session.adapterRepository;
2738
+ const resolveAgent = (reference) => {
2739
+ const resolution = agentBindings.get(getRuntimeBindingKey(reference));
2740
+ if (resolution === void 0) return Object.freeze({ kind: "absent" });
2741
+ if (resolution.kind === "ambiguous") return Object.freeze({
2742
+ candidateCount: resolution.candidateCount,
2743
+ kind: "ambiguous"
2744
+ });
2745
+ resolvedAgents.set(resolution.agent.id, resolution.agent);
2746
+ return Object.freeze({
2747
+ agent: resolution.resolved,
2748
+ kind: "matched"
2749
+ });
2750
+ };
2600
2751
  const context = Object.freeze({
2601
- agents,
2602
- project,
2603
- repository: session.reader,
2752
+ agent: scopedAgent,
2753
+ repository,
2754
+ resolveAgent,
2604
2755
  ...signal === void 0 ? {} : { signal }
2605
2756
  });
2606
2757
  let candidate;
@@ -2615,15 +2766,15 @@ var invokeAdapter = async (adapter, project, session, options, outputCounts, sig
2615
2766
  adapterId: adapter.id,
2616
2767
  cause: error,
2617
2768
  code: "ADAPTER_EXECUTION_FAILED",
2618
- operation: "inspect-project"
2769
+ operation: "validate-project"
2619
2770
  });
2620
2771
  }
2621
2772
  return validateRuntimeAdapterResult(candidate, {
2622
2773
  adapterId: adapter.id,
2623
- agents,
2774
+ agents: [...resolvedAgents.values()],
2624
2775
  limits: options.limits,
2625
2776
  project,
2626
- repository: session.reader,
2777
+ repository: session.adapterRepository,
2627
2778
  ...signal === void 0 ? {} : { signal }
2628
2779
  }, outputCounts);
2629
2780
  };
@@ -2654,10 +2805,20 @@ var inspectRuntimeAdapters = async (project, session, options, signal) => {
2654
2805
  diagnostics: 0,
2655
2806
  evidence: 0
2656
2807
  };
2808
+ const agentsByRuntimeId = /* @__PURE__ */ new Map();
2809
+ for (const agent of project.agents) {
2810
+ const runtimeAgents = agentsByRuntimeId.get(agent.declaration.runtime.id);
2811
+ if (runtimeAgents === void 0) agentsByRuntimeId.set(agent.declaration.runtime.id, [agent]);
2812
+ else runtimeAgents.push(agent);
2813
+ }
2657
2814
  for (const adapter of options.adapters) {
2658
- const result = await invokeAdapter(adapter, project, session, options, outputCounts, signal);
2659
- evidence.push(...result.evidence);
2660
- diagnostics.push(...result.diagnostics);
2815
+ const agents = agentsByRuntimeId.get(adapter.id) ?? [];
2816
+ const agentBindings = createRuntimeAgentBindingIndex(agents);
2817
+ for (const agent of agents) {
2818
+ const result = await invokeAdapter(adapter, agent, agentBindings, project, session, options, outputCounts, signal);
2819
+ evidence.push(...result.evidence);
2820
+ diagnostics.push(...result.diagnostics);
2821
+ }
2661
2822
  }
2662
2823
  return freezeRecursively({
2663
2824
  diagnostics: normalizeDiagnostics(diagnostics),
@@ -2667,6 +2828,8 @@ var inspectRuntimeAdapters = async (project, session, options, signal) => {
2667
2828
  //#endregion
2668
2829
  //#region src/repository-inspection-session/index.ts
2669
2830
  var MANIFEST_PATH$3 = parseRepositoryPath("/moldea/moldea.yaml");
2831
+ var SOURCE_PAGE_ENTRIES = 256;
2832
+ var SOURCE_PAGE_BYTES = 65536;
2670
2833
  var invalidSourceData$3 = (operation, path) => {
2671
2834
  throw new RepositorySourceException({
2672
2835
  code: "INVALID_SOURCE_DATA",
@@ -2680,11 +2843,16 @@ var copyReaderEntry$1 = (candidate, operation, fallbackPath, expectedPath) => {
2680
2843
  const record = candidate;
2681
2844
  const pathCandidate = record["path"];
2682
2845
  const typeCandidate = record["type"];
2846
+ const byteLength = record["byteLength"];
2847
+ const contentIdentity = record["contentIdentity"];
2683
2848
  if (typeof pathCandidate !== "string" || !isRepositoryPath(pathCandidate)) return invalidSourceData$3(operation, fallbackPath);
2684
2849
  const path = parseRepositoryPath(pathCandidate);
2685
2850
  if (expectedPath !== void 0 && path !== expectedPath) return invalidSourceData$3(operation, path);
2686
2851
  if (typeCandidate !== "file" && typeCandidate !== "directory" && typeCandidate !== "symlink") return invalidSourceData$3(operation, path);
2852
+ if (typeCandidate === "file" ? !Number.isSafeInteger(byteLength) || byteLength < 0 || contentIdentity !== null && typeof contentIdentity !== "string" : byteLength !== null || contentIdentity !== null) return invalidSourceData$3(operation, path);
2687
2853
  return {
2854
+ byteLength,
2855
+ contentIdentity,
2688
2856
  path,
2689
2857
  type: typeCandidate
2690
2858
  };
@@ -2698,11 +2866,11 @@ var throwResourceLimitExceeded = (limit) => {
2698
2866
  throw new CoreOperationException({
2699
2867
  code: "RESOURCE_LIMIT_EXCEEDED",
2700
2868
  limit,
2701
- operation: "inspect-project"
2869
+ operation: "validate-project"
2702
2870
  });
2703
2871
  };
2704
2872
  var createSourceOptions = (sessionSignal, operationSignal) => {
2705
- const signal = sessionSignal ?? operationSignal;
2873
+ const signal = sessionSignal === void 0 ? operationSignal : operationSignal === void 0 || operationSignal === sessionSignal ? sessionSignal : AbortSignal.any([sessionSignal, operationSignal]);
2706
2874
  return signal === void 0 ? void 0 : { signal };
2707
2875
  };
2708
2876
  /**
@@ -2710,19 +2878,25 @@ var createSourceOptions = (sessionSignal, operationSignal) => {
2710
2878
  * @param repository The source-neutral reader bound to one coherent snapshot.
2711
2879
  * @param limits The immutable resource limits for the inspection.
2712
2880
  * @param signal Optional cancellation shared by every inspection operation.
2713
- * @returns A frozen session whose reader owns inspection-local budgets and byte caching.
2881
+ * @returns A session that pages source metadata and range-reads bounded canonical files.
2714
2882
  * @throws
2715
2883
  * - ABORTED: Repository inspection was aborted before the session was created.
2716
2884
  */
2717
2885
  var createRepositoryInspectionSession = (repository, limits, signal) => {
2718
- const readCache = /* @__PURE__ */ new Map();
2719
2886
  const seenPaths = /* @__PURE__ */ new Set();
2720
2887
  let totalBytesRead = 0;
2888
+ const adapterLimits = Object.freeze({
2889
+ maxEntries: limits.maxEntries,
2890
+ maxFileBytes: limits.maxFileBytes,
2891
+ maxPageBytes: Math.min(SOURCE_PAGE_BYTES, limits.maxFileBytes),
2892
+ maxPageEntries: Math.min(SOURCE_PAGE_ENTRIES, limits.maxEntries),
2893
+ maxTotalBytesRead: limits.maxTotalBytesRead
2894
+ });
2721
2895
  const createAbortedException = (abortedSignal) => {
2722
2896
  return new CoreOperationException({
2723
2897
  cause: abortedSignal.reason,
2724
2898
  code: "ABORTED",
2725
- operation: "inspect-project"
2899
+ operation: "validate-project"
2726
2900
  });
2727
2901
  };
2728
2902
  const registerPath = (path) => {
@@ -2732,10 +2906,13 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2732
2906
  };
2733
2907
  const throwIfSignalAborted = (operationSignal) => {
2734
2908
  const abortedSignal = signal?.aborted ? signal : operationSignal?.aborted ? operationSignal : null;
2735
- if (abortedSignal === null) return;
2736
- throw createAbortedException(abortedSignal);
2909
+ if (abortedSignal !== null) throw createAbortedException(abortedSignal);
2737
2910
  };
2738
2911
  throwIfSignalAborted();
2912
+ const reserveReadBytes = (byteLength) => {
2913
+ if (byteLength > limits.maxTotalBytesRead - totalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead");
2914
+ totalBytesRead += byteLength;
2915
+ };
2739
2916
  const getEntry = async (path, options) => {
2740
2917
  const parsedPath = parseRepositoryPath(path);
2741
2918
  throwIfSignalAborted(options?.signal);
@@ -2744,82 +2921,184 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
2744
2921
  throwIfSignalAborted(options?.signal);
2745
2922
  return candidate === null ? null : copyReaderEntry$1(candidate, "get-entry", parsedPath, parsedPath);
2746
2923
  };
2747
- const listEntries = (options) => {
2924
+ const iterateEntries = (options) => {
2748
2925
  const prefix = options?.prefix === void 0 ? REPOSITORY_ROOT : parseRepositoryPath(options.prefix);
2749
2926
  return { async *[Symbol.asyncIterator]() {
2750
- throwIfSignalAborted(options?.signal);
2751
2927
  registerPath(prefix);
2752
2928
  const yieldedPaths = /* @__PURE__ */ new Set();
2753
- const sourceOptions = createSourceOptions(signal, options?.signal);
2754
- const sourceListOptions = sourceOptions === void 0 ? { prefix } : {
2755
- prefix,
2756
- ...sourceOptions
2757
- };
2758
- for await (const candidate of repository.listEntries(sourceListOptions)) {
2929
+ let cursor;
2930
+ let previousPath = null;
2931
+ do {
2759
2932
  throwIfSignalAborted(options?.signal);
2760
- const entry = copyReaderEntry$1(candidate, "list-entries", prefix);
2761
- if (!isStrictDescendant(entry.path, prefix) || yieldedPaths.has(entry.path)) return invalidSourceData$3("list-entries", entry.path);
2762
- yieldedPaths.add(entry.path);
2763
- registerPath(entry.path);
2764
- yield entry;
2765
- }
2766
- throwIfSignalAborted(options?.signal);
2933
+ const page = await repository.listEntriesPage({
2934
+ ...cursor === void 0 ? {} : { cursor },
2935
+ maxEntries: Math.min(SOURCE_PAGE_ENTRIES, limits.maxEntries),
2936
+ prefix,
2937
+ ...createSourceOptions(signal, options?.signal) ?? {}
2938
+ });
2939
+ if (page.snapshot.id !== repository.snapshot.id || page.entries.length === 0 && !page.isComplete) return invalidSourceData$3("list-entries-page", prefix);
2940
+ for (const candidate of page.entries) {
2941
+ throwIfSignalAborted(options?.signal);
2942
+ const entry = copyReaderEntry$1(candidate, "list-entries-page", prefix);
2943
+ if (!isStrictDescendant(entry.path, prefix) || yieldedPaths.has(entry.path) || previousPath !== null && compareRepositoryPaths(previousPath, entry.path) >= 0) return invalidSourceData$3("list-entries-page", entry.path);
2944
+ yieldedPaths.add(entry.path);
2945
+ previousPath = entry.path;
2946
+ registerPath(entry.path);
2947
+ yield entry;
2948
+ }
2949
+ if (page.isComplete) {
2950
+ if (page.nextCursor !== null) return invalidSourceData$3("list-entries-page", prefix);
2951
+ return;
2952
+ }
2953
+ if (page.nextCursor === null || page.nextCursor === cursor) return invalidSourceData$3("list-entries-page", prefix);
2954
+ cursor = page.nextCursor;
2955
+ } while (true);
2767
2956
  } };
2768
2957
  };
2769
- const loadFile = async (path) => {
2770
- const content = await repository.readFile(path, createSourceOptions(signal, void 0));
2771
- throwIfSignalAborted();
2772
- if (!(content instanceof Uint8Array)) return invalidSourceData$3("read-file", path);
2958
+ const loadFile = async (path, operationSignal) => {
2773
2959
  const fileLimit = path === MANIFEST_PATH$3 ? "maxManifestBytes" : "maxFileBytes";
2774
- if (content.byteLength > limits[fileLimit]) return throwResourceLimitExceeded(fileLimit);
2775
- if (totalBytesRead + content.byteLength > limits.maxTotalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead");
2776
- totalBytesRead += content.byteLength;
2777
- return new Uint8Array(content);
2778
- };
2779
- const removeFailedRead = async (path, readPromise) => {
2780
- try {
2781
- return await readPromise;
2782
- } catch (error) {
2783
- readCache.delete(path);
2784
- throw error;
2960
+ const firstPage = await repository.readFilePage(path, {
2961
+ maxBytes: Math.min(SOURCE_PAGE_BYTES, Math.max(1, limits[fileLimit])),
2962
+ offset: 0,
2963
+ ...createSourceOptions(signal, operationSignal) ?? {}
2964
+ });
2965
+ throwIfSignalAborted(operationSignal);
2966
+ 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);
2968
+ reserveReadBytes(firstPage.totalBytes);
2969
+ const content = new Uint8Array(firstPage.totalBytes);
2970
+ let offset = 0;
2971
+ let page = firstPage;
2972
+ let isFirstPage = true;
2973
+ while (isFirstPage || offset < content.byteLength) {
2974
+ isFirstPage = false;
2975
+ if (page.snapshot.id !== repository.snapshot.id || page.offset !== offset || page.totalBytes !== content.byteLength || !(page.bytes instanceof Uint8Array) || page.bytes.byteLength > SOURCE_PAGE_BYTES || page.bytes.byteLength > content.byteLength - offset || page.bytes.byteLength === 0 && !page.isComplete) return invalidSourceData$3("read-file-page", path);
2976
+ content.set(page.bytes, offset);
2977
+ offset += page.bytes.byteLength;
2978
+ if (page.isComplete) {
2979
+ if (page.nextOffset !== null || offset !== content.byteLength) return invalidSourceData$3("read-file-page", path);
2980
+ break;
2981
+ }
2982
+ if (page.nextOffset !== offset) return invalidSourceData$3("read-file-page", path);
2983
+ page = await repository.readFilePage(path, {
2984
+ maxBytes: Math.min(SOURCE_PAGE_BYTES, Math.max(1, content.byteLength - offset)),
2985
+ offset,
2986
+ ...createSourceOptions(signal, operationSignal) ?? {}
2987
+ });
2988
+ throwIfSignalAborted(operationSignal);
2785
2989
  }
2990
+ return content;
2786
2991
  };
2787
- const waitForRead = (readPromise, operationSignal) => {
2788
- throwIfSignalAborted(operationSignal);
2789
- if (operationSignal === void 0 || operationSignal === signal) return readPromise;
2790
- let handleAbort;
2791
- const cancellationPromise = new Promise((resolve, reject) => {
2792
- handleAbort = () => {
2793
- const abortedSignal = signal?.aborted ? signal : operationSignal;
2794
- reject(createAbortedException(abortedSignal));
2992
+ const awaitWithCancellation = async (promise, operationSignal) => {
2993
+ const callerSignal = createSourceOptions(signal, operationSignal)?.signal;
2994
+ if (callerSignal === void 0) return promise;
2995
+ if (callerSignal.aborted) throw createAbortedException(callerSignal);
2996
+ return new Promise((resolve, reject) => {
2997
+ const cleanup = () => callerSignal.removeEventListener("abort", abort);
2998
+ const abort = () => {
2999
+ cleanup();
3000
+ reject(createAbortedException(callerSignal));
2795
3001
  };
2796
- operationSignal.addEventListener("abort", handleAbort, { once: true });
2797
- if (operationSignal.aborted) handleAbort();
2798
- });
2799
- return Promise.race([readPromise, cancellationPromise]).finally(() => {
2800
- operationSignal.removeEventListener("abort", handleAbort);
3002
+ callerSignal.addEventListener("abort", abort, { once: true });
3003
+ promise.then((result) => {
3004
+ cleanup();
3005
+ resolve(result);
3006
+ }, (error) => {
3007
+ cleanup();
3008
+ reject(error instanceof Error ? error : new RepositorySourceException({
3009
+ cause: error,
3010
+ code: "INVALID_SOURCE_DATA",
3011
+ operation: "read-file-page",
3012
+ path: null,
3013
+ retryable: false
3014
+ }));
3015
+ });
2801
3016
  });
2802
3017
  };
2803
- const readFile = async (path, options) => {
3018
+ const readCompleteFile = async (path, options) => {
2804
3019
  const parsedPath = parseRepositoryPath(path);
2805
3020
  throwIfSignalAborted(options?.signal);
2806
3021
  registerPath(parsedPath);
2807
- let readPromise = readCache.get(parsedPath);
2808
- if (readPromise === void 0) {
2809
- readPromise = removeFailedRead(parsedPath, loadFile(parsedPath));
2810
- readCache.set(parsedPath, readPromise);
2811
- }
2812
- const content = await waitForRead(readPromise, options?.signal);
3022
+ const content = await awaitWithCancellation(loadFile(parsedPath, options?.signal), options?.signal);
2813
3023
  throwIfSignalAborted(options?.signal);
2814
- return new Uint8Array(content);
3024
+ return content.slice();
3025
+ };
3026
+ const listAdapterEntriesPage = async (options) => {
3027
+ const prefix = options.prefix === void 0 ? REPOSITORY_ROOT : parseRepositoryPath(options.prefix);
3028
+ registerPath(prefix);
3029
+ if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1 || options.maxEntries > adapterLimits.maxPageEntries) return throwResourceLimitExceeded("maxEntries");
3030
+ throwIfSignalAborted(options.signal);
3031
+ const page = await repository.listEntriesPage({
3032
+ ...options.cursor === void 0 ? {} : { cursor: options.cursor },
3033
+ maxEntries: options.maxEntries,
3034
+ prefix,
3035
+ ...createSourceOptions(signal, options.signal) ?? {}
3036
+ });
3037
+ throwIfSignalAborted(options.signal);
3038
+ if (page.snapshot.id !== repository.snapshot.id || page.entries.length > options.maxEntries || page.entries.length === 0 && !page.isComplete || (page.isComplete ? page.nextCursor !== null : page.nextCursor === null)) return invalidSourceData$3("list-entries-page", prefix);
3039
+ let previousPath = null;
3040
+ const entries = page.entries.map((candidate) => {
3041
+ const entry = copyReaderEntry$1(candidate, "list-entries-page", prefix);
3042
+ if (!isStrictDescendant(entry.path, prefix) || previousPath !== null && compareRepositoryPaths(previousPath, entry.path) >= 0) return invalidSourceData$3("list-entries-page", entry.path);
3043
+ previousPath = entry.path;
3044
+ registerPath(entry.path);
3045
+ return entry;
3046
+ });
3047
+ return Object.freeze({
3048
+ entries: Object.freeze(entries),
3049
+ isComplete: page.isComplete,
3050
+ nextCursor: page.nextCursor,
3051
+ snapshot: repository.snapshot
3052
+ });
3053
+ };
3054
+ const readAdapterFilePage = async (path, options) => {
3055
+ const parsedPath = parseRepositoryPath(path);
3056
+ if (!Number.isSafeInteger(options.offset) || options.offset < 0 || !Number.isSafeInteger(options.maxBytes) || options.maxBytes < 1 || options.maxBytes > adapterLimits.maxPageBytes) return invalidSourceData$3("read-file-page", parsedPath);
3057
+ throwIfSignalAborted(options.signal);
3058
+ registerPath(parsedPath);
3059
+ const reservedBytes = Math.min(options.maxBytes, limits.maxTotalBytesRead - totalBytesRead);
3060
+ if (reservedBytes < 1) return throwResourceLimitExceeded("maxTotalBytesRead");
3061
+ reserveReadBytes(reservedBytes);
3062
+ let page;
3063
+ try {
3064
+ page = await repository.readFilePage(parsedPath, {
3065
+ maxBytes: reservedBytes,
3066
+ offset: options.offset,
3067
+ ...createSourceOptions(signal, options.signal) ?? {}
3068
+ });
3069
+ } catch (error) {
3070
+ totalBytesRead -= reservedBytes;
3071
+ throw error;
3072
+ }
3073
+ throwIfSignalAborted(options.signal);
3074
+ if (page.snapshot.id !== repository.snapshot.id || page.offset !== options.offset || !(page.bytes instanceof Uint8Array) || !Number.isSafeInteger(page.totalBytes) || page.totalBytes < 0 || page.totalBytes > limits.maxFileBytes || page.bytes.byteLength > reservedBytes || page.bytes.byteLength > page.totalBytes - page.offset || page.bytes.byteLength === 0 && !page.isComplete || (page.isComplete ? page.nextOffset !== null || page.offset + page.bytes.byteLength !== page.totalBytes : page.nextOffset !== page.offset + page.bytes.byteLength)) {
3075
+ totalBytesRead -= reservedBytes;
3076
+ return invalidSourceData$3("read-file-page", parsedPath);
3077
+ }
3078
+ totalBytesRead -= reservedBytes - page.bytes.byteLength;
3079
+ return Object.freeze({
3080
+ bytes: page.bytes.slice(),
3081
+ isComplete: page.isComplete,
3082
+ nextOffset: page.nextOffset,
3083
+ offset: page.offset,
3084
+ snapshot: repository.snapshot,
3085
+ totalBytes: page.totalBytes
3086
+ });
2815
3087
  };
2816
- const reader = Object.freeze({
3088
+ const adapterRepository = Object.freeze({
2817
3089
  getEntry,
2818
- listEntries,
2819
- readFile
3090
+ limits: adapterLimits,
3091
+ listEntriesPage: listAdapterEntriesPage,
3092
+ readFilePage: readAdapterFilePage,
3093
+ snapshot: repository.snapshot
2820
3094
  });
2821
3095
  return Object.freeze({
2822
- reader,
3096
+ adapterRepository,
3097
+ reader: Object.freeze({
3098
+ getEntry,
3099
+ iterateEntries,
3100
+ readCompleteFile
3101
+ }),
2823
3102
  throwIfAborted: throwIfSignalAborted
2824
3103
  });
2825
3104
  };
@@ -2845,7 +3124,7 @@ var findNonblankLine = (lines, startIndex) => {
2845
3124
  * - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
2846
3125
  */
2847
3126
  var validateAgentDescription = (asset, agentId, kind, limits) => {
2848
- const diagnostics = createCoreDiagnosticCollector(limits, "inspect-project");
3127
+ const diagnostics = createCoreDiagnosticCollector(limits, "validate-project");
2849
3128
  const value = trimRepositoryFormatWhitespace(asset.content);
2850
3129
  const scalarLength = countUnicodeScalars$1(value);
2851
3130
  const code = kind === "description" ? "MOLDEA_AGENT_DESCRIPTION_INVALID" : "MOLDEA_AGENT_HANDOFF_DESCRIPTION_INVALID";
@@ -2888,7 +3167,7 @@ var validateAgentDescription = (asset, agentId, kind, limits) => {
2888
3167
  * - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
2889
3168
  */
2890
3169
  var validateAgentInstruction = (asset, agentId, limits) => {
2891
- const diagnostics = createCoreDiagnosticCollector(limits, "inspect-project");
3170
+ const diagnostics = createCoreDiagnosticCollector(limits, "validate-project");
2892
3171
  if (!hasNonWhitespace(asset.content)) {
2893
3172
  diagnostics.add({
2894
3173
  code: "MOLDEA_AGENT_INSTRUCTION_EMPTY",
@@ -2924,7 +3203,7 @@ var validateAgentInstruction = (asset, agentId, limits) => {
2924
3203
  var invalidSourceData$2 = (path) => {
2925
3204
  throw new RepositorySourceException({
2926
3205
  code: "INVALID_SOURCE_DATA",
2927
- operation: "read-file",
3206
+ operation: "read-file-page",
2928
3207
  path,
2929
3208
  retryable: false
2930
3209
  });
@@ -2947,12 +3226,12 @@ var invalidSourceData$2 = (path) => {
2947
3226
  * - ABORTED: Repository inspection or the repository read was aborted.
2948
3227
  */
2949
3228
  var readRepositoryTextAsset = async (repository, path, limits, signal) => {
2950
- const content = await repository.readFile(path, signal === void 0 ? void 0 : { signal });
3229
+ const content = await repository.readCompleteFile(path, signal === void 0 ? void 0 : { signal });
2951
3230
  if (!(content instanceof Uint8Array)) return invalidSourceData$2(path);
2952
3231
  const normalized = normalizeTextDocument({
2953
3232
  content,
2954
3233
  path
2955
- }, limits, "inspect-project");
3234
+ }, limits, "validate-project");
2956
3235
  if (!normalized.valid || normalized.text === null) return freezeRecursively({
2957
3236
  asset: null,
2958
3237
  diagnostics: normalized.diagnostics,
@@ -3025,7 +3304,7 @@ var recordOccurrence = (occurrences, variableId, range) => {
3025
3304
  * - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
3026
3305
  */
3027
3306
  var validateRuntimePlaceholders = (manifestPath, agentId, instruction, declaredVariables, limits) => {
3028
- const collector = createCoreDiagnosticCollector(limits, "inspect-project");
3307
+ const collector = createCoreDiagnosticCollector(limits, "validate-project");
3029
3308
  const locator = createSourceLocator(instruction.content);
3030
3309
  const occurrences = /* @__PURE__ */ new Map();
3031
3310
  let cursor = 0;
@@ -3182,7 +3461,7 @@ var readInstruction = async (repository, path, agentId, declaration, options, co
3182
3461
  */
3183
3462
  var inspectAgentAssets = async (repository, manifest, discovery, options, signal) => {
3184
3463
  options = createCoreOperationOptionsSnapshot(options);
3185
- const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
3464
+ const collector = createCoreDiagnosticCollector(options.limits, "validate-project");
3186
3465
  const agents = [];
3187
3466
  const registeredAgents = manifest.agents ?? {};
3188
3467
  const registeredIds = new Set(Object.keys(registeredAgents));
@@ -3354,11 +3633,16 @@ var copyReaderEntry = (candidate, operation, expectedPath) => {
3354
3633
  const record = candidate;
3355
3634
  const pathCandidate = record["path"];
3356
3635
  const typeCandidate = record["type"];
3636
+ const byteLength = record["byteLength"];
3637
+ const contentIdentity = record["contentIdentity"];
3357
3638
  if (typeof pathCandidate !== "string" || !isRepositoryPath(pathCandidate)) return invalidSourceData$1(operation, expectedPath ?? null);
3358
3639
  const path = parseRepositoryPath(pathCandidate);
3359
3640
  if (expectedPath !== void 0 && path !== expectedPath) return invalidSourceData$1(operation, path);
3360
3641
  if (typeCandidate !== "file" && typeCandidate !== "directory" && typeCandidate !== "symlink") return invalidSourceData$1(operation, path);
3642
+ if (typeCandidate === "file" ? !Number.isSafeInteger(byteLength) || byteLength < 0 || contentIdentity !== null && typeof contentIdentity !== "string" : byteLength !== null || contentIdentity !== null) return invalidSourceData$1(operation, path);
3361
3643
  return {
3644
+ byteLength,
3645
+ contentIdentity,
3362
3646
  path,
3363
3647
  type: typeCandidate
3364
3648
  };
@@ -3408,7 +3692,7 @@ var retainCanonicalFile = (inventory, path, fileKind, agentId) => {
3408
3692
  inventory.runtimeGuidance.add(path);
3409
3693
  return;
3410
3694
  }
3411
- if (agentId === null) return invalidSourceData$1("list-entries", path);
3695
+ if (agentId === null) return invalidSourceData$1("list-entries-page", path);
3412
3696
  const agent = getAgent(inventory.agents, agentId);
3413
3697
  if (fileKind === "agent-description") agent.description = path;
3414
3698
  else if (fileKind === "agent-instruction") agent.instruction = path;
@@ -3458,7 +3742,7 @@ var retainFoundationEntry = (entry, expectedPath, missingCode, working, diagnost
3458
3742
  addEntryTypeDiagnostic(diagnostics, entry, "file");
3459
3743
  };
3460
3744
  var checkFoundationListing = (listedEntry, exactEntry) => {
3461
- if (exactEntry === null || exactEntry.type !== listedEntry.type) return invalidSourceData$1("list-entries", listedEntry.path);
3745
+ if (exactEntry === null || exactEntry.type !== listedEntry.type) return invalidSourceData$1("list-entries-page", listedEntry.path);
3462
3746
  };
3463
3747
  /**
3464
3748
  * Discovers and classifies version 1 canonical assets through one repository snapshot.
@@ -3478,7 +3762,7 @@ var checkFoundationListing = (listedEntry, exactEntry) => {
3478
3762
  * - ABORTED: Discovery or a repository operation was aborted.
3479
3763
  */
3480
3764
  var discoverCanonicalAssets = async (repository, limits, signal, foundations) => {
3481
- const diagnostics = createCoreDiagnosticCollector(limits, "inspect-project");
3765
+ const diagnostics = createCoreDiagnosticCollector(limits, "validate-project");
3482
3766
  const working = createWorkingInventory();
3483
3767
  const moldeaEntry = foundations === void 0 ? await readExactEntry(repository, MOLDEA_ROOT$1, signal) : foundations.moldeaRoot;
3484
3768
  if (moldeaEntry === null) {
@@ -3507,10 +3791,10 @@ var discoverCanonicalAssets = async (repository, limits, signal, foundations) =>
3507
3791
  prefix: MOLDEA_ROOT$1,
3508
3792
  signal
3509
3793
  };
3510
- for await (const candidate of repository.listEntries(listOptions)) {
3511
- const entry = copyReaderEntry(candidate, "list-entries");
3512
- if (!entry.path.startsWith("/moldea/")) return invalidSourceData$1("list-entries", entry.path);
3513
- if (seenPaths.has(entry.path)) return invalidSourceData$1("list-entries", entry.path);
3794
+ for await (const candidate of repository.iterateEntries(listOptions)) {
3795
+ const entry = copyReaderEntry(candidate, "list-entries-page");
3796
+ if (!entry.path.startsWith("/moldea/")) return invalidSourceData$1("list-entries-page", entry.path);
3797
+ if (seenPaths.has(entry.path)) return invalidSourceData$1("list-entries-page", entry.path);
3514
3798
  seenPaths.add(entry.path);
3515
3799
  if (entry.path === MANIFEST_PATH$1) {
3516
3800
  checkFoundationListing(entry, manifestEntry);
@@ -3546,8 +3830,8 @@ var discoverCanonicalAssets = async (repository, limits, signal, foundations) =>
3546
3830
  });
3547
3831
  }
3548
3832
  }
3549
- if (manifestEntry !== null !== listedManifest) return invalidSourceData$1("list-entries", MANIFEST_PATH$1);
3550
- if (projectEntry !== null !== listedProject) return invalidSourceData$1("list-entries", PROJECT_PATH$1);
3833
+ if (manifestEntry !== null !== listedManifest) return invalidSourceData$1("list-entries-page", MANIFEST_PATH$1);
3834
+ if (projectEntry !== null !== listedProject) return invalidSourceData$1("list-entries-page", PROJECT_PATH$1);
3551
3835
  return finalizeResult(working, diagnostics);
3552
3836
  };
3553
3837
  //#endregion
@@ -3732,7 +4016,7 @@ var collectCycleDiagnostics = (index, diagnostics) => {
3732
4016
  * - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
3733
4017
  */
3734
4018
  var validateDecisionGraph = (candidates, limits) => {
3735
- const diagnostics = createCoreDiagnosticCollector(limits, "inspect-project");
4019
+ const diagnostics = createCoreDiagnosticCollector(limits, "validate-project");
3736
4020
  const index = buildGraphIndex(candidates);
3737
4021
  collectDuplicateDiagnostics(index, diagnostics);
3738
4022
  collectReferenceDiagnostics(index, diagnostics);
@@ -3745,7 +4029,7 @@ var validateDecisionGraph = (candidates, limits) => {
3745
4029
  var invalidSourceData = (path) => {
3746
4030
  throw new RepositorySourceException({
3747
4031
  code: "INVALID_SOURCE_DATA",
3748
- operation: "read-file",
4032
+ operation: "read-file-page",
3749
4033
  path,
3750
4034
  retryable: false
3751
4035
  });
@@ -3774,17 +4058,17 @@ var sortDecisions = (decisions) => {
3774
4058
  */
3775
4059
  var readDecisionGraph = async (repository, paths, options, signal) => {
3776
4060
  options = createCoreOperationOptionsSnapshot(options);
3777
- const diagnostics = createCoreDiagnosticCollector(options.limits, "inspect-project");
4061
+ const diagnostics = createCoreDiagnosticCollector(options.limits, "validate-project");
3778
4062
  const candidates = [];
3779
4063
  const decisions = [];
3780
4064
  const readOptions = signal === void 0 ? void 0 : { signal };
3781
4065
  for (const path of [...paths].sort(compareExactStrings)) {
3782
- const content = await repository.readFile(path, readOptions);
4066
+ const content = await repository.readCompleteFile(path, readOptions);
3783
4067
  if (!(content instanceof Uint8Array)) return invalidSourceData(path);
3784
4068
  const parsed = await parseDecisionDocument({
3785
4069
  content: content.slice(),
3786
4070
  path
3787
- }, options, "inspect-project");
4071
+ }, options, "validate-project");
3788
4072
  for (const diagnostic of parsed.diagnostics) diagnostics.merge(diagnostic);
3789
4073
  candidates.push({
3790
4074
  decision: parsed.decision,
@@ -3860,7 +4144,7 @@ var validateDecisionRelationship = (referencedPath, pointer, context, agentId) =
3860
4144
  * - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
3861
4145
  */
3862
4146
  var validateManifestRelationships = (manifestPath, manifest, discovery, decisions, limits) => {
3863
- const collector = createCoreDiagnosticCollector(limits, "inspect-project");
4147
+ const collector = createCoreDiagnosticCollector(limits, "validate-project");
3864
4148
  const validAgentContextPaths = new Set(discovery.inventory.context);
3865
4149
  const validTopLevelContextPaths = new Set(validAgentContextPaths);
3866
4150
  if (discovery.inventory.project !== null) validTopLevelContextPaths.add(discovery.inventory.project);
@@ -3925,9 +4209,11 @@ var inspectMirror = async (repository, manifestPath, canonicalInstruction, mirro
3925
4209
  return null;
3926
4210
  }
3927
4211
  return {
4212
+ byteLength: mirrorResult.asset.utf8ByteLength,
3928
4213
  canonicalDigest: canonicalInstruction.digest,
3929
4214
  digest: mirrorResult.asset.digest,
3930
- path: mirrorPath
4215
+ path: mirrorPath,
4216
+ scalarLength: mirrorResult.asset.scalarLength
3931
4217
  };
3932
4218
  };
3933
4219
  /**
@@ -3951,7 +4237,7 @@ var inspectMirror = async (repository, manifestPath, canonicalInstruction, mirro
3951
4237
  */
3952
4238
  var inspectMirrors = async (repository, manifestPath, agents, options, signal) => {
3953
4239
  options = createCoreOperationOptionsSnapshot(options);
3954
- const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
4240
+ const collector = createCoreDiagnosticCollector(options.limits, "validate-project");
3955
4241
  const agentMirrors = [];
3956
4242
  const operationOptions = signal === void 0 ? void 0 : { signal };
3957
4243
  for (const agent of [...agents].sort((left, right) => compareExactStrings(left.id, right.id))) {
@@ -3999,7 +4285,7 @@ var addDiagnostics$2 = (collector, diagnostics) => {
3999
4285
  */
4000
4286
  var readProjectFile = async (repository, path, options, signal) => {
4001
4287
  options = createCoreOperationOptionsSnapshot(options);
4002
- const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
4288
+ const collector = createCoreDiagnosticCollector(options.limits, "validate-project");
4003
4289
  let project = null;
4004
4290
  if (path !== null) {
4005
4291
  const result = await readRepositoryTextAsset(repository, path, options.limits, signal);
@@ -4040,7 +4326,7 @@ var readProjectFile = async (repository, path, options, signal) => {
4040
4326
  */
4041
4327
  var readProjectAssets = async (repository, manifest, discovery, options, signal, projectFile) => {
4042
4328
  options = createCoreOperationOptionsSnapshot(options);
4043
- const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
4329
+ const collector = createCoreDiagnosticCollector(options.limits, "validate-project");
4044
4330
  const context = [];
4045
4331
  const inspectedProject = projectFile ?? await readProjectFile(repository, discovery.inventory.project, options, signal);
4046
4332
  addDiagnostics$2(collector, inspectedProject.diagnostics);
@@ -4307,7 +4593,7 @@ var validateCandidate = (candidate, entry, manifestPath, collector) => {
4307
4593
  * - ABORTED: Repository inspection or a repository lookup was aborted.
4308
4594
  */
4309
4595
  var validateRepositoryReferences = async (repository, manifestPath, manifest, discovery, limits, signal) => {
4310
- const collector = createCoreDiagnosticCollector(limits, "inspect-project");
4596
+ const collector = createCoreDiagnosticCollector(limits, "validate-project");
4311
4597
  const blockedPaths = new Set(discovery.diagnostics.flatMap((diagnostic) => diagnostic.path === null ? [] : [diagnostic.path]));
4312
4598
  const entryCache = /* @__PURE__ */ new Map();
4313
4599
  const operationOptions = signal === void 0 ? void 0 : { signal };
@@ -4358,7 +4644,7 @@ var isBlockedPath = (path, blockedPaths) => {
4358
4644
  */
4359
4645
  var readRuntimeGuidance = async (repository, manifestPath, manifest, discovery, options, signal) => {
4360
4646
  options = createCoreOperationOptionsSnapshot(options);
4361
- const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
4647
+ const collector = createCoreDiagnosticCollector(options.limits, "validate-project");
4362
4648
  const runtimes = [];
4363
4649
  const discoveredPaths = new Set(discovery.inventory.runtimeGuidance);
4364
4650
  const blockedPaths = new Set(discovery.diagnostics.flatMap((diagnostic) => diagnostic.path === null ? [] : [diagnostic.path]));
@@ -4421,7 +4707,7 @@ var addDiagnostics = (collector, diagnostics) => {
4421
4707
  var inspectUniversalProject = async (context, options) => {
4422
4708
  options = createCoreOperationOptionsSnapshot(options);
4423
4709
  const { session, signal } = context;
4424
- const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
4710
+ const collector = createCoreDiagnosticCollector(options.limits, "validate-project");
4425
4711
  const operationOptions = signal === void 0 ? void 0 : { signal };
4426
4712
  const moldeaRoot = await session.reader.getEntry(MOLDEA_ROOT, operationOptions);
4427
4713
  let manifestEntry = null;
@@ -4433,9 +4719,9 @@ var inspectUniversalProject = async (context, options) => {
4433
4719
  manifestEntry = await session.reader.getEntry(MANIFEST_PATH, operationOptions);
4434
4720
  if (manifestEntry?.type === "file") {
4435
4721
  const parsedManifest = await inspectManifestDocument({
4436
- content: await session.reader.readFile(MANIFEST_PATH, operationOptions),
4722
+ content: await session.reader.readCompleteFile(MANIFEST_PATH, operationOptions),
4437
4723
  path: MANIFEST_PATH
4438
- }, options, "inspect-project");
4724
+ }, options, "validate-project");
4439
4725
  formatVersion = parsedManifest.formatVersion;
4440
4726
  runtimeLocations = parsedManifest.runtimeLocations;
4441
4727
  addDiagnostics(collector, parsedManifest.diagnostics);
@@ -4493,35 +4779,35 @@ var inspectUniversalProject = async (context, options) => {
4493
4779
  });
4494
4780
  };
4495
4781
  //#endregion
4496
- //#region src/project-inspection/index.ts
4782
+ //#region src/project-validation/index.ts
4497
4783
  var isRecord$1 = (candidate) => {
4498
4784
  return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
4499
4785
  };
4500
- var invalidArgument$1 = () => {
4786
+ var invalidArgument$2 = () => {
4501
4787
  throw new CoreOperationException({
4502
4788
  code: "INVALID_ARGUMENT",
4503
- operation: "inspect-project"
4789
+ operation: "validate-project"
4504
4790
  });
4505
4791
  };
4506
4792
  var isRepositoryReader = (candidate) => {
4507
- return isRecord$1(candidate) && typeof candidate["getEntry"] === "function" && typeof candidate["listEntries"] === "function" && typeof candidate["readFile"] === "function";
4793
+ return isRecord$1(candidate) && isRecord$1(candidate["snapshot"]) && typeof candidate["snapshot"]["id"] === "string" && typeof candidate["snapshot"]["sourceKind"] === "string" && typeof candidate["compare"] === "function" && typeof candidate["getEntry"] === "function" && typeof candidate["listEntriesPage"] === "function" && typeof candidate["readFilePage"] === "function";
4508
4794
  };
4509
4795
  var isAbortSignal = (candidate) => {
4510
4796
  return isRecord$1(candidate) && typeof candidate["aborted"] === "boolean" && typeof candidate["addEventListener"] === "function" && typeof candidate["removeEventListener"] === "function";
4511
4797
  };
4512
4798
  var validateInput = (candidate) => {
4513
4799
  try {
4514
- if (!isRecord$1(candidate)) return invalidArgument$1();
4800
+ if (!isRecord$1(candidate)) return invalidArgument$2();
4515
4801
  const repository = candidate["repository"];
4516
4802
  const signal = candidate["signal"];
4517
- if (!isRepositoryReader(repository) || signal !== void 0 && !isAbortSignal(signal)) return invalidArgument$1();
4803
+ if (Reflect.ownKeys(candidate).some((key) => key !== "repository" && key !== "signal") || !isRepositoryReader(repository) || signal !== void 0 && !isAbortSignal(signal)) return invalidArgument$2();
4518
4804
  return {
4519
4805
  repository,
4520
4806
  ...signal === void 0 ? {} : { signal }
4521
4807
  };
4522
4808
  } catch (error) {
4523
4809
  if (error instanceof CoreOperationException) throw error;
4524
- return invalidArgument$1();
4810
+ return invalidArgument$2();
4525
4811
  }
4526
4812
  };
4527
4813
  /**
@@ -4543,7 +4829,7 @@ var validateInput = (candidate) => {
4543
4829
  * - ABORTED: Project inspection or a repository operation was aborted.
4544
4830
  * - ADAPTER_EXECUTION_FAILED: A runtime adapter failed or returned an invalid result.
4545
4831
  */
4546
- var inspectProject = async (input, options) => {
4832
+ var validateProjectState = async (input, options) => {
4547
4833
  const validatedInput = validateInput(input);
4548
4834
  const operationOptions = createCoreOperationOptionsSnapshot(options);
4549
4835
  const session = createRepositoryInspectionSession(validatedInput.repository, operationOptions.limits, validatedInput.signal);
@@ -4552,28 +4838,200 @@ var inspectProject = async (input, options) => {
4552
4838
  ...validatedInput.signal === void 0 ? {} : { signal: validatedInput.signal }
4553
4839
  }, operationOptions);
4554
4840
  if (universal.project === null) return freezeRecursively({
4555
- diagnostics: universal.diagnostics,
4556
- evidence: [],
4557
- formatVersion: universal.formatVersion,
4558
4841
  project: null,
4559
- valid: false
4842
+ result: {
4843
+ diagnostics: universal.diagnostics,
4844
+ evidence: [],
4845
+ formatVersion: universal.formatVersion,
4846
+ source: validatedInput.repository.snapshot,
4847
+ summary: null,
4848
+ valid: false
4849
+ }
4560
4850
  });
4851
+ const projectSummary = createProjectSummary(universal.project);
4561
4852
  const availabilityDiagnostics = validateRuntimeAdapterAvailability(universal.runtimeLocations, universal.project.formatVersion, operationOptions);
4562
4853
  if (availabilityDiagnostics.length > 0) return freezeRecursively({
4563
- diagnostics: availabilityDiagnostics,
4564
- evidence: [],
4565
- formatVersion: universal.formatVersion,
4566
- project: null,
4567
- valid: false
4854
+ project: universal.project,
4855
+ result: {
4856
+ diagnostics: availabilityDiagnostics,
4857
+ evidence: [],
4858
+ formatVersion: universal.formatVersion,
4859
+ source: validatedInput.repository.snapshot,
4860
+ summary: projectSummary,
4861
+ valid: false
4862
+ }
4568
4863
  });
4569
4864
  const adapterInspection = await inspectRuntimeAdapters(universal.project, session, operationOptions, validatedInput.signal);
4570
4865
  const valid = adapterInspection.diagnostics.length === 0;
4571
4866
  return freezeRecursively({
4572
- diagnostics: adapterInspection.diagnostics,
4573
- evidence: adapterInspection.evidence,
4574
- formatVersion: universal.formatVersion,
4575
- project: valid ? universal.project : null,
4576
- valid
4867
+ project: universal.project,
4868
+ result: {
4869
+ diagnostics: adapterInspection.diagnostics,
4870
+ evidence: adapterInspection.evidence,
4871
+ formatVersion: universal.formatVersion,
4872
+ source: validatedInput.repository.snapshot,
4873
+ summary: projectSummary,
4874
+ valid
4875
+ }
4876
+ });
4877
+ };
4878
+ /** Validates one project without returning canonical document bodies. */
4879
+ var validateProject = async (input, options) => (await validateProjectState(input, options)).result;
4880
+ //#endregion
4881
+ //#region src/project-inspection-page/index.ts
4882
+ var CURSOR_PREFIX = "core3";
4883
+ var encoder = new TextEncoder();
4884
+ var invalidArgument$1 = () => {
4885
+ throw new CoreOperationException({
4886
+ code: "INVALID_ARGUMENT",
4887
+ operation: "inspect-project-page"
4888
+ });
4889
+ };
4890
+ /** Serializes the closed inspection records with recursively sorted object keys. */
4891
+ var serializeDeterministically = (candidate) => {
4892
+ if (candidate === null || typeof candidate !== "object") {
4893
+ const serialized = JSON.stringify(candidate);
4894
+ return serialized === void 0 ? "null" : serialized;
4895
+ }
4896
+ if (Array.isArray(candidate)) return `[${candidate.map((item) => serializeDeterministically(item)).join(",")}]`;
4897
+ 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(",")}}`;
4898
+ };
4899
+ var createIdentity = (parts) => createRepositoryIdentity(parts.map((part) => encoder.encode(part)));
4900
+ var createItemBaseKey = (item) => {
4901
+ if (item.kind === "metadata") return [
4902
+ "0",
4903
+ item.metadata.path,
4904
+ item.metadata.kind,
4905
+ item.metadata.agentId ?? "",
4906
+ item.metadata.decisionId ?? "",
4907
+ item.metadata.digest
4908
+ ].join(":");
4909
+ if (item.kind === "diagnostic") return [
4910
+ "1",
4911
+ item.diagnostic.source,
4912
+ item.diagnostic.code,
4913
+ item.diagnostic.path ?? "",
4914
+ createIdentity([serializeDeterministically(item.diagnostic)])
4915
+ ].join(":");
4916
+ return [
4917
+ "2",
4918
+ item.evidence.source,
4919
+ item.evidence.kind,
4920
+ item.evidence.agentId ?? "",
4921
+ createIdentity([serializeDeterministically(item.evidence)])
4922
+ ].join(":");
4923
+ };
4924
+ /** Assigns stable unique keys without relying on collection offsets. */
4925
+ var createKeyedItems = (items) => {
4926
+ const sorted = items.map((item) => ({
4927
+ baseKey: createItemBaseKey(item),
4928
+ item
4929
+ })).sort((left, right) => left.baseKey < right.baseKey ? -1 : left.baseKey > right.baseKey ? 1 : 0);
4930
+ const occurrences = /* @__PURE__ */ new Map();
4931
+ return sorted.map(({ baseKey, item }) => {
4932
+ const occurrence = occurrences.get(baseKey) ?? 0;
4933
+ occurrences.set(baseKey, occurrence + 1);
4934
+ return {
4935
+ item,
4936
+ key: `${baseKey}:${String(occurrence).padStart(10, "0")}`
4937
+ };
4938
+ });
4939
+ };
4940
+ var createCursorChecksum = (inspectionDigest, lastKey, view) => createIdentity([
4941
+ CURSOR_PREFIX,
4942
+ inspectionDigest,
4943
+ lastKey,
4944
+ view
4945
+ ]);
4946
+ var encodeCursor = (inspectionDigest, view, lastKey) => [
4947
+ CURSOR_PREFIX,
4948
+ view,
4949
+ encodeURIComponent(lastKey),
4950
+ inspectionDigest,
4951
+ createCursorChecksum(inspectionDigest, lastKey, view)
4952
+ ].join(":");
4953
+ var decodeCursor = (cursor, inspectionDigest, view) => {
4954
+ 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);
4956
+ if (match === null || match[1] !== view || match[3] !== inspectionDigest) return invalidArgument$1();
4957
+ let lastKey;
4958
+ try {
4959
+ lastKey = decodeURIComponent(match[2]);
4960
+ } catch {
4961
+ return invalidArgument$1();
4962
+ }
4963
+ if (lastKey.length === 0 || match[4] !== createCursorChecksum(inspectionDigest, lastKey, view)) return invalidArgument$1();
4964
+ return lastKey;
4965
+ };
4966
+ var selectViewItems = (items, view) => view === "all" ? items : items.filter(({ item }) => {
4967
+ if (view === "diagnostics") return item.kind === "diagnostic";
4968
+ return item.kind === view;
4969
+ });
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);
4977
+ const metadata = state.project === null ? [] : collectProjectMetadata(state.project);
4978
+ const allItems = createKeyedItems([
4979
+ ...metadata.map((metadataItem) => ({
4980
+ kind: "metadata",
4981
+ metadata: metadataItem
4982
+ })),
4983
+ ...state.result.diagnostics.map((diagnostic) => ({
4984
+ diagnostic,
4985
+ kind: "diagnostic"
4986
+ })),
4987
+ ...state.result.evidence.map((evidence) => ({
4988
+ evidence,
4989
+ kind: "evidence"
4990
+ }))
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,
5023
+ formatVersion: state.result.formatVersion,
5024
+ inspectionDigest,
5025
+ page: {
5026
+ isComplete: nextCursor === null,
5027
+ nextCursor,
5028
+ records,
5029
+ totalItems: items.length
5030
+ },
5031
+ source: state.result.source,
5032
+ summary: state.result.summary,
5033
+ valid: state.result.valid,
5034
+ view: input.view
4577
5035
  });
4578
5036
  };
4579
5037
  //#endregion
@@ -4979,13 +5437,15 @@ var createCore = (options) => {
4979
5437
  calculateContentDigest: (input) => {
4980
5438
  return calculateContentDigest(input, createCoreOperationOptionsSnapshot(snapshot).limits);
4981
5439
  },
4982
- inspectProject: (input) => inspectProject(input, createCoreOperationOptionsSnapshot(snapshot)),
5440
+ inspectProjectPage: (input) => inspectProjectPage(input, createCoreOperationOptionsSnapshot(snapshot)),
5441
+ validateProject: (input) => validateProject(input, createCoreOperationOptionsSnapshot(snapshot)),
4983
5442
  matchManifestScope: (input) => matchManifestScope(input, createCoreOperationOptionsSnapshot(snapshot)),
4984
5443
  normalizeText: (input) => {
4985
5444
  return normalizeTextDocument(input, createCoreOperationOptionsSnapshot(snapshot).limits, "normalize-text");
4986
5445
  },
4987
5446
  parseDecision: (input) => parseDecisionDocument(input, createCoreOperationOptionsSnapshot(snapshot)),
4988
- parseManifest: (input) => parseManifestDocument(input, createCoreOperationOptionsSnapshot(snapshot))
5447
+ parseManifest: (input) => parseManifestDocument(input, createCoreOperationOptionsSnapshot(snapshot)),
5448
+ readCanonicalContentPage: (input) => readCanonicalContentPage(input, createCoreOperationOptionsSnapshot(snapshot))
4989
5449
  });
4990
5450
  };
4991
5451
  //#endregion