@moldea.ai/core 1.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.
- package/LICENSE +21 -0
- package/README.md +135 -0
- package/dist/adapter/index.d.ts +48 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter-availability/index.d.ts +15 -0
- package/dist/adapter-availability/index.d.ts.map +1 -0
- package/dist/adapter-execution/index.d.ts +31 -0
- package/dist/adapter-execution/index.d.ts.map +1 -0
- package/dist/adapter-validation/index.d.ts +44 -0
- package/dist/adapter-validation/index.d.ts.map +1 -0
- package/dist/adapter.js +0 -0
- package/dist/agent-asset-validation/index.d.ts +34 -0
- package/dist/agent-asset-validation/index.d.ts.map +1 -0
- package/dist/agent-assets/index.d.ts +38 -0
- package/dist/agent-assets/index.d.ts.map +1 -0
- package/dist/canonical-discovery/index.d.ts +47 -0
- package/dist/canonical-discovery/index.d.ts.map +1 -0
- package/dist/canonical-discovery-validation/index.d.ts +26 -0
- package/dist/canonical-discovery-validation/index.d.ts.map +1 -0
- package/dist/constants/index.d.ts +6 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/contracts/index.d.ts +178 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/decision/index.d.ts +16 -0
- package/dist/decision/index.d.ts.map +1 -0
- package/dist/decision-graph/index.d.ts +28 -0
- package/dist/decision-graph/index.d.ts.map +1 -0
- package/dist/decision-graph-validation/index.d.ts +25 -0
- package/dist/decision-graph-validation/index.d.ts.map +1 -0
- package/dist/decision-validation/index.d.ts +34 -0
- package/dist/decision-validation/index.d.ts.map +1 -0
- package/dist/diagnostic-utilities/index.d.ts +62 -0
- package/dist/diagnostic-utilities/index.d.ts.map +1 -0
- package/dist/diagnostics/index.d.ts +43 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/exceptions/index.d.ts +38 -0
- package/dist/exceptions/index.d.ts.map +1 -0
- package/dist/format/index.d.ts +80 -0
- package/dist/format/index.d.ts.map +1 -0
- package/dist/format-validation/index.d.ts +51 -0
- package/dist/format-validation/index.d.ts.map +1 -0
- package/dist/format.js +0 -0
- package/dist/immutable/index.d.ts +13 -0
- package/dist/immutable/index.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4609 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest/index.d.ts +32 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest-relationship-validation/index.d.ts +18 -0
- package/dist/manifest-relationship-validation/index.d.ts.map +1 -0
- package/dist/manifest-validation/index.d.ts +28 -0
- package/dist/manifest-validation/index.d.ts.map +1 -0
- package/dist/mirrors/index.d.ts +35 -0
- package/dist/mirrors/index.d.ts.map +1 -0
- package/dist/options/index.d.ts +26 -0
- package/dist/options/index.d.ts.map +1 -0
- package/dist/project-assets/index.d.ts +56 -0
- package/dist/project-assets/index.d.ts.map +1 -0
- package/dist/project-index/index.d.ts +20 -0
- package/dist/project-index/index.d.ts.map +1 -0
- package/dist/project-inspection/index.d.ts +23 -0
- package/dist/project-inspection/index.d.ts.map +1 -0
- package/dist/repository-inspection-session/index.d.ts +22 -0
- package/dist/repository-inspection-session/index.d.ts.map +1 -0
- package/dist/repository-reference-validation/index.d.ts +25 -0
- package/dist/repository-reference-validation/index.d.ts.map +1 -0
- package/dist/repository-text/index.d.ts +27 -0
- package/dist/repository-text/index.d.ts.map +1 -0
- package/dist/runtime-guidance/index.d.ts +32 -0
- package/dist/runtime-guidance/index.d.ts.map +1 -0
- package/dist/runtime-placeholder-validation/index.d.ts +17 -0
- package/dist/runtime-placeholder-validation/index.d.ts.map +1 -0
- package/dist/source-location/index.d.ts +11 -0
- package/dist/source-location/index.d.ts.map +1 -0
- package/dist/text/index.d.ts +33 -0
- package/dist/text/index.d.ts.map +1 -0
- package/dist/universal-project-inspection/index.d.ts +35 -0
- package/dist/universal-project-inspection/index.d.ts.map +1 -0
- package/dist/yaml/index.d.ts +40 -0
- package/dist/yaml/index.d.ts.map +1 -0
- package/package.json +62 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4609 @@
|
|
|
1
|
+
import { REPOSITORY_ROOT, RepositoryPathException, RepositorySourceException, isRepositoryPath, parseRepositoryPath } from "@moldea.ai/repository";
|
|
2
|
+
import { Exception } from "error-message-utils";
|
|
3
|
+
import { Parser, isAlias, isMap, isScalar, isSeq, parseAllDocuments } from "yaml";
|
|
4
|
+
//#region src/constants/index.ts
|
|
5
|
+
var RECOGNIZED_RUNTIME_ADAPTER_IDS = Object.freeze([
|
|
6
|
+
"anthropic",
|
|
7
|
+
"claude-agent-sdk",
|
|
8
|
+
"cloudflare-agents",
|
|
9
|
+
"custom",
|
|
10
|
+
"eve",
|
|
11
|
+
"google-genai",
|
|
12
|
+
"langchain",
|
|
13
|
+
"langgraph",
|
|
14
|
+
"openai",
|
|
15
|
+
"openai-agents-sdk",
|
|
16
|
+
"pydantic-ai",
|
|
17
|
+
"vercel-ai-sdk"
|
|
18
|
+
]);
|
|
19
|
+
var SUPPORTED_REPOSITORY_FORMAT_VERSIONS = Object.freeze([1]);
|
|
20
|
+
var DEFAULT_CORE_RESOURCE_LIMITS = Object.freeze({
|
|
21
|
+
maxDiagnostics: 1e4,
|
|
22
|
+
maxEntries: 1e5,
|
|
23
|
+
maxEvidence: 1e4,
|
|
24
|
+
maxFileBytes: 8388608,
|
|
25
|
+
maxManifestBytes: 2097152,
|
|
26
|
+
maxTotalBytesRead: 134217728
|
|
27
|
+
});
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/exceptions/index.ts
|
|
30
|
+
var CONFIGURATION_ERROR_MESSAGES = {
|
|
31
|
+
DUPLICATE_ADAPTER_ID: "A runtime adapter ID is registered more than once.",
|
|
32
|
+
INVALID_ADAPTER_DEFINITION: "A runtime adapter definition is invalid.",
|
|
33
|
+
INVALID_RESOURCE_LIMIT: "A Core resource limit is invalid.",
|
|
34
|
+
RESERVED_ADAPTER_ID: "A reserved runtime adapter ID was supplied."
|
|
35
|
+
};
|
|
36
|
+
var OPERATION_ERROR_MESSAGES = {
|
|
37
|
+
ABORTED: "The Core operation was aborted.",
|
|
38
|
+
ADAPTER_EXECUTION_FAILED: "A runtime adapter failed during inspection.",
|
|
39
|
+
INVALID_ARGUMENT: "The Core operation received an invalid argument.",
|
|
40
|
+
RESOURCE_LIMIT_EXCEEDED: "A Core resource limit was exceeded."
|
|
41
|
+
};
|
|
42
|
+
var OPERATION_ERROR_RETRYABILITY = {
|
|
43
|
+
ABORTED: true,
|
|
44
|
+
ADAPTER_EXECUTION_FAILED: false,
|
|
45
|
+
INVALID_ARGUMENT: false,
|
|
46
|
+
RESOURCE_LIMIT_EXCEEDED: false
|
|
47
|
+
};
|
|
48
|
+
var attachCause = (exception, cause) => {
|
|
49
|
+
if (cause === void 0) return;
|
|
50
|
+
Object.defineProperty(exception, "cause", {
|
|
51
|
+
configurable: true,
|
|
52
|
+
enumerable: false,
|
|
53
|
+
value: cause,
|
|
54
|
+
writable: false
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
/** Represents invalid immutable Core configuration. */
|
|
58
|
+
var CoreConfigurationException = class extends Exception {
|
|
59
|
+
code;
|
|
60
|
+
operation;
|
|
61
|
+
adapterId;
|
|
62
|
+
/** Creates a configuration exception with safe adapter metadata. */
|
|
63
|
+
constructor(options) {
|
|
64
|
+
super(CONFIGURATION_ERROR_MESSAGES[options.code], options.code);
|
|
65
|
+
this.code = options.code;
|
|
66
|
+
this.name = "CoreConfigurationException";
|
|
67
|
+
this.operation = options.operation;
|
|
68
|
+
this.adapterId = options.adapterId ?? null;
|
|
69
|
+
attachCause(this, options.cause);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/** Represents an operational failure that prevented Core from completing. */
|
|
73
|
+
var CoreOperationException = class extends Exception {
|
|
74
|
+
code;
|
|
75
|
+
operation;
|
|
76
|
+
retryable;
|
|
77
|
+
adapterId;
|
|
78
|
+
agentId;
|
|
79
|
+
limit;
|
|
80
|
+
/** Creates an operation exception with derived retry and safe scope metadata. */
|
|
81
|
+
constructor(options) {
|
|
82
|
+
super(OPERATION_ERROR_MESSAGES[options.code], options.code);
|
|
83
|
+
this.code = options.code;
|
|
84
|
+
this.name = "CoreOperationException";
|
|
85
|
+
this.operation = options.operation;
|
|
86
|
+
this.retryable = OPERATION_ERROR_RETRYABILITY[options.code];
|
|
87
|
+
this.adapterId = options.adapterId ?? null;
|
|
88
|
+
this.agentId = options.agentId ?? null;
|
|
89
|
+
this.limit = options.limit ?? null;
|
|
90
|
+
attachCause(this, options.cause);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/format-validation/index.ts
|
|
95
|
+
var STABLE_ID_PATTERN$1 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
96
|
+
var VARIABLE_ID_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
97
|
+
var WINDOWS_RESERVED_ID_PATTERN$1 = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/u;
|
|
98
|
+
var LINE_BREAK_CHARACTERS = [
|
|
99
|
+
"\n",
|
|
100
|
+
"\r",
|
|
101
|
+
"
",
|
|
102
|
+
"\u2028",
|
|
103
|
+
"\u2029"
|
|
104
|
+
];
|
|
105
|
+
var UNSUPPORTED_GLOB_PATTERN = /[!?[\]{}]/u;
|
|
106
|
+
var hasLineBreak = (value) => {
|
|
107
|
+
return LINE_BREAK_CHARACTERS.some((lineBreak) => value.includes(lineBreak));
|
|
108
|
+
};
|
|
109
|
+
/** Determines whether a code point belongs to the Repository Format version 1 whitespace set. */
|
|
110
|
+
var isRepositoryFormatWhitespace = (codePoint) => {
|
|
111
|
+
return codePoint >= 9 && codePoint <= 13 || codePoint === 32 || codePoint === 133 || codePoint === 160 || codePoint === 5760 || codePoint >= 8192 && codePoint <= 8202 || codePoint === 8232 || codePoint === 8233 || codePoint === 8239 || codePoint === 8287 || codePoint === 12288;
|
|
112
|
+
};
|
|
113
|
+
/** Compares strings by exact Unicode code-point order without locale behavior. */
|
|
114
|
+
var compareExactStrings = (left, right) => {
|
|
115
|
+
const leftScalars = left[Symbol.iterator]();
|
|
116
|
+
const rightScalars = right[Symbol.iterator]();
|
|
117
|
+
while (true) {
|
|
118
|
+
const leftScalar = leftScalars.next();
|
|
119
|
+
const rightScalar = rightScalars.next();
|
|
120
|
+
if (leftScalar.done === true) return rightScalar.done === true ? 0 : -1;
|
|
121
|
+
if (rightScalar.done === true) return 1;
|
|
122
|
+
const leftCodePoint = leftScalar.value.codePointAt(0) ?? 0;
|
|
123
|
+
const rightCodePoint = rightScalar.value.codePointAt(0) ?? 0;
|
|
124
|
+
if (leftCodePoint !== rightCodePoint) return leftCodePoint < rightCodePoint ? -1 : 1;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
/** Counts Unicode scalar values in valid JavaScript text. */
|
|
128
|
+
var countUnicodeScalars$1 = (value) => {
|
|
129
|
+
const scalars = value[Symbol.iterator]();
|
|
130
|
+
let count = 0;
|
|
131
|
+
while (scalars.next().done !== true) count += 1;
|
|
132
|
+
return count;
|
|
133
|
+
};
|
|
134
|
+
/** Determines whether a JavaScript string contains only Unicode scalar values. */
|
|
135
|
+
var isUnicodeScalarText = (value) => {
|
|
136
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
137
|
+
const codeUnit = value.charCodeAt(index);
|
|
138
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
139
|
+
if (index + 1 >= value.length) return false;
|
|
140
|
+
const nextCodeUnit = value.charCodeAt(index + 1);
|
|
141
|
+
if (nextCodeUnit < 56320 || nextCodeUnit > 57343) return false;
|
|
142
|
+
index += 1;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (codeUnit >= 56320 && codeUnit <= 57343) return false;
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
};
|
|
149
|
+
/** Determines whether a value contains at least one non-whitespace scalar. */
|
|
150
|
+
var hasNonWhitespace = (value) => {
|
|
151
|
+
for (const scalar of value) if (!isRepositoryFormatWhitespace(scalar.codePointAt(0) ?? 0)) return true;
|
|
152
|
+
return false;
|
|
153
|
+
};
|
|
154
|
+
/** Determines whether a value begins or ends with version 1 whitespace. */
|
|
155
|
+
var hasSurroundingWhitespace = (value) => {
|
|
156
|
+
const firstCodePoint = value.codePointAt(0);
|
|
157
|
+
const lastCodePoint = value.codePointAt(value.length - 1);
|
|
158
|
+
return firstCodePoint !== void 0 && isRepositoryFormatWhitespace(firstCodePoint) || lastCodePoint !== void 0 && isRepositoryFormatWhitespace(lastCodePoint);
|
|
159
|
+
};
|
|
160
|
+
/** Removes only Repository Format version 1 whitespace from both edges. */
|
|
161
|
+
var trimRepositoryFormatWhitespace = (value) => {
|
|
162
|
+
let start = 0;
|
|
163
|
+
let end = value.length;
|
|
164
|
+
while (start < end) {
|
|
165
|
+
const codePoint = value.codePointAt(start);
|
|
166
|
+
if (codePoint === void 0 || !isRepositoryFormatWhitespace(codePoint)) break;
|
|
167
|
+
start += codePoint > 65535 ? 2 : 1;
|
|
168
|
+
}
|
|
169
|
+
while (end > start) {
|
|
170
|
+
const codePoint = value.codePointAt(end - 1);
|
|
171
|
+
if (codePoint === void 0 || !isRepositoryFormatWhitespace(codePoint)) break;
|
|
172
|
+
end -= 1;
|
|
173
|
+
}
|
|
174
|
+
return value.slice(start, end);
|
|
175
|
+
};
|
|
176
|
+
/** Determines whether a string is non-empty and contains no line break. */
|
|
177
|
+
var isNonEmptySingleLine = (value) => {
|
|
178
|
+
return value.length > 0 && !hasLineBreak(value);
|
|
179
|
+
};
|
|
180
|
+
/** Determines whether a value satisfies stable moldea ID syntax. */
|
|
181
|
+
var isStableId$1 = (value) => {
|
|
182
|
+
return value.length <= 64 && STABLE_ID_PATTERN$1.test(value);
|
|
183
|
+
};
|
|
184
|
+
/** Determines whether an otherwise valid stable ID is filesystem-reserved. */
|
|
185
|
+
var isReservedId = (value) => WINDOWS_RESERVED_ID_PATTERN$1.test(value);
|
|
186
|
+
/** Determines whether a value satisfies runtime-variable ID syntax. */
|
|
187
|
+
var isVariableId = (value) => {
|
|
188
|
+
return value.length <= 64 && VARIABLE_ID_PATTERN.test(value);
|
|
189
|
+
};
|
|
190
|
+
/** Determines whether a parsed capability description satisfies version 1 limits. */
|
|
191
|
+
var isCapabilityDescription = (value) => {
|
|
192
|
+
const scalarLength = countUnicodeScalars$1(value);
|
|
193
|
+
return scalarLength >= 1 && scalarLength <= 1e3 && !hasSurroundingWhitespace(value) && !hasLineBreak(value) && !value.includes("\0") && !value.includes("{{") && !value.includes("}}");
|
|
194
|
+
};
|
|
195
|
+
/** Parses a non-root repository-format logical path without throwing. */
|
|
196
|
+
var parseManifestPath = (value) => {
|
|
197
|
+
return value !== "/" && isRepositoryPath(value) ? parseRepositoryPath(value) : null;
|
|
198
|
+
};
|
|
199
|
+
/** Determines whether a path is the canonical manifest path. */
|
|
200
|
+
var isCanonicalManifestPath = (path) => {
|
|
201
|
+
return path === "/moldea/moldea.yaml";
|
|
202
|
+
};
|
|
203
|
+
/** Extracts the stable millisecond ID from one canonical immediate decision path. */
|
|
204
|
+
var parseDecisionIdFromPath = (path) => {
|
|
205
|
+
const match = /^\/moldea\/decisions\/(\d{13})-([a-z0-9]+(?:-[a-z0-9]+)*)\.md$/u.exec(path);
|
|
206
|
+
const id = match?.[1];
|
|
207
|
+
const slug = match?.[2];
|
|
208
|
+
return id !== void 0 && slug !== void 0 && isStableId$1(slug) ? id : null;
|
|
209
|
+
};
|
|
210
|
+
/** Determines whether a path is an allowed focused-context relationship key. */
|
|
211
|
+
var isContextPath = (path, allowProject) => {
|
|
212
|
+
if (allowProject && path === "/moldea/project.md") return true;
|
|
213
|
+
return path.startsWith("/moldea/context/") && path.endsWith(".md");
|
|
214
|
+
};
|
|
215
|
+
/** Determines whether a path has the canonical immediate decision filename shape. */
|
|
216
|
+
var isDecisionPath = (path) => {
|
|
217
|
+
return parseDecisionIdFromPath(path) !== null;
|
|
218
|
+
};
|
|
219
|
+
/** Determines whether a path is canonical runtime guidance. */
|
|
220
|
+
var isRuntimeGuidancePath = (path) => {
|
|
221
|
+
return path.startsWith("/moldea/runtimes/") && path.endsWith(".md");
|
|
222
|
+
};
|
|
223
|
+
/** Determines whether a mirror path is outside canonical moldea content. */
|
|
224
|
+
var isMirrorPath = (path) => {
|
|
225
|
+
return path !== "/moldea" && !path.startsWith("/moldea/");
|
|
226
|
+
};
|
|
227
|
+
/** Determines whether an impact path contains prohibited glob metacharacters. */
|
|
228
|
+
var hasUnsupportedGlobMetacharacter = (value) => {
|
|
229
|
+
return UNSUPPORTED_GLOB_PATTERN.test(value);
|
|
230
|
+
};
|
|
231
|
+
/** Determines whether a string satisfies the version 1 simple-glob grammar. */
|
|
232
|
+
var isSimpleGlob = (value) => {
|
|
233
|
+
if (hasUnsupportedGlobMetacharacter(value) || value.includes("***")) return false;
|
|
234
|
+
if (value.slice(1).split("/").some((segment) => segment.includes("**") && segment !== "**")) return false;
|
|
235
|
+
return value.includes("*") && isRepositoryPath(value.replaceAll("*", "x"));
|
|
236
|
+
};
|
|
237
|
+
/** Determines whether a repository-reference symbol has the common safe shape. */
|
|
238
|
+
var isRepositorySymbol = (value) => {
|
|
239
|
+
return isNonEmptySingleLine(value) && !value.includes("\0");
|
|
240
|
+
};
|
|
241
|
+
/** Determines whether a canonical moldea reference incorrectly carries a symbol. */
|
|
242
|
+
var isCanonicalMoldeaPath = (path) => {
|
|
243
|
+
return path === "/moldea" || path.startsWith("/moldea/");
|
|
244
|
+
};
|
|
245
|
+
/** Sorts repository references by path and then optional symbol. */
|
|
246
|
+
var sortRepositoryReferences = (references) => {
|
|
247
|
+
return [...references].sort((left, right) => {
|
|
248
|
+
return compareExactStrings(left.path, right.path) || compareExactStrings(left.symbol ?? "", right.symbol ?? "");
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region src/immutable/index.ts
|
|
253
|
+
/**
|
|
254
|
+
* Recursively freezes a Core-owned value without cloning it.
|
|
255
|
+
* @param value The detached acyclic value whose already-frozen objects are deeply frozen.
|
|
256
|
+
* @returns The same value with every mutable reachable object frozen.
|
|
257
|
+
*/
|
|
258
|
+
var freezeRecursively = (value) => {
|
|
259
|
+
if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value;
|
|
260
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
261
|
+
const child = value[key];
|
|
262
|
+
freezeRecursively(child);
|
|
263
|
+
}
|
|
264
|
+
return Object.freeze(value);
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Creates a record that cannot collide with prototype-chain properties.
|
|
268
|
+
* @param entries The own string-keyed entries to assign.
|
|
269
|
+
* @returns A mutable null-prototype record for later normalization and freezing.
|
|
270
|
+
*/
|
|
271
|
+
var createNullPrototypeRecord = (entries) => {
|
|
272
|
+
const record = Object.create(null);
|
|
273
|
+
for (const [key, value] of entries) record[key] = value;
|
|
274
|
+
return record;
|
|
275
|
+
};
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/diagnostic-utilities/index.ts
|
|
278
|
+
var CORE_DIAGNOSTIC_MESSAGES = {
|
|
279
|
+
MOLDEA_AGENT_DESCRIPTION_INVALID: "The agent description is invalid.",
|
|
280
|
+
MOLDEA_AGENT_DESCRIPTION_MISSING: "The agent description is missing.",
|
|
281
|
+
MOLDEA_AGENT_DIRECTORY_MISSING: "The registered agent directory is missing.",
|
|
282
|
+
MOLDEA_AGENT_DIRECTORY_UNREGISTERED: "The agent directory is not registered.",
|
|
283
|
+
MOLDEA_AGENT_HANDOFF_DESCRIPTION_INVALID: "The agent handoff description is invalid.",
|
|
284
|
+
MOLDEA_AGENT_IDENTITY_INVALID: "The agent instruction identity is invalid.",
|
|
285
|
+
MOLDEA_AGENT_INSTRUCTION_EMPTY: "The agent instruction is empty.",
|
|
286
|
+
MOLDEA_AGENT_INSTRUCTION_MISSING: "The agent instruction is missing.",
|
|
287
|
+
MOLDEA_CANONICAL_ASSET_SYMLINK: "The canonical asset is a symlink.",
|
|
288
|
+
MOLDEA_CANONICAL_PATH_UNRECOGNIZED: "The canonical path is unrecognized.",
|
|
289
|
+
MOLDEA_CAPABILITY_DESCRIPTION_INVALID: "The capability description is invalid.",
|
|
290
|
+
MOLDEA_CAPABILITY_DESCRIPTION_MISSING: "The capability description is missing.",
|
|
291
|
+
MOLDEA_CONTEXT_FILE_EMPTY: "The focused context file is empty.",
|
|
292
|
+
MOLDEA_CONTEXT_PATH_INVALID: "The context relationship path is invalid.",
|
|
293
|
+
MOLDEA_CONTEXT_RELATIONSHIP_EMPTY: "The declared relationship has no bindings or impact paths.",
|
|
294
|
+
MOLDEA_DECISION_BODY_EMPTY: "The decision Markdown body is empty.",
|
|
295
|
+
MOLDEA_DECISION_CREATED_AT_INVALID: "The decision createdAt value is invalid.",
|
|
296
|
+
MOLDEA_DECISION_FILENAME_INVALID: "The decision path or filename is invalid.",
|
|
297
|
+
MOLDEA_DECISION_FRONTMATTER_INVALID: "The decision frontmatter is invalid.",
|
|
298
|
+
MOLDEA_DECISION_FRONTMATTER_MISSING: "The decision frontmatter or its delimiters are missing.",
|
|
299
|
+
MOLDEA_DECISION_ID_DUPLICATE: "The decision ID is duplicated.",
|
|
300
|
+
MOLDEA_DECISION_PROPERTY_UNKNOWN: "The decision frontmatter contains an unknown property.",
|
|
301
|
+
MOLDEA_DECISION_REFERENCE_MISSING: "The referenced decision does not exist.",
|
|
302
|
+
MOLDEA_DECISION_RELATIONSHIP_INACTIVE: "The referenced decision is not accepted.",
|
|
303
|
+
MOLDEA_DECISION_SELF_SUPERSESSION: "The decision supersedes itself.",
|
|
304
|
+
MOLDEA_DECISION_STATUS_INVALID: "The decision status is invalid.",
|
|
305
|
+
MOLDEA_DECISION_SUPERSEDED_ORPHAN: "The superseded decision has no active supersession relationship.",
|
|
306
|
+
MOLDEA_DECISION_SUPERSESSION_CYCLE: "The decision supersession graph contains a cycle.",
|
|
307
|
+
MOLDEA_DECISION_SUPERSESSION_STATUS_INVALID: "The decision supersession relationship has inconsistent statuses.",
|
|
308
|
+
MOLDEA_DECISION_TIMESTAMP_MISMATCH: "The decision filename timestamp and createdAt value do not match.",
|
|
309
|
+
MOLDEA_ENTRY_TYPE_INVALID: "The repository entry type is invalid.",
|
|
310
|
+
MOLDEA_RUNTIME_ADAPTER_FORMAT_UNSUPPORTED: "The configured runtime adapter does not support the repository format version.",
|
|
311
|
+
MOLDEA_RUNTIME_ADAPTER_UNAVAILABLE: "The declared runtime adapter is unavailable.",
|
|
312
|
+
MOLDEA_RUNTIME_ID_INVALID: "The runtime adapter ID is invalid.",
|
|
313
|
+
MOLDEA_GLOB_INVALID: "The impact pattern is invalid.",
|
|
314
|
+
MOLDEA_ID_DUPLICATE: "An ID is duplicated within its required scope.",
|
|
315
|
+
MOLDEA_ID_INVALID: "The ID is invalid.",
|
|
316
|
+
MOLDEA_ID_RESERVED: "The ID uses a reserved filesystem name.",
|
|
317
|
+
MOLDEA_IMPACT_PATH_MISSING: "The exact impact path does not exist.",
|
|
318
|
+
MOLDEA_IMPACT_PATH_NOT_FILE: "The exact impact path is not a regular file.",
|
|
319
|
+
MOLDEA_MANIFEST_MISSING: "The project manifest is missing.",
|
|
320
|
+
MOLDEA_MANIFEST_PATH_INVALID: "The manifest path is not the canonical manifest path.",
|
|
321
|
+
MOLDEA_MANIFEST_PROPERTY_UNKNOWN: "The manifest contains an unknown property.",
|
|
322
|
+
MOLDEA_MANIFEST_ROOT_INVALID: "The manifest root is not a mapping.",
|
|
323
|
+
MOLDEA_MANIFEST_VALUE_INVALID: "The manifest value is invalid.",
|
|
324
|
+
MOLDEA_MANIFEST_VERSION_INVALID: "The manifest version is not a valid integer major version.",
|
|
325
|
+
MOLDEA_MANIFEST_VERSION_MISSING: "The manifest version is missing.",
|
|
326
|
+
MOLDEA_MANIFEST_VERSION_UNSUPPORTED: "The manifest version is unsupported.",
|
|
327
|
+
MOLDEA_MIRROR_PATH_DUPLICATE: "The mirror path is assigned more than once.",
|
|
328
|
+
MOLDEA_MIRROR_PATH_INSIDE_MOLDEA: "The mirror path is inside /moldea.",
|
|
329
|
+
MOLDEA_MIRROR_PATH_INVALID: "The mirror path is invalid.",
|
|
330
|
+
MOLDEA_MIRROR_MISSING: "The declared mirror does not exist.",
|
|
331
|
+
MOLDEA_MIRROR_NOT_FILE: "The declared mirror is not a regular file.",
|
|
332
|
+
MOLDEA_MIRROR_STALE: "The declared mirror differs from its canonical instruction.",
|
|
333
|
+
MOLDEA_MIRROR_SYMLINK: "The declared mirror is a symlink.",
|
|
334
|
+
MOLDEA_PATH_DUPLICATE: "The path is duplicated.",
|
|
335
|
+
MOLDEA_PATH_INVALID: "The manifest logical path is invalid.",
|
|
336
|
+
MOLDEA_PATTERN_DUPLICATE: "The impact pattern is duplicated.",
|
|
337
|
+
MOLDEA_PROJECT_FILE_EMPTY: "The project file is empty.",
|
|
338
|
+
MOLDEA_PROJECT_FILE_MISSING: "The project file is missing.",
|
|
339
|
+
MOLDEA_REFERENCE_MISSING: "The referenced repository path does not exist.",
|
|
340
|
+
MOLDEA_REFERENCE_NOT_FILE: "The referenced repository path is not a regular file.",
|
|
341
|
+
MOLDEA_REFERENCE_SYMLINK: "The referenced repository path is a symlink.",
|
|
342
|
+
MOLDEA_RUNTIME_GUIDANCE_EMPTY: "The runtime guidance is empty.",
|
|
343
|
+
MOLDEA_RUNTIME_GUIDANCE_MISSING: "The referenced runtime guidance does not exist.",
|
|
344
|
+
MOLDEA_SKILL_IMPLEMENTATION_MISSING: "The registered skill implementation is missing.",
|
|
345
|
+
MOLDEA_SYMBOL_FORBIDDEN: "A canonical moldea reference must not include a symbol.",
|
|
346
|
+
MOLDEA_SYMBOL_INVALID: "The repository-reference symbol is invalid.",
|
|
347
|
+
MOLDEA_TEXT_EMPTY: "The required text document is empty.",
|
|
348
|
+
MOLDEA_TEXT_INVALID_UNICODE: "The text document contains an invalid Unicode scalar representation.",
|
|
349
|
+
MOLDEA_TEXT_INVALID_UTF8: "The text document is not valid UTF-8.",
|
|
350
|
+
MOLDEA_TEXT_NUL_FORBIDDEN: "The text document contains a forbidden NUL character.",
|
|
351
|
+
MOLDEA_TOOL_IMPLEMENTATION_MISSING: "The registered tool implementation is missing.",
|
|
352
|
+
MOLDEA_VARIABLE_ID_INVALID: "The runtime-variable ID is invalid.",
|
|
353
|
+
MOLDEA_VARIABLE_PLACEHOLDER_MALFORMED: "The agent instruction contains a malformed runtime-variable placeholder.",
|
|
354
|
+
MOLDEA_VARIABLE_PROVIDER_UNDECLARED: "A variable-provider binding exists for an undeclared variable.",
|
|
355
|
+
MOLDEA_VARIABLE_UNDECLARED: "The agent instruction references an undeclared runtime variable.",
|
|
356
|
+
MOLDEA_VARIABLE_UNUSED: "The declared runtime variable is unused by the agent instruction.",
|
|
357
|
+
MOLDEA_YAML_DUPLICATE_KEY: "The YAML document contains a duplicate mapping key.",
|
|
358
|
+
MOLDEA_YAML_FEATURE_UNSUPPORTED: "The YAML document uses an unsupported feature.",
|
|
359
|
+
MOLDEA_YAML_MALFORMED: "The YAML document is malformed.",
|
|
360
|
+
MOLDEA_YAML_MULTIPLE_DOCUMENTS: "The YAML stream contains more than one document."
|
|
361
|
+
};
|
|
362
|
+
var ENTITY_KEYS = [
|
|
363
|
+
"agentId",
|
|
364
|
+
"capabilityKind",
|
|
365
|
+
"capabilityId",
|
|
366
|
+
"decisionId",
|
|
367
|
+
"variableId",
|
|
368
|
+
"adapterId"
|
|
369
|
+
];
|
|
370
|
+
var CORE_DIAGNOSTIC_BUDGET = Symbol("core-diagnostic-budget");
|
|
371
|
+
/** Creates a detached limits view with one diagnostic budget for a complete Core operation. */
|
|
372
|
+
var createCoreOperationResourceLimits = (limits) => {
|
|
373
|
+
if (limits[CORE_DIAGNOSTIC_BUDGET] !== void 0) return limits;
|
|
374
|
+
return Object.freeze({
|
|
375
|
+
...limits,
|
|
376
|
+
[CORE_DIAGNOSTIC_BUDGET]: { count: 0 }
|
|
377
|
+
});
|
|
378
|
+
};
|
|
379
|
+
/** Registers raw diagnostics against the shared operation budget before normalization. */
|
|
380
|
+
var registerCoreDiagnosticCandidates = (limits, amount, operation, fallbackCount = 0, adapterId) => {
|
|
381
|
+
const budget = limits[CORE_DIAGNOSTIC_BUDGET];
|
|
382
|
+
const currentCount = budget?.count ?? fallbackCount;
|
|
383
|
+
if (amount > limits.maxDiagnostics - currentCount) throw new CoreOperationException({
|
|
384
|
+
...adapterId === void 0 ? {} : { adapterId },
|
|
385
|
+
code: "RESOURCE_LIMIT_EXCEEDED",
|
|
386
|
+
limit: "maxDiagnostics",
|
|
387
|
+
operation
|
|
388
|
+
});
|
|
389
|
+
if (budget !== void 0) budget.count += amount;
|
|
390
|
+
return fallbackCount + amount;
|
|
391
|
+
};
|
|
392
|
+
var compareNullableStrings$2 = (left, right) => {
|
|
393
|
+
if (left === null) return right === null ? 0 : -1;
|
|
394
|
+
return right === null ? 1 : compareExactStrings(left, right);
|
|
395
|
+
};
|
|
396
|
+
/**
|
|
397
|
+
* Normalizes trusted JSON-scalar diagnostic metadata.
|
|
398
|
+
* @param details The optional detached metadata record.
|
|
399
|
+
* @returns A frozen null-prototype record with canonical key order.
|
|
400
|
+
*/
|
|
401
|
+
var normalizeDiagnosticDetails = (details) => {
|
|
402
|
+
return freezeRecursively(createNullPrototypeRecord(Object.entries(details ?? {}).sort(([left], [right]) => compareExactStrings(left, right)).map(([key, value]) => [key, typeof value === "number" && Object.is(value, -0) ? 0 : value])));
|
|
403
|
+
};
|
|
404
|
+
/**
|
|
405
|
+
* Normalizes a trusted diagnostic entity into canonical key order.
|
|
406
|
+
* @param entity The optional diagnostic entity.
|
|
407
|
+
* @returns The frozen normalized entity or `null`.
|
|
408
|
+
*/
|
|
409
|
+
var normalizeDiagnosticEntity = (entity) => {
|
|
410
|
+
if (entity === void 0 || entity === null) return null;
|
|
411
|
+
return freezeRecursively(createNullPrototypeRecord(ENTITY_KEYS.flatMap((key) => {
|
|
412
|
+
const value = entity[key];
|
|
413
|
+
return value === void 0 ? [] : [[key, value]];
|
|
414
|
+
})));
|
|
415
|
+
};
|
|
416
|
+
/** Serializes unordered diagnostic details with explicit exact-code-point key ordering. */
|
|
417
|
+
var serializeDiagnosticDetails = (details) => {
|
|
418
|
+
const entries = Object.entries(details).sort(([left], [right]) => compareExactStrings(left, right));
|
|
419
|
+
return JSON.stringify(entries);
|
|
420
|
+
};
|
|
421
|
+
var serializeDiagnostic = (diagnostic) => {
|
|
422
|
+
const entity = diagnostic.entity === null ? null : ENTITY_KEYS.map((key) => [key, diagnostic.entity?.[key] ?? null]);
|
|
423
|
+
const range = diagnostic.range === null ? null : [
|
|
424
|
+
diagnostic.range.start.line,
|
|
425
|
+
diagnostic.range.start.column,
|
|
426
|
+
diagnostic.range.start.offset,
|
|
427
|
+
diagnostic.range.end.line,
|
|
428
|
+
diagnostic.range.end.column,
|
|
429
|
+
diagnostic.range.end.offset
|
|
430
|
+
];
|
|
431
|
+
return JSON.stringify([
|
|
432
|
+
diagnostic.code,
|
|
433
|
+
serializeDiagnosticDetails(diagnostic.details),
|
|
434
|
+
entity,
|
|
435
|
+
diagnostic.message,
|
|
436
|
+
diagnostic.path,
|
|
437
|
+
diagnostic.pointer,
|
|
438
|
+
range,
|
|
439
|
+
diagnostic.source
|
|
440
|
+
]);
|
|
441
|
+
};
|
|
442
|
+
var compareRanges = (left, right) => {
|
|
443
|
+
if (left === null) return right === null ? 0 : -1;
|
|
444
|
+
if (right === null) return 1;
|
|
445
|
+
return left.start.line - right.start.line || left.start.column - right.start.column || left.start.offset - right.start.offset || left.end.line - right.end.line || left.end.column - right.end.column || left.end.offset - right.end.offset;
|
|
446
|
+
};
|
|
447
|
+
var readEntityValue = (entity, key) => {
|
|
448
|
+
return entity?.[key] ?? "";
|
|
449
|
+
};
|
|
450
|
+
var compareDiagnostics = (left, right) => {
|
|
451
|
+
return compareNullableStrings$2(left.path, right.path) || compareRanges(left.range, right.range) || compareNullableStrings$2(left.pointer, right.pointer) || compareExactStrings(left.source, right.source) || compareExactStrings(left.code, right.code) || compareExactStrings(readEntityValue(left.entity, "agentId"), readEntityValue(right.entity, "agentId")) || compareExactStrings(readEntityValue(left.entity, "capabilityKind"), readEntityValue(right.entity, "capabilityKind")) || compareExactStrings(readEntityValue(left.entity, "capabilityId"), readEntityValue(right.entity, "capabilityId")) || compareExactStrings(readEntityValue(left.entity, "decisionId"), readEntityValue(right.entity, "decisionId")) || compareExactStrings(readEntityValue(left.entity, "variableId"), readEntityValue(right.entity, "variableId")) || compareExactStrings(readEntityValue(left.entity, "adapterId"), readEntityValue(right.entity, "adapterId")) || compareExactStrings(serializeDiagnosticDetails(left.details), serializeDiagnosticDetails(right.details)) || compareExactStrings(left.message, right.message);
|
|
452
|
+
};
|
|
453
|
+
/**
|
|
454
|
+
* Creates one normalized, immutable Core diagnostic.
|
|
455
|
+
* @param input The trusted Core-owned diagnostic fields to normalize.
|
|
456
|
+
* @returns A deeply immutable public diagnostic value.
|
|
457
|
+
*/
|
|
458
|
+
var createCoreDiagnostic = (input) => {
|
|
459
|
+
const message = CORE_DIAGNOSTIC_MESSAGES[input.code];
|
|
460
|
+
if (message === void 0) throw new TypeError("The requested Core diagnostic is not implemented.");
|
|
461
|
+
return freezeRecursively({
|
|
462
|
+
code: input.code,
|
|
463
|
+
details: normalizeDiagnosticDetails(input.details),
|
|
464
|
+
entity: normalizeDiagnosticEntity(input.entity),
|
|
465
|
+
message,
|
|
466
|
+
path: input.path,
|
|
467
|
+
pointer: input.pointer ?? null,
|
|
468
|
+
range: input.range ?? null,
|
|
469
|
+
source: "core"
|
|
470
|
+
});
|
|
471
|
+
};
|
|
472
|
+
/**
|
|
473
|
+
* Deduplicates and sorts already-normalized Core and adapter diagnostics.
|
|
474
|
+
* @param candidates The immutable diagnostics to combine.
|
|
475
|
+
* @returns A frozen deterministic diagnostic collection.
|
|
476
|
+
*/
|
|
477
|
+
var normalizeDiagnostics = (candidates) => {
|
|
478
|
+
const diagnostics = /* @__PURE__ */ new Map();
|
|
479
|
+
for (const diagnostic of candidates) {
|
|
480
|
+
const key = serializeDiagnostic(diagnostic);
|
|
481
|
+
if (diagnostics.has(key)) continue;
|
|
482
|
+
diagnostics.set(key, diagnostic);
|
|
483
|
+
}
|
|
484
|
+
return freezeRecursively([...diagnostics.values()].sort(compareDiagnostics));
|
|
485
|
+
};
|
|
486
|
+
/**
|
|
487
|
+
* Creates a deterministic, deduplicating diagnostic collector for one operation.
|
|
488
|
+
* @param limits The Core resource limits governing diagnostic output.
|
|
489
|
+
* @param operation The operation reported by resource failures.
|
|
490
|
+
* @returns A collector that freezes and sorts its final diagnostic array.
|
|
491
|
+
*/
|
|
492
|
+
var createCoreDiagnosticCollector = (limits, operation) => {
|
|
493
|
+
const diagnostics = /* @__PURE__ */ new Map();
|
|
494
|
+
let proposalCount = 0;
|
|
495
|
+
let fallbackRawCount = 0;
|
|
496
|
+
const retain = (input) => {
|
|
497
|
+
const diagnostic = createCoreDiagnostic(input);
|
|
498
|
+
const key = serializeDiagnostic(diagnostic);
|
|
499
|
+
proposalCount += 1;
|
|
500
|
+
if (!diagnostics.has(key)) diagnostics.set(key, diagnostic);
|
|
501
|
+
};
|
|
502
|
+
return {
|
|
503
|
+
add: (input) => {
|
|
504
|
+
fallbackRawCount = registerCoreDiagnosticCandidates(limits, 1, operation, fallbackRawCount);
|
|
505
|
+
retain(input);
|
|
506
|
+
},
|
|
507
|
+
merge: retain,
|
|
508
|
+
finalize: () => normalizeDiagnostics([...diagnostics.values()]),
|
|
509
|
+
get size() {
|
|
510
|
+
return proposalCount;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
};
|
|
514
|
+
/**
|
|
515
|
+
* Escapes one JSON Pointer reference token.
|
|
516
|
+
* @param value The decoded mapping key or array index token.
|
|
517
|
+
* @returns The RFC 6901 escaped token.
|
|
518
|
+
*/
|
|
519
|
+
var escapeJsonPointerSegment = (value) => {
|
|
520
|
+
return value.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
521
|
+
};
|
|
522
|
+
//#endregion
|
|
523
|
+
//#region src/decision-validation/index.ts
|
|
524
|
+
var DECISION_STATUSES = /* @__PURE__ */ new Set([
|
|
525
|
+
"accepted",
|
|
526
|
+
"proposed",
|
|
527
|
+
"rejected",
|
|
528
|
+
"superseded"
|
|
529
|
+
]);
|
|
530
|
+
var DECISION_ID_PATTERN = /^\d{13}$/u;
|
|
531
|
+
var CREATED_AT_PATTERN = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d\.\d{3}Z$/u;
|
|
532
|
+
var getEntity = (decisionId) => {
|
|
533
|
+
return decisionId === null ? null : { decisionId };
|
|
534
|
+
};
|
|
535
|
+
var addInvalidFrontmatter = (context, range, reason, pointer = null) => {
|
|
536
|
+
context.diagnostics.add({
|
|
537
|
+
code: "MOLDEA_DECISION_FRONTMATTER_INVALID",
|
|
538
|
+
details: { reason },
|
|
539
|
+
entity: getEntity(context.decisionId),
|
|
540
|
+
path: context.path,
|
|
541
|
+
pointer,
|
|
542
|
+
range
|
|
543
|
+
});
|
|
544
|
+
};
|
|
545
|
+
var isCanonicalCreatedAt = (value) => {
|
|
546
|
+
if (!CREATED_AT_PATTERN.test(value)) return false;
|
|
547
|
+
const timestamp = Date.parse(value);
|
|
548
|
+
return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value;
|
|
549
|
+
};
|
|
550
|
+
var readMapping$1 = (node, context) => {
|
|
551
|
+
if (node.kind !== "mapping") {
|
|
552
|
+
addInvalidFrontmatter(context, node.range, "mapping-required");
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
const entries = /* @__PURE__ */ new Map();
|
|
556
|
+
for (const entry of node.entries) {
|
|
557
|
+
if (entry.key.kind !== "scalar" || typeof entry.key.value !== "string") {
|
|
558
|
+
addInvalidFrontmatter(context, entry.key.range, "string-key-required");
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const key = entry.key.value;
|
|
562
|
+
if (key !== "status" && key !== "createdAt" && key !== "supersedes") {
|
|
563
|
+
context.diagnostics.add({
|
|
564
|
+
code: "MOLDEA_DECISION_PROPERTY_UNKNOWN",
|
|
565
|
+
entity: getEntity(context.decisionId),
|
|
566
|
+
path: context.path,
|
|
567
|
+
pointer: `/${escapeJsonPointerSegment(key)}`,
|
|
568
|
+
range: entry.key.range
|
|
569
|
+
});
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
entries.set(key, entry.value);
|
|
573
|
+
}
|
|
574
|
+
return entries;
|
|
575
|
+
};
|
|
576
|
+
var readStatus = (node, context) => {
|
|
577
|
+
if (node?.kind !== "scalar" || typeof node.value !== "string" || !DECISION_STATUSES.has(node.value)) {
|
|
578
|
+
context.diagnostics.add({
|
|
579
|
+
code: "MOLDEA_DECISION_STATUS_INVALID",
|
|
580
|
+
entity: getEntity(context.decisionId),
|
|
581
|
+
path: context.path,
|
|
582
|
+
pointer: "/status",
|
|
583
|
+
range: node?.range ?? null
|
|
584
|
+
});
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
return node.value;
|
|
588
|
+
};
|
|
589
|
+
var readCreatedAt = (node, context) => {
|
|
590
|
+
if (node?.kind !== "scalar" || typeof node.value !== "string" || !isCanonicalCreatedAt(node.value)) {
|
|
591
|
+
context.diagnostics.add({
|
|
592
|
+
code: "MOLDEA_DECISION_CREATED_AT_INVALID",
|
|
593
|
+
entity: getEntity(context.decisionId),
|
|
594
|
+
path: context.path,
|
|
595
|
+
pointer: "/createdAt",
|
|
596
|
+
range: node?.range ?? null
|
|
597
|
+
});
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
if (context.decisionId !== null && Date.parse(node.value) !== Number(context.decisionId)) context.diagnostics.add({
|
|
601
|
+
code: "MOLDEA_DECISION_TIMESTAMP_MISMATCH",
|
|
602
|
+
entity: getEntity(context.decisionId),
|
|
603
|
+
path: context.path,
|
|
604
|
+
pointer: "/createdAt",
|
|
605
|
+
range: node.range
|
|
606
|
+
});
|
|
607
|
+
return node.value;
|
|
608
|
+
};
|
|
609
|
+
var readSupersedes = (node, context) => {
|
|
610
|
+
if (node === void 0) return [];
|
|
611
|
+
if (node.kind !== "sequence" || node.items.length === 0) {
|
|
612
|
+
addInvalidFrontmatter(context, node.range, "non-empty-supersedes-sequence-required", "/supersedes");
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
const supersedes = [];
|
|
616
|
+
const seen = /* @__PURE__ */ new Set();
|
|
617
|
+
node.items.forEach((item, index) => {
|
|
618
|
+
const pointer = `/supersedes/${index}`;
|
|
619
|
+
if (item.kind !== "scalar" || typeof item.value !== "string" || !DECISION_ID_PATTERN.test(item.value)) {
|
|
620
|
+
addInvalidFrontmatter(context, item.range, "decision-id-string-required", pointer);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
if (seen.has(item.value)) {
|
|
624
|
+
context.diagnostics.add({
|
|
625
|
+
code: "MOLDEA_ID_DUPLICATE",
|
|
626
|
+
entity: getEntity(context.decisionId),
|
|
627
|
+
path: context.path,
|
|
628
|
+
pointer,
|
|
629
|
+
range: item.range
|
|
630
|
+
});
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
seen.add(item.value);
|
|
634
|
+
supersedes.push(item.value);
|
|
635
|
+
if (item.value === context.decisionId) context.diagnostics.add({
|
|
636
|
+
code: "MOLDEA_DECISION_SELF_SUPERSESSION",
|
|
637
|
+
entity: getEntity(context.decisionId),
|
|
638
|
+
path: context.path,
|
|
639
|
+
pointer,
|
|
640
|
+
range: item.range
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
return supersedes.sort(compareExactStrings);
|
|
644
|
+
};
|
|
645
|
+
/**
|
|
646
|
+
* Locates exact decision frontmatter delimiters and preserves the complete Markdown body.
|
|
647
|
+
* @param value The complete normalized decision document.
|
|
648
|
+
* @param locator The full-document scalar-aware source locator.
|
|
649
|
+
* @param context The decision diagnostic context.
|
|
650
|
+
* @returns The YAML prefix and exact body, or `null` when delimiters are unavailable.
|
|
651
|
+
*/
|
|
652
|
+
var extractDecisionSections = (value, locator, context) => {
|
|
653
|
+
const firstLineEnd = value.indexOf("\n");
|
|
654
|
+
const openingLineEnd = firstLineEnd === -1 ? value.length : firstLineEnd;
|
|
655
|
+
if (value.slice(0, openingLineEnd) !== "---") {
|
|
656
|
+
context.diagnostics.add({
|
|
657
|
+
code: "MOLDEA_DECISION_FRONTMATTER_MISSING",
|
|
658
|
+
entity: getEntity(context.decisionId),
|
|
659
|
+
path: context.path,
|
|
660
|
+
range: locator.locateRange(0, openingLineEnd)
|
|
661
|
+
});
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
let lineStart = firstLineEnd === -1 ? value.length : firstLineEnd + 1;
|
|
665
|
+
while (lineStart <= value.length) {
|
|
666
|
+
const nextLineBreak = value.indexOf("\n", lineStart);
|
|
667
|
+
const lineEnd = nextLineBreak === -1 ? value.length : nextLineBreak;
|
|
668
|
+
if (value.slice(lineStart, lineEnd) === "---") {
|
|
669
|
+
const bodyStart = nextLineBreak === -1 ? lineEnd : nextLineBreak + 1;
|
|
670
|
+
return {
|
|
671
|
+
body: value.slice(bodyStart),
|
|
672
|
+
bodyRange: locator.locateRange(bodyStart, value.length),
|
|
673
|
+
yaml: value.slice(0, lineStart)
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
if (nextLineBreak === -1) break;
|
|
677
|
+
lineStart = nextLineBreak + 1;
|
|
678
|
+
}
|
|
679
|
+
context.diagnostics.add({
|
|
680
|
+
code: "MOLDEA_DECISION_FRONTMATTER_MISSING",
|
|
681
|
+
entity: getEntity(context.decisionId),
|
|
682
|
+
path: context.path,
|
|
683
|
+
range: locator.locateRange(value.length, value.length)
|
|
684
|
+
});
|
|
685
|
+
return null;
|
|
686
|
+
};
|
|
687
|
+
/**
|
|
688
|
+
* Validates one parsed decision-frontmatter mapping without resolving other files.
|
|
689
|
+
* @param node The strict parser-neutral YAML root.
|
|
690
|
+
* @param context The decision diagnostic context.
|
|
691
|
+
* @returns Canonical decision metadata when every document-level rule is valid.
|
|
692
|
+
*/
|
|
693
|
+
var validateDecisionFrontmatter = (node, context) => {
|
|
694
|
+
const initialDiagnosticCount = context.diagnostics.size;
|
|
695
|
+
const entries = readMapping$1(node, context);
|
|
696
|
+
if (entries === null) return null;
|
|
697
|
+
const status = readStatus(entries.get("status"), context);
|
|
698
|
+
const createdAt = readCreatedAt(entries.get("createdAt"), context);
|
|
699
|
+
const supersedes = readSupersedes(entries.get("supersedes"), context);
|
|
700
|
+
if (status === null || createdAt === null || supersedes === null || context.diagnostics.size > initialDiagnosticCount) return null;
|
|
701
|
+
return {
|
|
702
|
+
createdAt,
|
|
703
|
+
status,
|
|
704
|
+
supersedes
|
|
705
|
+
};
|
|
706
|
+
};
|
|
707
|
+
/** Adds the decision-body diagnostic when the preserved Markdown has no content. */
|
|
708
|
+
var validateDecisionBody = (body, range, context) => {
|
|
709
|
+
if (hasNonWhitespace(body)) return true;
|
|
710
|
+
context.diagnostics.add({
|
|
711
|
+
code: "MOLDEA_DECISION_BODY_EMPTY",
|
|
712
|
+
entity: getEntity(context.decisionId),
|
|
713
|
+
path: context.path,
|
|
714
|
+
range
|
|
715
|
+
});
|
|
716
|
+
return false;
|
|
717
|
+
};
|
|
718
|
+
//#endregion
|
|
719
|
+
//#region src/options/index.ts
|
|
720
|
+
/** Creates an operation-local options view with one shared raw-diagnostic budget. */
|
|
721
|
+
var createCoreOperationOptionsSnapshot = (options) => {
|
|
722
|
+
return Object.freeze({
|
|
723
|
+
adapters: options.adapters,
|
|
724
|
+
limits: createCoreOperationResourceLimits(options.limits)
|
|
725
|
+
});
|
|
726
|
+
};
|
|
727
|
+
var RESOURCE_LIMIT_KEYS = [
|
|
728
|
+
"maxEntries",
|
|
729
|
+
"maxTotalBytesRead",
|
|
730
|
+
"maxFileBytes",
|
|
731
|
+
"maxManifestBytes",
|
|
732
|
+
"maxDiagnostics",
|
|
733
|
+
"maxEvidence"
|
|
734
|
+
];
|
|
735
|
+
var STABLE_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
736
|
+
var WINDOWS_RESERVED_ID_PATTERN = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/u;
|
|
737
|
+
var compareStrings = (left, right) => {
|
|
738
|
+
if (left < right) return -1;
|
|
739
|
+
if (left > right) return 1;
|
|
740
|
+
return 0;
|
|
741
|
+
};
|
|
742
|
+
var invalidAdapter = (adapterId) => {
|
|
743
|
+
throw new CoreConfigurationException({
|
|
744
|
+
...adapterId === void 0 ? {} : { adapterId },
|
|
745
|
+
code: "INVALID_ADAPTER_DEFINITION",
|
|
746
|
+
operation: "create-core"
|
|
747
|
+
});
|
|
748
|
+
};
|
|
749
|
+
var isRecord$3 = (value) => {
|
|
750
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
751
|
+
};
|
|
752
|
+
var isStableId = (value) => {
|
|
753
|
+
return value.length >= 1 && value.length <= 64 && STABLE_ID_PATTERN.test(value);
|
|
754
|
+
};
|
|
755
|
+
/**
|
|
756
|
+
* Validates, copies, fills, and freezes all configured resource limits.
|
|
757
|
+
* @param candidate The untrusted optional limit overrides.
|
|
758
|
+
* @returns The complete frozen resource limits.
|
|
759
|
+
* @throws
|
|
760
|
+
* - INVALID_RESOURCE_LIMIT: A Core resource limit is invalid.
|
|
761
|
+
*/
|
|
762
|
+
var normalizeLimits = (candidate) => {
|
|
763
|
+
if (candidate === void 0) return DEFAULT_CORE_RESOURCE_LIMITS;
|
|
764
|
+
if (!isRecord$3(candidate)) throw new CoreConfigurationException({
|
|
765
|
+
code: "INVALID_RESOURCE_LIMIT",
|
|
766
|
+
operation: "create-core"
|
|
767
|
+
});
|
|
768
|
+
const knownKeys = new Set(RESOURCE_LIMIT_KEYS);
|
|
769
|
+
if (Object.keys(candidate).some((key) => !knownKeys.has(key))) throw new CoreConfigurationException({
|
|
770
|
+
code: "INVALID_RESOURCE_LIMIT",
|
|
771
|
+
operation: "create-core"
|
|
772
|
+
});
|
|
773
|
+
const normalized = { ...DEFAULT_CORE_RESOURCE_LIMITS };
|
|
774
|
+
for (const key of RESOURCE_LIMIT_KEYS) {
|
|
775
|
+
const value = candidate[key];
|
|
776
|
+
if (value === void 0) continue;
|
|
777
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new CoreConfigurationException({
|
|
778
|
+
code: "INVALID_RESOURCE_LIMIT",
|
|
779
|
+
operation: "create-core"
|
|
780
|
+
});
|
|
781
|
+
normalized[key] = value;
|
|
782
|
+
}
|
|
783
|
+
return freezeRecursively(normalized);
|
|
784
|
+
};
|
|
785
|
+
/**
|
|
786
|
+
* Snapshots one adapter definition without freezing caller-owned state.
|
|
787
|
+
* @param candidate The untrusted adapter definition.
|
|
788
|
+
* @returns A frozen detached adapter snapshot.
|
|
789
|
+
* @throws
|
|
790
|
+
* - RESERVED_ADAPTER_ID: A reserved runtime adapter ID was supplied.
|
|
791
|
+
* - INVALID_ADAPTER_DEFINITION: A runtime adapter definition is invalid.
|
|
792
|
+
*/
|
|
793
|
+
var normalizeAdapter = (candidate) => {
|
|
794
|
+
if (!isRecord$3(candidate)) return invalidAdapter();
|
|
795
|
+
const id = candidate["id"];
|
|
796
|
+
if (typeof id !== "string" || !isStableId(id) || WINDOWS_RESERVED_ID_PATTERN.test(id)) return invalidAdapter();
|
|
797
|
+
if (id === "custom") throw new CoreConfigurationException({
|
|
798
|
+
adapterId: id,
|
|
799
|
+
code: "RESERVED_ADAPTER_ID",
|
|
800
|
+
operation: "create-core"
|
|
801
|
+
});
|
|
802
|
+
if (!RECOGNIZED_RUNTIME_ADAPTER_IDS.includes(id)) return invalidAdapter(id);
|
|
803
|
+
const versions = candidate["supportedRepositoryFormatVersions"];
|
|
804
|
+
const inspect = candidate["inspect"];
|
|
805
|
+
if (!Array.isArray(versions) || versions.length === 0 || typeof inspect !== "function") return invalidAdapter(id);
|
|
806
|
+
const supportedVersions = /* @__PURE__ */ new Set();
|
|
807
|
+
for (const version of versions) {
|
|
808
|
+
if (!SUPPORTED_REPOSITORY_FORMAT_VERSIONS.includes(version) || supportedVersions.has(version)) return invalidAdapter(id);
|
|
809
|
+
supportedVersions.add(version);
|
|
810
|
+
}
|
|
811
|
+
const inspectReference = inspect;
|
|
812
|
+
return freezeRecursively({
|
|
813
|
+
id,
|
|
814
|
+
inspect: async (context) => inspectReference.call(candidate, context),
|
|
815
|
+
supportedRepositoryFormatVersions: [...supportedVersions].sort((left, right) => left - right)
|
|
816
|
+
});
|
|
817
|
+
};
|
|
818
|
+
/**
|
|
819
|
+
* Validates, snapshots, and deterministically sorts the configured adapter registry.
|
|
820
|
+
* @param candidate The untrusted optional adapter definitions.
|
|
821
|
+
* @returns The frozen adapter snapshots in canonical ID order.
|
|
822
|
+
* @throws
|
|
823
|
+
* - DUPLICATE_ADAPTER_ID: A runtime adapter ID is registered more than once.
|
|
824
|
+
* - RESERVED_ADAPTER_ID: A reserved runtime adapter ID was supplied.
|
|
825
|
+
* - INVALID_ADAPTER_DEFINITION: A runtime adapter definition is invalid.
|
|
826
|
+
*/
|
|
827
|
+
var normalizeAdapters = (candidate) => {
|
|
828
|
+
if (candidate === void 0) return Object.freeze([]);
|
|
829
|
+
if (!Array.isArray(candidate)) return invalidAdapter();
|
|
830
|
+
const adapters = Array.from(candidate, (adapter) => normalizeAdapter(adapter));
|
|
831
|
+
const ids = /* @__PURE__ */ new Set();
|
|
832
|
+
for (const adapter of adapters) {
|
|
833
|
+
if (ids.has(adapter.id)) throw new CoreConfigurationException({
|
|
834
|
+
adapterId: adapter.id,
|
|
835
|
+
code: "DUPLICATE_ADAPTER_ID",
|
|
836
|
+
operation: "create-core"
|
|
837
|
+
});
|
|
838
|
+
ids.add(adapter.id);
|
|
839
|
+
}
|
|
840
|
+
return freezeRecursively(adapters.sort((left, right) => compareStrings(left.id, right.id)));
|
|
841
|
+
};
|
|
842
|
+
/**
|
|
843
|
+
* Validates and snapshots caller-owned Core construction options.
|
|
844
|
+
* @param options The optional adapters and resource-limit overrides.
|
|
845
|
+
* @returns A deeply frozen, deterministically ordered configuration snapshot.
|
|
846
|
+
* @throws
|
|
847
|
+
* - DUPLICATE_ADAPTER_ID: A runtime adapter ID is registered more than once.
|
|
848
|
+
* - RESERVED_ADAPTER_ID: A reserved runtime adapter ID was supplied.
|
|
849
|
+
* - INVALID_ADAPTER_DEFINITION: A runtime adapter definition is invalid.
|
|
850
|
+
* - INVALID_RESOURCE_LIMIT: A Core resource limit is invalid.
|
|
851
|
+
*/
|
|
852
|
+
var normalizeCoreOptions = (options) => {
|
|
853
|
+
if (options !== void 0 && !isRecord$3(options)) return invalidAdapter();
|
|
854
|
+
return freezeRecursively({
|
|
855
|
+
adapters: normalizeAdapters(options?.["adapters"]),
|
|
856
|
+
limits: normalizeLimits(options?.["limits"])
|
|
857
|
+
});
|
|
858
|
+
};
|
|
859
|
+
//#endregion
|
|
860
|
+
//#region src/source-location/index.ts
|
|
861
|
+
var findLineIndex = (lineStarts, offset) => {
|
|
862
|
+
let lower = 0;
|
|
863
|
+
let upper = lineStarts.length - 1;
|
|
864
|
+
while (lower < upper) {
|
|
865
|
+
const middle = Math.ceil((lower + upper) / 2);
|
|
866
|
+
if ((lineStarts[middle] ?? 0) <= offset) lower = middle;
|
|
867
|
+
else upper = middle - 1;
|
|
868
|
+
}
|
|
869
|
+
return lower;
|
|
870
|
+
};
|
|
871
|
+
/**
|
|
872
|
+
* Creates an efficient UTF-16-offset to Unicode-scalar source locator.
|
|
873
|
+
* @param value The already-normalized valid Unicode scalar text.
|
|
874
|
+
* @returns A locator producing one-based lines and columns with zero-based scalar offsets.
|
|
875
|
+
*/
|
|
876
|
+
var createSourceLocator = (value) => {
|
|
877
|
+
const scalarOffsets = new Uint32Array(value.length + 1);
|
|
878
|
+
const lineStarts = [0];
|
|
879
|
+
let scalarOffset = 0;
|
|
880
|
+
for (let codeUnitOffset = 0; codeUnitOffset < value.length;) {
|
|
881
|
+
const codePoint = value.codePointAt(codeUnitOffset);
|
|
882
|
+
const width = codePoint !== void 0 && codePoint > 65535 ? 2 : 1;
|
|
883
|
+
scalarOffsets[codeUnitOffset] = scalarOffset;
|
|
884
|
+
for (let interiorOffset = 1; interiorOffset < width; interiorOffset += 1) scalarOffsets[codeUnitOffset + interiorOffset] = scalarOffset;
|
|
885
|
+
codeUnitOffset += width;
|
|
886
|
+
scalarOffset += 1;
|
|
887
|
+
scalarOffsets[codeUnitOffset] = scalarOffset;
|
|
888
|
+
if (codePoint === 10) lineStarts.push(codeUnitOffset);
|
|
889
|
+
}
|
|
890
|
+
const locatePosition = (candidateOffset) => {
|
|
891
|
+
const codeUnitOffset = Math.max(0, Math.min(value.length, candidateOffset));
|
|
892
|
+
const lineIndex = findLineIndex(lineStarts, codeUnitOffset);
|
|
893
|
+
const lineStart = lineStarts[lineIndex] ?? 0;
|
|
894
|
+
const positionScalarOffset = scalarOffsets[codeUnitOffset] ?? 0;
|
|
895
|
+
return {
|
|
896
|
+
column: positionScalarOffset - (scalarOffsets[lineStart] ?? 0) + 1,
|
|
897
|
+
line: lineIndex + 1,
|
|
898
|
+
offset: positionScalarOffset
|
|
899
|
+
};
|
|
900
|
+
};
|
|
901
|
+
return Object.freeze({ locateRange: (startOffset, endOffset) => ({
|
|
902
|
+
end: locatePosition(endOffset),
|
|
903
|
+
start: locatePosition(startOffset)
|
|
904
|
+
}) });
|
|
905
|
+
};
|
|
906
|
+
//#endregion
|
|
907
|
+
//#region src/text/index.ts
|
|
908
|
+
var encoder = new TextEncoder();
|
|
909
|
+
var decoder = new TextDecoder("utf-8", {
|
|
910
|
+
fatal: true,
|
|
911
|
+
ignoreBOM: true
|
|
912
|
+
});
|
|
913
|
+
var isRecord$2 = (value) => {
|
|
914
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
915
|
+
};
|
|
916
|
+
/**
|
|
917
|
+
* Measures valid scalar UTF-8 bytes and stops once the operation budget is exceeded.
|
|
918
|
+
* @param value The decoded string to measure without lossy replacement.
|
|
919
|
+
* @param maximumByteLength The threshold after which measurement may stop early.
|
|
920
|
+
* @returns The measured bytes, a value above the threshold, or `null` for invalid Unicode.
|
|
921
|
+
*/
|
|
922
|
+
var measureScalarUtf8ByteLength = (value, maximumByteLength) => {
|
|
923
|
+
let byteLength = 0;
|
|
924
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
925
|
+
const codeUnit = value.charCodeAt(index);
|
|
926
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
927
|
+
if (index + 1 >= value.length) return null;
|
|
928
|
+
const nextCodeUnit = value.charCodeAt(index + 1);
|
|
929
|
+
if (nextCodeUnit < 56320 || nextCodeUnit > 57343) return null;
|
|
930
|
+
byteLength += 4;
|
|
931
|
+
if (byteLength > maximumByteLength) return byteLength;
|
|
932
|
+
index += 1;
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
if (codeUnit >= 56320 && codeUnit <= 57343) return null;
|
|
936
|
+
byteLength += codeUnit <= 127 ? 1 : codeUnit <= 2047 ? 2 : 3;
|
|
937
|
+
if (byteLength > maximumByteLength) return byteLength;
|
|
938
|
+
}
|
|
939
|
+
return byteLength;
|
|
940
|
+
};
|
|
941
|
+
var countUnicodeScalars = (value) => {
|
|
942
|
+
let count = 0;
|
|
943
|
+
for (let index = 0; index < value.length; count += 1) {
|
|
944
|
+
const codePoint = value.codePointAt(index);
|
|
945
|
+
if (codePoint === void 0) break;
|
|
946
|
+
index += codePoint > 65535 ? 2 : 1;
|
|
947
|
+
}
|
|
948
|
+
return count;
|
|
949
|
+
};
|
|
950
|
+
/**
|
|
951
|
+
* Collects every normalized NUL location or rejects when the diagnostic budget is exhausted.
|
|
952
|
+
* @param value The normalized scalar text to inspect.
|
|
953
|
+
* @param path The logical document path attached to each diagnostic.
|
|
954
|
+
* @param diagnostics The operation collector receiving every NUL diagnostic.
|
|
955
|
+
* @throws
|
|
956
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
|
|
957
|
+
*/
|
|
958
|
+
var collectNulDiagnostics = (value, path, diagnostics) => {
|
|
959
|
+
let column = 1;
|
|
960
|
+
let line = 1;
|
|
961
|
+
let offset = 0;
|
|
962
|
+
for (const scalar of value) {
|
|
963
|
+
if (scalar === "\0") diagnostics.add({
|
|
964
|
+
code: "MOLDEA_TEXT_NUL_FORBIDDEN",
|
|
965
|
+
path,
|
|
966
|
+
range: {
|
|
967
|
+
end: {
|
|
968
|
+
column: column + 1,
|
|
969
|
+
line,
|
|
970
|
+
offset: offset + 1
|
|
971
|
+
},
|
|
972
|
+
start: {
|
|
973
|
+
column,
|
|
974
|
+
line,
|
|
975
|
+
offset
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
if (scalar === "\n") {
|
|
980
|
+
column = 1;
|
|
981
|
+
line += 1;
|
|
982
|
+
} else column += 1;
|
|
983
|
+
offset += 1;
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
var invalidArgument$1 = (operation) => {
|
|
987
|
+
throw new CoreOperationException({
|
|
988
|
+
code: "INVALID_ARGUMENT",
|
|
989
|
+
operation
|
|
990
|
+
});
|
|
991
|
+
};
|
|
992
|
+
var enforceFileLimit = (byteLength, limits, operation, limit) => {
|
|
993
|
+
if (byteLength <= limits[limit]) return;
|
|
994
|
+
throw new CoreOperationException({
|
|
995
|
+
code: "RESOURCE_LIMIT_EXCEEDED",
|
|
996
|
+
limit,
|
|
997
|
+
operation
|
|
998
|
+
});
|
|
999
|
+
};
|
|
1000
|
+
var invalidResult = (diagnostics) => {
|
|
1001
|
+
return freezeRecursively({
|
|
1002
|
+
diagnostics: [...diagnostics],
|
|
1003
|
+
text: null,
|
|
1004
|
+
valid: false
|
|
1005
|
+
});
|
|
1006
|
+
};
|
|
1007
|
+
/**
|
|
1008
|
+
* Validates one public text input, enforces its source-byte budget, and decodes strict UTF-8.
|
|
1009
|
+
* @param input The untrusted public text-document input.
|
|
1010
|
+
* @param limits The immutable Core resource limits.
|
|
1011
|
+
* @param operation The public operation requesting the input.
|
|
1012
|
+
* @param limit The source-byte limit applied by the operation.
|
|
1013
|
+
* @param diagnostics The operation collector receiving text diagnostics.
|
|
1014
|
+
* @returns Decoded text or a frozen structural failure result.
|
|
1015
|
+
* @throws
|
|
1016
|
+
* - INVALID_REPOSITORY_PATH: The repository path is invalid.
|
|
1017
|
+
* - INVALID_ARGUMENT: The Core operation received an invalid argument.
|
|
1018
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
|
|
1019
|
+
*/
|
|
1020
|
+
var readInput = (input, limits, operation, limit, diagnostics) => {
|
|
1021
|
+
if (!isRecord$2(input)) return invalidArgument$1(operation);
|
|
1022
|
+
const pathCandidate = input["path"];
|
|
1023
|
+
const content = input["content"];
|
|
1024
|
+
if (typeof pathCandidate !== "string" || typeof content !== "string" && !(content instanceof Uint8Array)) return invalidArgument$1(operation);
|
|
1025
|
+
const path = parseRepositoryPath(pathCandidate);
|
|
1026
|
+
if (typeof content === "string") {
|
|
1027
|
+
const byteLength = measureScalarUtf8ByteLength(content, limits[limit]);
|
|
1028
|
+
if (byteLength === null) {
|
|
1029
|
+
diagnostics.add({
|
|
1030
|
+
code: "MOLDEA_TEXT_INVALID_UNICODE",
|
|
1031
|
+
path
|
|
1032
|
+
});
|
|
1033
|
+
return invalidResult(diagnostics.finalize());
|
|
1034
|
+
}
|
|
1035
|
+
enforceFileLimit(byteLength, limits, operation, limit);
|
|
1036
|
+
return {
|
|
1037
|
+
path,
|
|
1038
|
+
value: content
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
enforceFileLimit(content.byteLength, limits, operation, limit);
|
|
1042
|
+
const bytes = new Uint8Array(content);
|
|
1043
|
+
try {
|
|
1044
|
+
return {
|
|
1045
|
+
path,
|
|
1046
|
+
value: decoder.decode(bytes)
|
|
1047
|
+
};
|
|
1048
|
+
} catch {
|
|
1049
|
+
diagnostics.add({
|
|
1050
|
+
code: "MOLDEA_TEXT_INVALID_UTF8",
|
|
1051
|
+
path
|
|
1052
|
+
});
|
|
1053
|
+
return invalidResult(diagnostics.finalize());
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
/**
|
|
1057
|
+
* Validates and normalizes one text document under an operation-specific resource budget.
|
|
1058
|
+
* @param input The logical path and decoded string or exact source bytes.
|
|
1059
|
+
* @param limits The immutable Core resource limits.
|
|
1060
|
+
* @param operation The public Core operation requesting normalization.
|
|
1061
|
+
* @param limit The source-byte limit applied by the operation.
|
|
1062
|
+
* @returns A frozen normalized result or structural text diagnostics.
|
|
1063
|
+
* @throws
|
|
1064
|
+
* - INVALID_REPOSITORY_PATH: The repository path is invalid.
|
|
1065
|
+
* - INVALID_ARGUMENT: The Core operation received an invalid argument.
|
|
1066
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
|
|
1067
|
+
*/
|
|
1068
|
+
var normalizeTextDocument = (input, limits, operation, limit = "maxFileBytes") => {
|
|
1069
|
+
const diagnostics = createCoreDiagnosticCollector(limits, operation);
|
|
1070
|
+
const decoded = readInput(input, limits, operation, limit, diagnostics);
|
|
1071
|
+
if ("valid" in decoded) return decoded;
|
|
1072
|
+
const value = (decoded.value.startsWith("") ? decoded.value.slice(1) : decoded.value).replace(/\r\n?/gu, "\n");
|
|
1073
|
+
collectNulDiagnostics(value, decoded.path, diagnostics);
|
|
1074
|
+
if (diagnostics.size > 0) return invalidResult(diagnostics.finalize());
|
|
1075
|
+
return freezeRecursively({
|
|
1076
|
+
diagnostics: [],
|
|
1077
|
+
text: freezeRecursively({
|
|
1078
|
+
scalarLength: countUnicodeScalars(value),
|
|
1079
|
+
utf8ByteLength: encoder.encode(value).byteLength,
|
|
1080
|
+
value
|
|
1081
|
+
}),
|
|
1082
|
+
valid: true
|
|
1083
|
+
});
|
|
1084
|
+
};
|
|
1085
|
+
var toLowercaseHex = (bytes) => {
|
|
1086
|
+
let value = "";
|
|
1087
|
+
for (const byte of bytes) value += byte.toString(16).padStart(2, "0");
|
|
1088
|
+
return value;
|
|
1089
|
+
};
|
|
1090
|
+
/**
|
|
1091
|
+
* Calculates the canonical digest for already-normalized text.
|
|
1092
|
+
* @param text The validated normalized text to encode and hash.
|
|
1093
|
+
* @returns A promise resolving to the canonical lowercase SHA-256 digest.
|
|
1094
|
+
*/
|
|
1095
|
+
var calculateNormalizedTextDigest = async (text) => {
|
|
1096
|
+
const bytes = encoder.encode(text.value);
|
|
1097
|
+
return `sha256:${toLowercaseHex(new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", bytes)))}`;
|
|
1098
|
+
};
|
|
1099
|
+
/**
|
|
1100
|
+
* Calculates the normalized SHA-256 digest for one text document.
|
|
1101
|
+
* @param input The logical path and decoded string or exact source bytes.
|
|
1102
|
+
* @param limits The immutable Core resource limits.
|
|
1103
|
+
* @returns A promise resolving to a frozen digest result or structural text diagnostics.
|
|
1104
|
+
* @throws
|
|
1105
|
+
* - INVALID_REPOSITORY_PATH: The repository path is invalid.
|
|
1106
|
+
* - INVALID_ARGUMENT: The Core operation received an invalid argument.
|
|
1107
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
|
|
1108
|
+
*/
|
|
1109
|
+
var calculateContentDigest = async (input, limits) => {
|
|
1110
|
+
const normalized = normalizeTextDocument(input, limits, "calculate-content-digest");
|
|
1111
|
+
if (!normalized.valid || normalized.text === null) return freezeRecursively({
|
|
1112
|
+
diagnostics: normalized.diagnostics,
|
|
1113
|
+
digest: null,
|
|
1114
|
+
text: null,
|
|
1115
|
+
valid: false
|
|
1116
|
+
});
|
|
1117
|
+
return freezeRecursively({
|
|
1118
|
+
diagnostics: [],
|
|
1119
|
+
digest: await calculateNormalizedTextDigest(normalized.text),
|
|
1120
|
+
text: normalized.text,
|
|
1121
|
+
valid: true
|
|
1122
|
+
});
|
|
1123
|
+
};
|
|
1124
|
+
//#endregion
|
|
1125
|
+
//#region src/yaml/index.ts
|
|
1126
|
+
var CORE_TAGS = /* @__PURE__ */ new Set([
|
|
1127
|
+
"tag:yaml.org,2002:bool",
|
|
1128
|
+
"tag:yaml.org,2002:float",
|
|
1129
|
+
"tag:yaml.org,2002:int",
|
|
1130
|
+
"tag:yaml.org,2002:map",
|
|
1131
|
+
"tag:yaml.org,2002:null",
|
|
1132
|
+
"tag:yaml.org,2002:seq",
|
|
1133
|
+
"tag:yaml.org,2002:str"
|
|
1134
|
+
]);
|
|
1135
|
+
var toRange = (range, locator) => {
|
|
1136
|
+
return locator.locateRange(range?.[0] ?? 0, range?.[1] ?? range?.[0] ?? 0);
|
|
1137
|
+
};
|
|
1138
|
+
var addUnsupportedFeature = (diagnostics, path, range, reason) => {
|
|
1139
|
+
diagnostics.add({
|
|
1140
|
+
code: "MOLDEA_YAML_FEATURE_UNSUPPORTED",
|
|
1141
|
+
details: { reason },
|
|
1142
|
+
path,
|
|
1143
|
+
range
|
|
1144
|
+
});
|
|
1145
|
+
};
|
|
1146
|
+
var isUnicodeScalarString = (value) => {
|
|
1147
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1148
|
+
const codeUnit = value.charCodeAt(index);
|
|
1149
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
1150
|
+
const nextCodeUnit = value.charCodeAt(index + 1);
|
|
1151
|
+
if (!(nextCodeUnit >= 56320 && nextCodeUnit <= 57343)) return false;
|
|
1152
|
+
index += 1;
|
|
1153
|
+
} else if (codeUnit >= 56320 && codeUnit <= 57343) return false;
|
|
1154
|
+
}
|
|
1155
|
+
return true;
|
|
1156
|
+
};
|
|
1157
|
+
var isCoreTagCompatible = (node) => {
|
|
1158
|
+
switch (node.tag) {
|
|
1159
|
+
case "tag:yaml.org,2002:bool": return isScalar(node) && typeof node.value === "boolean";
|
|
1160
|
+
case "tag:yaml.org,2002:float": return isScalar(node) && typeof node.value === "number";
|
|
1161
|
+
case "tag:yaml.org,2002:int": return isScalar(node) && typeof node.value === "bigint";
|
|
1162
|
+
case "tag:yaml.org,2002:map": return isMap(node);
|
|
1163
|
+
case "tag:yaml.org,2002:null": return isScalar(node) && node.value === null;
|
|
1164
|
+
case "tag:yaml.org,2002:seq": return isSeq(node);
|
|
1165
|
+
case "tag:yaml.org,2002:str": return isScalar(node) && typeof node.value === "string";
|
|
1166
|
+
default: return false;
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
var convertNode = (node, path, locator, diagnostics) => {
|
|
1170
|
+
const range = toRange(node.range, locator);
|
|
1171
|
+
if (isAlias(node)) {
|
|
1172
|
+
addUnsupportedFeature(diagnostics, path, range, "alias");
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
if ("anchor" in node && typeof node.anchor === "string") addUnsupportedFeature(diagnostics, path, range, "anchor");
|
|
1176
|
+
if (node.tag !== void 0) {
|
|
1177
|
+
if (!CORE_TAGS.has(node.tag)) {
|
|
1178
|
+
addUnsupportedFeature(diagnostics, path, range, "custom-tag");
|
|
1179
|
+
return null;
|
|
1180
|
+
}
|
|
1181
|
+
if (!isCoreTagCompatible(node)) {
|
|
1182
|
+
diagnostics.add({
|
|
1183
|
+
code: "MOLDEA_YAML_MALFORMED",
|
|
1184
|
+
details: { reason: "tag-value" },
|
|
1185
|
+
path,
|
|
1186
|
+
range
|
|
1187
|
+
});
|
|
1188
|
+
return null;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
if (isScalar(node)) {
|
|
1192
|
+
const value = node.value;
|
|
1193
|
+
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "bigint" && typeof value !== "boolean") {
|
|
1194
|
+
diagnostics.add({
|
|
1195
|
+
code: "MOLDEA_YAML_MALFORMED",
|
|
1196
|
+
details: { reason: "scalar-type" },
|
|
1197
|
+
path,
|
|
1198
|
+
range
|
|
1199
|
+
});
|
|
1200
|
+
return null;
|
|
1201
|
+
}
|
|
1202
|
+
if (typeof value === "string" && !isUnicodeScalarString(value)) {
|
|
1203
|
+
diagnostics.add({
|
|
1204
|
+
code: "MOLDEA_YAML_MALFORMED",
|
|
1205
|
+
details: { reason: "invalid-unicode" },
|
|
1206
|
+
path,
|
|
1207
|
+
range
|
|
1208
|
+
});
|
|
1209
|
+
return null;
|
|
1210
|
+
}
|
|
1211
|
+
return {
|
|
1212
|
+
kind: "scalar",
|
|
1213
|
+
range,
|
|
1214
|
+
source: node.source,
|
|
1215
|
+
style: node.type ?? null,
|
|
1216
|
+
value
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
if (isSeq(node)) {
|
|
1220
|
+
const items = [];
|
|
1221
|
+
for (const item of node.items) {
|
|
1222
|
+
if (item === null) {
|
|
1223
|
+
items.push({
|
|
1224
|
+
kind: "scalar",
|
|
1225
|
+
range,
|
|
1226
|
+
source: "",
|
|
1227
|
+
style: null,
|
|
1228
|
+
value: null
|
|
1229
|
+
});
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
const converted = convertNode(item, path, locator, diagnostics);
|
|
1233
|
+
if (converted !== null) items.push(converted);
|
|
1234
|
+
}
|
|
1235
|
+
return {
|
|
1236
|
+
items,
|
|
1237
|
+
kind: "sequence",
|
|
1238
|
+
range
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
if (isMap(node)) {
|
|
1242
|
+
const entries = [];
|
|
1243
|
+
for (const pair of node.items) {
|
|
1244
|
+
const key = convertNode(pair.key, path, locator, diagnostics);
|
|
1245
|
+
const value = pair.value === null ? {
|
|
1246
|
+
kind: "scalar",
|
|
1247
|
+
range: key?.range ?? range,
|
|
1248
|
+
source: "",
|
|
1249
|
+
style: null,
|
|
1250
|
+
value: null
|
|
1251
|
+
} : convertNode(pair.value, path, locator, diagnostics);
|
|
1252
|
+
if (key?.kind === "scalar" && key.value === "<<" && key.style === "PLAIN") addUnsupportedFeature(diagnostics, path, key.range, "merge-key");
|
|
1253
|
+
if (key !== null && value !== null) entries.push({
|
|
1254
|
+
key,
|
|
1255
|
+
value
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
return {
|
|
1259
|
+
entries,
|
|
1260
|
+
kind: "mapping",
|
|
1261
|
+
range
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
diagnostics.add({
|
|
1265
|
+
code: "MOLDEA_YAML_MALFORMED",
|
|
1266
|
+
details: { reason: "node-type" },
|
|
1267
|
+
path,
|
|
1268
|
+
range
|
|
1269
|
+
});
|
|
1270
|
+
return null;
|
|
1271
|
+
};
|
|
1272
|
+
var addParserDiagnostic = (code, start, end, path, locator, diagnostics) => {
|
|
1273
|
+
if (code === "BAD_DIRECTIVE") return;
|
|
1274
|
+
if (code === "DUPLICATE_KEY") {
|
|
1275
|
+
diagnostics.add({
|
|
1276
|
+
code: "MOLDEA_YAML_DUPLICATE_KEY",
|
|
1277
|
+
path,
|
|
1278
|
+
range: locator.locateRange(start, end)
|
|
1279
|
+
});
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (code === "TAG_RESOLVE_FAILED") return;
|
|
1283
|
+
diagnostics.add({
|
|
1284
|
+
code: "MOLDEA_YAML_MALFORMED",
|
|
1285
|
+
details: { reason: code === "RESOURCE_EXHAUSTION" ? "resource-exhaustion" : "syntax" },
|
|
1286
|
+
path,
|
|
1287
|
+
range: locator.locateRange(start, end)
|
|
1288
|
+
});
|
|
1289
|
+
};
|
|
1290
|
+
/**
|
|
1291
|
+
* Parses one normalized YAML 1.2 Core Schema document into parser-neutral nodes.
|
|
1292
|
+
* @param value The normalized manifest text.
|
|
1293
|
+
* @param path The logical source path used by diagnostics.
|
|
1294
|
+
* @param locator The scalar-aware source locator for the normalized text.
|
|
1295
|
+
* @param diagnostics The operation diagnostic collector.
|
|
1296
|
+
* @returns The private parsed tree only when the YAML substrate is trustworthy.
|
|
1297
|
+
*/
|
|
1298
|
+
var parseStrictYaml = (value, path, locator, diagnostics) => {
|
|
1299
|
+
const initialDiagnosticCount = diagnostics.size;
|
|
1300
|
+
for (const token of new Parser().parse(value)) if (token.type === "directive") addUnsupportedFeature(diagnostics, path, locator.locateRange(token.offset, token.offset + token.source.length), "directive");
|
|
1301
|
+
const documents = parseAllDocuments(value, {
|
|
1302
|
+
intAsBigInt: true,
|
|
1303
|
+
keepSourceTokens: true,
|
|
1304
|
+
logLevel: "silent",
|
|
1305
|
+
merge: false,
|
|
1306
|
+
prettyErrors: false,
|
|
1307
|
+
resolveKnownTags: false,
|
|
1308
|
+
schema: "core",
|
|
1309
|
+
strict: true,
|
|
1310
|
+
uniqueKeys: true,
|
|
1311
|
+
version: "1.2"
|
|
1312
|
+
});
|
|
1313
|
+
if (documents.length > 1) {
|
|
1314
|
+
const range = documents[1]?.range;
|
|
1315
|
+
diagnostics.add({
|
|
1316
|
+
code: "MOLDEA_YAML_MULTIPLE_DOCUMENTS",
|
|
1317
|
+
path,
|
|
1318
|
+
range: locator.locateRange(range?.[0] ?? 0, range?.[0] ?? 0)
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
for (const document of documents) for (const error of [...document.errors, ...document.warnings]) addParserDiagnostic(error.code, error.pos[0], error.pos[1], path, locator, diagnostics);
|
|
1322
|
+
if (documents.length > 1 || diagnostics.size > initialDiagnosticCount) return {
|
|
1323
|
+
valid: false,
|
|
1324
|
+
value: null
|
|
1325
|
+
};
|
|
1326
|
+
const document = documents[0];
|
|
1327
|
+
if (document === void 0 || document.contents === null) return {
|
|
1328
|
+
valid: true,
|
|
1329
|
+
value: {
|
|
1330
|
+
kind: "scalar",
|
|
1331
|
+
range: locator.locateRange(0, 0),
|
|
1332
|
+
source: "",
|
|
1333
|
+
style: null,
|
|
1334
|
+
value: null
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
const parsedValue = convertNode(document.contents, path, locator, diagnostics);
|
|
1338
|
+
return {
|
|
1339
|
+
valid: parsedValue !== null && diagnostics.size === initialDiagnosticCount,
|
|
1340
|
+
value: diagnostics.size === initialDiagnosticCount ? parsedValue : null
|
|
1341
|
+
};
|
|
1342
|
+
};
|
|
1343
|
+
//#endregion
|
|
1344
|
+
//#region src/decision/index.ts
|
|
1345
|
+
/**
|
|
1346
|
+
* Parses and validates one complete decision document without resolving cross-file relationships.
|
|
1347
|
+
* @param input The canonical logical path and exact decision text or bytes.
|
|
1348
|
+
* @param options The immutable Core limits and adapter snapshots.
|
|
1349
|
+
* @param operation The owning public or repository-level operation for typed failures.
|
|
1350
|
+
* @returns A frozen all-or-nothing decision parse result.
|
|
1351
|
+
* @throws
|
|
1352
|
+
* - INVALID_REPOSITORY_PATH: The repository path is invalid.
|
|
1353
|
+
* - INVALID_ARGUMENT: The Core operation received an invalid argument.
|
|
1354
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
|
|
1355
|
+
*/
|
|
1356
|
+
var parseDecisionDocument = async (input, options, operation = "parse-decision") => {
|
|
1357
|
+
options = createCoreOperationOptionsSnapshot(options);
|
|
1358
|
+
const normalized = normalizeTextDocument(input, options.limits, operation);
|
|
1359
|
+
const path = parseRepositoryPath(input.path);
|
|
1360
|
+
const diagnostics = createCoreDiagnosticCollector(options.limits, operation);
|
|
1361
|
+
const decisionId = parseDecisionIdFromPath(path);
|
|
1362
|
+
if (decisionId === null) diagnostics.add({
|
|
1363
|
+
code: "MOLDEA_DECISION_FILENAME_INVALID",
|
|
1364
|
+
path
|
|
1365
|
+
});
|
|
1366
|
+
for (const diagnostic of normalized.diagnostics) diagnostics.merge(diagnostic);
|
|
1367
|
+
if (!normalized.valid || normalized.text === null) return freezeRecursively({
|
|
1368
|
+
decision: null,
|
|
1369
|
+
diagnostics: diagnostics.finalize(),
|
|
1370
|
+
valid: false
|
|
1371
|
+
});
|
|
1372
|
+
const context = {
|
|
1373
|
+
decisionId,
|
|
1374
|
+
diagnostics,
|
|
1375
|
+
path
|
|
1376
|
+
};
|
|
1377
|
+
const locator = createSourceLocator(normalized.text.value);
|
|
1378
|
+
const sections = extractDecisionSections(normalized.text.value, locator, context);
|
|
1379
|
+
let metadata = null;
|
|
1380
|
+
if (sections !== null) {
|
|
1381
|
+
validateDecisionBody(sections.body, sections.bodyRange, context);
|
|
1382
|
+
const parsed = parseStrictYaml(sections.yaml, path, locator, diagnostics);
|
|
1383
|
+
if (parsed.valid && parsed.value !== null) metadata = validateDecisionFrontmatter(parsed.value, context);
|
|
1384
|
+
}
|
|
1385
|
+
if (decisionId === null || sections === null || metadata === null || diagnostics.size > 0) return freezeRecursively({
|
|
1386
|
+
decision: null,
|
|
1387
|
+
diagnostics: diagnostics.finalize(),
|
|
1388
|
+
valid: false
|
|
1389
|
+
});
|
|
1390
|
+
const digest = await calculateNormalizedTextDigest(normalized.text);
|
|
1391
|
+
return freezeRecursively({
|
|
1392
|
+
decision: {
|
|
1393
|
+
asset: {
|
|
1394
|
+
content: normalized.text.value,
|
|
1395
|
+
digest,
|
|
1396
|
+
path,
|
|
1397
|
+
scalarLength: normalized.text.scalarLength,
|
|
1398
|
+
utf8ByteLength: normalized.text.utf8ByteLength
|
|
1399
|
+
},
|
|
1400
|
+
body: sections.body,
|
|
1401
|
+
createdAt: metadata.createdAt,
|
|
1402
|
+
id: decisionId,
|
|
1403
|
+
path,
|
|
1404
|
+
status: metadata.status,
|
|
1405
|
+
supersedes: metadata.supersedes
|
|
1406
|
+
},
|
|
1407
|
+
diagnostics: diagnostics.finalize(),
|
|
1408
|
+
valid: true
|
|
1409
|
+
});
|
|
1410
|
+
};
|
|
1411
|
+
//#endregion
|
|
1412
|
+
//#region src/manifest-validation/index.ts
|
|
1413
|
+
var childPointer$1 = (pointer, key) => {
|
|
1414
|
+
return `${pointer}/${escapeJsonPointerSegment(key)}`;
|
|
1415
|
+
};
|
|
1416
|
+
var addInvalidValue = (context, pointer, range, reason, entity = null) => {
|
|
1417
|
+
context.diagnostics.add({
|
|
1418
|
+
code: "MOLDEA_MANIFEST_VALUE_INVALID",
|
|
1419
|
+
details: { reason },
|
|
1420
|
+
entity,
|
|
1421
|
+
path: context.path,
|
|
1422
|
+
pointer,
|
|
1423
|
+
range
|
|
1424
|
+
});
|
|
1425
|
+
};
|
|
1426
|
+
var readMapping = (node, pointer, allowedProperties, context, entity = null) => {
|
|
1427
|
+
if (node.kind !== "mapping") {
|
|
1428
|
+
addInvalidValue(context, pointer, node.range, "mapping-required", entity);
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1432
|
+
for (const entry of node.entries) {
|
|
1433
|
+
if (entry.key.kind !== "scalar" || typeof entry.key.value !== "string") {
|
|
1434
|
+
addInvalidValue(context, pointer, entry.key.range, "string-key-required", entity);
|
|
1435
|
+
continue;
|
|
1436
|
+
}
|
|
1437
|
+
const key = entry.key.value;
|
|
1438
|
+
if (allowedProperties !== null && !allowedProperties.has(key)) {
|
|
1439
|
+
context.diagnostics.add({
|
|
1440
|
+
code: "MOLDEA_MANIFEST_PROPERTY_UNKNOWN",
|
|
1441
|
+
entity,
|
|
1442
|
+
path: context.path,
|
|
1443
|
+
pointer: childPointer$1(pointer, key),
|
|
1444
|
+
range: entry.key.range
|
|
1445
|
+
});
|
|
1446
|
+
continue;
|
|
1447
|
+
}
|
|
1448
|
+
entries.set(key, {
|
|
1449
|
+
key: entry.key,
|
|
1450
|
+
value: entry.value
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
return entries;
|
|
1454
|
+
};
|
|
1455
|
+
var readString = (entry, pointer, context, options = {}) => {
|
|
1456
|
+
if (entry === void 0) {
|
|
1457
|
+
if (options.required === true) addInvalidValue(context, pointer, null, "required", options.entity ?? null);
|
|
1458
|
+
return null;
|
|
1459
|
+
}
|
|
1460
|
+
if (entry.value.kind !== "scalar" || typeof entry.value.value !== "string") {
|
|
1461
|
+
addInvalidValue(context, pointer, entry.value.range, "string-required", options.entity ?? null);
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1464
|
+
const value = entry.value.value;
|
|
1465
|
+
if (!hasNonWhitespace(value) || options.singleLine === true && !isNonEmptySingleLine(value)) {
|
|
1466
|
+
addInvalidValue(context, pointer, entry.value.range, "string-invalid", options.entity ?? null);
|
|
1467
|
+
return null;
|
|
1468
|
+
}
|
|
1469
|
+
return value;
|
|
1470
|
+
};
|
|
1471
|
+
var validateStableId = (value, range, pointer, context, entity = null) => {
|
|
1472
|
+
if (!isStableId$1(value)) {
|
|
1473
|
+
context.diagnostics.add({
|
|
1474
|
+
code: "MOLDEA_ID_INVALID",
|
|
1475
|
+
entity,
|
|
1476
|
+
path: context.path,
|
|
1477
|
+
pointer,
|
|
1478
|
+
range
|
|
1479
|
+
});
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
if (isReservedId(value)) {
|
|
1483
|
+
context.diagnostics.add({
|
|
1484
|
+
code: "MOLDEA_ID_RESERVED",
|
|
1485
|
+
entity,
|
|
1486
|
+
path: context.path,
|
|
1487
|
+
pointer,
|
|
1488
|
+
range
|
|
1489
|
+
});
|
|
1490
|
+
return false;
|
|
1491
|
+
}
|
|
1492
|
+
return true;
|
|
1493
|
+
};
|
|
1494
|
+
var readReference = (node, pointer, context, entity = null) => {
|
|
1495
|
+
const entries = readMapping(node, pointer, /* @__PURE__ */ new Set(["path", "symbol"]), context, entity);
|
|
1496
|
+
if (entries === null) return null;
|
|
1497
|
+
const pathEntry = entries.get("path");
|
|
1498
|
+
if (pathEntry?.value.kind !== "scalar" || typeof pathEntry.value.value !== "string") {
|
|
1499
|
+
context.diagnostics.add({
|
|
1500
|
+
code: "MOLDEA_PATH_INVALID",
|
|
1501
|
+
details: { reason: pathEntry === void 0 ? "missing" : "type" },
|
|
1502
|
+
entity,
|
|
1503
|
+
path: context.path,
|
|
1504
|
+
pointer: childPointer$1(pointer, "path"),
|
|
1505
|
+
range: pathEntry?.value.range ?? null
|
|
1506
|
+
});
|
|
1507
|
+
return null;
|
|
1508
|
+
}
|
|
1509
|
+
const path = parseManifestPath(pathEntry.value.value);
|
|
1510
|
+
if (path === null) {
|
|
1511
|
+
context.diagnostics.add({
|
|
1512
|
+
code: "MOLDEA_PATH_INVALID",
|
|
1513
|
+
entity,
|
|
1514
|
+
path: context.path,
|
|
1515
|
+
pointer: childPointer$1(pointer, "path"),
|
|
1516
|
+
range: pathEntry.value.range
|
|
1517
|
+
});
|
|
1518
|
+
return null;
|
|
1519
|
+
}
|
|
1520
|
+
const symbolEntry = entries.get("symbol");
|
|
1521
|
+
if (symbolEntry === void 0) return { path };
|
|
1522
|
+
if (symbolEntry.value.kind !== "scalar" || typeof symbolEntry.value.value !== "string" || !isRepositorySymbol(symbolEntry.value.value)) {
|
|
1523
|
+
context.diagnostics.add({
|
|
1524
|
+
code: "MOLDEA_SYMBOL_INVALID",
|
|
1525
|
+
entity,
|
|
1526
|
+
path: context.path,
|
|
1527
|
+
pointer: childPointer$1(pointer, "symbol"),
|
|
1528
|
+
range: symbolEntry.value.range
|
|
1529
|
+
});
|
|
1530
|
+
return null;
|
|
1531
|
+
}
|
|
1532
|
+
if (isCanonicalMoldeaPath(path)) {
|
|
1533
|
+
context.diagnostics.add({
|
|
1534
|
+
code: "MOLDEA_SYMBOL_FORBIDDEN",
|
|
1535
|
+
entity,
|
|
1536
|
+
path: context.path,
|
|
1537
|
+
pointer: childPointer$1(pointer, "symbol"),
|
|
1538
|
+
range: symbolEntry.value.range
|
|
1539
|
+
});
|
|
1540
|
+
return null;
|
|
1541
|
+
}
|
|
1542
|
+
return {
|
|
1543
|
+
path,
|
|
1544
|
+
symbol: symbolEntry.value.value
|
|
1545
|
+
};
|
|
1546
|
+
};
|
|
1547
|
+
var readReferenceList = (node, pointer, context, entity = null) => {
|
|
1548
|
+
if (node.kind !== "sequence" || node.items.length === 0) {
|
|
1549
|
+
addInvalidValue(context, pointer, node.range, "non-empty-sequence-required", entity);
|
|
1550
|
+
return [];
|
|
1551
|
+
}
|
|
1552
|
+
const references = [];
|
|
1553
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1554
|
+
node.items.forEach((item, index) => {
|
|
1555
|
+
const reference = readReference(item, childPointer$1(pointer, String(index)), context, entity);
|
|
1556
|
+
if (reference === null) return;
|
|
1557
|
+
const key = `${reference.path}\0${reference.symbol ?? ""}`;
|
|
1558
|
+
if (seen.has(key)) {
|
|
1559
|
+
context.diagnostics.add({
|
|
1560
|
+
code: "MOLDEA_PATH_DUPLICATE",
|
|
1561
|
+
entity,
|
|
1562
|
+
path: context.path,
|
|
1563
|
+
pointer: childPointer$1(pointer, String(index)),
|
|
1564
|
+
range: item.range
|
|
1565
|
+
});
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
seen.add(key);
|
|
1569
|
+
references.push(reference);
|
|
1570
|
+
});
|
|
1571
|
+
return sortRepositoryReferences(references);
|
|
1572
|
+
};
|
|
1573
|
+
var readAffectedBy = (node, pointer, context, entity = null) => {
|
|
1574
|
+
if (node.kind !== "sequence" || node.items.length === 0) {
|
|
1575
|
+
addInvalidValue(context, pointer, node.range, "non-empty-sequence-required", entity);
|
|
1576
|
+
return [];
|
|
1577
|
+
}
|
|
1578
|
+
const values = [];
|
|
1579
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1580
|
+
node.items.forEach((item, index) => {
|
|
1581
|
+
const itemPointer = childPointer$1(pointer, String(index));
|
|
1582
|
+
if (item.kind !== "scalar" || typeof item.value !== "string") {
|
|
1583
|
+
addInvalidValue(context, itemPointer, item.range, "string-required", entity);
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
const isGlob = item.value.includes("*") || hasUnsupportedGlobMetacharacter(item.value);
|
|
1587
|
+
if (!(isGlob ? isSimpleGlob(item.value) : parseManifestPath(item.value) !== null)) {
|
|
1588
|
+
context.diagnostics.add({
|
|
1589
|
+
code: isGlob ? "MOLDEA_GLOB_INVALID" : "MOLDEA_PATH_INVALID",
|
|
1590
|
+
entity,
|
|
1591
|
+
path: context.path,
|
|
1592
|
+
pointer: itemPointer,
|
|
1593
|
+
range: item.range
|
|
1594
|
+
});
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
if (seen.has(item.value)) {
|
|
1598
|
+
context.diagnostics.add({
|
|
1599
|
+
code: isGlob ? "MOLDEA_PATTERN_DUPLICATE" : "MOLDEA_PATH_DUPLICATE",
|
|
1600
|
+
entity,
|
|
1601
|
+
path: context.path,
|
|
1602
|
+
pointer: itemPointer,
|
|
1603
|
+
range: item.range
|
|
1604
|
+
});
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
seen.add(item.value);
|
|
1608
|
+
values.push(item.value);
|
|
1609
|
+
});
|
|
1610
|
+
return values.sort(compareExactStrings);
|
|
1611
|
+
};
|
|
1612
|
+
var readRelationship = (node, pointer, context) => {
|
|
1613
|
+
const entries = readMapping(node, pointer, /* @__PURE__ */ new Set(["affectedBy", "bindings"]), context);
|
|
1614
|
+
if (entries === null) return null;
|
|
1615
|
+
const bindingsEntry = entries.get("bindings");
|
|
1616
|
+
const affectedByEntry = entries.get("affectedBy");
|
|
1617
|
+
if (bindingsEntry === void 0 && affectedByEntry === void 0) context.diagnostics.add({
|
|
1618
|
+
code: "MOLDEA_CONTEXT_RELATIONSHIP_EMPTY",
|
|
1619
|
+
path: context.path,
|
|
1620
|
+
pointer,
|
|
1621
|
+
range: node.range
|
|
1622
|
+
});
|
|
1623
|
+
return {
|
|
1624
|
+
...bindingsEntry === void 0 ? {} : { bindings: readReferenceList(bindingsEntry.value, childPointer$1(pointer, "bindings"), context) },
|
|
1625
|
+
...affectedByEntry === void 0 ? {} : { affectedBy: readAffectedBy(affectedByEntry.value, childPointer$1(pointer, "affectedBy"), context) }
|
|
1626
|
+
};
|
|
1627
|
+
};
|
|
1628
|
+
var readRelationshipRecord = (node, pointer, kind, context) => {
|
|
1629
|
+
const entries = readMapping(node, pointer, null, context);
|
|
1630
|
+
if (entries === null) return null;
|
|
1631
|
+
const normalized = [];
|
|
1632
|
+
for (const [key, entry] of [...entries].sort(([left], [right]) => compareExactStrings(left, right))) {
|
|
1633
|
+
const path = parseManifestPath(key);
|
|
1634
|
+
if (!(path !== null && (kind === "context" ? isContextPath(path, true) : isDecisionPath(path)))) {
|
|
1635
|
+
context.diagnostics.add({
|
|
1636
|
+
code: kind === "context" ? "MOLDEA_CONTEXT_PATH_INVALID" : "MOLDEA_DECISION_FILENAME_INVALID",
|
|
1637
|
+
path: context.path,
|
|
1638
|
+
pointer: childPointer$1(pointer, key),
|
|
1639
|
+
range: entry.key.range
|
|
1640
|
+
});
|
|
1641
|
+
continue;
|
|
1642
|
+
}
|
|
1643
|
+
const relationship = readRelationship(entry.value, childPointer$1(pointer, key), context);
|
|
1644
|
+
if (relationship !== null) normalized.push([key, relationship]);
|
|
1645
|
+
}
|
|
1646
|
+
return createNullPrototypeRecord(normalized);
|
|
1647
|
+
};
|
|
1648
|
+
var readPathList = (node, pointer, kind, context, entity) => {
|
|
1649
|
+
if (node.kind !== "sequence") {
|
|
1650
|
+
addInvalidValue(context, pointer, node.range, "sequence-required", entity);
|
|
1651
|
+
return [];
|
|
1652
|
+
}
|
|
1653
|
+
const paths = [];
|
|
1654
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1655
|
+
node.items.forEach((item, index) => {
|
|
1656
|
+
const itemPointer = childPointer$1(pointer, String(index));
|
|
1657
|
+
if (item.kind !== "scalar" || typeof item.value !== "string") {
|
|
1658
|
+
addInvalidValue(context, itemPointer, item.range, "string-required", entity);
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
const path = parseManifestPath(item.value);
|
|
1662
|
+
if (!(path !== null && (kind === "context" ? isContextPath(path, false) : kind === "decision" ? isDecisionPath(path) : isMirrorPath(path))) || path === null) {
|
|
1663
|
+
const code = kind === "context" ? "MOLDEA_CONTEXT_PATH_INVALID" : kind === "decision" ? "MOLDEA_DECISION_FILENAME_INVALID" : path !== null && !isMirrorPath(path) ? "MOLDEA_MIRROR_PATH_INSIDE_MOLDEA" : "MOLDEA_MIRROR_PATH_INVALID";
|
|
1664
|
+
context.diagnostics.add({
|
|
1665
|
+
code,
|
|
1666
|
+
entity,
|
|
1667
|
+
path: context.path,
|
|
1668
|
+
pointer: itemPointer,
|
|
1669
|
+
range: item.range
|
|
1670
|
+
});
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
if (seen.has(path)) {
|
|
1674
|
+
context.diagnostics.add({
|
|
1675
|
+
code: kind === "mirror" ? "MOLDEA_MIRROR_PATH_DUPLICATE" : "MOLDEA_PATH_DUPLICATE",
|
|
1676
|
+
entity,
|
|
1677
|
+
path: context.path,
|
|
1678
|
+
pointer: itemPointer,
|
|
1679
|
+
range: item.range
|
|
1680
|
+
});
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
if (kind === "mirror" && context.mirrorPaths.has(path)) {
|
|
1684
|
+
context.diagnostics.add({
|
|
1685
|
+
code: "MOLDEA_MIRROR_PATH_DUPLICATE",
|
|
1686
|
+
entity,
|
|
1687
|
+
path: context.path,
|
|
1688
|
+
pointer: itemPointer,
|
|
1689
|
+
range: item.range
|
|
1690
|
+
});
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
seen.add(path);
|
|
1694
|
+
if (kind === "mirror") context.mirrorPaths.add(path);
|
|
1695
|
+
paths.push(path);
|
|
1696
|
+
});
|
|
1697
|
+
return paths.sort(compareExactStrings);
|
|
1698
|
+
};
|
|
1699
|
+
var readRuntime = (node, pointer, context, agentId) => {
|
|
1700
|
+
const entity = { agentId };
|
|
1701
|
+
const entries = readMapping(node, pointer, /* @__PURE__ */ new Set(["guidance", "id"]), context, entity);
|
|
1702
|
+
if (entries === null) return null;
|
|
1703
|
+
const idEntry = entries.get("id");
|
|
1704
|
+
if (idEntry?.value.kind !== "scalar" || typeof idEntry.value.value !== "string") {
|
|
1705
|
+
context.diagnostics.add({
|
|
1706
|
+
code: "MOLDEA_RUNTIME_ID_INVALID",
|
|
1707
|
+
entity,
|
|
1708
|
+
path: context.path,
|
|
1709
|
+
pointer: childPointer$1(pointer, "id"),
|
|
1710
|
+
range: idEntry?.value.range ?? null
|
|
1711
|
+
});
|
|
1712
|
+
return null;
|
|
1713
|
+
}
|
|
1714
|
+
const id = idEntry.value.value;
|
|
1715
|
+
if (!isStableId$1(id) || isReservedId(id) || !RECOGNIZED_RUNTIME_ADAPTER_IDS.includes(id)) context.diagnostics.add({
|
|
1716
|
+
code: "MOLDEA_RUNTIME_ID_INVALID",
|
|
1717
|
+
entity,
|
|
1718
|
+
path: context.path,
|
|
1719
|
+
pointer: childPointer$1(pointer, "id"),
|
|
1720
|
+
range: idEntry.value.range
|
|
1721
|
+
});
|
|
1722
|
+
else context.runtimeLocations.push({
|
|
1723
|
+
adapterId: id,
|
|
1724
|
+
agentId,
|
|
1725
|
+
path: context.path,
|
|
1726
|
+
pointer: childPointer$1(pointer, "id"),
|
|
1727
|
+
range: idEntry.value.range
|
|
1728
|
+
});
|
|
1729
|
+
const guidanceEntry = entries.get("guidance");
|
|
1730
|
+
let guidance = null;
|
|
1731
|
+
if (guidanceEntry !== void 0) {
|
|
1732
|
+
if (guidanceEntry.value.kind === "scalar" && typeof guidanceEntry.value.value === "string") {
|
|
1733
|
+
const candidate = parseManifestPath(guidanceEntry.value.value);
|
|
1734
|
+
if (candidate !== null && isRuntimeGuidancePath(candidate)) guidance = candidate;
|
|
1735
|
+
else context.diagnostics.add({
|
|
1736
|
+
code: "MOLDEA_PATH_INVALID",
|
|
1737
|
+
details: { reason: "runtime-guidance" },
|
|
1738
|
+
entity,
|
|
1739
|
+
path: context.path,
|
|
1740
|
+
pointer: childPointer$1(pointer, "guidance"),
|
|
1741
|
+
range: guidanceEntry.value.range
|
|
1742
|
+
});
|
|
1743
|
+
} else context.diagnostics.add({
|
|
1744
|
+
code: "MOLDEA_PATH_INVALID",
|
|
1745
|
+
details: { reason: "runtime-guidance" },
|
|
1746
|
+
entity,
|
|
1747
|
+
path: context.path,
|
|
1748
|
+
pointer: childPointer$1(pointer, "guidance"),
|
|
1749
|
+
range: guidanceEntry.value.range
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
return {
|
|
1753
|
+
id,
|
|
1754
|
+
...guidance === null ? {} : { guidance }
|
|
1755
|
+
};
|
|
1756
|
+
};
|
|
1757
|
+
var readVariables = (node, pointer, context, agentId) => {
|
|
1758
|
+
const entries = readMapping(node, pointer, null, context, { agentId });
|
|
1759
|
+
if (entries === null) return null;
|
|
1760
|
+
const normalized = [];
|
|
1761
|
+
for (const [variableId, entry] of [...entries].sort(([left], [right]) => compareExactStrings(left, right))) {
|
|
1762
|
+
const entity = {
|
|
1763
|
+
agentId,
|
|
1764
|
+
variableId
|
|
1765
|
+
};
|
|
1766
|
+
if (!isVariableId(variableId)) context.diagnostics.add({
|
|
1767
|
+
code: "MOLDEA_VARIABLE_ID_INVALID",
|
|
1768
|
+
entity,
|
|
1769
|
+
path: context.path,
|
|
1770
|
+
pointer: childPointer$1(pointer, variableId),
|
|
1771
|
+
range: entry.key.range
|
|
1772
|
+
});
|
|
1773
|
+
const variablePointer = childPointer$1(pointer, variableId);
|
|
1774
|
+
const variableEntries = readMapping(entry.value, variablePointer, /* @__PURE__ */ new Set(["description"]), context, entity);
|
|
1775
|
+
if (variableEntries === null) continue;
|
|
1776
|
+
const description = readString(variableEntries.get("description"), childPointer$1(variablePointer, "description"), context, {
|
|
1777
|
+
entity,
|
|
1778
|
+
required: true
|
|
1779
|
+
});
|
|
1780
|
+
if (description !== null) normalized.push([variableId, { description }]);
|
|
1781
|
+
}
|
|
1782
|
+
return createNullPrototypeRecord(normalized);
|
|
1783
|
+
};
|
|
1784
|
+
var readBindings = (node, pointer, context, agentId, declaredVariables) => {
|
|
1785
|
+
const entity = { agentId };
|
|
1786
|
+
const entries = readMapping(node, pointer, /* @__PURE__ */ new Set([
|
|
1787
|
+
"inputSchema",
|
|
1788
|
+
"instructionLoader",
|
|
1789
|
+
"outputSchema",
|
|
1790
|
+
"runtimeAgent",
|
|
1791
|
+
"variableProviders"
|
|
1792
|
+
]), context, entity);
|
|
1793
|
+
if (entries === null) return null;
|
|
1794
|
+
const referenceProperty = (key) => {
|
|
1795
|
+
const entry = entries.get(key);
|
|
1796
|
+
return entry === void 0 ? null : readReference(entry.value, childPointer$1(pointer, key), context, entity);
|
|
1797
|
+
};
|
|
1798
|
+
const inputSchema = referenceProperty("inputSchema");
|
|
1799
|
+
const instructionLoader = referenceProperty("instructionLoader");
|
|
1800
|
+
const outputSchema = referenceProperty("outputSchema");
|
|
1801
|
+
const runtimeAgent = referenceProperty("runtimeAgent");
|
|
1802
|
+
const providersEntry = entries.get("variableProviders");
|
|
1803
|
+
let variableProviders = null;
|
|
1804
|
+
if (providersEntry !== void 0) {
|
|
1805
|
+
const providers = readMapping(providersEntry.value, childPointer$1(pointer, "variableProviders"), null, context, entity);
|
|
1806
|
+
if (providers !== null) {
|
|
1807
|
+
const normalized = [];
|
|
1808
|
+
for (const [variableId, providerEntry] of [...providers].sort(([left], [right]) => compareExactStrings(left, right))) {
|
|
1809
|
+
const variableEntity = {
|
|
1810
|
+
agentId,
|
|
1811
|
+
variableId
|
|
1812
|
+
};
|
|
1813
|
+
const providerPointer = childPointer$1(childPointer$1(pointer, "variableProviders"), variableId);
|
|
1814
|
+
if (!isVariableId(variableId)) context.diagnostics.add({
|
|
1815
|
+
code: "MOLDEA_VARIABLE_ID_INVALID",
|
|
1816
|
+
entity: variableEntity,
|
|
1817
|
+
path: context.path,
|
|
1818
|
+
pointer: providerPointer,
|
|
1819
|
+
range: providerEntry.key.range
|
|
1820
|
+
});
|
|
1821
|
+
else if (!declaredVariables.has(variableId)) context.diagnostics.add({
|
|
1822
|
+
code: "MOLDEA_VARIABLE_PROVIDER_UNDECLARED",
|
|
1823
|
+
entity: variableEntity,
|
|
1824
|
+
path: context.path,
|
|
1825
|
+
pointer: providerPointer,
|
|
1826
|
+
range: providerEntry.key.range
|
|
1827
|
+
});
|
|
1828
|
+
const reference = readReference(providerEntry.value, providerPointer, context, variableEntity);
|
|
1829
|
+
if (reference !== null) normalized.push([variableId, reference]);
|
|
1830
|
+
}
|
|
1831
|
+
variableProviders = createNullPrototypeRecord(normalized);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
return {
|
|
1835
|
+
...runtimeAgent === null ? {} : { runtimeAgent },
|
|
1836
|
+
...inputSchema === null ? {} : { inputSchema },
|
|
1837
|
+
...outputSchema === null ? {} : { outputSchema },
|
|
1838
|
+
...instructionLoader === null ? {} : { instructionLoader },
|
|
1839
|
+
...variableProviders === null ? {} : { variableProviders }
|
|
1840
|
+
};
|
|
1841
|
+
};
|
|
1842
|
+
var readCapability$1 = (node, pointer, context, agentId, capabilityId, kind) => {
|
|
1843
|
+
const entity = {
|
|
1844
|
+
agentId,
|
|
1845
|
+
capabilityId,
|
|
1846
|
+
capabilityKind: kind
|
|
1847
|
+
};
|
|
1848
|
+
const entries = readMapping(node, pointer, kind === "tool" ? /* @__PURE__ */ new Set([
|
|
1849
|
+
"affectedBy",
|
|
1850
|
+
"description",
|
|
1851
|
+
"implementation",
|
|
1852
|
+
"inputSchema",
|
|
1853
|
+
"name",
|
|
1854
|
+
"outputSchema",
|
|
1855
|
+
"registration"
|
|
1856
|
+
]) : /* @__PURE__ */ new Set([
|
|
1857
|
+
"affectedBy",
|
|
1858
|
+
"description",
|
|
1859
|
+
"implementation",
|
|
1860
|
+
"name",
|
|
1861
|
+
"registration"
|
|
1862
|
+
]), context, entity);
|
|
1863
|
+
if (entries === null) return null;
|
|
1864
|
+
const name = readString(entries.get("name"), childPointer$1(pointer, "name"), context, {
|
|
1865
|
+
entity,
|
|
1866
|
+
required: true,
|
|
1867
|
+
singleLine: true
|
|
1868
|
+
});
|
|
1869
|
+
const descriptionEntry = entries.get("description");
|
|
1870
|
+
let description = null;
|
|
1871
|
+
if (descriptionEntry === void 0) context.diagnostics.add({
|
|
1872
|
+
code: "MOLDEA_CAPABILITY_DESCRIPTION_MISSING",
|
|
1873
|
+
entity,
|
|
1874
|
+
path: context.path,
|
|
1875
|
+
pointer: childPointer$1(pointer, "description")
|
|
1876
|
+
});
|
|
1877
|
+
else if (descriptionEntry.value.kind !== "scalar" || typeof descriptionEntry.value.value !== "string" || !isCapabilityDescription(descriptionEntry.value.value)) context.diagnostics.add({
|
|
1878
|
+
code: "MOLDEA_CAPABILITY_DESCRIPTION_INVALID",
|
|
1879
|
+
entity,
|
|
1880
|
+
path: context.path,
|
|
1881
|
+
pointer: childPointer$1(pointer, "description"),
|
|
1882
|
+
range: descriptionEntry.value.range
|
|
1883
|
+
});
|
|
1884
|
+
else description = descriptionEntry.value.value;
|
|
1885
|
+
const implementationEntry = entries.get("implementation");
|
|
1886
|
+
let implementation = null;
|
|
1887
|
+
if (implementationEntry === void 0) context.diagnostics.add({
|
|
1888
|
+
code: kind === "tool" ? "MOLDEA_TOOL_IMPLEMENTATION_MISSING" : "MOLDEA_SKILL_IMPLEMENTATION_MISSING",
|
|
1889
|
+
entity,
|
|
1890
|
+
path: context.path,
|
|
1891
|
+
pointer: childPointer$1(pointer, "implementation")
|
|
1892
|
+
});
|
|
1893
|
+
else implementation = readReference(implementationEntry.value, childPointer$1(pointer, "implementation"), context, entity);
|
|
1894
|
+
const optionalReference = (key) => {
|
|
1895
|
+
const entry = entries.get(key);
|
|
1896
|
+
return entry === void 0 ? null : readReference(entry.value, childPointer$1(pointer, key), context, entity);
|
|
1897
|
+
};
|
|
1898
|
+
const registration = optionalReference("registration");
|
|
1899
|
+
const inputSchema = kind === "tool" ? optionalReference("inputSchema") : null;
|
|
1900
|
+
const outputSchema = kind === "tool" ? optionalReference("outputSchema") : null;
|
|
1901
|
+
const affectedByEntry = entries.get("affectedBy");
|
|
1902
|
+
const affectedBy = affectedByEntry === void 0 ? null : readAffectedBy(affectedByEntry.value, childPointer$1(pointer, "affectedBy"), context, entity);
|
|
1903
|
+
if (name === null || description === null || implementation === null) return null;
|
|
1904
|
+
return {
|
|
1905
|
+
name,
|
|
1906
|
+
description,
|
|
1907
|
+
implementation,
|
|
1908
|
+
...registration === null ? {} : { registration },
|
|
1909
|
+
...inputSchema === null ? {} : { inputSchema },
|
|
1910
|
+
...outputSchema === null ? {} : { outputSchema },
|
|
1911
|
+
...affectedBy === null ? {} : { affectedBy }
|
|
1912
|
+
};
|
|
1913
|
+
};
|
|
1914
|
+
var readCapabilities = (node, pointer, context, agentId, kind) => {
|
|
1915
|
+
const entries = readMapping(node, pointer, null, context, { agentId });
|
|
1916
|
+
if (entries === null) return null;
|
|
1917
|
+
const normalized = [];
|
|
1918
|
+
for (const [capabilityId, entry] of [...entries].sort(([left], [right]) => compareExactStrings(left, right))) {
|
|
1919
|
+
const entity = {
|
|
1920
|
+
agentId,
|
|
1921
|
+
capabilityId,
|
|
1922
|
+
capabilityKind: kind
|
|
1923
|
+
};
|
|
1924
|
+
const capabilityPointer = childPointer$1(pointer, capabilityId);
|
|
1925
|
+
validateStableId(capabilityId, entry.key.range, capabilityPointer, context, entity);
|
|
1926
|
+
const capability = readCapability$1(entry.value, capabilityPointer, context, agentId, capabilityId, kind);
|
|
1927
|
+
if (capability !== null) normalized.push([capabilityId, capability]);
|
|
1928
|
+
}
|
|
1929
|
+
return createNullPrototypeRecord(normalized);
|
|
1930
|
+
};
|
|
1931
|
+
var readUnresolved = (node, pointer, context, agentId) => {
|
|
1932
|
+
const entries = readMapping(node, pointer, null, context, agentId === void 0 ? null : { agentId });
|
|
1933
|
+
if (entries === null) return null;
|
|
1934
|
+
const normalized = [];
|
|
1935
|
+
for (const [requirementId, entry] of [...entries].sort(([left], [right]) => compareExactStrings(left, right))) {
|
|
1936
|
+
const entity = agentId === void 0 ? null : { agentId };
|
|
1937
|
+
const requirementPointer = childPointer$1(pointer, requirementId);
|
|
1938
|
+
validateStableId(requirementId, entry.key.range, requirementPointer, context, entity);
|
|
1939
|
+
const requirementEntries = readMapping(entry.value, requirementPointer, /* @__PURE__ */ new Set([
|
|
1940
|
+
"category",
|
|
1941
|
+
"description",
|
|
1942
|
+
"effect",
|
|
1943
|
+
"reference",
|
|
1944
|
+
"related",
|
|
1945
|
+
"resolution"
|
|
1946
|
+
]), context, entity);
|
|
1947
|
+
if (requirementEntries === null) continue;
|
|
1948
|
+
const category = readString(requirementEntries.get("category"), childPointer$1(requirementPointer, "category"), context, {
|
|
1949
|
+
...entity === null ? {} : { entity },
|
|
1950
|
+
required: true
|
|
1951
|
+
});
|
|
1952
|
+
if (category !== null && !isStableId$1(category)) context.diagnostics.add({
|
|
1953
|
+
code: "MOLDEA_ID_INVALID",
|
|
1954
|
+
entity,
|
|
1955
|
+
path: context.path,
|
|
1956
|
+
pointer: childPointer$1(requirementPointer, "category"),
|
|
1957
|
+
range: requirementEntries.get("category")?.value.range ?? null
|
|
1958
|
+
});
|
|
1959
|
+
const effectEntry = requirementEntries.get("effect");
|
|
1960
|
+
let effect = null;
|
|
1961
|
+
if (effectEntry?.value.kind === "scalar" && (effectEntry.value.value === "blocking" || effectEntry.value.value === "warning" || effectEntry.value.value === "informational")) effect = effectEntry.value.value;
|
|
1962
|
+
else addInvalidValue(context, childPointer$1(requirementPointer, "effect"), effectEntry?.value.range ?? null, effectEntry === void 0 ? "required" : "enum", entity);
|
|
1963
|
+
const description = readString(requirementEntries.get("description"), childPointer$1(requirementPointer, "description"), context, {
|
|
1964
|
+
...entity === null ? {} : { entity },
|
|
1965
|
+
required: true
|
|
1966
|
+
});
|
|
1967
|
+
const resolution = readString(requirementEntries.get("resolution"), childPointer$1(requirementPointer, "resolution"), context, {
|
|
1968
|
+
...entity === null ? {} : { entity },
|
|
1969
|
+
required: true
|
|
1970
|
+
});
|
|
1971
|
+
const relatedEntry = requirementEntries.get("related");
|
|
1972
|
+
const related = relatedEntry === void 0 ? null : readReferenceList(relatedEntry.value, childPointer$1(requirementPointer, "related"), context, entity);
|
|
1973
|
+
const reference = readString(requirementEntries.get("reference"), childPointer$1(requirementPointer, "reference"), context, {
|
|
1974
|
+
...entity === null ? {} : { entity },
|
|
1975
|
+
singleLine: true
|
|
1976
|
+
});
|
|
1977
|
+
if (category !== null && effect !== null && description !== null && resolution !== null) normalized.push([requirementId, {
|
|
1978
|
+
category,
|
|
1979
|
+
effect,
|
|
1980
|
+
description,
|
|
1981
|
+
resolution,
|
|
1982
|
+
...related === null ? {} : { related },
|
|
1983
|
+
...reference === null ? {} : { reference }
|
|
1984
|
+
}]);
|
|
1985
|
+
}
|
|
1986
|
+
return createNullPrototypeRecord(normalized);
|
|
1987
|
+
};
|
|
1988
|
+
var findMappingProperty = (node, property) => {
|
|
1989
|
+
if (node.kind !== "mapping") return null;
|
|
1990
|
+
for (const entry of node.entries) if (entry.key.kind === "scalar" && entry.key.value === property) return entry.value;
|
|
1991
|
+
return null;
|
|
1992
|
+
};
|
|
1993
|
+
/**
|
|
1994
|
+
* Detects a supported repository-format version without treating the remaining manifest as valid.
|
|
1995
|
+
* @param node The trustworthy strict-YAML manifest root.
|
|
1996
|
+
* @returns The supported parsed version when its declaration is independently unambiguous.
|
|
1997
|
+
*/
|
|
1998
|
+
var detectSupportedManifestVersion = (node) => {
|
|
1999
|
+
if (node.kind !== "mapping") return null;
|
|
2000
|
+
const versionNode = findMappingProperty(node, "version");
|
|
2001
|
+
return versionNode?.kind === "scalar" && versionNode.value === 1n ? 1 : null;
|
|
2002
|
+
};
|
|
2003
|
+
var validateAgentOwnedIdUniqueness = (node, pointer, context, agentId) => {
|
|
2004
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2005
|
+
for (const property of [
|
|
2006
|
+
"tools",
|
|
2007
|
+
"skills",
|
|
2008
|
+
"unresolved"
|
|
2009
|
+
]) {
|
|
2010
|
+
const record = findMappingProperty(node, property);
|
|
2011
|
+
if (record?.kind !== "mapping") continue;
|
|
2012
|
+
for (const entry of record.entries) {
|
|
2013
|
+
if (entry.key.kind !== "scalar" || typeof entry.key.value !== "string") continue;
|
|
2014
|
+
if (seen.has(entry.key.value)) context.diagnostics.add({
|
|
2015
|
+
code: "MOLDEA_ID_DUPLICATE",
|
|
2016
|
+
entity: { agentId },
|
|
2017
|
+
path: context.path,
|
|
2018
|
+
pointer: childPointer$1(childPointer$1(pointer, property), entry.key.value),
|
|
2019
|
+
range: entry.key.range
|
|
2020
|
+
});
|
|
2021
|
+
seen.add(entry.key.value);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
var readAgent = (node, pointer, context, agentId) => {
|
|
2026
|
+
const entity = { agentId };
|
|
2027
|
+
const entries = readMapping(node, pointer, /* @__PURE__ */ new Set([
|
|
2028
|
+
"affectedBy",
|
|
2029
|
+
"bindings",
|
|
2030
|
+
"context",
|
|
2031
|
+
"decisions",
|
|
2032
|
+
"runtime",
|
|
2033
|
+
"mirrors",
|
|
2034
|
+
"skills",
|
|
2035
|
+
"tools",
|
|
2036
|
+
"unresolved",
|
|
2037
|
+
"variables"
|
|
2038
|
+
]), context, entity);
|
|
2039
|
+
if (entries === null) return null;
|
|
2040
|
+
validateAgentOwnedIdUniqueness(node, pointer, context, agentId);
|
|
2041
|
+
const runtimeEntry = entries.get("runtime");
|
|
2042
|
+
let runtime = null;
|
|
2043
|
+
if (runtimeEntry === void 0) addInvalidValue(context, childPointer$1(pointer, "runtime"), null, "required", entity);
|
|
2044
|
+
else runtime = readRuntime(runtimeEntry.value, childPointer$1(pointer, "runtime"), context, agentId);
|
|
2045
|
+
const variablesEntry = entries.get("variables");
|
|
2046
|
+
const variables = variablesEntry === void 0 ? null : readVariables(variablesEntry.value, childPointer$1(pointer, "variables"), context, agentId);
|
|
2047
|
+
const declaredVariables = new Set(Object.keys(variables ?? {}));
|
|
2048
|
+
const bindingsEntry = entries.get("bindings");
|
|
2049
|
+
const bindings = bindingsEntry === void 0 ? null : readBindings(bindingsEntry.value, childPointer$1(pointer, "bindings"), context, agentId, declaredVariables);
|
|
2050
|
+
const contextEntry = entries.get("context");
|
|
2051
|
+
const contextPaths = contextEntry === void 0 ? null : readPathList(contextEntry.value, childPointer$1(pointer, "context"), "context", context, entity);
|
|
2052
|
+
const decisionsEntry = entries.get("decisions");
|
|
2053
|
+
const decisions = decisionsEntry === void 0 ? null : readPathList(decisionsEntry.value, childPointer$1(pointer, "decisions"), "decision", context, entity);
|
|
2054
|
+
const toolsEntry = entries.get("tools");
|
|
2055
|
+
const tools = toolsEntry === void 0 ? null : readCapabilities(toolsEntry.value, childPointer$1(pointer, "tools"), context, agentId, "tool");
|
|
2056
|
+
const skillsEntry = entries.get("skills");
|
|
2057
|
+
const skills = skillsEntry === void 0 ? null : readCapabilities(skillsEntry.value, childPointer$1(pointer, "skills"), context, agentId, "skill");
|
|
2058
|
+
const affectedByEntry = entries.get("affectedBy");
|
|
2059
|
+
const affectedBy = affectedByEntry === void 0 ? null : readAffectedBy(affectedByEntry.value, childPointer$1(pointer, "affectedBy"), context, entity);
|
|
2060
|
+
const mirrorsEntry = entries.get("mirrors");
|
|
2061
|
+
const mirrors = mirrorsEntry === void 0 ? null : readPathList(mirrorsEntry.value, childPointer$1(pointer, "mirrors"), "mirror", context, entity);
|
|
2062
|
+
const unresolvedEntry = entries.get("unresolved");
|
|
2063
|
+
const unresolved = unresolvedEntry === void 0 ? null : readUnresolved(unresolvedEntry.value, childPointer$1(pointer, "unresolved"), context, agentId);
|
|
2064
|
+
if (runtime === null) return null;
|
|
2065
|
+
return {
|
|
2066
|
+
runtime,
|
|
2067
|
+
...contextPaths === null ? {} : { context: contextPaths },
|
|
2068
|
+
...decisions === null ? {} : { decisions },
|
|
2069
|
+
...variables === null ? {} : { variables },
|
|
2070
|
+
...bindings === null ? {} : { bindings },
|
|
2071
|
+
...tools === null ? {} : { tools },
|
|
2072
|
+
...skills === null ? {} : { skills },
|
|
2073
|
+
...affectedBy === null ? {} : { affectedBy },
|
|
2074
|
+
...mirrors === null ? {} : { mirrors },
|
|
2075
|
+
...unresolved === null ? {} : { unresolved }
|
|
2076
|
+
};
|
|
2077
|
+
};
|
|
2078
|
+
var readAgents = (node, pointer, context) => {
|
|
2079
|
+
const entries = readMapping(node, pointer, null, context);
|
|
2080
|
+
if (entries === null) return null;
|
|
2081
|
+
const normalized = [];
|
|
2082
|
+
for (const [agentId, entry] of [...entries].sort(([left], [right]) => compareExactStrings(left, right))) {
|
|
2083
|
+
const agentPointer = childPointer$1(pointer, agentId);
|
|
2084
|
+
validateStableId(agentId, entry.key.range, agentPointer, context, { agentId });
|
|
2085
|
+
const agent = readAgent(entry.value, agentPointer, context, agentId);
|
|
2086
|
+
if (agent !== null) normalized.push([agentId, agent]);
|
|
2087
|
+
}
|
|
2088
|
+
return createNullPrototypeRecord(normalized);
|
|
2089
|
+
};
|
|
2090
|
+
/**
|
|
2091
|
+
* Validates and normalizes a strict YAML document as a version 1 moldea manifest.
|
|
2092
|
+
* @param node The parser-neutral YAML root node.
|
|
2093
|
+
* @param path The canonical or caller-supplied logical manifest path.
|
|
2094
|
+
* @param diagnostics The operation diagnostic collector.
|
|
2095
|
+
* @param runtimeLocations Mutable private source locations populated for repository inspection.
|
|
2096
|
+
* @returns The normalized manifest when its supported version can be interpreted.
|
|
2097
|
+
*/
|
|
2098
|
+
var validateManifest = (node, path, diagnostics, runtimeLocations) => {
|
|
2099
|
+
const context = {
|
|
2100
|
+
diagnostics,
|
|
2101
|
+
mirrorPaths: /* @__PURE__ */ new Set(),
|
|
2102
|
+
path,
|
|
2103
|
+
runtimeLocations
|
|
2104
|
+
};
|
|
2105
|
+
if (node.kind !== "mapping") {
|
|
2106
|
+
diagnostics.add({
|
|
2107
|
+
code: "MOLDEA_MANIFEST_ROOT_INVALID",
|
|
2108
|
+
path,
|
|
2109
|
+
range: node.range
|
|
2110
|
+
});
|
|
2111
|
+
return null;
|
|
2112
|
+
}
|
|
2113
|
+
const versionNode = findMappingProperty(node, "version");
|
|
2114
|
+
if (versionNode === null) {
|
|
2115
|
+
diagnostics.add({
|
|
2116
|
+
code: "MOLDEA_MANIFEST_VERSION_MISSING",
|
|
2117
|
+
path,
|
|
2118
|
+
pointer: "/version"
|
|
2119
|
+
});
|
|
2120
|
+
return null;
|
|
2121
|
+
}
|
|
2122
|
+
if (!(versionNode.kind === "scalar" && typeof versionNode.value === "bigint") || versionNode.kind !== "scalar" || versionNode.value <= 0n) {
|
|
2123
|
+
diagnostics.add({
|
|
2124
|
+
code: "MOLDEA_MANIFEST_VERSION_INVALID",
|
|
2125
|
+
path,
|
|
2126
|
+
pointer: "/version",
|
|
2127
|
+
range: versionNode.range
|
|
2128
|
+
});
|
|
2129
|
+
return null;
|
|
2130
|
+
}
|
|
2131
|
+
if (versionNode.value !== 1n) {
|
|
2132
|
+
diagnostics.add({
|
|
2133
|
+
code: "MOLDEA_MANIFEST_VERSION_UNSUPPORTED",
|
|
2134
|
+
path,
|
|
2135
|
+
pointer: "/version",
|
|
2136
|
+
range: versionNode.range
|
|
2137
|
+
});
|
|
2138
|
+
return null;
|
|
2139
|
+
}
|
|
2140
|
+
const entries = readMapping(node, "", /* @__PURE__ */ new Set([
|
|
2141
|
+
"agents",
|
|
2142
|
+
"context",
|
|
2143
|
+
"decisions",
|
|
2144
|
+
"unresolved",
|
|
2145
|
+
"version"
|
|
2146
|
+
]), context);
|
|
2147
|
+
if (entries === null) return null;
|
|
2148
|
+
const contextEntry = entries.get("context");
|
|
2149
|
+
const contextRelationships = contextEntry === void 0 ? null : readRelationshipRecord(contextEntry.value, "/context", "context", context);
|
|
2150
|
+
const decisionsEntry = entries.get("decisions");
|
|
2151
|
+
const decisionRelationships = decisionsEntry === void 0 ? null : readRelationshipRecord(decisionsEntry.value, "/decisions", "decision", context);
|
|
2152
|
+
const unresolvedEntry = entries.get("unresolved");
|
|
2153
|
+
const unresolved = unresolvedEntry === void 0 ? null : readUnresolved(unresolvedEntry.value, "/unresolved", context);
|
|
2154
|
+
const agentsEntry = entries.get("agents");
|
|
2155
|
+
const agents = agentsEntry === void 0 ? null : readAgents(agentsEntry.value, "/agents", context);
|
|
2156
|
+
return {
|
|
2157
|
+
version: 1,
|
|
2158
|
+
...contextRelationships === null ? {} : { context: contextRelationships },
|
|
2159
|
+
...decisionRelationships === null ? {} : { decisions: decisionRelationships },
|
|
2160
|
+
...unresolved === null ? {} : { unresolved },
|
|
2161
|
+
...agents === null ? {} : { agents }
|
|
2162
|
+
};
|
|
2163
|
+
};
|
|
2164
|
+
//#endregion
|
|
2165
|
+
//#region src/manifest/index.ts
|
|
2166
|
+
/**
|
|
2167
|
+
* Inspects one complete manifest while retaining an independently trustworthy supported version.
|
|
2168
|
+
* @param input The canonical logical path and exact manifest text or bytes.
|
|
2169
|
+
* @param options The immutable Core limits and adapter snapshots.
|
|
2170
|
+
* @param operation The owning public or repository-level operation for typed failures.
|
|
2171
|
+
* @returns Frozen inspection state with all-or-nothing manifest assets and values.
|
|
2172
|
+
* @throws
|
|
2173
|
+
* - INVALID_REPOSITORY_PATH: The repository path is invalid.
|
|
2174
|
+
* - INVALID_ARGUMENT: The Core operation received an invalid argument.
|
|
2175
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
|
|
2176
|
+
*/
|
|
2177
|
+
var inspectManifestDocument = async (input, options, operation = "parse-manifest") => {
|
|
2178
|
+
options = createCoreOperationOptionsSnapshot(options);
|
|
2179
|
+
const normalized = normalizeTextDocument(input, options.limits, operation, "maxManifestBytes");
|
|
2180
|
+
const path = parseRepositoryPath(input.path);
|
|
2181
|
+
const diagnostics = createCoreDiagnosticCollector(options.limits, operation);
|
|
2182
|
+
if (!isCanonicalManifestPath(path)) diagnostics.add({
|
|
2183
|
+
code: "MOLDEA_MANIFEST_PATH_INVALID",
|
|
2184
|
+
path
|
|
2185
|
+
});
|
|
2186
|
+
for (const diagnostic of normalized.diagnostics) diagnostics.merge(diagnostic);
|
|
2187
|
+
if (!normalized.valid || normalized.text === null) return freezeRecursively({
|
|
2188
|
+
asset: null,
|
|
2189
|
+
diagnostics: diagnostics.finalize(),
|
|
2190
|
+
formatVersion: null,
|
|
2191
|
+
manifest: null,
|
|
2192
|
+
runtimeLocations: [],
|
|
2193
|
+
valid: false
|
|
2194
|
+
});
|
|
2195
|
+
const locator = createSourceLocator(normalized.text.value);
|
|
2196
|
+
const parsed = parseStrictYaml(normalized.text.value, path, locator, diagnostics);
|
|
2197
|
+
const formatVersion = parsed.value === null ? null : detectSupportedManifestVersion(parsed.value);
|
|
2198
|
+
const runtimeLocations = [];
|
|
2199
|
+
const manifest = parsed.valid && parsed.value !== null ? validateManifest(parsed.value, path, diagnostics, runtimeLocations) : null;
|
|
2200
|
+
if (manifest === null || diagnostics.size > 0) return freezeRecursively({
|
|
2201
|
+
asset: null,
|
|
2202
|
+
diagnostics: diagnostics.finalize(),
|
|
2203
|
+
formatVersion,
|
|
2204
|
+
manifest: null,
|
|
2205
|
+
runtimeLocations,
|
|
2206
|
+
valid: false
|
|
2207
|
+
});
|
|
2208
|
+
const digest = await calculateNormalizedTextDigest(normalized.text);
|
|
2209
|
+
return freezeRecursively({
|
|
2210
|
+
asset: {
|
|
2211
|
+
content: normalized.text.value,
|
|
2212
|
+
digest,
|
|
2213
|
+
path,
|
|
2214
|
+
scalarLength: normalized.text.scalarLength,
|
|
2215
|
+
utf8ByteLength: normalized.text.utf8ByteLength
|
|
2216
|
+
},
|
|
2217
|
+
diagnostics: diagnostics.finalize(),
|
|
2218
|
+
formatVersion,
|
|
2219
|
+
manifest,
|
|
2220
|
+
runtimeLocations,
|
|
2221
|
+
valid: true
|
|
2222
|
+
});
|
|
2223
|
+
};
|
|
2224
|
+
/**
|
|
2225
|
+
* Parses and validates one complete version 1 manifest document.
|
|
2226
|
+
* @param input The canonical logical path and exact manifest text or bytes.
|
|
2227
|
+
* @param options The immutable Core limits and adapter snapshots.
|
|
2228
|
+
* @returns A frozen all-or-nothing manifest parse result.
|
|
2229
|
+
* @throws
|
|
2230
|
+
* - INVALID_REPOSITORY_PATH: The repository path is invalid.
|
|
2231
|
+
* - INVALID_ARGUMENT: The Core operation received an invalid argument.
|
|
2232
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
|
|
2233
|
+
*/
|
|
2234
|
+
var parseManifestDocument = async (input, options) => {
|
|
2235
|
+
const inspection = await inspectManifestDocument(input, options);
|
|
2236
|
+
return freezeRecursively({
|
|
2237
|
+
asset: inspection.asset,
|
|
2238
|
+
diagnostics: inspection.diagnostics,
|
|
2239
|
+
manifest: inspection.manifest,
|
|
2240
|
+
valid: inspection.valid
|
|
2241
|
+
});
|
|
2242
|
+
};
|
|
2243
|
+
//#endregion
|
|
2244
|
+
//#region src/adapter-availability/index.ts
|
|
2245
|
+
/**
|
|
2246
|
+
* Validates adapter availability and format support before any runtime adapter runs.
|
|
2247
|
+
* @param locations The recognized runtime declarations retained from manifest parsing.
|
|
2248
|
+
* @param formatVersion The active repository-format version.
|
|
2249
|
+
* @param options The immutable Core adapter registry and limits.
|
|
2250
|
+
* @returns Frozen deterministic preflight diagnostics.
|
|
2251
|
+
* @throws
|
|
2252
|
+
* - RESOURCE_LIMIT_EXCEEDED: The raw Core diagnostic limit was exceeded.
|
|
2253
|
+
*/
|
|
2254
|
+
var validateRuntimeAdapterAvailability = (locations, formatVersion, options) => {
|
|
2255
|
+
const diagnostics = createCoreDiagnosticCollector(options.limits, "inspect-project");
|
|
2256
|
+
const adapters = new Map(options.adapters.map((adapter) => [adapter.id, adapter]));
|
|
2257
|
+
for (const location of locations) {
|
|
2258
|
+
if (location.adapterId === "custom") continue;
|
|
2259
|
+
const adapter = adapters.get(location.adapterId);
|
|
2260
|
+
if (adapter === void 0) {
|
|
2261
|
+
diagnostics.add({
|
|
2262
|
+
code: "MOLDEA_RUNTIME_ADAPTER_UNAVAILABLE",
|
|
2263
|
+
entity: {
|
|
2264
|
+
adapterId: location.adapterId,
|
|
2265
|
+
agentId: location.agentId
|
|
2266
|
+
},
|
|
2267
|
+
path: location.path,
|
|
2268
|
+
pointer: location.pointer,
|
|
2269
|
+
range: location.range
|
|
2270
|
+
});
|
|
2271
|
+
continue;
|
|
2272
|
+
}
|
|
2273
|
+
if (!adapter.supportedRepositoryFormatVersions.includes(formatVersion)) diagnostics.add({
|
|
2274
|
+
code: "MOLDEA_RUNTIME_ADAPTER_FORMAT_UNSUPPORTED",
|
|
2275
|
+
entity: {
|
|
2276
|
+
adapterId: location.adapterId,
|
|
2277
|
+
agentId: location.agentId
|
|
2278
|
+
},
|
|
2279
|
+
path: location.path,
|
|
2280
|
+
pointer: location.pointer,
|
|
2281
|
+
range: location.range
|
|
2282
|
+
});
|
|
2283
|
+
}
|
|
2284
|
+
return diagnostics.finalize();
|
|
2285
|
+
};
|
|
2286
|
+
//#endregion
|
|
2287
|
+
//#region src/adapter-validation/index.ts
|
|
2288
|
+
var EVIDENCE_KINDS = /* @__PURE__ */ new Set([
|
|
2289
|
+
"runtime-package",
|
|
2290
|
+
"language",
|
|
2291
|
+
"agent-definition",
|
|
2292
|
+
"instruction-loader",
|
|
2293
|
+
"schema",
|
|
2294
|
+
"tool-registration",
|
|
2295
|
+
"skill-registration",
|
|
2296
|
+
"handoff-registration",
|
|
2297
|
+
"variable-provider",
|
|
2298
|
+
"runtime-pattern"
|
|
2299
|
+
]);
|
|
2300
|
+
var DIAGNOSTIC_ENTITY_KEYS = /* @__PURE__ */ new Set([
|
|
2301
|
+
"agentId",
|
|
2302
|
+
"capabilityKind",
|
|
2303
|
+
"capabilityId",
|
|
2304
|
+
"decisionId",
|
|
2305
|
+
"variableId",
|
|
2306
|
+
"adapterId"
|
|
2307
|
+
]);
|
|
2308
|
+
var POSITION_KEYS = /* @__PURE__ */ new Set([
|
|
2309
|
+
"line",
|
|
2310
|
+
"column",
|
|
2311
|
+
"offset"
|
|
2312
|
+
]);
|
|
2313
|
+
var RANGE_KEYS = /* @__PURE__ */ new Set(["start", "end"]);
|
|
2314
|
+
var REFERENCE_KEYS = /* @__PURE__ */ new Set(["path", "symbol"]);
|
|
2315
|
+
var EVIDENCE_KEYS = /* @__PURE__ */ new Set([
|
|
2316
|
+
"source",
|
|
2317
|
+
"kind",
|
|
2318
|
+
"agentId",
|
|
2319
|
+
"capabilityKind",
|
|
2320
|
+
"capabilityId",
|
|
2321
|
+
"runtimeName",
|
|
2322
|
+
"references",
|
|
2323
|
+
"details"
|
|
2324
|
+
]);
|
|
2325
|
+
var DIAGNOSTIC_KEYS = /* @__PURE__ */ new Set([
|
|
2326
|
+
"source",
|
|
2327
|
+
"code",
|
|
2328
|
+
"message",
|
|
2329
|
+
"path",
|
|
2330
|
+
"pointer",
|
|
2331
|
+
"range",
|
|
2332
|
+
"entity",
|
|
2333
|
+
"details"
|
|
2334
|
+
]);
|
|
2335
|
+
var ADAPTER_RESULT_KEYS = /* @__PURE__ */ new Set(["evidence", "diagnostics"]);
|
|
2336
|
+
var ADAPTER_CODE_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
2337
|
+
var isRecord$1 = (candidate) => {
|
|
2338
|
+
return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
|
|
2339
|
+
};
|
|
2340
|
+
var invalidAdapterResult = (adapterId) => {
|
|
2341
|
+
throw new CoreOperationException({
|
|
2342
|
+
adapterId,
|
|
2343
|
+
cause: /* @__PURE__ */ new TypeError("The runtime adapter result is invalid."),
|
|
2344
|
+
code: "ADAPTER_EXECUTION_FAILED",
|
|
2345
|
+
operation: "validate-adapter"
|
|
2346
|
+
});
|
|
2347
|
+
};
|
|
2348
|
+
var registerRawOutput = (outputCounts, key, amount, context) => {
|
|
2349
|
+
const limit = key === "diagnostics" ? "maxDiagnostics" : "maxEvidence";
|
|
2350
|
+
if (key === "diagnostics") {
|
|
2351
|
+
outputCounts.diagnostics = registerCoreDiagnosticCandidates(context.limits, amount, "validate-adapter", outputCounts.diagnostics, context.adapterId);
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
if (amount > context.limits[limit] - outputCounts[key]) throw new CoreOperationException({
|
|
2355
|
+
adapterId: context.adapterId,
|
|
2356
|
+
code: "RESOURCE_LIMIT_EXCEEDED",
|
|
2357
|
+
limit,
|
|
2358
|
+
operation: "validate-adapter"
|
|
2359
|
+
});
|
|
2360
|
+
outputCounts[key] += amount;
|
|
2361
|
+
};
|
|
2362
|
+
var hasOnlyKeys = (candidate, keys) => {
|
|
2363
|
+
return Reflect.ownKeys(candidate).every((key) => typeof key === "string" && keys.has(key));
|
|
2364
|
+
};
|
|
2365
|
+
var isSafeString = (candidate) => {
|
|
2366
|
+
return typeof candidate === "string" && isUnicodeScalarText(candidate) && !candidate.includes("\0");
|
|
2367
|
+
};
|
|
2368
|
+
var normalizeDetails = (candidate, adapterId) => {
|
|
2369
|
+
if (!isRecord$1(candidate)) return invalidAdapterResult(adapterId);
|
|
2370
|
+
const entries = [];
|
|
2371
|
+
for (const key of Reflect.ownKeys(candidate)) {
|
|
2372
|
+
if (typeof key !== "string" || !isUnicodeScalarText(key) || key.includes("\0")) return invalidAdapterResult(adapterId);
|
|
2373
|
+
const descriptor = Object.getOwnPropertyDescriptor(candidate, key);
|
|
2374
|
+
if (descriptor === void 0 || !descriptor.enumerable || !("value" in descriptor)) return invalidAdapterResult(adapterId);
|
|
2375
|
+
const detail = descriptor.value;
|
|
2376
|
+
if (detail !== null && typeof detail !== "boolean" && !(typeof detail === "string" && isUnicodeScalarText(detail) && !detail.includes("\0")) && !(typeof detail === "number" && Number.isFinite(detail))) return invalidAdapterResult(adapterId);
|
|
2377
|
+
entries.push([key, typeof detail === "number" && Object.is(detail, -0) ? 0 : detail]);
|
|
2378
|
+
}
|
|
2379
|
+
return normalizeDiagnosticDetails(createNullPrototypeRecord(entries));
|
|
2380
|
+
};
|
|
2381
|
+
var normalizePosition = (candidate, adapterId) => {
|
|
2382
|
+
if (!isRecord$1(candidate) || !hasOnlyKeys(candidate, POSITION_KEYS) || !Number.isSafeInteger(candidate["line"]) || !Number.isSafeInteger(candidate["column"]) || !Number.isSafeInteger(candidate["offset"]) || candidate["line"] < 1 || candidate["column"] < 1 || candidate["offset"] < 0) return invalidAdapterResult(adapterId);
|
|
2383
|
+
return freezeRecursively({
|
|
2384
|
+
column: candidate["column"],
|
|
2385
|
+
line: candidate["line"],
|
|
2386
|
+
offset: candidate["offset"]
|
|
2387
|
+
});
|
|
2388
|
+
};
|
|
2389
|
+
var normalizeRange = (candidate, adapterId) => {
|
|
2390
|
+
if (candidate === null) return null;
|
|
2391
|
+
if (!isRecord$1(candidate) || !hasOnlyKeys(candidate, RANGE_KEYS)) return invalidAdapterResult(adapterId);
|
|
2392
|
+
const start = normalizePosition(candidate["start"], adapterId);
|
|
2393
|
+
const end = normalizePosition(candidate["end"], adapterId);
|
|
2394
|
+
if (!(end.line > start.line || end.line === start.line && end.column >= start.column) || end.offset < start.offset) return invalidAdapterResult(adapterId);
|
|
2395
|
+
return freezeRecursively({
|
|
2396
|
+
end,
|
|
2397
|
+
start
|
|
2398
|
+
});
|
|
2399
|
+
};
|
|
2400
|
+
var isJsonPointer = (candidate) => {
|
|
2401
|
+
return isUnicodeScalarText(candidate) && !candidate.includes("\0") && (candidate === "" || candidate.startsWith("/") && !/~(?![01])/u.test(candidate));
|
|
2402
|
+
};
|
|
2403
|
+
var readCapability = (agent, capabilityKind, capabilityId) => {
|
|
2404
|
+
const capabilities = capabilityKind === "tool" ? agent.declaration.tools : agent.declaration.skills;
|
|
2405
|
+
return capabilities !== void 0 && Object.hasOwn(capabilities, capabilityId) ? capabilities[capabilityId] : void 0;
|
|
2406
|
+
};
|
|
2407
|
+
var normalizeEntity = (candidate, context) => {
|
|
2408
|
+
if (candidate === null) return null;
|
|
2409
|
+
if (!isRecord$1(candidate) || !hasOnlyKeys(candidate, DIAGNOSTIC_ENTITY_KEYS)) return invalidAdapterResult(context.adapterId);
|
|
2410
|
+
const entity = {};
|
|
2411
|
+
for (const key of DIAGNOSTIC_ENTITY_KEYS) {
|
|
2412
|
+
const candidateValue = candidate[key];
|
|
2413
|
+
if (candidateValue === void 0) continue;
|
|
2414
|
+
if (!isSafeString(candidateValue) || candidateValue.length === 0) return invalidAdapterResult(context.adapterId);
|
|
2415
|
+
entity[key] = candidateValue;
|
|
2416
|
+
}
|
|
2417
|
+
const agentId = entity["agentId"];
|
|
2418
|
+
const agent = agentId === void 0 ? void 0 : context.agentsById.get(agentId);
|
|
2419
|
+
if (agentId !== void 0 && agent === void 0) return invalidAdapterResult(context.adapterId);
|
|
2420
|
+
if (entity["adapterId"] !== void 0 && entity["adapterId"] !== context.adapterId) return invalidAdapterResult(context.adapterId);
|
|
2421
|
+
const capabilityKind = entity["capabilityKind"];
|
|
2422
|
+
const capabilityId = entity["capabilityId"];
|
|
2423
|
+
if (capabilityKind === void 0 !== (capabilityId === void 0) || capabilityKind !== void 0 && capabilityKind !== "tool" && capabilityKind !== "skill" || capabilityKind !== void 0 && capabilityId !== void 0 && (agent === void 0 || readCapability(agent, capabilityKind, capabilityId) === void 0)) return invalidAdapterResult(context.adapterId);
|
|
2424
|
+
const variableId = entity["variableId"];
|
|
2425
|
+
if (variableId !== void 0 && (agent === void 0 || agent.declaration.variables === void 0 || !Object.hasOwn(agent.declaration.variables, variableId))) return invalidAdapterResult(context.adapterId);
|
|
2426
|
+
const decisionId = entity["decisionId"];
|
|
2427
|
+
if (decisionId !== void 0 && !context.decisionIds.has(decisionId)) return invalidAdapterResult(context.adapterId);
|
|
2428
|
+
return normalizeDiagnosticEntity(entity);
|
|
2429
|
+
};
|
|
2430
|
+
var normalizeReference = (candidate, adapterId) => {
|
|
2431
|
+
if (!isRecord$1(candidate) || !hasOnlyKeys(candidate, REFERENCE_KEYS)) return invalidAdapterResult(adapterId);
|
|
2432
|
+
const pathCandidate = candidate["path"];
|
|
2433
|
+
const symbolCandidate = candidate["symbol"];
|
|
2434
|
+
if (typeof pathCandidate !== "string" || !isRepositoryPath(pathCandidate)) return invalidAdapterResult(adapterId);
|
|
2435
|
+
const path = parseRepositoryPath(pathCandidate);
|
|
2436
|
+
if (symbolCandidate !== void 0 && (typeof symbolCandidate !== "string" || !isUnicodeScalarText(symbolCandidate) || !isRepositorySymbol(symbolCandidate) || isCanonicalMoldeaPath(path))) return invalidAdapterResult(adapterId);
|
|
2437
|
+
return freezeRecursively({
|
|
2438
|
+
path,
|
|
2439
|
+
...symbolCandidate === void 0 ? {} : { symbol: symbolCandidate }
|
|
2440
|
+
});
|
|
2441
|
+
};
|
|
2442
|
+
var normalizeReferences = (candidate, adapterId) => {
|
|
2443
|
+
if (!Array.isArray(candidate) || candidate.length === 0) return invalidAdapterResult(adapterId);
|
|
2444
|
+
const references = candidate.map((reference) => normalizeReference(reference, adapterId));
|
|
2445
|
+
const referenceKeys = /* @__PURE__ */ new Set();
|
|
2446
|
+
for (const reference of references) {
|
|
2447
|
+
const key = JSON.stringify([reference.path, reference.symbol ?? null]);
|
|
2448
|
+
if (referenceKeys.has(key)) return invalidAdapterResult(adapterId);
|
|
2449
|
+
referenceKeys.add(key);
|
|
2450
|
+
}
|
|
2451
|
+
return freezeRecursively(sortRepositoryReferences(references));
|
|
2452
|
+
};
|
|
2453
|
+
var normalizeEvidence = (candidate, context) => {
|
|
2454
|
+
if (!isRecord$1(candidate) || !hasOnlyKeys(candidate, EVIDENCE_KEYS)) return invalidAdapterResult(context.adapterId);
|
|
2455
|
+
const source = candidate["source"];
|
|
2456
|
+
const kind = candidate["kind"];
|
|
2457
|
+
const agentId = candidate["agentId"];
|
|
2458
|
+
const capabilityKind = candidate["capabilityKind"];
|
|
2459
|
+
const capabilityId = candidate["capabilityId"];
|
|
2460
|
+
const runtimeName = candidate["runtimeName"];
|
|
2461
|
+
if (source !== context.adapterId || typeof kind !== "string" || !EVIDENCE_KINDS.has(kind) || agentId !== null && !isSafeString(agentId) || capabilityKind !== null && capabilityKind !== "tool" && capabilityKind !== "skill" || capabilityId !== null && !isSafeString(capabilityId) || runtimeName !== null && (!isSafeString(runtimeName) || !isNonEmptySingleLine(runtimeName) || hasSurroundingWhitespace(runtimeName))) return invalidAdapterResult(context.adapterId);
|
|
2462
|
+
const scopedAgent = agentId === null ? void 0 : context.agentsById.get(agentId);
|
|
2463
|
+
if (agentId !== null && scopedAgent === void 0 || capabilityKind === null !== (capabilityId === null) || capabilityKind !== null && capabilityId !== null && (scopedAgent === void 0 || readCapability(scopedAgent, capabilityKind, capabilityId) === void 0)) return invalidAdapterResult(context.adapterId);
|
|
2464
|
+
return freezeRecursively({
|
|
2465
|
+
agentId,
|
|
2466
|
+
capabilityId,
|
|
2467
|
+
capabilityKind,
|
|
2468
|
+
details: normalizeDetails(candidate["details"], context.adapterId),
|
|
2469
|
+
kind,
|
|
2470
|
+
references: normalizeReferences(candidate["references"], context.adapterId),
|
|
2471
|
+
runtimeName,
|
|
2472
|
+
source
|
|
2473
|
+
});
|
|
2474
|
+
};
|
|
2475
|
+
var validateEvidenceReferences = async (evidence, context) => {
|
|
2476
|
+
const entries = /* @__PURE__ */ new Map();
|
|
2477
|
+
const operationOptions = context.signal === void 0 ? void 0 : { signal: context.signal };
|
|
2478
|
+
for (const item of evidence) for (const reference of item.references) {
|
|
2479
|
+
let entry = entries.get(reference.path);
|
|
2480
|
+
if (entry === void 0 && !entries.has(reference.path)) {
|
|
2481
|
+
entry = await context.repository.getEntry(reference.path, operationOptions);
|
|
2482
|
+
entries.set(reference.path, entry);
|
|
2483
|
+
}
|
|
2484
|
+
if (entry?.type !== "file") return invalidAdapterResult(context.adapterId);
|
|
2485
|
+
}
|
|
2486
|
+
};
|
|
2487
|
+
var normalizeDiagnostic = (candidate, context) => {
|
|
2488
|
+
if (!isRecord$1(candidate) || !hasOnlyKeys(candidate, DIAGNOSTIC_KEYS)) return invalidAdapterResult(context.adapterId);
|
|
2489
|
+
const source = candidate["source"];
|
|
2490
|
+
const code = candidate["code"];
|
|
2491
|
+
const message = candidate["message"];
|
|
2492
|
+
const pathCandidate = candidate["path"];
|
|
2493
|
+
const pointer = candidate["pointer"];
|
|
2494
|
+
const namespace = `${context.adapterId.toUpperCase().replaceAll("-", "_")}_`;
|
|
2495
|
+
if (source !== context.adapterId || !isSafeString(code) || !ADAPTER_CODE_PATTERN.test(code) || !code.startsWith(namespace) || code.length === namespace.length || !isSafeString(message) || message.length === 0 || pathCandidate !== null && (typeof pathCandidate !== "string" || !isRepositoryPath(pathCandidate)) || pointer !== null && (typeof pointer !== "string" || !isJsonPointer(pointer))) return invalidAdapterResult(context.adapterId);
|
|
2496
|
+
const path = pathCandidate === null ? null : parseRepositoryPath(pathCandidate);
|
|
2497
|
+
const range = normalizeRange(candidate["range"], context.adapterId);
|
|
2498
|
+
if (path === null && (pointer !== null || range !== null)) return invalidAdapterResult(context.adapterId);
|
|
2499
|
+
return freezeRecursively({
|
|
2500
|
+
code,
|
|
2501
|
+
details: normalizeDetails(candidate["details"], context.adapterId),
|
|
2502
|
+
entity: normalizeEntity(candidate["entity"], context),
|
|
2503
|
+
message,
|
|
2504
|
+
path,
|
|
2505
|
+
pointer,
|
|
2506
|
+
range,
|
|
2507
|
+
source
|
|
2508
|
+
});
|
|
2509
|
+
};
|
|
2510
|
+
var compareNullableStrings$1 = (left, right) => {
|
|
2511
|
+
return left === null ? right === null ? 0 : -1 : right === null ? 1 : compareExactStrings(left, right);
|
|
2512
|
+
};
|
|
2513
|
+
var compareEvidence = (left, right) => {
|
|
2514
|
+
const leftReferences = JSON.stringify(left.references.map((reference) => [reference.path, reference.symbol ?? null]));
|
|
2515
|
+
const rightReferences = JSON.stringify(right.references.map((reference) => [reference.path, reference.symbol ?? null]));
|
|
2516
|
+
return compareExactStrings(left.source, right.source) || compareExactStrings(left.kind, right.kind) || compareNullableStrings$1(left.agentId, right.agentId) || compareNullableStrings$1(left.capabilityKind, right.capabilityKind) || compareNullableStrings$1(left.capabilityId, right.capabilityId) || compareNullableStrings$1(left.runtimeName, right.runtimeName) || compareExactStrings(leftReferences, rightReferences) || compareExactStrings(serializeDiagnosticDetails(left.details), serializeDiagnosticDetails(right.details));
|
|
2517
|
+
};
|
|
2518
|
+
var serializeEvidence = (evidence) => {
|
|
2519
|
+
return JSON.stringify([
|
|
2520
|
+
evidence.source,
|
|
2521
|
+
evidence.kind,
|
|
2522
|
+
evidence.agentId,
|
|
2523
|
+
evidence.capabilityKind,
|
|
2524
|
+
evidence.capabilityId,
|
|
2525
|
+
evidence.runtimeName,
|
|
2526
|
+
evidence.references.map((reference) => [reference.path, reference.symbol ?? null]),
|
|
2527
|
+
serializeDiagnosticDetails(evidence.details)
|
|
2528
|
+
]);
|
|
2529
|
+
};
|
|
2530
|
+
/**
|
|
2531
|
+
* Deduplicates and sorts normalized evidence from every completed adapter.
|
|
2532
|
+
* @param candidates The normalized evidence items to combine.
|
|
2533
|
+
* @returns A frozen deterministic evidence collection.
|
|
2534
|
+
*/
|
|
2535
|
+
var normalizeRuntimeAdapterEvidence = (candidates) => {
|
|
2536
|
+
const evidence = /* @__PURE__ */ new Map();
|
|
2537
|
+
for (const item of candidates) evidence.set(serializeEvidence(item), item);
|
|
2538
|
+
return freezeRecursively([...evidence.values()].sort(compareEvidence));
|
|
2539
|
+
};
|
|
2540
|
+
/**
|
|
2541
|
+
* Validates and normalizes one untrusted runtime adapter result.
|
|
2542
|
+
* @param candidate The result returned by the adapter implementation.
|
|
2543
|
+
* @param context The invoking adapter scope, project, reader, limits, and signal.
|
|
2544
|
+
* @returns A promise resolving to frozen deterministic evidence and diagnostics.
|
|
2545
|
+
* @throws
|
|
2546
|
+
* - INVALID_REPOSITORY_PATH: An adapter repository reference path is invalid.
|
|
2547
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
2548
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
2549
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during validation.
|
|
2550
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
2551
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
2552
|
+
* - ABORTED: Adapter validation or a repository operation was aborted.
|
|
2553
|
+
* - ADAPTER_EXECUTION_FAILED: The adapter result violates its public contract.
|
|
2554
|
+
*/
|
|
2555
|
+
var validateRuntimeAdapterResult = async (candidate, context, outputCounts) => {
|
|
2556
|
+
let diagnostics;
|
|
2557
|
+
let evidence;
|
|
2558
|
+
let validationState;
|
|
2559
|
+
try {
|
|
2560
|
+
if (!isRecord$1(candidate) || !hasOnlyKeys(candidate, ADAPTER_RESULT_KEYS)) return invalidAdapterResult(context.adapterId);
|
|
2561
|
+
const evidenceValue = candidate["evidence"];
|
|
2562
|
+
const diagnosticsValue = candidate["diagnostics"];
|
|
2563
|
+
if (!Array.isArray(evidenceValue) || !Array.isArray(diagnosticsValue)) return invalidAdapterResult(context.adapterId);
|
|
2564
|
+
const evidenceCandidates = evidenceValue;
|
|
2565
|
+
const diagnosticCandidates = diagnosticsValue;
|
|
2566
|
+
registerRawOutput(outputCounts, "evidence", evidenceCandidates.length, context);
|
|
2567
|
+
registerRawOutput(outputCounts, "diagnostics", diagnosticCandidates.length, context);
|
|
2568
|
+
validationState = {
|
|
2569
|
+
...context,
|
|
2570
|
+
agentsById: new Map(context.agents.map((agent) => [agent.id, agent])),
|
|
2571
|
+
decisionIds: new Set(context.project.decisions.map(({ decision }) => decision.id))
|
|
2572
|
+
};
|
|
2573
|
+
diagnostics = diagnosticCandidates.map((diagnostic) => normalizeDiagnostic(diagnostic, validationState));
|
|
2574
|
+
evidence = normalizeRuntimeAdapterEvidence(evidenceCandidates.map((evidenceCandidate) => normalizeEvidence(evidenceCandidate, validationState)));
|
|
2575
|
+
} catch (error) {
|
|
2576
|
+
if (error instanceof CoreOperationException && error.code === "RESOURCE_LIMIT_EXCEEDED") throw error;
|
|
2577
|
+
return invalidAdapterResult(context.adapterId);
|
|
2578
|
+
}
|
|
2579
|
+
try {
|
|
2580
|
+
await validateEvidenceReferences(evidence, validationState);
|
|
2581
|
+
return freezeRecursively({
|
|
2582
|
+
diagnostics: normalizeDiagnostics(diagnostics),
|
|
2583
|
+
evidence
|
|
2584
|
+
});
|
|
2585
|
+
} catch (error) {
|
|
2586
|
+
if (error instanceof RepositoryPathException || error instanceof RepositorySourceException || error instanceof CoreOperationException) throw error;
|
|
2587
|
+
return invalidAdapterResult(context.adapterId);
|
|
2588
|
+
}
|
|
2589
|
+
};
|
|
2590
|
+
//#endregion
|
|
2591
|
+
//#region src/adapter-execution/index.ts
|
|
2592
|
+
var isInspectionBoundaryFailure = (error) => {
|
|
2593
|
+
return error instanceof RepositoryPathException || error instanceof RepositorySourceException || error instanceof CoreOperationException && error.operation === "inspect-project" && (error.code === "ABORTED" || error.code === "RESOURCE_LIMIT_EXCEEDED");
|
|
2594
|
+
};
|
|
2595
|
+
var invokeAdapter = async (adapter, project, session, options, outputCounts, signal) => {
|
|
2596
|
+
const agents = freezeRecursively(project.agents.filter(({ declaration }) => declaration.runtime.id === adapter.id));
|
|
2597
|
+
if (agents.length === 0) return freezeRecursively({
|
|
2598
|
+
diagnostics: [],
|
|
2599
|
+
evidence: []
|
|
2600
|
+
});
|
|
2601
|
+
const context = Object.freeze({
|
|
2602
|
+
agents,
|
|
2603
|
+
project,
|
|
2604
|
+
repository: session.reader,
|
|
2605
|
+
...signal === void 0 ? {} : { signal }
|
|
2606
|
+
});
|
|
2607
|
+
let candidate;
|
|
2608
|
+
try {
|
|
2609
|
+
session.throwIfAborted();
|
|
2610
|
+
candidate = await adapter.inspect(context);
|
|
2611
|
+
session.throwIfAborted();
|
|
2612
|
+
} catch (error) {
|
|
2613
|
+
session.throwIfAborted();
|
|
2614
|
+
if (isInspectionBoundaryFailure(error)) throw error;
|
|
2615
|
+
throw new CoreOperationException({
|
|
2616
|
+
adapterId: adapter.id,
|
|
2617
|
+
cause: error,
|
|
2618
|
+
code: "ADAPTER_EXECUTION_FAILED",
|
|
2619
|
+
operation: "inspect-project"
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
return validateRuntimeAdapterResult(candidate, {
|
|
2623
|
+
adapterId: adapter.id,
|
|
2624
|
+
agents,
|
|
2625
|
+
limits: options.limits,
|
|
2626
|
+
project,
|
|
2627
|
+
repository: session.reader,
|
|
2628
|
+
...signal === void 0 ? {} : { signal }
|
|
2629
|
+
}, outputCounts);
|
|
2630
|
+
};
|
|
2631
|
+
/**
|
|
2632
|
+
* Invokes every applicable configured adapter in canonical ID order.
|
|
2633
|
+
* @param project The complete frozen universal project index.
|
|
2634
|
+
* @param session The shared inspection reader, cache, and resource budget.
|
|
2635
|
+
* @param options The immutable Core adapter registry and resource limits.
|
|
2636
|
+
* @param signal Optional cancellation shared by the complete inspection.
|
|
2637
|
+
* @returns A promise resolving to normalized evidence and adapter diagnostics.
|
|
2638
|
+
* @throws
|
|
2639
|
+
* - INVALID_REPOSITORY_PATH: An adapter repository path is invalid.
|
|
2640
|
+
* - ENTRY_NOT_FOUND: A requested repository entry is absent.
|
|
2641
|
+
* - ENTRY_NOT_FILE: A requested repository entry is not a regular file.
|
|
2642
|
+
* - ENTRY_NOT_DIRECTORY: A requested repository entry is not a directory.
|
|
2643
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
2644
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
2645
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during adapter inspection.
|
|
2646
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
2647
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
2648
|
+
* - ABORTED: Adapter inspection or a repository operation was aborted.
|
|
2649
|
+
* - ADAPTER_EXECUTION_FAILED: An adapter failed or returned an invalid result.
|
|
2650
|
+
*/
|
|
2651
|
+
var inspectRuntimeAdapters = async (project, session, options, signal) => {
|
|
2652
|
+
const evidence = [];
|
|
2653
|
+
const diagnostics = [];
|
|
2654
|
+
const outputCounts = {
|
|
2655
|
+
diagnostics: 0,
|
|
2656
|
+
evidence: 0
|
|
2657
|
+
};
|
|
2658
|
+
for (const adapter of options.adapters) {
|
|
2659
|
+
const result = await invokeAdapter(adapter, project, session, options, outputCounts, signal);
|
|
2660
|
+
evidence.push(...result.evidence);
|
|
2661
|
+
diagnostics.push(...result.diagnostics);
|
|
2662
|
+
}
|
|
2663
|
+
return freezeRecursively({
|
|
2664
|
+
diagnostics: normalizeDiagnostics(diagnostics),
|
|
2665
|
+
evidence: normalizeRuntimeAdapterEvidence(evidence)
|
|
2666
|
+
});
|
|
2667
|
+
};
|
|
2668
|
+
//#endregion
|
|
2669
|
+
//#region src/repository-inspection-session/index.ts
|
|
2670
|
+
var MANIFEST_PATH$3 = parseRepositoryPath("/moldea/moldea.yaml");
|
|
2671
|
+
var invalidSourceData$3 = (operation, path) => {
|
|
2672
|
+
throw new RepositorySourceException({
|
|
2673
|
+
code: "INVALID_SOURCE_DATA",
|
|
2674
|
+
operation,
|
|
2675
|
+
path,
|
|
2676
|
+
retryable: false
|
|
2677
|
+
});
|
|
2678
|
+
};
|
|
2679
|
+
var copyReaderEntry$1 = (candidate, operation, fallbackPath, expectedPath) => {
|
|
2680
|
+
if (typeof candidate !== "object" || candidate === null) return invalidSourceData$3(operation, fallbackPath);
|
|
2681
|
+
const record = candidate;
|
|
2682
|
+
const pathCandidate = record["path"];
|
|
2683
|
+
const typeCandidate = record["type"];
|
|
2684
|
+
if (typeof pathCandidate !== "string" || !isRepositoryPath(pathCandidate)) return invalidSourceData$3(operation, fallbackPath);
|
|
2685
|
+
const path = parseRepositoryPath(pathCandidate);
|
|
2686
|
+
if (expectedPath !== void 0 && path !== expectedPath) return invalidSourceData$3(operation, path);
|
|
2687
|
+
if (typeCandidate !== "file" && typeCandidate !== "directory" && typeCandidate !== "symlink") return invalidSourceData$3(operation, path);
|
|
2688
|
+
return {
|
|
2689
|
+
path,
|
|
2690
|
+
type: typeCandidate
|
|
2691
|
+
};
|
|
2692
|
+
};
|
|
2693
|
+
var isStrictDescendant = (path, prefix) => {
|
|
2694
|
+
if (path === prefix) return false;
|
|
2695
|
+
const descendantPrefix = prefix === REPOSITORY_ROOT ? REPOSITORY_ROOT : `${prefix}/`;
|
|
2696
|
+
return path.startsWith(descendantPrefix);
|
|
2697
|
+
};
|
|
2698
|
+
var throwResourceLimitExceeded = (limit) => {
|
|
2699
|
+
throw new CoreOperationException({
|
|
2700
|
+
code: "RESOURCE_LIMIT_EXCEEDED",
|
|
2701
|
+
limit,
|
|
2702
|
+
operation: "inspect-project"
|
|
2703
|
+
});
|
|
2704
|
+
};
|
|
2705
|
+
var createSourceOptions = (sessionSignal, operationSignal) => {
|
|
2706
|
+
const signal = sessionSignal ?? operationSignal;
|
|
2707
|
+
return signal === void 0 ? void 0 : { signal };
|
|
2708
|
+
};
|
|
2709
|
+
/**
|
|
2710
|
+
* Creates the isolated reader state for one repository inspection.
|
|
2711
|
+
* @param repository The source-neutral reader bound to one coherent snapshot.
|
|
2712
|
+
* @param limits The immutable resource limits for the inspection.
|
|
2713
|
+
* @param signal Optional cancellation shared by every inspection operation.
|
|
2714
|
+
* @returns A frozen session whose reader owns inspection-local budgets and byte caching.
|
|
2715
|
+
* @throws
|
|
2716
|
+
* - ABORTED: Repository inspection was aborted before the session was created.
|
|
2717
|
+
*/
|
|
2718
|
+
var createRepositoryInspectionSession = (repository, limits, signal) => {
|
|
2719
|
+
const readCache = /* @__PURE__ */ new Map();
|
|
2720
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
2721
|
+
let totalBytesRead = 0;
|
|
2722
|
+
const createAbortedException = (abortedSignal) => {
|
|
2723
|
+
return new CoreOperationException({
|
|
2724
|
+
cause: abortedSignal.reason,
|
|
2725
|
+
code: "ABORTED",
|
|
2726
|
+
operation: "inspect-project"
|
|
2727
|
+
});
|
|
2728
|
+
};
|
|
2729
|
+
const registerPath = (path) => {
|
|
2730
|
+
if (path === REPOSITORY_ROOT || seenPaths.has(path)) return;
|
|
2731
|
+
if (seenPaths.size >= limits.maxEntries) return throwResourceLimitExceeded("maxEntries");
|
|
2732
|
+
seenPaths.add(path);
|
|
2733
|
+
};
|
|
2734
|
+
const throwIfSignalAborted = (operationSignal) => {
|
|
2735
|
+
const abortedSignal = signal?.aborted ? signal : operationSignal?.aborted ? operationSignal : null;
|
|
2736
|
+
if (abortedSignal === null) return;
|
|
2737
|
+
throw createAbortedException(abortedSignal);
|
|
2738
|
+
};
|
|
2739
|
+
throwIfSignalAborted();
|
|
2740
|
+
const getEntry = async (path, options) => {
|
|
2741
|
+
const parsedPath = parseRepositoryPath(path);
|
|
2742
|
+
throwIfSignalAborted(options?.signal);
|
|
2743
|
+
registerPath(parsedPath);
|
|
2744
|
+
const candidate = await repository.getEntry(parsedPath, createSourceOptions(signal, options?.signal));
|
|
2745
|
+
throwIfSignalAborted(options?.signal);
|
|
2746
|
+
return candidate === null ? null : copyReaderEntry$1(candidate, "get-entry", parsedPath, parsedPath);
|
|
2747
|
+
};
|
|
2748
|
+
const listEntries = (options) => {
|
|
2749
|
+
const prefix = options?.prefix === void 0 ? REPOSITORY_ROOT : parseRepositoryPath(options.prefix);
|
|
2750
|
+
return { async *[Symbol.asyncIterator]() {
|
|
2751
|
+
throwIfSignalAborted(options?.signal);
|
|
2752
|
+
registerPath(prefix);
|
|
2753
|
+
const yieldedPaths = /* @__PURE__ */ new Set();
|
|
2754
|
+
const sourceOptions = createSourceOptions(signal, options?.signal);
|
|
2755
|
+
const sourceListOptions = sourceOptions === void 0 ? { prefix } : {
|
|
2756
|
+
prefix,
|
|
2757
|
+
...sourceOptions
|
|
2758
|
+
};
|
|
2759
|
+
for await (const candidate of repository.listEntries(sourceListOptions)) {
|
|
2760
|
+
throwIfSignalAborted(options?.signal);
|
|
2761
|
+
const entry = copyReaderEntry$1(candidate, "list-entries", prefix);
|
|
2762
|
+
if (!isStrictDescendant(entry.path, prefix) || yieldedPaths.has(entry.path)) return invalidSourceData$3("list-entries", entry.path);
|
|
2763
|
+
yieldedPaths.add(entry.path);
|
|
2764
|
+
registerPath(entry.path);
|
|
2765
|
+
yield entry;
|
|
2766
|
+
}
|
|
2767
|
+
throwIfSignalAborted(options?.signal);
|
|
2768
|
+
} };
|
|
2769
|
+
};
|
|
2770
|
+
const loadFile = async (path) => {
|
|
2771
|
+
const content = await repository.readFile(path, createSourceOptions(signal, void 0));
|
|
2772
|
+
throwIfSignalAborted();
|
|
2773
|
+
if (!(content instanceof Uint8Array)) return invalidSourceData$3("read-file", path);
|
|
2774
|
+
const fileLimit = path === MANIFEST_PATH$3 ? "maxManifestBytes" : "maxFileBytes";
|
|
2775
|
+
if (content.byteLength > limits[fileLimit]) return throwResourceLimitExceeded(fileLimit);
|
|
2776
|
+
if (totalBytesRead + content.byteLength > limits.maxTotalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead");
|
|
2777
|
+
totalBytesRead += content.byteLength;
|
|
2778
|
+
return new Uint8Array(content);
|
|
2779
|
+
};
|
|
2780
|
+
const removeFailedRead = async (path, readPromise) => {
|
|
2781
|
+
try {
|
|
2782
|
+
return await readPromise;
|
|
2783
|
+
} catch (error) {
|
|
2784
|
+
readCache.delete(path);
|
|
2785
|
+
throw error;
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
const waitForRead = (readPromise, operationSignal) => {
|
|
2789
|
+
throwIfSignalAborted(operationSignal);
|
|
2790
|
+
if (operationSignal === void 0 || operationSignal === signal) return readPromise;
|
|
2791
|
+
let handleAbort;
|
|
2792
|
+
const cancellationPromise = new Promise((resolve, reject) => {
|
|
2793
|
+
handleAbort = () => {
|
|
2794
|
+
const abortedSignal = signal?.aborted ? signal : operationSignal;
|
|
2795
|
+
reject(createAbortedException(abortedSignal));
|
|
2796
|
+
};
|
|
2797
|
+
operationSignal.addEventListener("abort", handleAbort, { once: true });
|
|
2798
|
+
if (operationSignal.aborted) handleAbort();
|
|
2799
|
+
});
|
|
2800
|
+
return Promise.race([readPromise, cancellationPromise]).finally(() => {
|
|
2801
|
+
operationSignal.removeEventListener("abort", handleAbort);
|
|
2802
|
+
});
|
|
2803
|
+
};
|
|
2804
|
+
const readFile = async (path, options) => {
|
|
2805
|
+
const parsedPath = parseRepositoryPath(path);
|
|
2806
|
+
throwIfSignalAborted(options?.signal);
|
|
2807
|
+
registerPath(parsedPath);
|
|
2808
|
+
let readPromise = readCache.get(parsedPath);
|
|
2809
|
+
if (readPromise === void 0) {
|
|
2810
|
+
readPromise = removeFailedRead(parsedPath, loadFile(parsedPath));
|
|
2811
|
+
readCache.set(parsedPath, readPromise);
|
|
2812
|
+
}
|
|
2813
|
+
const content = await waitForRead(readPromise, options?.signal);
|
|
2814
|
+
throwIfSignalAborted(options?.signal);
|
|
2815
|
+
return new Uint8Array(content);
|
|
2816
|
+
};
|
|
2817
|
+
const reader = Object.freeze({
|
|
2818
|
+
getEntry,
|
|
2819
|
+
listEntries,
|
|
2820
|
+
readFile
|
|
2821
|
+
});
|
|
2822
|
+
return Object.freeze({
|
|
2823
|
+
reader,
|
|
2824
|
+
throwIfAborted: throwIfSignalAborted
|
|
2825
|
+
});
|
|
2826
|
+
};
|
|
2827
|
+
//#endregion
|
|
2828
|
+
//#region src/agent-asset-validation/index.ts
|
|
2829
|
+
var OPENING_HEADING_PATTERN = /^#{1,6} /u;
|
|
2830
|
+
/** Finds the first nonblank line at or after one normalized line index. */
|
|
2831
|
+
var findNonblankLine = (lines, startIndex) => {
|
|
2832
|
+
for (let index = startIndex; index < lines.length; index += 1) {
|
|
2833
|
+
const line = lines[index];
|
|
2834
|
+
if (line !== void 0 && hasNonWhitespace(line)) return index;
|
|
2835
|
+
}
|
|
2836
|
+
return null;
|
|
2837
|
+
};
|
|
2838
|
+
/**
|
|
2839
|
+
* Validates the effective value of one agent description asset.
|
|
2840
|
+
* @param asset The normalized complete description or handoff-description asset.
|
|
2841
|
+
* @param agentId The owning registered agent ID.
|
|
2842
|
+
* @param kind The description role determining its diagnostic code.
|
|
2843
|
+
* @param limits The Core diagnostic budget.
|
|
2844
|
+
* @returns The frozen effective description when every version 1 rule passes.
|
|
2845
|
+
* @throws
|
|
2846
|
+
* - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
|
|
2847
|
+
*/
|
|
2848
|
+
var validateAgentDescription = (asset, agentId, kind, limits) => {
|
|
2849
|
+
const diagnostics = createCoreDiagnosticCollector(limits, "inspect-project");
|
|
2850
|
+
const value = trimRepositoryFormatWhitespace(asset.content);
|
|
2851
|
+
const scalarLength = countUnicodeScalars$1(value);
|
|
2852
|
+
const code = kind === "description" ? "MOLDEA_AGENT_DESCRIPTION_INVALID" : "MOLDEA_AGENT_HANDOFF_DESCRIPTION_INVALID";
|
|
2853
|
+
if (scalarLength === 0) diagnostics.add({
|
|
2854
|
+
code,
|
|
2855
|
+
details: { reason: "empty" },
|
|
2856
|
+
entity: { agentId },
|
|
2857
|
+
path: asset.path
|
|
2858
|
+
});
|
|
2859
|
+
if (scalarLength > 1e3) diagnostics.add({
|
|
2860
|
+
code,
|
|
2861
|
+
details: { reason: "too-long" },
|
|
2862
|
+
entity: { agentId },
|
|
2863
|
+
path: asset.path
|
|
2864
|
+
});
|
|
2865
|
+
if (value.includes("{{") || value.includes("}}")) diagnostics.add({
|
|
2866
|
+
code,
|
|
2867
|
+
details: { reason: "runtime-variable-delimiter" },
|
|
2868
|
+
entity: { agentId },
|
|
2869
|
+
path: asset.path
|
|
2870
|
+
});
|
|
2871
|
+
const finalizedDiagnostics = diagnostics.finalize();
|
|
2872
|
+
return freezeRecursively({
|
|
2873
|
+
description: finalizedDiagnostics.length === 0 ? {
|
|
2874
|
+
asset,
|
|
2875
|
+
scalarLength,
|
|
2876
|
+
value
|
|
2877
|
+
} : null,
|
|
2878
|
+
diagnostics: finalizedDiagnostics,
|
|
2879
|
+
valid: finalizedDiagnostics.length === 0
|
|
2880
|
+
});
|
|
2881
|
+
};
|
|
2882
|
+
/**
|
|
2883
|
+
* Validates one canonical instruction's non-empty content and opening identity.
|
|
2884
|
+
* @param asset The normalized complete instruction asset.
|
|
2885
|
+
* @param agentId The owning registered agent ID.
|
|
2886
|
+
* @param limits The Core diagnostic budget.
|
|
2887
|
+
* @returns The frozen instruction result, retaining non-empty content for later validation.
|
|
2888
|
+
* @throws
|
|
2889
|
+
* - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
|
|
2890
|
+
*/
|
|
2891
|
+
var validateAgentInstruction = (asset, agentId, limits) => {
|
|
2892
|
+
const diagnostics = createCoreDiagnosticCollector(limits, "inspect-project");
|
|
2893
|
+
if (!hasNonWhitespace(asset.content)) {
|
|
2894
|
+
diagnostics.add({
|
|
2895
|
+
code: "MOLDEA_AGENT_INSTRUCTION_EMPTY",
|
|
2896
|
+
entity: { agentId },
|
|
2897
|
+
path: asset.path
|
|
2898
|
+
});
|
|
2899
|
+
return freezeRecursively({
|
|
2900
|
+
diagnostics: diagnostics.finalize(),
|
|
2901
|
+
instruction: null,
|
|
2902
|
+
valid: false
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
const identityToken = `\`${agentId}\``;
|
|
2906
|
+
const lines = asset.content.split("\n");
|
|
2907
|
+
const openingIndex = findNonblankLine(lines, 0);
|
|
2908
|
+
let identityIndex = openingIndex;
|
|
2909
|
+
if (openingIndex !== null && OPENING_HEADING_PATTERN.test(lines[openingIndex] ?? "")) identityIndex = findNonblankLine(lines, openingIndex + 1);
|
|
2910
|
+
if (identityIndex === null || !(lines[identityIndex] ?? "").includes(identityToken)) diagnostics.add({
|
|
2911
|
+
code: "MOLDEA_AGENT_IDENTITY_INVALID",
|
|
2912
|
+
details: { reason: asset.content.includes(identityToken) ? "misplaced" : "missing" },
|
|
2913
|
+
entity: { agentId },
|
|
2914
|
+
path: asset.path
|
|
2915
|
+
});
|
|
2916
|
+
const finalizedDiagnostics = diagnostics.finalize();
|
|
2917
|
+
return freezeRecursively({
|
|
2918
|
+
diagnostics: finalizedDiagnostics,
|
|
2919
|
+
instruction: asset,
|
|
2920
|
+
valid: finalizedDiagnostics.length === 0
|
|
2921
|
+
});
|
|
2922
|
+
};
|
|
2923
|
+
//#endregion
|
|
2924
|
+
//#region src/repository-text/index.ts
|
|
2925
|
+
var invalidSourceData$2 = (path) => {
|
|
2926
|
+
throw new RepositorySourceException({
|
|
2927
|
+
code: "INVALID_SOURCE_DATA",
|
|
2928
|
+
operation: "read-file",
|
|
2929
|
+
path,
|
|
2930
|
+
retryable: false
|
|
2931
|
+
});
|
|
2932
|
+
};
|
|
2933
|
+
/**
|
|
2934
|
+
* Reads and normalizes one canonical Markdown asset through a repository reader.
|
|
2935
|
+
* @param repository The coherent source-neutral repository reader.
|
|
2936
|
+
* @param path The discovered regular-file path to read exactly once.
|
|
2937
|
+
* @param limits The Core limits applied to the source bytes and diagnostics.
|
|
2938
|
+
* @param signal Optional cancellation forwarded to the repository read.
|
|
2939
|
+
* @returns A deeply immutable indexed asset or its deterministic text diagnostics.
|
|
2940
|
+
* @throws
|
|
2941
|
+
* - ENTRY_NOT_FOUND: The discovered file disappeared from the reader snapshot.
|
|
2942
|
+
* - ENTRY_NOT_FILE: The discovered entry is no longer a regular file.
|
|
2943
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
2944
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
2945
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during the read.
|
|
2946
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
2947
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
2948
|
+
* - ABORTED: Repository inspection or the repository read was aborted.
|
|
2949
|
+
*/
|
|
2950
|
+
var readRepositoryTextAsset = async (repository, path, limits, signal) => {
|
|
2951
|
+
const content = await repository.readFile(path, signal === void 0 ? void 0 : { signal });
|
|
2952
|
+
if (!(content instanceof Uint8Array)) return invalidSourceData$2(path);
|
|
2953
|
+
const normalized = normalizeTextDocument({
|
|
2954
|
+
content,
|
|
2955
|
+
path
|
|
2956
|
+
}, limits, "inspect-project");
|
|
2957
|
+
if (!normalized.valid || normalized.text === null) return freezeRecursively({
|
|
2958
|
+
asset: null,
|
|
2959
|
+
diagnostics: normalized.diagnostics,
|
|
2960
|
+
valid: false
|
|
2961
|
+
});
|
|
2962
|
+
return freezeRecursively({
|
|
2963
|
+
asset: {
|
|
2964
|
+
content: normalized.text.value,
|
|
2965
|
+
digest: await calculateNormalizedTextDigest(normalized.text),
|
|
2966
|
+
path,
|
|
2967
|
+
scalarLength: normalized.text.scalarLength,
|
|
2968
|
+
utf8ByteLength: normalized.text.utf8ByteLength
|
|
2969
|
+
},
|
|
2970
|
+
diagnostics: [],
|
|
2971
|
+
valid: true
|
|
2972
|
+
});
|
|
2973
|
+
};
|
|
2974
|
+
//#endregion
|
|
2975
|
+
//#region src/runtime-placeholder-validation/index.ts
|
|
2976
|
+
var readBraceRunEnd = (content, startOffset, brace) => {
|
|
2977
|
+
let endOffset = startOffset + 1;
|
|
2978
|
+
while (content[endOffset] === brace) endOffset += 1;
|
|
2979
|
+
return endOffset;
|
|
2980
|
+
};
|
|
2981
|
+
/** Finds the end of the next closing-delimiter run without rescanning prior content. */
|
|
2982
|
+
var findClosingRunEnd = (content, startOffset) => {
|
|
2983
|
+
let cursor = startOffset;
|
|
2984
|
+
while (cursor < content.length) {
|
|
2985
|
+
if (content[cursor] !== "}") {
|
|
2986
|
+
cursor += 1;
|
|
2987
|
+
continue;
|
|
2988
|
+
}
|
|
2989
|
+
const runEnd = readBraceRunEnd(content, cursor, "}");
|
|
2990
|
+
if (runEnd - cursor >= 2) return runEnd;
|
|
2991
|
+
cursor = runEnd;
|
|
2992
|
+
}
|
|
2993
|
+
return content.length;
|
|
2994
|
+
};
|
|
2995
|
+
/** Adds one malformed-placeholder diagnostic for an exact normalized source span. */
|
|
2996
|
+
var addMalformedDiagnostic = (collector, instruction, agentId, reason, range) => {
|
|
2997
|
+
collector.add({
|
|
2998
|
+
code: "MOLDEA_VARIABLE_PLACEHOLDER_MALFORMED",
|
|
2999
|
+
details: { reason },
|
|
3000
|
+
entity: { agentId },
|
|
3001
|
+
path: instruction.path,
|
|
3002
|
+
range
|
|
3003
|
+
});
|
|
3004
|
+
};
|
|
3005
|
+
/** Records a valid placeholder occurrence and its first normalized source range. */
|
|
3006
|
+
var recordOccurrence = (occurrences, variableId, range) => {
|
|
3007
|
+
const occurrence = occurrences.get(variableId);
|
|
3008
|
+
if (occurrence === void 0) {
|
|
3009
|
+
occurrences.set(variableId, {
|
|
3010
|
+
count: 1,
|
|
3011
|
+
firstRange: range
|
|
3012
|
+
});
|
|
3013
|
+
return;
|
|
3014
|
+
}
|
|
3015
|
+
occurrence.count += 1;
|
|
3016
|
+
};
|
|
3017
|
+
/**
|
|
3018
|
+
* Validates runtime-variable placeholders in one normalized canonical agent instruction.
|
|
3019
|
+
* @param manifestPath The canonical manifest path containing variable declarations.
|
|
3020
|
+
* @param agentId The owning agent ID.
|
|
3021
|
+
* @param instruction The normalized non-empty canonical instruction asset.
|
|
3022
|
+
* @param declaredVariables The owning agent's normalized variable declarations.
|
|
3023
|
+
* @param limits The resource limits applied to emitted diagnostics.
|
|
3024
|
+
* @returns Frozen deterministic placeholder diagnostics.
|
|
3025
|
+
* @throws
|
|
3026
|
+
* - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
|
|
3027
|
+
*/
|
|
3028
|
+
var validateRuntimePlaceholders = (manifestPath, agentId, instruction, declaredVariables, limits) => {
|
|
3029
|
+
const collector = createCoreDiagnosticCollector(limits, "inspect-project");
|
|
3030
|
+
const locator = createSourceLocator(instruction.content);
|
|
3031
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
3032
|
+
let cursor = 0;
|
|
3033
|
+
while (cursor < instruction.content.length) {
|
|
3034
|
+
const character = instruction.content[cursor];
|
|
3035
|
+
if (character === "}") {
|
|
3036
|
+
const runEnd = readBraceRunEnd(instruction.content, cursor, "}");
|
|
3037
|
+
const runLength = runEnd - cursor;
|
|
3038
|
+
if (runLength >= 2) addMalformedDiagnostic(collector, instruction, agentId, runLength === 2 ? "unmatched-closing" : "invalid-closing-delimiter", locator.locateRange(cursor, runEnd));
|
|
3039
|
+
cursor = runEnd;
|
|
3040
|
+
continue;
|
|
3041
|
+
}
|
|
3042
|
+
if (character !== "{") {
|
|
3043
|
+
cursor += 1;
|
|
3044
|
+
continue;
|
|
3045
|
+
}
|
|
3046
|
+
const openingEnd = readBraceRunEnd(instruction.content, cursor, "{");
|
|
3047
|
+
const openingLength = openingEnd - cursor;
|
|
3048
|
+
if (openingLength === 1) {
|
|
3049
|
+
cursor = openingEnd;
|
|
3050
|
+
continue;
|
|
3051
|
+
}
|
|
3052
|
+
if (openingLength > 2) {
|
|
3053
|
+
addMalformedDiagnostic(collector, instruction, agentId, "invalid-opening-delimiter", locator.locateRange(cursor, openingEnd));
|
|
3054
|
+
cursor = openingEnd;
|
|
3055
|
+
continue;
|
|
3056
|
+
}
|
|
3057
|
+
let candidateCursor = openingEnd;
|
|
3058
|
+
let isCandidateComplete = false;
|
|
3059
|
+
while (candidateCursor < instruction.content.length) {
|
|
3060
|
+
const candidateCharacter = instruction.content[candidateCursor];
|
|
3061
|
+
if (candidateCharacter === "{") {
|
|
3062
|
+
const nestedOpeningEnd = readBraceRunEnd(instruction.content, candidateCursor, "{");
|
|
3063
|
+
if (nestedOpeningEnd - candidateCursor >= 2) {
|
|
3064
|
+
const malformedEnd = findClosingRunEnd(instruction.content, nestedOpeningEnd);
|
|
3065
|
+
addMalformedDiagnostic(collector, instruction, agentId, "nested-opening", locator.locateRange(cursor, malformedEnd));
|
|
3066
|
+
cursor = malformedEnd;
|
|
3067
|
+
isCandidateComplete = true;
|
|
3068
|
+
break;
|
|
3069
|
+
}
|
|
3070
|
+
candidateCursor = nestedOpeningEnd;
|
|
3071
|
+
continue;
|
|
3072
|
+
}
|
|
3073
|
+
if (candidateCharacter !== "}") {
|
|
3074
|
+
candidateCursor += 1;
|
|
3075
|
+
continue;
|
|
3076
|
+
}
|
|
3077
|
+
const closingEnd = readBraceRunEnd(instruction.content, candidateCursor, "}");
|
|
3078
|
+
const closingLength = closingEnd - candidateCursor;
|
|
3079
|
+
if (closingLength === 1) {
|
|
3080
|
+
candidateCursor = closingEnd;
|
|
3081
|
+
continue;
|
|
3082
|
+
}
|
|
3083
|
+
if (closingLength > 2) {
|
|
3084
|
+
addMalformedDiagnostic(collector, instruction, agentId, "invalid-closing-delimiter", locator.locateRange(cursor, closingEnd));
|
|
3085
|
+
cursor = closingEnd;
|
|
3086
|
+
isCandidateComplete = true;
|
|
3087
|
+
break;
|
|
3088
|
+
}
|
|
3089
|
+
const variableId = instruction.content.slice(openingEnd, candidateCursor);
|
|
3090
|
+
const placeholderRange = locator.locateRange(cursor, closingEnd);
|
|
3091
|
+
if (isVariableId(variableId)) recordOccurrence(occurrences, variableId, placeholderRange);
|
|
3092
|
+
else addMalformedDiagnostic(collector, instruction, agentId, "invalid-variable-id", placeholderRange);
|
|
3093
|
+
cursor = closingEnd;
|
|
3094
|
+
isCandidateComplete = true;
|
|
3095
|
+
break;
|
|
3096
|
+
}
|
|
3097
|
+
if (!isCandidateComplete) {
|
|
3098
|
+
addMalformedDiagnostic(collector, instruction, agentId, "unmatched-opening", locator.locateRange(cursor, instruction.content.length));
|
|
3099
|
+
cursor = instruction.content.length;
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
for (const variableId of [...occurrences.keys()].sort(compareExactStrings)) {
|
|
3103
|
+
if (declaredVariables[variableId] !== void 0) continue;
|
|
3104
|
+
const occurrence = occurrences.get(variableId);
|
|
3105
|
+
if (occurrence === void 0) continue;
|
|
3106
|
+
collector.add({
|
|
3107
|
+
code: "MOLDEA_VARIABLE_UNDECLARED",
|
|
3108
|
+
details: { occurrences: occurrence.count },
|
|
3109
|
+
entity: {
|
|
3110
|
+
agentId,
|
|
3111
|
+
variableId
|
|
3112
|
+
},
|
|
3113
|
+
path: instruction.path,
|
|
3114
|
+
range: occurrence.firstRange
|
|
3115
|
+
});
|
|
3116
|
+
}
|
|
3117
|
+
for (const variableId of Object.keys(declaredVariables).sort(compareExactStrings)) {
|
|
3118
|
+
if (occurrences.has(variableId)) continue;
|
|
3119
|
+
collector.add({
|
|
3120
|
+
code: "MOLDEA_VARIABLE_UNUSED",
|
|
3121
|
+
entity: {
|
|
3122
|
+
agentId,
|
|
3123
|
+
variableId
|
|
3124
|
+
},
|
|
3125
|
+
path: manifestPath,
|
|
3126
|
+
pointer: `/agents/${escapeJsonPointerSegment(agentId)}/variables/${escapeJsonPointerSegment(variableId)}`
|
|
3127
|
+
});
|
|
3128
|
+
}
|
|
3129
|
+
return collector.finalize();
|
|
3130
|
+
};
|
|
3131
|
+
//#endregion
|
|
3132
|
+
//#region src/agent-assets/index.ts
|
|
3133
|
+
var MANIFEST_PATH$2 = parseRepositoryPath("/moldea/moldea.yaml");
|
|
3134
|
+
var addDiagnostics$4 = (collector, diagnostics) => {
|
|
3135
|
+
for (const diagnostic of diagnostics) collector.merge(diagnostic);
|
|
3136
|
+
};
|
|
3137
|
+
var isBlockedPath$2 = (path, blockedPaths) => {
|
|
3138
|
+
let candidate = path;
|
|
3139
|
+
while (candidate.length > 0) {
|
|
3140
|
+
if (blockedPaths.has(candidate)) return true;
|
|
3141
|
+
const separatorIndex = candidate.lastIndexOf("/");
|
|
3142
|
+
if (separatorIndex <= 0) return false;
|
|
3143
|
+
candidate = candidate.slice(0, separatorIndex);
|
|
3144
|
+
}
|
|
3145
|
+
return false;
|
|
3146
|
+
};
|
|
3147
|
+
/** Reads and validates a required or optional description asset. */
|
|
3148
|
+
var readDescription = async (repository, path, agentId, kind, options, collector, signal) => {
|
|
3149
|
+
const readResult = await readRepositoryTextAsset(repository, path, options.limits, signal);
|
|
3150
|
+
addDiagnostics$4(collector, readResult.diagnostics);
|
|
3151
|
+
if (readResult.asset === null) return null;
|
|
3152
|
+
const validation = validateAgentDescription(readResult.asset, agentId, kind, options.limits);
|
|
3153
|
+
addDiagnostics$4(collector, validation.diagnostics);
|
|
3154
|
+
return validation.description;
|
|
3155
|
+
};
|
|
3156
|
+
/** Reads and validates a mandatory instruction asset. */
|
|
3157
|
+
var readInstruction = async (repository, path, agentId, declaration, options, collector, signal) => {
|
|
3158
|
+
const readResult = await readRepositoryTextAsset(repository, path, options.limits, signal);
|
|
3159
|
+
addDiagnostics$4(collector, readResult.diagnostics);
|
|
3160
|
+
if (readResult.asset === null) return null;
|
|
3161
|
+
const validation = validateAgentInstruction(readResult.asset, agentId, options.limits);
|
|
3162
|
+
addDiagnostics$4(collector, validation.diagnostics);
|
|
3163
|
+
if (validation.instruction !== null) addDiagnostics$4(collector, validateRuntimePlaceholders(MANIFEST_PATH$2, agentId, validation.instruction, declaration.variables ?? {}, options.limits));
|
|
3164
|
+
return validation.instruction;
|
|
3165
|
+
};
|
|
3166
|
+
/**
|
|
3167
|
+
* Reconciles registered agents with their convention-owned directories and Markdown assets.
|
|
3168
|
+
* @param repository The coherent source-neutral repository reader.
|
|
3169
|
+
* @param manifest The normalized manifest containing registered agents.
|
|
3170
|
+
* @param discovery The canonical inventory and path-owned structural diagnostics.
|
|
3171
|
+
* @param options The immutable Core configuration snapshot.
|
|
3172
|
+
* @param signal Optional cancellation forwarded to every repository read.
|
|
3173
|
+
* @returns Deeply immutable registered-agent assets and deterministic diagnostics.
|
|
3174
|
+
* @throws
|
|
3175
|
+
* - ENTRY_NOT_FOUND: A discovered agent asset disappeared from the reader snapshot.
|
|
3176
|
+
* - ENTRY_NOT_FILE: A discovered agent asset is no longer a regular file.
|
|
3177
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
3178
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
3179
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during agent reads.
|
|
3180
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
3181
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
3182
|
+
* - ABORTED: Agent inspection or a repository operation was aborted.
|
|
3183
|
+
*/
|
|
3184
|
+
var inspectAgentAssets = async (repository, manifest, discovery, options, signal) => {
|
|
3185
|
+
options = createCoreOperationOptionsSnapshot(options);
|
|
3186
|
+
const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
|
|
3187
|
+
const agents = [];
|
|
3188
|
+
const registeredAgents = manifest.agents ?? {};
|
|
3189
|
+
const registeredIds = new Set(Object.keys(registeredAgents));
|
|
3190
|
+
const discoveredById = new Map(discovery.inventory.agents.map((agent) => [agent.id, agent]));
|
|
3191
|
+
const blockedPaths = new Set(discovery.diagnostics.flatMap((diagnostic) => diagnostic.path === null ? [] : [diagnostic.path]));
|
|
3192
|
+
for (const discovered of discovery.inventory.agents) if (!registeredIds.has(discovered.id)) collector.add({
|
|
3193
|
+
code: "MOLDEA_AGENT_DIRECTORY_UNREGISTERED",
|
|
3194
|
+
entity: { agentId: discovered.id },
|
|
3195
|
+
path: discovered.path
|
|
3196
|
+
});
|
|
3197
|
+
for (const agentId of [...registeredIds].sort(compareExactStrings)) {
|
|
3198
|
+
const declaration = registeredAgents[agentId];
|
|
3199
|
+
if (declaration === void 0) continue;
|
|
3200
|
+
const discovered = discoveredById.get(agentId);
|
|
3201
|
+
if (discovered === void 0) {
|
|
3202
|
+
const directoryPath = parseRepositoryPath(`/moldea/agents/${agentId}`);
|
|
3203
|
+
if (!isBlockedPath$2(directoryPath, blockedPaths)) collector.add({
|
|
3204
|
+
code: "MOLDEA_AGENT_DIRECTORY_MISSING",
|
|
3205
|
+
entity: { agentId },
|
|
3206
|
+
path: directoryPath
|
|
3207
|
+
});
|
|
3208
|
+
agents.push({
|
|
3209
|
+
declaration,
|
|
3210
|
+
description: null,
|
|
3211
|
+
handoffDescription: null,
|
|
3212
|
+
id: agentId,
|
|
3213
|
+
instruction: null
|
|
3214
|
+
});
|
|
3215
|
+
continue;
|
|
3216
|
+
}
|
|
3217
|
+
let description = null;
|
|
3218
|
+
let instruction = null;
|
|
3219
|
+
let handoffDescription = null;
|
|
3220
|
+
if (discovered.description === null) {
|
|
3221
|
+
const path = parseRepositoryPath(`${discovered.path}/description.md`);
|
|
3222
|
+
if (!isBlockedPath$2(path, blockedPaths)) collector.add({
|
|
3223
|
+
code: "MOLDEA_AGENT_DESCRIPTION_MISSING",
|
|
3224
|
+
entity: { agentId },
|
|
3225
|
+
path
|
|
3226
|
+
});
|
|
3227
|
+
} else description = await readDescription(repository, discovered.description, agentId, "description", options, collector, signal);
|
|
3228
|
+
if (discovered.instruction === null) {
|
|
3229
|
+
const path = parseRepositoryPath(`${discovered.path}/instruction.md`);
|
|
3230
|
+
if (!isBlockedPath$2(path, blockedPaths)) collector.add({
|
|
3231
|
+
code: "MOLDEA_AGENT_INSTRUCTION_MISSING",
|
|
3232
|
+
entity: { agentId },
|
|
3233
|
+
path
|
|
3234
|
+
});
|
|
3235
|
+
} else instruction = await readInstruction(repository, discovered.instruction, agentId, declaration, options, collector, signal);
|
|
3236
|
+
if (discovered.handoffDescription !== null) handoffDescription = await readDescription(repository, discovered.handoffDescription, agentId, "handoff-description", options, collector, signal);
|
|
3237
|
+
agents.push({
|
|
3238
|
+
declaration,
|
|
3239
|
+
description,
|
|
3240
|
+
handoffDescription,
|
|
3241
|
+
id: agentId,
|
|
3242
|
+
instruction
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
const diagnostics = collector.finalize();
|
|
3246
|
+
return freezeRecursively({
|
|
3247
|
+
agents,
|
|
3248
|
+
diagnostics,
|
|
3249
|
+
valid: diagnostics.length === 0
|
|
3250
|
+
});
|
|
3251
|
+
};
|
|
3252
|
+
//#endregion
|
|
3253
|
+
//#region src/canonical-discovery-validation/index.ts
|
|
3254
|
+
var AGENT_ASSET_PATTERN = /^\/moldea\/agents\/([^/]+)\/(description|instruction|handoff-description)\.md$/u;
|
|
3255
|
+
var AGENT_DIRECTORY_PATTERN = /^\/moldea\/agents\/([^/]+)$/u;
|
|
3256
|
+
var DECISION_CANDIDATE_PATTERN = /^\/moldea\/decisions\/[^/]+\.md$/u;
|
|
3257
|
+
var readCanonicalFile = (path) => {
|
|
3258
|
+
if (path === "/moldea/moldea.yaml") return {
|
|
3259
|
+
agentId: null,
|
|
3260
|
+
fileKind: "manifest",
|
|
3261
|
+
kind: "canonical-file"
|
|
3262
|
+
};
|
|
3263
|
+
if (path === "/moldea/project.md") return {
|
|
3264
|
+
agentId: null,
|
|
3265
|
+
fileKind: "project",
|
|
3266
|
+
kind: "canonical-file"
|
|
3267
|
+
};
|
|
3268
|
+
if (isContextPath(path, false)) return {
|
|
3269
|
+
agentId: null,
|
|
3270
|
+
fileKind: "context",
|
|
3271
|
+
kind: "canonical-file"
|
|
3272
|
+
};
|
|
3273
|
+
if (DECISION_CANDIDATE_PATTERN.test(path)) return {
|
|
3274
|
+
agentId: null,
|
|
3275
|
+
fileKind: "decision",
|
|
3276
|
+
kind: "canonical-file"
|
|
3277
|
+
};
|
|
3278
|
+
if (isRuntimeGuidancePath(path)) return {
|
|
3279
|
+
agentId: null,
|
|
3280
|
+
fileKind: "runtime-guidance",
|
|
3281
|
+
kind: "canonical-file"
|
|
3282
|
+
};
|
|
3283
|
+
const agentAsset = AGENT_ASSET_PATTERN.exec(path);
|
|
3284
|
+
if (agentAsset === null) return null;
|
|
3285
|
+
const agentId = agentAsset[1];
|
|
3286
|
+
const fileName = agentAsset[2];
|
|
3287
|
+
if (agentId === void 0 || fileName === void 0) return null;
|
|
3288
|
+
return {
|
|
3289
|
+
agentId,
|
|
3290
|
+
fileKind: fileName === "description" ? "agent-description" : fileName === "instruction" ? "agent-instruction" : "agent-handoff-description",
|
|
3291
|
+
kind: "canonical-file"
|
|
3292
|
+
};
|
|
3293
|
+
};
|
|
3294
|
+
var readAgentDirectoryId = (path) => {
|
|
3295
|
+
return AGENT_DIRECTORY_PATTERN.exec(path)?.[1] ?? null;
|
|
3296
|
+
};
|
|
3297
|
+
var isRequiredDirectoryPath = (path) => {
|
|
3298
|
+
return path === "/moldea" || path === "/moldea/context" || path === "/moldea/decisions" || path === "/moldea/runtimes" || path === "/moldea/agents";
|
|
3299
|
+
};
|
|
3300
|
+
var isPermittedStructuralDirectoryPath = (path) => {
|
|
3301
|
+
return isRequiredDirectoryPath(path) || path.startsWith("/moldea/context/") || path.startsWith("/moldea/runtimes/");
|
|
3302
|
+
};
|
|
3303
|
+
/**
|
|
3304
|
+
* Classifies one reader entry against the version 1 canonical layout.
|
|
3305
|
+
* @param entry The detached entry beneath the canonical moldea root.
|
|
3306
|
+
* @returns Its canonical category or the precise structural failure category.
|
|
3307
|
+
*/
|
|
3308
|
+
var classifyCanonicalEntry = (entry) => {
|
|
3309
|
+
const canonicalFile = readCanonicalFile(entry.path);
|
|
3310
|
+
if (canonicalFile !== null) {
|
|
3311
|
+
if (entry.type === "file") return canonicalFile;
|
|
3312
|
+
return entry.type === "symlink" ? { kind: "canonical-asset-symlink" } : {
|
|
3313
|
+
expectedType: "file",
|
|
3314
|
+
kind: "entry-type-invalid"
|
|
3315
|
+
};
|
|
3316
|
+
}
|
|
3317
|
+
const agentId = readAgentDirectoryId(entry.path);
|
|
3318
|
+
if (agentId !== null) return entry.type === "directory" ? {
|
|
3319
|
+
agentId,
|
|
3320
|
+
kind: "agent-directory"
|
|
3321
|
+
} : {
|
|
3322
|
+
expectedType: "directory",
|
|
3323
|
+
kind: "entry-type-invalid"
|
|
3324
|
+
};
|
|
3325
|
+
if (isRequiredDirectoryPath(entry.path)) return entry.type === "directory" ? { kind: "structural-directory" } : {
|
|
3326
|
+
expectedType: "directory",
|
|
3327
|
+
kind: "entry-type-invalid"
|
|
3328
|
+
};
|
|
3329
|
+
if (entry.type === "directory") return isPermittedStructuralDirectoryPath(entry.path) ? { kind: "structural-directory" } : { kind: "ignored-directory" };
|
|
3330
|
+
return { kind: "unrecognized" };
|
|
3331
|
+
};
|
|
3332
|
+
//#endregion
|
|
3333
|
+
//#region src/canonical-discovery/index.ts
|
|
3334
|
+
var MOLDEA_ROOT$1 = parseRepositoryPath("/moldea");
|
|
3335
|
+
var MANIFEST_PATH$1 = parseRepositoryPath("/moldea/moldea.yaml");
|
|
3336
|
+
var PROJECT_PATH$1 = parseRepositoryPath("/moldea/project.md");
|
|
3337
|
+
var createWorkingInventory = () => ({
|
|
3338
|
+
agents: /* @__PURE__ */ new Map(),
|
|
3339
|
+
context: /* @__PURE__ */ new Set(),
|
|
3340
|
+
decisions: /* @__PURE__ */ new Set(),
|
|
3341
|
+
manifest: null,
|
|
3342
|
+
project: null,
|
|
3343
|
+
runtimeGuidance: /* @__PURE__ */ new Set()
|
|
3344
|
+
});
|
|
3345
|
+
var invalidSourceData$1 = (operation, path) => {
|
|
3346
|
+
throw new RepositorySourceException({
|
|
3347
|
+
code: "INVALID_SOURCE_DATA",
|
|
3348
|
+
operation,
|
|
3349
|
+
path,
|
|
3350
|
+
retryable: false
|
|
3351
|
+
});
|
|
3352
|
+
};
|
|
3353
|
+
var copyReaderEntry = (candidate, operation, expectedPath) => {
|
|
3354
|
+
if (typeof candidate !== "object" || candidate === null) return invalidSourceData$1(operation, expectedPath ?? null);
|
|
3355
|
+
const record = candidate;
|
|
3356
|
+
const pathCandidate = record["path"];
|
|
3357
|
+
const typeCandidate = record["type"];
|
|
3358
|
+
if (typeof pathCandidate !== "string" || !isRepositoryPath(pathCandidate)) return invalidSourceData$1(operation, expectedPath ?? null);
|
|
3359
|
+
const path = parseRepositoryPath(pathCandidate);
|
|
3360
|
+
if (expectedPath !== void 0 && path !== expectedPath) return invalidSourceData$1(operation, path);
|
|
3361
|
+
if (typeCandidate !== "file" && typeCandidate !== "directory" && typeCandidate !== "symlink") return invalidSourceData$1(operation, path);
|
|
3362
|
+
return {
|
|
3363
|
+
path,
|
|
3364
|
+
type: typeCandidate
|
|
3365
|
+
};
|
|
3366
|
+
};
|
|
3367
|
+
var readExactEntry = async (repository, path, signal) => {
|
|
3368
|
+
const candidate = await repository.getEntry(path, signal === void 0 ? void 0 : { signal });
|
|
3369
|
+
return candidate === null ? null : copyReaderEntry(candidate, "get-entry", path);
|
|
3370
|
+
};
|
|
3371
|
+
var addEntryTypeDiagnostic = (diagnostics, entry, expectedType) => {
|
|
3372
|
+
diagnostics.add({
|
|
3373
|
+
code: "MOLDEA_ENTRY_TYPE_INVALID",
|
|
3374
|
+
details: {
|
|
3375
|
+
actualType: entry.type,
|
|
3376
|
+
expectedType
|
|
3377
|
+
},
|
|
3378
|
+
path: entry.path
|
|
3379
|
+
});
|
|
3380
|
+
};
|
|
3381
|
+
var getAgent = (agents, agentId) => {
|
|
3382
|
+
const existing = agents.get(agentId);
|
|
3383
|
+
if (existing !== void 0) return existing;
|
|
3384
|
+
const created = {
|
|
3385
|
+
description: null,
|
|
3386
|
+
handoffDescription: null,
|
|
3387
|
+
id: agentId,
|
|
3388
|
+
instruction: null,
|
|
3389
|
+
path: parseRepositoryPath(`/moldea/agents/${agentId}`)
|
|
3390
|
+
};
|
|
3391
|
+
agents.set(agentId, created);
|
|
3392
|
+
return created;
|
|
3393
|
+
};
|
|
3394
|
+
var retainCanonicalFile = (inventory, path, fileKind, agentId) => {
|
|
3395
|
+
switch (fileKind) {
|
|
3396
|
+
case "manifest":
|
|
3397
|
+
inventory.manifest = path;
|
|
3398
|
+
return;
|
|
3399
|
+
case "project":
|
|
3400
|
+
inventory.project = path;
|
|
3401
|
+
return;
|
|
3402
|
+
case "context":
|
|
3403
|
+
inventory.context.add(path);
|
|
3404
|
+
return;
|
|
3405
|
+
case "decision":
|
|
3406
|
+
inventory.decisions.add(path);
|
|
3407
|
+
return;
|
|
3408
|
+
case "runtime-guidance":
|
|
3409
|
+
inventory.runtimeGuidance.add(path);
|
|
3410
|
+
return;
|
|
3411
|
+
}
|
|
3412
|
+
if (agentId === null) return invalidSourceData$1("list-entries", path);
|
|
3413
|
+
const agent = getAgent(inventory.agents, agentId);
|
|
3414
|
+
if (fileKind === "agent-description") agent.description = path;
|
|
3415
|
+
else if (fileKind === "agent-instruction") agent.instruction = path;
|
|
3416
|
+
else agent.handoffDescription = path;
|
|
3417
|
+
};
|
|
3418
|
+
var finalizeInventory = (working) => {
|
|
3419
|
+
const sortPaths = (paths) => {
|
|
3420
|
+
return [...paths].sort(compareExactStrings);
|
|
3421
|
+
};
|
|
3422
|
+
return freezeRecursively({
|
|
3423
|
+
agents: [...working.agents.values()].sort((left, right) => compareExactStrings(left.id, right.id)).map((agent) => freezeRecursively({ ...agent })),
|
|
3424
|
+
context: sortPaths(working.context),
|
|
3425
|
+
decisions: sortPaths(working.decisions),
|
|
3426
|
+
manifest: working.manifest,
|
|
3427
|
+
project: working.project,
|
|
3428
|
+
runtimeGuidance: sortPaths(working.runtimeGuidance)
|
|
3429
|
+
});
|
|
3430
|
+
};
|
|
3431
|
+
var finalizeResult = (working, diagnostics) => {
|
|
3432
|
+
const finalizedDiagnostics = diagnostics.finalize();
|
|
3433
|
+
return freezeRecursively({
|
|
3434
|
+
diagnostics: finalizedDiagnostics,
|
|
3435
|
+
inventory: finalizeInventory(working),
|
|
3436
|
+
valid: finalizedDiagnostics.length === 0
|
|
3437
|
+
});
|
|
3438
|
+
};
|
|
3439
|
+
var retainFoundationEntry = (entry, expectedPath, missingCode, working, diagnostics) => {
|
|
3440
|
+
if (entry === null) {
|
|
3441
|
+
diagnostics.add({
|
|
3442
|
+
code: missingCode,
|
|
3443
|
+
path: expectedPath
|
|
3444
|
+
});
|
|
3445
|
+
return;
|
|
3446
|
+
}
|
|
3447
|
+
if (entry.type === "file") {
|
|
3448
|
+
if (expectedPath === MANIFEST_PATH$1) working.manifest = expectedPath;
|
|
3449
|
+
else working.project = expectedPath;
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
if (entry.type === "symlink") {
|
|
3453
|
+
diagnostics.add({
|
|
3454
|
+
code: "MOLDEA_CANONICAL_ASSET_SYMLINK",
|
|
3455
|
+
path: expectedPath
|
|
3456
|
+
});
|
|
3457
|
+
return;
|
|
3458
|
+
}
|
|
3459
|
+
addEntryTypeDiagnostic(diagnostics, entry, "file");
|
|
3460
|
+
};
|
|
3461
|
+
var checkFoundationListing = (listedEntry, exactEntry) => {
|
|
3462
|
+
if (exactEntry === null || exactEntry.type !== listedEntry.type) return invalidSourceData$1("list-entries", listedEntry.path);
|
|
3463
|
+
};
|
|
3464
|
+
/**
|
|
3465
|
+
* Discovers and classifies version 1 canonical assets through one repository snapshot.
|
|
3466
|
+
* @param repository The coherent source-neutral repository reader.
|
|
3467
|
+
* @param limits The immutable Core limits governing diagnostic collection.
|
|
3468
|
+
* @param signal Optional cancellation forwarded to every repository operation.
|
|
3469
|
+
* @param foundations Optional exact foundation entries already observed by this inspection.
|
|
3470
|
+
* @returns A deeply immutable inventory and its deterministic structural diagnostics.
|
|
3471
|
+
* @throws
|
|
3472
|
+
* - ENTRY_NOT_FOUND: The reader snapshot became inconsistent during discovery.
|
|
3473
|
+
* - ENTRY_NOT_DIRECTORY: The reader snapshot became inconsistent during discovery.
|
|
3474
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
3475
|
+
* - SOURCE_UNAVAILABLE: The repository source was unavailable.
|
|
3476
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during discovery.
|
|
3477
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
3478
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
3479
|
+
* - ABORTED: Discovery or a repository operation was aborted.
|
|
3480
|
+
*/
|
|
3481
|
+
var discoverCanonicalAssets = async (repository, limits, signal, foundations) => {
|
|
3482
|
+
const diagnostics = createCoreDiagnosticCollector(limits, "inspect-project");
|
|
3483
|
+
const working = createWorkingInventory();
|
|
3484
|
+
const moldeaEntry = foundations === void 0 ? await readExactEntry(repository, MOLDEA_ROOT$1, signal) : foundations.moldeaRoot;
|
|
3485
|
+
if (moldeaEntry === null) {
|
|
3486
|
+
diagnostics.add({
|
|
3487
|
+
code: "MOLDEA_MANIFEST_MISSING",
|
|
3488
|
+
path: MANIFEST_PATH$1
|
|
3489
|
+
});
|
|
3490
|
+
diagnostics.add({
|
|
3491
|
+
code: "MOLDEA_PROJECT_FILE_MISSING",
|
|
3492
|
+
path: PROJECT_PATH$1
|
|
3493
|
+
});
|
|
3494
|
+
return finalizeResult(working, diagnostics);
|
|
3495
|
+
}
|
|
3496
|
+
if (moldeaEntry.type !== "directory") {
|
|
3497
|
+
addEntryTypeDiagnostic(diagnostics, moldeaEntry, "directory");
|
|
3498
|
+
return finalizeResult(working, diagnostics);
|
|
3499
|
+
}
|
|
3500
|
+
const manifestEntry = foundations === void 0 ? await readExactEntry(repository, MANIFEST_PATH$1, signal) : foundations.manifest;
|
|
3501
|
+
const projectEntry = foundations === void 0 ? await readExactEntry(repository, PROJECT_PATH$1, signal) : foundations.project;
|
|
3502
|
+
retainFoundationEntry(manifestEntry, MANIFEST_PATH$1, "MOLDEA_MANIFEST_MISSING", working, diagnostics);
|
|
3503
|
+
retainFoundationEntry(projectEntry, PROJECT_PATH$1, "MOLDEA_PROJECT_FILE_MISSING", working, diagnostics);
|
|
3504
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
3505
|
+
let listedManifest = false;
|
|
3506
|
+
let listedProject = false;
|
|
3507
|
+
const listOptions = signal === void 0 ? { prefix: MOLDEA_ROOT$1 } : {
|
|
3508
|
+
prefix: MOLDEA_ROOT$1,
|
|
3509
|
+
signal
|
|
3510
|
+
};
|
|
3511
|
+
for await (const candidate of repository.listEntries(listOptions)) {
|
|
3512
|
+
const entry = copyReaderEntry(candidate, "list-entries");
|
|
3513
|
+
if (!entry.path.startsWith("/moldea/")) return invalidSourceData$1("list-entries", entry.path);
|
|
3514
|
+
if (seenPaths.has(entry.path)) return invalidSourceData$1("list-entries", entry.path);
|
|
3515
|
+
seenPaths.add(entry.path);
|
|
3516
|
+
if (entry.path === MANIFEST_PATH$1) {
|
|
3517
|
+
checkFoundationListing(entry, manifestEntry);
|
|
3518
|
+
listedManifest = true;
|
|
3519
|
+
continue;
|
|
3520
|
+
}
|
|
3521
|
+
if (entry.path === PROJECT_PATH$1) {
|
|
3522
|
+
checkFoundationListing(entry, projectEntry);
|
|
3523
|
+
listedProject = true;
|
|
3524
|
+
continue;
|
|
3525
|
+
}
|
|
3526
|
+
const classification = classifyCanonicalEntry(entry);
|
|
3527
|
+
switch (classification.kind) {
|
|
3528
|
+
case "canonical-file":
|
|
3529
|
+
retainCanonicalFile(working, entry.path, classification.fileKind, classification.agentId);
|
|
3530
|
+
break;
|
|
3531
|
+
case "agent-directory":
|
|
3532
|
+
getAgent(working.agents, classification.agentId);
|
|
3533
|
+
break;
|
|
3534
|
+
case "canonical-asset-symlink":
|
|
3535
|
+
diagnostics.add({
|
|
3536
|
+
code: "MOLDEA_CANONICAL_ASSET_SYMLINK",
|
|
3537
|
+
path: entry.path
|
|
3538
|
+
});
|
|
3539
|
+
break;
|
|
3540
|
+
case "entry-type-invalid":
|
|
3541
|
+
addEntryTypeDiagnostic(diagnostics, entry, classification.expectedType);
|
|
3542
|
+
break;
|
|
3543
|
+
case "unrecognized": diagnostics.add({
|
|
3544
|
+
code: "MOLDEA_CANONICAL_PATH_UNRECOGNIZED",
|
|
3545
|
+
details: { entryType: entry.type },
|
|
3546
|
+
path: entry.path
|
|
3547
|
+
});
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
if (manifestEntry !== null !== listedManifest) return invalidSourceData$1("list-entries", MANIFEST_PATH$1);
|
|
3551
|
+
if (projectEntry !== null !== listedProject) return invalidSourceData$1("list-entries", PROJECT_PATH$1);
|
|
3552
|
+
return finalizeResult(working, diagnostics);
|
|
3553
|
+
};
|
|
3554
|
+
//#endregion
|
|
3555
|
+
//#region src/decision-graph-validation/index.ts
|
|
3556
|
+
var sortCandidates = (candidates) => {
|
|
3557
|
+
return [...candidates].sort((left, right) => compareExactStrings(left.path, right.path));
|
|
3558
|
+
};
|
|
3559
|
+
var buildGraphIndex = (candidates) => {
|
|
3560
|
+
const mutableGroups = /* @__PURE__ */ new Map();
|
|
3561
|
+
for (const candidate of sortCandidates(candidates)) {
|
|
3562
|
+
if (candidate.id === null) continue;
|
|
3563
|
+
const group = mutableGroups.get(candidate.id) ?? [];
|
|
3564
|
+
group.push(candidate);
|
|
3565
|
+
mutableGroups.set(candidate.id, group);
|
|
3566
|
+
}
|
|
3567
|
+
const candidateGroups = /* @__PURE__ */ new Map();
|
|
3568
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
3569
|
+
for (const id of [...mutableGroups.keys()].sort(compareExactStrings)) {
|
|
3570
|
+
const group = mutableGroups.get(id) ?? [];
|
|
3571
|
+
candidateGroups.set(id, group);
|
|
3572
|
+
if (group.length === 1 && group[0]?.decision !== null) {
|
|
3573
|
+
const decision = group[0]?.decision;
|
|
3574
|
+
if (decision !== void 0) nodes.set(id, decision);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
const edges = /* @__PURE__ */ new Map();
|
|
3578
|
+
for (const [id, decision] of nodes) {
|
|
3579
|
+
const resolvedTargets = decision.supersedes.filter((targetId) => {
|
|
3580
|
+
const group = candidateGroups.get(targetId);
|
|
3581
|
+
return group?.length === 1 && group[0]?.decision !== null;
|
|
3582
|
+
});
|
|
3583
|
+
edges.set(id, resolvedTargets);
|
|
3584
|
+
}
|
|
3585
|
+
return {
|
|
3586
|
+
candidateGroups,
|
|
3587
|
+
edges,
|
|
3588
|
+
nodes
|
|
3589
|
+
};
|
|
3590
|
+
};
|
|
3591
|
+
var collectDuplicateDiagnostics = (index, diagnostics) => {
|
|
3592
|
+
for (const [id, candidates] of index.candidateGroups) {
|
|
3593
|
+
if (candidates.length < 2) continue;
|
|
3594
|
+
for (const candidate of candidates) diagnostics.add({
|
|
3595
|
+
code: "MOLDEA_DECISION_ID_DUPLICATE",
|
|
3596
|
+
details: { occurrences: candidates.length },
|
|
3597
|
+
entity: { decisionId: id },
|
|
3598
|
+
path: candidate.path
|
|
3599
|
+
});
|
|
3600
|
+
}
|
|
3601
|
+
};
|
|
3602
|
+
var collectReferenceDiagnostics = (index, diagnostics) => {
|
|
3603
|
+
for (const decision of index.nodes.values()) for (const targetId of decision.supersedes) {
|
|
3604
|
+
if (index.candidateGroups.has(targetId)) continue;
|
|
3605
|
+
diagnostics.add({
|
|
3606
|
+
code: "MOLDEA_DECISION_REFERENCE_MISSING",
|
|
3607
|
+
details: { referencedDecisionId: targetId },
|
|
3608
|
+
entity: { decisionId: decision.id },
|
|
3609
|
+
path: decision.path,
|
|
3610
|
+
pointer: "/supersedes"
|
|
3611
|
+
});
|
|
3612
|
+
}
|
|
3613
|
+
};
|
|
3614
|
+
var collectStatusDiagnostics = (index, diagnostics) => {
|
|
3615
|
+
const activelySupersededIds = /* @__PURE__ */ new Set();
|
|
3616
|
+
for (const decision of index.nodes.values()) {
|
|
3617
|
+
if (decision.status !== "accepted" && decision.status !== "superseded") continue;
|
|
3618
|
+
for (const targetId of index.edges.get(decision.id) ?? []) {
|
|
3619
|
+
const target = index.nodes.get(targetId);
|
|
3620
|
+
if (target === void 0) continue;
|
|
3621
|
+
if (target.status === "superseded") {
|
|
3622
|
+
activelySupersededIds.add(targetId);
|
|
3623
|
+
continue;
|
|
3624
|
+
}
|
|
3625
|
+
diagnostics.add({
|
|
3626
|
+
code: "MOLDEA_DECISION_SUPERSESSION_STATUS_INVALID",
|
|
3627
|
+
details: {
|
|
3628
|
+
referencedDecisionId: targetId,
|
|
3629
|
+
sourceStatus: decision.status,
|
|
3630
|
+
targetStatus: target.status
|
|
3631
|
+
},
|
|
3632
|
+
entity: { decisionId: decision.id },
|
|
3633
|
+
path: decision.path,
|
|
3634
|
+
pointer: "/supersedes"
|
|
3635
|
+
});
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
return activelySupersededIds;
|
|
3639
|
+
};
|
|
3640
|
+
var collectOrphanDiagnostics = (index, activelySupersededIds, diagnostics) => {
|
|
3641
|
+
for (const decision of index.nodes.values()) if (decision.status === "superseded" && !activelySupersededIds.has(decision.id)) diagnostics.add({
|
|
3642
|
+
code: "MOLDEA_DECISION_SUPERSEDED_ORPHAN",
|
|
3643
|
+
entity: { decisionId: decision.id },
|
|
3644
|
+
path: decision.path
|
|
3645
|
+
});
|
|
3646
|
+
};
|
|
3647
|
+
var calculateFinishOrder = (index) => {
|
|
3648
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3649
|
+
const finished = [];
|
|
3650
|
+
for (const startId of index.nodes.keys()) {
|
|
3651
|
+
if (visited.has(startId)) continue;
|
|
3652
|
+
const stack = [{
|
|
3653
|
+
id: startId,
|
|
3654
|
+
nextNeighbor: 0
|
|
3655
|
+
}];
|
|
3656
|
+
visited.add(startId);
|
|
3657
|
+
while (stack.length > 0) {
|
|
3658
|
+
const current = stack.at(-1);
|
|
3659
|
+
if (current === void 0) break;
|
|
3660
|
+
const neighbor = (index.edges.get(current.id) ?? [])[current.nextNeighbor];
|
|
3661
|
+
if (neighbor === void 0) {
|
|
3662
|
+
finished.push(current.id);
|
|
3663
|
+
stack.pop();
|
|
3664
|
+
continue;
|
|
3665
|
+
}
|
|
3666
|
+
current.nextNeighbor += 1;
|
|
3667
|
+
if (!visited.has(neighbor)) {
|
|
3668
|
+
visited.add(neighbor);
|
|
3669
|
+
stack.push({
|
|
3670
|
+
id: neighbor,
|
|
3671
|
+
nextNeighbor: 0
|
|
3672
|
+
});
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
return finished;
|
|
3677
|
+
};
|
|
3678
|
+
var buildReverseEdges = (index) => {
|
|
3679
|
+
const reverseEdges = /* @__PURE__ */ new Map();
|
|
3680
|
+
for (const id of index.nodes.keys()) reverseEdges.set(id, []);
|
|
3681
|
+
for (const [sourceId, targets] of index.edges) for (const targetId of targets) reverseEdges.get(targetId)?.push(sourceId);
|
|
3682
|
+
for (const sources of reverseEdges.values()) sources.sort(compareExactStrings);
|
|
3683
|
+
return reverseEdges;
|
|
3684
|
+
};
|
|
3685
|
+
var findStronglyConnectedComponents = (index) => {
|
|
3686
|
+
const reverseEdges = buildReverseEdges(index);
|
|
3687
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3688
|
+
const components = [];
|
|
3689
|
+
for (const startId of [...calculateFinishOrder(index)].reverse()) {
|
|
3690
|
+
if (visited.has(startId)) continue;
|
|
3691
|
+
const component = [];
|
|
3692
|
+
const stack = [startId];
|
|
3693
|
+
visited.add(startId);
|
|
3694
|
+
while (stack.length > 0) {
|
|
3695
|
+
const currentId = stack.pop();
|
|
3696
|
+
if (currentId === void 0) break;
|
|
3697
|
+
component.push(currentId);
|
|
3698
|
+
for (const sourceId of [...reverseEdges.get(currentId) ?? []].reverse()) if (!visited.has(sourceId)) {
|
|
3699
|
+
visited.add(sourceId);
|
|
3700
|
+
stack.push(sourceId);
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
components.push(component.sort(compareExactStrings));
|
|
3704
|
+
}
|
|
3705
|
+
return components.sort((left, right) => compareExactStrings(left[0] ?? "", right[0] ?? ""));
|
|
3706
|
+
};
|
|
3707
|
+
var collectCycleDiagnostics = (index, diagnostics) => {
|
|
3708
|
+
for (const component of findStronglyConnectedComponents(index)) {
|
|
3709
|
+
if (component.length < 2) continue;
|
|
3710
|
+
const cycleRepresentativeDecisionId = component[0];
|
|
3711
|
+
if (cycleRepresentativeDecisionId === void 0) continue;
|
|
3712
|
+
for (const decisionId of component) {
|
|
3713
|
+
const decision = index.nodes.get(decisionId);
|
|
3714
|
+
if (decision !== void 0) diagnostics.add({
|
|
3715
|
+
code: "MOLDEA_DECISION_SUPERSESSION_CYCLE",
|
|
3716
|
+
details: {
|
|
3717
|
+
cycleRepresentativeDecisionId,
|
|
3718
|
+
cycleSize: component.length
|
|
3719
|
+
},
|
|
3720
|
+
entity: { decisionId },
|
|
3721
|
+
path: decision.path,
|
|
3722
|
+
pointer: "/supersedes"
|
|
3723
|
+
});
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
};
|
|
3727
|
+
/**
|
|
3728
|
+
* Validates every trustworthy cross-file relationship in one decision candidate set.
|
|
3729
|
+
* @param candidates Discovered candidates with valid parsed decisions where available.
|
|
3730
|
+
* @param limits The immutable Core limits governing repository-level diagnostics.
|
|
3731
|
+
* @returns Deterministically sorted, deeply immutable graph diagnostics.
|
|
3732
|
+
* @throws
|
|
3733
|
+
* - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
|
|
3734
|
+
*/
|
|
3735
|
+
var validateDecisionGraph = (candidates, limits) => {
|
|
3736
|
+
const diagnostics = createCoreDiagnosticCollector(limits, "inspect-project");
|
|
3737
|
+
const index = buildGraphIndex(candidates);
|
|
3738
|
+
collectDuplicateDiagnostics(index, diagnostics);
|
|
3739
|
+
collectReferenceDiagnostics(index, diagnostics);
|
|
3740
|
+
collectCycleDiagnostics(index, diagnostics);
|
|
3741
|
+
collectOrphanDiagnostics(index, collectStatusDiagnostics(index, diagnostics), diagnostics);
|
|
3742
|
+
return diagnostics.finalize();
|
|
3743
|
+
};
|
|
3744
|
+
//#endregion
|
|
3745
|
+
//#region src/decision-graph/index.ts
|
|
3746
|
+
var invalidSourceData = (path) => {
|
|
3747
|
+
throw new RepositorySourceException({
|
|
3748
|
+
code: "INVALID_SOURCE_DATA",
|
|
3749
|
+
operation: "read-file",
|
|
3750
|
+
path,
|
|
3751
|
+
retryable: false
|
|
3752
|
+
});
|
|
3753
|
+
};
|
|
3754
|
+
var sortDecisions = (decisions) => {
|
|
3755
|
+
return [...decisions].sort((left, right) => {
|
|
3756
|
+
return compareExactStrings(left.id, right.id) || compareExactStrings(left.path, right.path);
|
|
3757
|
+
});
|
|
3758
|
+
};
|
|
3759
|
+
/**
|
|
3760
|
+
* Reads, parses, and validates every discovered decision through one repository snapshot.
|
|
3761
|
+
* @param repository The coherent source-neutral repository reader.
|
|
3762
|
+
* @param paths The decision candidates retained by canonical discovery.
|
|
3763
|
+
* @param options The immutable Core limits and adapter snapshots.
|
|
3764
|
+
* @param signal Optional cancellation forwarded to every repository read.
|
|
3765
|
+
* @returns A deeply immutable internal decision set and its deterministic diagnostics.
|
|
3766
|
+
* @throws
|
|
3767
|
+
* - ENTRY_NOT_FOUND: A discovered decision disappeared from the reader snapshot.
|
|
3768
|
+
* - ENTRY_NOT_FILE: A discovered decision is no longer a regular file.
|
|
3769
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
3770
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
3771
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during decision reads.
|
|
3772
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
3773
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
3774
|
+
* - ABORTED: Decision inspection or a repository operation was aborted.
|
|
3775
|
+
*/
|
|
3776
|
+
var readDecisionGraph = async (repository, paths, options, signal) => {
|
|
3777
|
+
options = createCoreOperationOptionsSnapshot(options);
|
|
3778
|
+
const diagnostics = createCoreDiagnosticCollector(options.limits, "inspect-project");
|
|
3779
|
+
const candidates = [];
|
|
3780
|
+
const decisions = [];
|
|
3781
|
+
const readOptions = signal === void 0 ? void 0 : { signal };
|
|
3782
|
+
for (const path of [...paths].sort(compareExactStrings)) {
|
|
3783
|
+
const content = await repository.readFile(path, readOptions);
|
|
3784
|
+
if (!(content instanceof Uint8Array)) return invalidSourceData(path);
|
|
3785
|
+
const parsed = await parseDecisionDocument({
|
|
3786
|
+
content: content.slice(),
|
|
3787
|
+
path
|
|
3788
|
+
}, options, "inspect-project");
|
|
3789
|
+
for (const diagnostic of parsed.diagnostics) diagnostics.merge(diagnostic);
|
|
3790
|
+
candidates.push({
|
|
3791
|
+
decision: parsed.decision,
|
|
3792
|
+
id: parseDecisionIdFromPath(path),
|
|
3793
|
+
path
|
|
3794
|
+
});
|
|
3795
|
+
if (parsed.decision !== null) decisions.push(parsed.decision);
|
|
3796
|
+
}
|
|
3797
|
+
for (const diagnostic of validateDecisionGraph(candidates, options.limits)) diagnostics.merge(diagnostic);
|
|
3798
|
+
const finalizedDiagnostics = diagnostics.finalize();
|
|
3799
|
+
return freezeRecursively({
|
|
3800
|
+
decisions: sortDecisions(decisions),
|
|
3801
|
+
diagnostics: finalizedDiagnostics,
|
|
3802
|
+
valid: finalizedDiagnostics.length === 0
|
|
3803
|
+
});
|
|
3804
|
+
};
|
|
3805
|
+
//#endregion
|
|
3806
|
+
//#region src/manifest-relationship-validation/index.ts
|
|
3807
|
+
/** Creates diagnostic entity metadata for a decision relationship. */
|
|
3808
|
+
var createDecisionEntity = (decisionPath, agentId) => {
|
|
3809
|
+
const decisionId = parseDecisionIdFromPath(decisionPath);
|
|
3810
|
+
return {
|
|
3811
|
+
...agentId === void 0 ? {} : { agentId },
|
|
3812
|
+
...decisionId === null ? {} : { decisionId }
|
|
3813
|
+
};
|
|
3814
|
+
};
|
|
3815
|
+
/** Adds a missing-context diagnostic unless discovery owns the target-path failure. */
|
|
3816
|
+
var validateContextRelationship = (referencedPath, pointer, validContextPaths, context, agentId) => {
|
|
3817
|
+
if (validContextPaths.has(referencedPath) || context.blockedPaths.has(referencedPath)) return;
|
|
3818
|
+
context.collector.add({
|
|
3819
|
+
code: "MOLDEA_REFERENCE_MISSING",
|
|
3820
|
+
details: { referencedPath },
|
|
3821
|
+
entity: agentId === void 0 ? null : { agentId },
|
|
3822
|
+
path: context.manifestPath,
|
|
3823
|
+
pointer
|
|
3824
|
+
});
|
|
3825
|
+
};
|
|
3826
|
+
/** Adds missing or inactive diagnostics for a decision relationship. */
|
|
3827
|
+
var validateDecisionRelationship = (referencedPath, pointer, context, agentId) => {
|
|
3828
|
+
if (!context.discoveredDecisionPaths.has(referencedPath)) {
|
|
3829
|
+
if (context.blockedPaths.has(referencedPath)) return;
|
|
3830
|
+
context.collector.add({
|
|
3831
|
+
code: "MOLDEA_DECISION_REFERENCE_MISSING",
|
|
3832
|
+
details: { referencedPath },
|
|
3833
|
+
entity: createDecisionEntity(referencedPath, agentId),
|
|
3834
|
+
path: context.manifestPath,
|
|
3835
|
+
pointer
|
|
3836
|
+
});
|
|
3837
|
+
return;
|
|
3838
|
+
}
|
|
3839
|
+
const parsedDecision = context.parsedDecisionsByPath.get(referencedPath);
|
|
3840
|
+
if (parsedDecision === void 0 || parsedDecision.status === "accepted") return;
|
|
3841
|
+
context.collector.add({
|
|
3842
|
+
code: "MOLDEA_DECISION_RELATIONSHIP_INACTIVE",
|
|
3843
|
+
details: {
|
|
3844
|
+
referencedPath,
|
|
3845
|
+
targetStatus: parsedDecision.status
|
|
3846
|
+
},
|
|
3847
|
+
entity: createDecisionEntity(referencedPath, agentId),
|
|
3848
|
+
path: context.manifestPath,
|
|
3849
|
+
pointer
|
|
3850
|
+
});
|
|
3851
|
+
};
|
|
3852
|
+
/**
|
|
3853
|
+
* Validates manifest and agent relationships against canonical discovery and parsed decisions.
|
|
3854
|
+
* @param manifestPath The canonical manifest path used for relationship diagnostics.
|
|
3855
|
+
* @param manifest The normalized manifest contract to validate.
|
|
3856
|
+
* @param discovery The canonical repository discovery result.
|
|
3857
|
+
* @param decisions The successfully parsed canonical decisions.
|
|
3858
|
+
* @param limits The resource limits applied to emitted diagnostics.
|
|
3859
|
+
* @returns The frozen deterministic relationship diagnostics.
|
|
3860
|
+
* @throws
|
|
3861
|
+
* - RESOURCE_LIMIT_EXCEEDED: The repository-level diagnostic budget was exceeded.
|
|
3862
|
+
*/
|
|
3863
|
+
var validateManifestRelationships = (manifestPath, manifest, discovery, decisions, limits) => {
|
|
3864
|
+
const collector = createCoreDiagnosticCollector(limits, "inspect-project");
|
|
3865
|
+
const validAgentContextPaths = new Set(discovery.inventory.context);
|
|
3866
|
+
const validTopLevelContextPaths = new Set(validAgentContextPaths);
|
|
3867
|
+
if (discovery.inventory.project !== null) validTopLevelContextPaths.add(discovery.inventory.project);
|
|
3868
|
+
const validationContext = {
|
|
3869
|
+
blockedPaths: new Set(discovery.diagnostics.flatMap((diagnostic) => diagnostic.path === null ? [] : [diagnostic.path])),
|
|
3870
|
+
collector,
|
|
3871
|
+
discoveredDecisionPaths: new Set(discovery.inventory.decisions),
|
|
3872
|
+
manifestPath,
|
|
3873
|
+
parsedDecisionsByPath: new Map(decisions.map((decision) => [decision.path, decision])),
|
|
3874
|
+
validAgentContextPaths,
|
|
3875
|
+
validTopLevelContextPaths
|
|
3876
|
+
};
|
|
3877
|
+
for (const referencedPathValue of Object.keys(manifest.context ?? {}).sort(compareExactStrings)) validateContextRelationship(parseRepositoryPath(referencedPathValue), `/context/${escapeJsonPointerSegment(referencedPathValue)}`, validationContext.validTopLevelContextPaths, validationContext);
|
|
3878
|
+
for (const referencedPathValue of Object.keys(manifest.decisions ?? {}).sort(compareExactStrings)) validateDecisionRelationship(parseRepositoryPath(referencedPathValue), `/decisions/${escapeJsonPointerSegment(referencedPathValue)}`, validationContext);
|
|
3879
|
+
for (const agentId of Object.keys(manifest.agents ?? {}).sort(compareExactStrings)) {
|
|
3880
|
+
const agent = manifest.agents?.[agentId];
|
|
3881
|
+
if (agent === void 0) continue;
|
|
3882
|
+
const escapedAgentId = escapeJsonPointerSegment(agentId);
|
|
3883
|
+
for (const referencedPath of [...agent.context ?? []].sort(compareExactStrings)) validateContextRelationship(referencedPath, `/agents/${escapedAgentId}/context`, validationContext.validAgentContextPaths, validationContext, agentId);
|
|
3884
|
+
for (const referencedPath of [...agent.decisions ?? []].sort(compareExactStrings)) validateDecisionRelationship(referencedPath, `/agents/${escapedAgentId}/decisions`, validationContext, agentId);
|
|
3885
|
+
}
|
|
3886
|
+
return collector.finalize();
|
|
3887
|
+
};
|
|
3888
|
+
//#endregion
|
|
3889
|
+
//#region src/mirrors/index.ts
|
|
3890
|
+
var addDiagnostics$3 = (collector, diagnostics) => {
|
|
3891
|
+
for (const diagnostic of diagnostics) collector.merge(diagnostic);
|
|
3892
|
+
};
|
|
3893
|
+
/** Adds one declaration-owned mirror diagnostic with safe target metadata. */
|
|
3894
|
+
var addMirrorDiagnostic = (collector, code, manifestPath, mirrorPath, pointer, agentId, entry) => {
|
|
3895
|
+
collector.add({
|
|
3896
|
+
code,
|
|
3897
|
+
details: {
|
|
3898
|
+
...entry === void 0 ? {} : { actualType: entry.type },
|
|
3899
|
+
mirrorPath
|
|
3900
|
+
},
|
|
3901
|
+
entity: { agentId },
|
|
3902
|
+
path: manifestPath,
|
|
3903
|
+
pointer
|
|
3904
|
+
});
|
|
3905
|
+
};
|
|
3906
|
+
/** Resolves, normalizes, and compares one declared mirror. */
|
|
3907
|
+
var inspectMirror = async (repository, manifestPath, canonicalInstruction, mirrorPath, pointer, agentId, options, collector, operationOptions, signal) => {
|
|
3908
|
+
const entry = await repository.getEntry(mirrorPath, operationOptions);
|
|
3909
|
+
if (entry === null) {
|
|
3910
|
+
addMirrorDiagnostic(collector, "MOLDEA_MIRROR_MISSING", manifestPath, mirrorPath, pointer, agentId);
|
|
3911
|
+
return null;
|
|
3912
|
+
}
|
|
3913
|
+
if (entry.type === "symlink") {
|
|
3914
|
+
addMirrorDiagnostic(collector, "MOLDEA_MIRROR_SYMLINK", manifestPath, mirrorPath, pointer, agentId, entry);
|
|
3915
|
+
return null;
|
|
3916
|
+
}
|
|
3917
|
+
if (entry.type !== "file") {
|
|
3918
|
+
addMirrorDiagnostic(collector, "MOLDEA_MIRROR_NOT_FILE", manifestPath, mirrorPath, pointer, agentId, entry);
|
|
3919
|
+
return null;
|
|
3920
|
+
}
|
|
3921
|
+
const mirrorResult = await readRepositoryTextAsset(repository, mirrorPath, options.limits, signal);
|
|
3922
|
+
addDiagnostics$3(collector, mirrorResult.diagnostics);
|
|
3923
|
+
if (mirrorResult.asset === null) return null;
|
|
3924
|
+
if (mirrorResult.asset.content !== canonicalInstruction.content) {
|
|
3925
|
+
addMirrorDiagnostic(collector, "MOLDEA_MIRROR_STALE", manifestPath, mirrorPath, pointer, agentId);
|
|
3926
|
+
return null;
|
|
3927
|
+
}
|
|
3928
|
+
return {
|
|
3929
|
+
canonicalDigest: canonicalInstruction.digest,
|
|
3930
|
+
digest: mirrorResult.asset.digest,
|
|
3931
|
+
path: mirrorPath
|
|
3932
|
+
};
|
|
3933
|
+
};
|
|
3934
|
+
/**
|
|
3935
|
+
* Compares every declared mirror with its available normalized canonical instruction.
|
|
3936
|
+
* @param repository The budget-aware reader for the active inspection session.
|
|
3937
|
+
* @param manifestPath The canonical manifest path used for declaration diagnostics.
|
|
3938
|
+
* @param agents The registered agent assets and normalized instruction prerequisites.
|
|
3939
|
+
* @param options The immutable Core configuration snapshot.
|
|
3940
|
+
* @param signal Optional cancellation forwarded to every mirror lookup and read.
|
|
3941
|
+
* @returns Deeply immutable index-ready mirrors and deterministic diagnostics.
|
|
3942
|
+
* @throws
|
|
3943
|
+
* - INVALID_REPOSITORY_PATH: A repository path is invalid.
|
|
3944
|
+
* - ENTRY_NOT_FOUND: A confirmed mirror file disappeared from the reader snapshot.
|
|
3945
|
+
* - ENTRY_NOT_FILE: A confirmed mirror file changed type during inspection.
|
|
3946
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
3947
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
3948
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during mirror inspection.
|
|
3949
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
3950
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
3951
|
+
* - ABORTED: Mirror inspection or a repository operation was aborted.
|
|
3952
|
+
*/
|
|
3953
|
+
var inspectMirrors = async (repository, manifestPath, agents, options, signal) => {
|
|
3954
|
+
options = createCoreOperationOptionsSnapshot(options);
|
|
3955
|
+
const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
|
|
3956
|
+
const agentMirrors = [];
|
|
3957
|
+
const operationOptions = signal === void 0 ? void 0 : { signal };
|
|
3958
|
+
for (const agent of [...agents].sort((left, right) => compareExactStrings(left.id, right.id))) {
|
|
3959
|
+
const mirrors = [];
|
|
3960
|
+
if (agent.instruction !== null) {
|
|
3961
|
+
const declaredMirrors = [...agent.declaration.mirrors ?? []].sort(compareExactStrings);
|
|
3962
|
+
for (const [index, mirrorPath] of declaredMirrors.entries()) {
|
|
3963
|
+
const mirror = await inspectMirror(repository, manifestPath, agent.instruction, mirrorPath, `/agents/${escapeJsonPointerSegment(agent.id)}/mirrors/${index}`, agent.id, options, collector, operationOptions, signal);
|
|
3964
|
+
if (mirror !== null) mirrors.push(mirror);
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
agentMirrors.push({
|
|
3968
|
+
id: agent.id,
|
|
3969
|
+
mirrors
|
|
3970
|
+
});
|
|
3971
|
+
}
|
|
3972
|
+
const diagnostics = collector.finalize();
|
|
3973
|
+
return freezeRecursively({
|
|
3974
|
+
agentMirrors,
|
|
3975
|
+
diagnostics,
|
|
3976
|
+
valid: diagnostics.length === 0
|
|
3977
|
+
});
|
|
3978
|
+
};
|
|
3979
|
+
//#endregion
|
|
3980
|
+
//#region src/project-assets/index.ts
|
|
3981
|
+
var addDiagnostics$2 = (collector, diagnostics) => {
|
|
3982
|
+
for (const diagnostic of diagnostics) collector.merge(diagnostic);
|
|
3983
|
+
};
|
|
3984
|
+
/**
|
|
3985
|
+
* Reads and validates the project foundation before canonical enumeration.
|
|
3986
|
+
* @param repository The coherent source-neutral reader for the active inspection session.
|
|
3987
|
+
* @param path The canonical project path when its exact entry is a regular file.
|
|
3988
|
+
* @param options The immutable Core configuration snapshot.
|
|
3989
|
+
* @param signal Optional cancellation forwarded to the repository read.
|
|
3990
|
+
* @returns The deeply immutable project asset or deterministic content diagnostics.
|
|
3991
|
+
* @throws
|
|
3992
|
+
* - ENTRY_NOT_FOUND: The located project file disappeared from the snapshot.
|
|
3993
|
+
* - ENTRY_NOT_FILE: The located project entry changed type during inspection.
|
|
3994
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
3995
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
3996
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during the project read.
|
|
3997
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
3998
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
3999
|
+
* - ABORTED: Project inspection or the repository read was aborted.
|
|
4000
|
+
*/
|
|
4001
|
+
var readProjectFile = async (repository, path, options, signal) => {
|
|
4002
|
+
options = createCoreOperationOptionsSnapshot(options);
|
|
4003
|
+
const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
|
|
4004
|
+
let project = null;
|
|
4005
|
+
if (path !== null) {
|
|
4006
|
+
const result = await readRepositoryTextAsset(repository, path, options.limits, signal);
|
|
4007
|
+
addDiagnostics$2(collector, result.diagnostics);
|
|
4008
|
+
if (result.asset !== null) {
|
|
4009
|
+
if (hasNonWhitespace(result.asset.content)) project = result.asset;
|
|
4010
|
+
else collector.add({
|
|
4011
|
+
code: "MOLDEA_PROJECT_FILE_EMPTY",
|
|
4012
|
+
path: result.asset.path
|
|
4013
|
+
});
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
const diagnostics = collector.finalize();
|
|
4017
|
+
return freezeRecursively({
|
|
4018
|
+
diagnostics,
|
|
4019
|
+
project,
|
|
4020
|
+
valid: diagnostics.length === 0
|
|
4021
|
+
});
|
|
4022
|
+
};
|
|
4023
|
+
/**
|
|
4024
|
+
* Reads the project foundation and every discovered focused-context asset.
|
|
4025
|
+
* @param repository The coherent source-neutral reader for the active inspection session.
|
|
4026
|
+
* @param manifest The normalized manifest used to attach context relationships, when valid.
|
|
4027
|
+
* @param discovery The canonical inventory and path-owned structural diagnostics.
|
|
4028
|
+
* @param options The immutable Core configuration snapshot.
|
|
4029
|
+
* @param signal Optional cancellation forwarded to every repository read.
|
|
4030
|
+
* @param projectFile Optional project foundation already read before canonical enumeration.
|
|
4031
|
+
* @returns Deeply immutable valid assets and deterministic content diagnostics.
|
|
4032
|
+
* @throws
|
|
4033
|
+
* - ENTRY_NOT_FOUND: A discovered project or context file disappeared from the snapshot.
|
|
4034
|
+
* - ENTRY_NOT_FILE: A discovered project or context entry changed type during inspection.
|
|
4035
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
4036
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
4037
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during asset reads.
|
|
4038
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
4039
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
4040
|
+
* - ABORTED: Project asset inspection or a repository operation was aborted.
|
|
4041
|
+
*/
|
|
4042
|
+
var readProjectAssets = async (repository, manifest, discovery, options, signal, projectFile) => {
|
|
4043
|
+
options = createCoreOperationOptionsSnapshot(options);
|
|
4044
|
+
const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
|
|
4045
|
+
const context = [];
|
|
4046
|
+
const inspectedProject = projectFile ?? await readProjectFile(repository, discovery.inventory.project, options, signal);
|
|
4047
|
+
addDiagnostics$2(collector, inspectedProject.diagnostics);
|
|
4048
|
+
for (const path of [...discovery.inventory.context].sort(compareExactStrings)) {
|
|
4049
|
+
const result = await readRepositoryTextAsset(repository, path, options.limits, signal);
|
|
4050
|
+
addDiagnostics$2(collector, result.diagnostics);
|
|
4051
|
+
if (result.asset === null) continue;
|
|
4052
|
+
if (!hasNonWhitespace(result.asset.content)) {
|
|
4053
|
+
collector.add({
|
|
4054
|
+
code: "MOLDEA_CONTEXT_FILE_EMPTY",
|
|
4055
|
+
path
|
|
4056
|
+
});
|
|
4057
|
+
continue;
|
|
4058
|
+
}
|
|
4059
|
+
context.push({
|
|
4060
|
+
asset: result.asset,
|
|
4061
|
+
relationships: manifest?.context?.[path] ?? null
|
|
4062
|
+
});
|
|
4063
|
+
}
|
|
4064
|
+
const diagnostics = collector.finalize();
|
|
4065
|
+
return freezeRecursively({
|
|
4066
|
+
context,
|
|
4067
|
+
diagnostics,
|
|
4068
|
+
project: inspectedProject.project,
|
|
4069
|
+
valid: diagnostics.length === 0
|
|
4070
|
+
});
|
|
4071
|
+
};
|
|
4072
|
+
//#endregion
|
|
4073
|
+
//#region src/project-index/index.ts
|
|
4074
|
+
var createIndexedDecisions = (decisions, manifest) => {
|
|
4075
|
+
return [...decisions].sort((left, right) => compareExactStrings(left.id, right.id) || compareExactStrings(left.path, right.path)).map((decision) => ({
|
|
4076
|
+
decision,
|
|
4077
|
+
relationships: manifest.value.decisions?.[decision.path] ?? null
|
|
4078
|
+
}));
|
|
4079
|
+
};
|
|
4080
|
+
var createIndexedAgents = (agents, agentMirrors) => {
|
|
4081
|
+
const mirrorsByAgent = new Map(agentMirrors.map((entry) => [entry.id, entry.mirrors]));
|
|
4082
|
+
const indexedAgents = [];
|
|
4083
|
+
if (mirrorsByAgent.size !== agents.length) return null;
|
|
4084
|
+
for (const agent of [...agents].sort((left, right) => compareExactStrings(left.id, right.id))) {
|
|
4085
|
+
const mirrors = mirrorsByAgent.get(agent.id);
|
|
4086
|
+
if (agent.description === null || agent.instruction === null || mirrors === void 0) return null;
|
|
4087
|
+
indexedAgents.push({
|
|
4088
|
+
context: [...agent.declaration.context ?? []].sort(compareExactStrings),
|
|
4089
|
+
decisions: [...agent.declaration.decisions ?? []].sort(compareExactStrings),
|
|
4090
|
+
declaration: agent.declaration,
|
|
4091
|
+
description: agent.description,
|
|
4092
|
+
handoffDescription: agent.handoffDescription,
|
|
4093
|
+
id: agent.id,
|
|
4094
|
+
instruction: agent.instruction,
|
|
4095
|
+
mirrors: [...mirrors].sort((left, right) => compareExactStrings(left.path, right.path))
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
return indexedAgents;
|
|
4099
|
+
};
|
|
4100
|
+
/**
|
|
4101
|
+
* Constructs one deterministic provisional index from complete universal validation outputs.
|
|
4102
|
+
* @param input The normalized assets and relationships retained by universal inspection.
|
|
4103
|
+
* @returns The deeply immutable project index, or null when a required asset is unavailable.
|
|
4104
|
+
*/
|
|
4105
|
+
var createProjectIndex = (input) => {
|
|
4106
|
+
const agents = createIndexedAgents(input.agents, input.agentMirrors);
|
|
4107
|
+
if (agents === null) return null;
|
|
4108
|
+
const unresolved = input.manifest.value.unresolved ?? createNullPrototypeRecord([]);
|
|
4109
|
+
return freezeRecursively({
|
|
4110
|
+
agents,
|
|
4111
|
+
context: [...input.context].sort((left, right) => compareExactStrings(left.asset.path, right.asset.path)),
|
|
4112
|
+
decisions: createIndexedDecisions(input.decisions, input.manifest),
|
|
4113
|
+
formatVersion: 1,
|
|
4114
|
+
manifest: input.manifest,
|
|
4115
|
+
project: input.project,
|
|
4116
|
+
runtimes: [...input.runtimes].sort((left, right) => compareExactStrings(left.asset.path, right.asset.path)),
|
|
4117
|
+
unresolved
|
|
4118
|
+
});
|
|
4119
|
+
};
|
|
4120
|
+
//#endregion
|
|
4121
|
+
//#region src/repository-reference-validation/index.ts
|
|
4122
|
+
var childPointer = (pointer, segment) => {
|
|
4123
|
+
return `${pointer}/${escapeJsonPointerSegment(segment)}`;
|
|
4124
|
+
};
|
|
4125
|
+
var compareNullableStrings = (left, right) => {
|
|
4126
|
+
if (left === void 0) return right === void 0 ? 0 : -1;
|
|
4127
|
+
return right === void 0 ? 1 : compareExactStrings(left, right);
|
|
4128
|
+
};
|
|
4129
|
+
var compareReferences = (left, right) => {
|
|
4130
|
+
return compareExactStrings(left.path, right.path) || compareNullableStrings(left.symbol, right.symbol);
|
|
4131
|
+
};
|
|
4132
|
+
/** Adds sorted repository-reference array entries with their normalized indexes. */
|
|
4133
|
+
var addReferenceCandidates = (candidates, references, pointer, entity) => {
|
|
4134
|
+
for (const [index, reference] of [...references ?? []].sort(compareReferences).entries()) candidates.push({
|
|
4135
|
+
entity,
|
|
4136
|
+
kind: "reference",
|
|
4137
|
+
path: reference.path,
|
|
4138
|
+
pointer: childPointer(pointer, String(index))
|
|
4139
|
+
});
|
|
4140
|
+
};
|
|
4141
|
+
/** Adds sorted exact impact paths while retaining skipped glob indexes. */
|
|
4142
|
+
var addImpactCandidates = (candidates, affectedBy, pointer, entity) => {
|
|
4143
|
+
for (const [index, pattern] of [...affectedBy ?? []].sort(compareExactStrings).entries()) {
|
|
4144
|
+
if (pattern.includes("*")) continue;
|
|
4145
|
+
candidates.push({
|
|
4146
|
+
entity,
|
|
4147
|
+
kind: "impact",
|
|
4148
|
+
path: parseRepositoryPath(pattern),
|
|
4149
|
+
pointer: childPointer(pointer, String(index))
|
|
4150
|
+
});
|
|
4151
|
+
}
|
|
4152
|
+
};
|
|
4153
|
+
/** Adds repository-backed declarations owned by one context or decision relationship. */
|
|
4154
|
+
var addRelationshipCandidates = (candidates, relationship, pointer, entity) => {
|
|
4155
|
+
addReferenceCandidates(candidates, relationship.bindings, childPointer(pointer, "bindings"), entity);
|
|
4156
|
+
addImpactCandidates(candidates, relationship.affectedBy, childPointer(pointer, "affectedBy"), entity);
|
|
4157
|
+
};
|
|
4158
|
+
/** Adds related repository references from sorted unresolved requirements. */
|
|
4159
|
+
var addUnresolvedCandidates = (candidates, requirements, pointer, entity) => {
|
|
4160
|
+
for (const requirementId of Object.keys(requirements ?? {}).sort(compareExactStrings)) {
|
|
4161
|
+
const requirement = requirements?.[requirementId];
|
|
4162
|
+
if (requirement === void 0) continue;
|
|
4163
|
+
addReferenceCandidates(candidates, requirement.related, childPointer(childPointer(pointer, requirementId), "related"), entity);
|
|
4164
|
+
}
|
|
4165
|
+
};
|
|
4166
|
+
/** Adds one capability's implementation, optional bindings, and exact impact paths. */
|
|
4167
|
+
var addCapabilityCandidates = (candidates, capability, capabilityKind, capabilityId, agentId, collectionPointer, additionalReferences = []) => {
|
|
4168
|
+
const capabilityPointer = childPointer(collectionPointer, capabilityId);
|
|
4169
|
+
const capabilityEntity = {
|
|
4170
|
+
agentId,
|
|
4171
|
+
capabilityId,
|
|
4172
|
+
capabilityKind
|
|
4173
|
+
};
|
|
4174
|
+
candidates.push({
|
|
4175
|
+
entity: capabilityEntity,
|
|
4176
|
+
kind: `${capabilityKind}-implementation`,
|
|
4177
|
+
path: capability.implementation.path,
|
|
4178
|
+
pointer: childPointer(capabilityPointer, "implementation")
|
|
4179
|
+
});
|
|
4180
|
+
for (const [property, reference] of [["registration", capability.registration], ...additionalReferences]) if (reference !== void 0) candidates.push({
|
|
4181
|
+
entity: capabilityEntity,
|
|
4182
|
+
kind: "reference",
|
|
4183
|
+
path: reference.path,
|
|
4184
|
+
pointer: childPointer(capabilityPointer, property)
|
|
4185
|
+
});
|
|
4186
|
+
addImpactCandidates(candidates, capability.affectedBy, childPointer(capabilityPointer, "affectedBy"), capabilityEntity);
|
|
4187
|
+
};
|
|
4188
|
+
/** Adds all repository-backed declarations owned by one normalized agent. */
|
|
4189
|
+
var addAgentCandidates = (candidates, agentId, agent) => {
|
|
4190
|
+
const pointer = `/agents/${escapeJsonPointerSegment(agentId)}`;
|
|
4191
|
+
const entity = { agentId };
|
|
4192
|
+
const bindingsPointer = childPointer(pointer, "bindings");
|
|
4193
|
+
const bindings = agent.bindings;
|
|
4194
|
+
for (const property of [
|
|
4195
|
+
"runtimeAgent",
|
|
4196
|
+
"inputSchema",
|
|
4197
|
+
"outputSchema",
|
|
4198
|
+
"instructionLoader"
|
|
4199
|
+
]) {
|
|
4200
|
+
const reference = bindings?.[property];
|
|
4201
|
+
if (reference !== void 0) candidates.push({
|
|
4202
|
+
entity,
|
|
4203
|
+
kind: "reference",
|
|
4204
|
+
path: reference.path,
|
|
4205
|
+
pointer: childPointer(bindingsPointer, property)
|
|
4206
|
+
});
|
|
4207
|
+
}
|
|
4208
|
+
for (const variableId of Object.keys(bindings?.variableProviders ?? {}).sort(compareExactStrings)) {
|
|
4209
|
+
const reference = bindings?.variableProviders?.[variableId];
|
|
4210
|
+
if (reference !== void 0) candidates.push({
|
|
4211
|
+
entity: {
|
|
4212
|
+
agentId,
|
|
4213
|
+
variableId
|
|
4214
|
+
},
|
|
4215
|
+
kind: "reference",
|
|
4216
|
+
path: reference.path,
|
|
4217
|
+
pointer: childPointer(childPointer(bindingsPointer, "variableProviders"), variableId)
|
|
4218
|
+
});
|
|
4219
|
+
}
|
|
4220
|
+
addImpactCandidates(candidates, agent.affectedBy, childPointer(pointer, "affectedBy"), entity);
|
|
4221
|
+
const toolsPointer = childPointer(pointer, "tools");
|
|
4222
|
+
for (const capabilityId of Object.keys(agent.tools ?? {}).sort(compareExactStrings)) {
|
|
4223
|
+
const capability = agent.tools?.[capabilityId];
|
|
4224
|
+
if (capability !== void 0) addCapabilityCandidates(candidates, capability, "tool", capabilityId, agentId, toolsPointer, [["inputSchema", capability.inputSchema], ["outputSchema", capability.outputSchema]]);
|
|
4225
|
+
}
|
|
4226
|
+
const skillsPointer = childPointer(pointer, "skills");
|
|
4227
|
+
for (const capabilityId of Object.keys(agent.skills ?? {}).sort(compareExactStrings)) {
|
|
4228
|
+
const capability = agent.skills?.[capabilityId];
|
|
4229
|
+
if (capability !== void 0) addCapabilityCandidates(candidates, capability, "skill", capabilityId, agentId, skillsPointer);
|
|
4230
|
+
}
|
|
4231
|
+
addUnresolvedCandidates(candidates, agent.unresolved, childPointer(pointer, "unresolved"), entity);
|
|
4232
|
+
};
|
|
4233
|
+
/** Collects every repository-backed manifest declaration in deterministic pointer order. */
|
|
4234
|
+
var collectCandidates = (manifest) => {
|
|
4235
|
+
const candidates = [];
|
|
4236
|
+
for (const property of ["context", "decisions"]) {
|
|
4237
|
+
const relationships = manifest[property];
|
|
4238
|
+
for (const referencedPath of Object.keys(relationships ?? {}).sort(compareExactStrings)) {
|
|
4239
|
+
const relationship = relationships?.[referencedPath];
|
|
4240
|
+
if (relationship === void 0) continue;
|
|
4241
|
+
const pointer = childPointer(`/${property}`, referencedPath);
|
|
4242
|
+
const decisionId = property === "decisions" ? parseDecisionIdFromPath(parseRepositoryPath(referencedPath)) : null;
|
|
4243
|
+
addRelationshipCandidates(candidates, relationship, pointer, decisionId === null ? null : { decisionId });
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
addUnresolvedCandidates(candidates, manifest.unresolved, "/unresolved", null);
|
|
4247
|
+
for (const agentId of Object.keys(manifest.agents ?? {}).sort(compareExactStrings)) {
|
|
4248
|
+
const agent = manifest.agents?.[agentId];
|
|
4249
|
+
if (agent !== void 0) addAgentCandidates(candidates, agentId, agent);
|
|
4250
|
+
}
|
|
4251
|
+
return candidates.sort((left, right) => compareExactStrings(left.pointer, right.pointer));
|
|
4252
|
+
};
|
|
4253
|
+
/** Tests whether discovery already owns a diagnostic for a path or one of its ancestors. */
|
|
4254
|
+
var isBlockedPath$1 = (path, blockedPaths) => {
|
|
4255
|
+
let candidate = path;
|
|
4256
|
+
while (candidate.length > 0) {
|
|
4257
|
+
if (blockedPaths.has(candidate)) return true;
|
|
4258
|
+
const separatorIndex = candidate.lastIndexOf("/");
|
|
4259
|
+
if (separatorIndex <= 0) return false;
|
|
4260
|
+
candidate = candidate.slice(0, separatorIndex);
|
|
4261
|
+
}
|
|
4262
|
+
return false;
|
|
4263
|
+
};
|
|
4264
|
+
/** Creates normalized diagnostic details for one failed exact lookup. */
|
|
4265
|
+
var createCandidateDetails = (candidate, entry) => {
|
|
4266
|
+
const details = { [candidate.kind === "impact" ? "impactPath" : "referencedPath"]: candidate.path };
|
|
4267
|
+
if (entry !== null) details["actualType"] = entry.type;
|
|
4268
|
+
if (candidate.kind === "tool-implementation" || candidate.kind === "skill-implementation") details["reason"] = entry === null ? "missing" : entry.type === "symlink" ? "symlink" : "not-file";
|
|
4269
|
+
return details;
|
|
4270
|
+
};
|
|
4271
|
+
/** Selects the most-specific diagnostic code for one failed exact lookup. */
|
|
4272
|
+
var getCandidateCode = (candidate, entry) => {
|
|
4273
|
+
if (entry?.type === "file") return null;
|
|
4274
|
+
if (candidate.kind === "tool-implementation") return "MOLDEA_TOOL_IMPLEMENTATION_MISSING";
|
|
4275
|
+
if (candidate.kind === "skill-implementation") return "MOLDEA_SKILL_IMPLEMENTATION_MISSING";
|
|
4276
|
+
if (candidate.kind === "impact") return entry === null ? "MOLDEA_IMPACT_PATH_MISSING" : "MOLDEA_IMPACT_PATH_NOT_FILE";
|
|
4277
|
+
if (entry === null) return "MOLDEA_REFERENCE_MISSING";
|
|
4278
|
+
return entry.type === "symlink" ? "MOLDEA_REFERENCE_SYMLINK" : "MOLDEA_REFERENCE_NOT_FILE";
|
|
4279
|
+
};
|
|
4280
|
+
/** Adds the one most-specific diagnostic for a resolved repository candidate. */
|
|
4281
|
+
var validateCandidate = (candidate, entry, manifestPath, collector) => {
|
|
4282
|
+
const code = getCandidateCode(candidate, entry);
|
|
4283
|
+
if (code === null) return;
|
|
4284
|
+
collector.add({
|
|
4285
|
+
code,
|
|
4286
|
+
details: createCandidateDetails(candidate, entry),
|
|
4287
|
+
entity: candidate.entity,
|
|
4288
|
+
path: manifestPath,
|
|
4289
|
+
pointer: candidate.pointer
|
|
4290
|
+
});
|
|
4291
|
+
};
|
|
4292
|
+
/**
|
|
4293
|
+
* Validates every manifest repository reference, binding, and exact impact path.
|
|
4294
|
+
* @param repository The budget-aware reader for the active inspection session.
|
|
4295
|
+
* @param manifestPath The canonical manifest path used for diagnostics.
|
|
4296
|
+
* @param manifest The normalized valid manifest contract to inspect.
|
|
4297
|
+
* @param discovery Canonical discovery state used to suppress dependent path diagnostics.
|
|
4298
|
+
* @param limits The resource limits applied to emitted diagnostics.
|
|
4299
|
+
* @param signal Optional cancellation forwarded to exact repository lookups.
|
|
4300
|
+
* @returns Deeply immutable deterministic repository-reference diagnostics.
|
|
4301
|
+
* @throws
|
|
4302
|
+
* - INVALID_REPOSITORY_PATH: A repository path is invalid.
|
|
4303
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
4304
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
4305
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during validation.
|
|
4306
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
4307
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
4308
|
+
* - ABORTED: Repository inspection or a repository lookup was aborted.
|
|
4309
|
+
*/
|
|
4310
|
+
var validateRepositoryReferences = async (repository, manifestPath, manifest, discovery, limits, signal) => {
|
|
4311
|
+
const collector = createCoreDiagnosticCollector(limits, "inspect-project");
|
|
4312
|
+
const blockedPaths = new Set(discovery.diagnostics.flatMap((diagnostic) => diagnostic.path === null ? [] : [diagnostic.path]));
|
|
4313
|
+
const entryCache = /* @__PURE__ */ new Map();
|
|
4314
|
+
const operationOptions = signal === void 0 ? void 0 : { signal };
|
|
4315
|
+
for (const candidate of collectCandidates(manifest)) {
|
|
4316
|
+
if (isBlockedPath$1(candidate.path, blockedPaths)) continue;
|
|
4317
|
+
let entryPromise = entryCache.get(candidate.path);
|
|
4318
|
+
if (entryPromise === void 0) {
|
|
4319
|
+
entryPromise = repository.getEntry(candidate.path, operationOptions);
|
|
4320
|
+
entryCache.set(candidate.path, entryPromise);
|
|
4321
|
+
}
|
|
4322
|
+
validateCandidate(candidate, await entryPromise, manifestPath, collector);
|
|
4323
|
+
}
|
|
4324
|
+
return collector.finalize();
|
|
4325
|
+
};
|
|
4326
|
+
//#endregion
|
|
4327
|
+
//#region src/runtime-guidance/index.ts
|
|
4328
|
+
var addDiagnostics$1 = (collector, diagnostics) => {
|
|
4329
|
+
for (const diagnostic of diagnostics) collector.merge(diagnostic);
|
|
4330
|
+
};
|
|
4331
|
+
var isBlockedPath = (path, blockedPaths) => {
|
|
4332
|
+
let candidate = path;
|
|
4333
|
+
while (candidate.length > 0) {
|
|
4334
|
+
if (blockedPaths.has(candidate)) return true;
|
|
4335
|
+
const separatorIndex = candidate.lastIndexOf("/");
|
|
4336
|
+
if (separatorIndex <= 0) return false;
|
|
4337
|
+
candidate = candidate.slice(0, separatorIndex);
|
|
4338
|
+
}
|
|
4339
|
+
return false;
|
|
4340
|
+
};
|
|
4341
|
+
/**
|
|
4342
|
+
* Reads all discovered runtime guidance and validates every manifest guidance relationship.
|
|
4343
|
+
* @param repository The coherent source-neutral repository reader.
|
|
4344
|
+
* @param manifestPath The canonical manifest path used for relationship diagnostics.
|
|
4345
|
+
* @param manifest The normalized manifest declaring optional agent guidance paths, when valid.
|
|
4346
|
+
* @param discovery The canonical inventory and path-owned structural diagnostics.
|
|
4347
|
+
* @param options The immutable Core configuration snapshot.
|
|
4348
|
+
* @param signal Optional cancellation forwarded to every repository read.
|
|
4349
|
+
* @returns Deeply immutable valid guidance assets and deterministic diagnostics.
|
|
4350
|
+
* @throws
|
|
4351
|
+
* - ENTRY_NOT_FOUND: A discovered guidance file disappeared from the reader snapshot.
|
|
4352
|
+
* - ENTRY_NOT_FILE: A discovered guidance entry is no longer a regular file.
|
|
4353
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
4354
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
4355
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during guidance reads.
|
|
4356
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
4357
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
4358
|
+
* - ABORTED: Guidance inspection or a repository operation was aborted.
|
|
4359
|
+
*/
|
|
4360
|
+
var readRuntimeGuidance = async (repository, manifestPath, manifest, discovery, options, signal) => {
|
|
4361
|
+
options = createCoreOperationOptionsSnapshot(options);
|
|
4362
|
+
const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
|
|
4363
|
+
const runtimes = [];
|
|
4364
|
+
const discoveredPaths = new Set(discovery.inventory.runtimeGuidance);
|
|
4365
|
+
const blockedPaths = new Set(discovery.diagnostics.flatMap((diagnostic) => diagnostic.path === null ? [] : [diagnostic.path]));
|
|
4366
|
+
for (const path of [...discoveredPaths].sort(compareExactStrings)) {
|
|
4367
|
+
const result = await readRepositoryTextAsset(repository, path, options.limits, signal);
|
|
4368
|
+
addDiagnostics$1(collector, result.diagnostics);
|
|
4369
|
+
if (result.asset === null) continue;
|
|
4370
|
+
if (!hasNonWhitespace(result.asset.content)) {
|
|
4371
|
+
collector.add({
|
|
4372
|
+
code: "MOLDEA_RUNTIME_GUIDANCE_EMPTY",
|
|
4373
|
+
path
|
|
4374
|
+
});
|
|
4375
|
+
continue;
|
|
4376
|
+
}
|
|
4377
|
+
runtimes.push({ asset: result.asset });
|
|
4378
|
+
}
|
|
4379
|
+
for (const agentId of Object.keys(manifest?.agents ?? {}).sort(compareExactStrings)) {
|
|
4380
|
+
const guidance = manifest?.agents?.[agentId]?.runtime.guidance;
|
|
4381
|
+
if (guidance === void 0 || discoveredPaths.has(guidance) || isBlockedPath(guidance, blockedPaths)) continue;
|
|
4382
|
+
collector.add({
|
|
4383
|
+
code: "MOLDEA_RUNTIME_GUIDANCE_MISSING",
|
|
4384
|
+
details: { referencedPath: guidance },
|
|
4385
|
+
entity: { agentId },
|
|
4386
|
+
path: manifestPath,
|
|
4387
|
+
pointer: `/agents/${escapeJsonPointerSegment(agentId)}/runtime/guidance`
|
|
4388
|
+
});
|
|
4389
|
+
}
|
|
4390
|
+
const diagnostics = collector.finalize();
|
|
4391
|
+
return freezeRecursively({
|
|
4392
|
+
diagnostics,
|
|
4393
|
+
runtimes,
|
|
4394
|
+
valid: diagnostics.length === 0
|
|
4395
|
+
});
|
|
4396
|
+
};
|
|
4397
|
+
//#endregion
|
|
4398
|
+
//#region src/universal-project-inspection/index.ts
|
|
4399
|
+
var MOLDEA_ROOT = parseRepositoryPath("/moldea");
|
|
4400
|
+
var MANIFEST_PATH = parseRepositoryPath("/moldea/moldea.yaml");
|
|
4401
|
+
var PROJECT_PATH = parseRepositoryPath("/moldea/project.md");
|
|
4402
|
+
var addDiagnostics = (collector, diagnostics) => {
|
|
4403
|
+
for (const diagnostic of diagnostics) collector.merge(diagnostic);
|
|
4404
|
+
};
|
|
4405
|
+
/**
|
|
4406
|
+
* Executes every universal repository-format phase before runtime adapter validation.
|
|
4407
|
+
* @param context The shared inspection session and optional cancellation signal.
|
|
4408
|
+
* @param options The immutable Core configuration snapshot.
|
|
4409
|
+
* @returns The frozen provisional index only when no universal diagnostic remains.
|
|
4410
|
+
* @throws
|
|
4411
|
+
* - INVALID_REPOSITORY_PATH: A repository path is invalid.
|
|
4412
|
+
* - ENTRY_NOT_FOUND: A discovered file disappeared from the reader snapshot.
|
|
4413
|
+
* - ENTRY_NOT_FILE: A discovered file changed type during inspection.
|
|
4414
|
+
* - ENTRY_NOT_DIRECTORY: A discovered directory changed type during inspection.
|
|
4415
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
4416
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
4417
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during inspection.
|
|
4418
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
4419
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
4420
|
+
* - ABORTED: Project inspection or a repository operation was aborted.
|
|
4421
|
+
*/
|
|
4422
|
+
var inspectUniversalProject = async (context, options) => {
|
|
4423
|
+
options = createCoreOperationOptionsSnapshot(options);
|
|
4424
|
+
const { session, signal } = context;
|
|
4425
|
+
const collector = createCoreDiagnosticCollector(options.limits, "inspect-project");
|
|
4426
|
+
const operationOptions = signal === void 0 ? void 0 : { signal };
|
|
4427
|
+
const moldeaRoot = await session.reader.getEntry(MOLDEA_ROOT, operationOptions);
|
|
4428
|
+
let manifestEntry = null;
|
|
4429
|
+
let projectEntry = null;
|
|
4430
|
+
let indexedManifest = null;
|
|
4431
|
+
let formatVersion = null;
|
|
4432
|
+
let runtimeLocations = [];
|
|
4433
|
+
if (moldeaRoot?.type === "directory") {
|
|
4434
|
+
manifestEntry = await session.reader.getEntry(MANIFEST_PATH, operationOptions);
|
|
4435
|
+
if (manifestEntry?.type === "file") {
|
|
4436
|
+
const parsedManifest = await inspectManifestDocument({
|
|
4437
|
+
content: await session.reader.readFile(MANIFEST_PATH, operationOptions),
|
|
4438
|
+
path: MANIFEST_PATH
|
|
4439
|
+
}, options, "inspect-project");
|
|
4440
|
+
formatVersion = parsedManifest.formatVersion;
|
|
4441
|
+
runtimeLocations = parsedManifest.runtimeLocations;
|
|
4442
|
+
addDiagnostics(collector, parsedManifest.diagnostics);
|
|
4443
|
+
if (parsedManifest.asset !== null && parsedManifest.manifest !== null) indexedManifest = {
|
|
4444
|
+
asset: parsedManifest.asset,
|
|
4445
|
+
value: parsedManifest.manifest
|
|
4446
|
+
};
|
|
4447
|
+
}
|
|
4448
|
+
projectEntry = await session.reader.getEntry(PROJECT_PATH, operationOptions);
|
|
4449
|
+
}
|
|
4450
|
+
const projectFile = await readProjectFile(session.reader, projectEntry?.type === "file" ? PROJECT_PATH : null, options, signal);
|
|
4451
|
+
addDiagnostics(collector, projectFile.diagnostics);
|
|
4452
|
+
const discovery = await discoverCanonicalAssets(session.reader, options.limits, signal, {
|
|
4453
|
+
manifest: manifestEntry,
|
|
4454
|
+
moldeaRoot,
|
|
4455
|
+
project: projectEntry
|
|
4456
|
+
});
|
|
4457
|
+
addDiagnostics(collector, discovery.diagnostics);
|
|
4458
|
+
session.throwIfAborted();
|
|
4459
|
+
const projectAssets = await readProjectAssets(session.reader, indexedManifest?.value ?? null, discovery, options, signal, projectFile);
|
|
4460
|
+
addDiagnostics(collector, projectAssets.diagnostics);
|
|
4461
|
+
const runtimeGuidance = await readRuntimeGuidance(session.reader, discovery.inventory.manifest ?? MANIFEST_PATH, indexedManifest?.value ?? null, discovery, options, signal);
|
|
4462
|
+
addDiagnostics(collector, runtimeGuidance.diagnostics);
|
|
4463
|
+
const decisionGraph = await readDecisionGraph(session.reader, discovery.inventory.decisions, options, signal);
|
|
4464
|
+
addDiagnostics(collector, decisionGraph.diagnostics);
|
|
4465
|
+
let agents = [];
|
|
4466
|
+
let agentMirrors = [];
|
|
4467
|
+
if (indexedManifest !== null) {
|
|
4468
|
+
const manifestPath = indexedManifest.asset.path;
|
|
4469
|
+
const agentAssets = await inspectAgentAssets(session.reader, indexedManifest.value, discovery, options, signal);
|
|
4470
|
+
agents = agentAssets.agents;
|
|
4471
|
+
addDiagnostics(collector, agentAssets.diagnostics);
|
|
4472
|
+
addDiagnostics(collector, validateManifestRelationships(manifestPath, indexedManifest.value, discovery, decisionGraph.decisions, options.limits));
|
|
4473
|
+
addDiagnostics(collector, await validateRepositoryReferences(session.reader, manifestPath, indexedManifest.value, discovery, options.limits, signal));
|
|
4474
|
+
const mirrorInspection = await inspectMirrors(session.reader, manifestPath, agents, options, signal);
|
|
4475
|
+
agentMirrors = mirrorInspection.agentMirrors;
|
|
4476
|
+
addDiagnostics(collector, mirrorInspection.diagnostics);
|
|
4477
|
+
}
|
|
4478
|
+
session.throwIfAborted();
|
|
4479
|
+
const diagnostics = collector.finalize();
|
|
4480
|
+
const project = diagnostics.length === 0 && indexedManifest !== null && projectAssets.project !== null ? createProjectIndex({
|
|
4481
|
+
agentMirrors,
|
|
4482
|
+
agents,
|
|
4483
|
+
context: projectAssets.context,
|
|
4484
|
+
decisions: decisionGraph.decisions,
|
|
4485
|
+
manifest: indexedManifest,
|
|
4486
|
+
project: projectAssets.project,
|
|
4487
|
+
runtimes: runtimeGuidance.runtimes
|
|
4488
|
+
}) : null;
|
|
4489
|
+
return freezeRecursively({
|
|
4490
|
+
diagnostics,
|
|
4491
|
+
formatVersion,
|
|
4492
|
+
project,
|
|
4493
|
+
runtimeLocations
|
|
4494
|
+
});
|
|
4495
|
+
};
|
|
4496
|
+
//#endregion
|
|
4497
|
+
//#region src/project-inspection/index.ts
|
|
4498
|
+
var isRecord = (candidate) => {
|
|
4499
|
+
return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
|
|
4500
|
+
};
|
|
4501
|
+
var invalidArgument = () => {
|
|
4502
|
+
throw new CoreOperationException({
|
|
4503
|
+
code: "INVALID_ARGUMENT",
|
|
4504
|
+
operation: "inspect-project"
|
|
4505
|
+
});
|
|
4506
|
+
};
|
|
4507
|
+
var isRepositoryReader = (candidate) => {
|
|
4508
|
+
return isRecord(candidate) && typeof candidate["getEntry"] === "function" && typeof candidate["listEntries"] === "function" && typeof candidate["readFile"] === "function";
|
|
4509
|
+
};
|
|
4510
|
+
var isAbortSignal = (candidate) => {
|
|
4511
|
+
return isRecord(candidate) && typeof candidate["aborted"] === "boolean" && typeof candidate["addEventListener"] === "function" && typeof candidate["removeEventListener"] === "function";
|
|
4512
|
+
};
|
|
4513
|
+
var validateInput = (candidate) => {
|
|
4514
|
+
try {
|
|
4515
|
+
if (!isRecord(candidate)) return invalidArgument();
|
|
4516
|
+
const repository = candidate["repository"];
|
|
4517
|
+
const signal = candidate["signal"];
|
|
4518
|
+
if (!isRepositoryReader(repository) || signal !== void 0 && !isAbortSignal(signal)) return invalidArgument();
|
|
4519
|
+
return {
|
|
4520
|
+
repository,
|
|
4521
|
+
...signal === void 0 ? {} : { signal }
|
|
4522
|
+
};
|
|
4523
|
+
} catch (error) {
|
|
4524
|
+
if (error instanceof CoreOperationException) throw error;
|
|
4525
|
+
return invalidArgument();
|
|
4526
|
+
}
|
|
4527
|
+
};
|
|
4528
|
+
/**
|
|
4529
|
+
* Inspects one coherent repository snapshot through universal and adapter validation.
|
|
4530
|
+
* @param input The untrusted source-neutral reader and optional cancellation signal.
|
|
4531
|
+
* @param options The immutable Core configuration snapshot.
|
|
4532
|
+
* @returns A promise resolving to the frozen all-or-nothing project inspection result.
|
|
4533
|
+
* @throws
|
|
4534
|
+
* - INVALID_ARGUMENT: The Core operation received an invalid argument.
|
|
4535
|
+
* - INVALID_REPOSITORY_PATH: A repository path is invalid.
|
|
4536
|
+
* - ENTRY_NOT_FOUND: A discovered file disappeared from the reader snapshot.
|
|
4537
|
+
* - ENTRY_NOT_FILE: A discovered file changed type during inspection.
|
|
4538
|
+
* - ENTRY_NOT_DIRECTORY: A discovered directory changed type during inspection.
|
|
4539
|
+
* - ACCESS_DENIED: Access to the repository source was denied.
|
|
4540
|
+
* - SOURCE_UNAVAILABLE: The repository source is unavailable.
|
|
4541
|
+
* - SNAPSHOT_CHANGED: The repository snapshot changed during inspection.
|
|
4542
|
+
* - INVALID_SOURCE_DATA: The repository reader returned invalid contract data.
|
|
4543
|
+
* - RESOURCE_LIMIT_EXCEEDED: A Core or repository resource limit was exceeded.
|
|
4544
|
+
* - ABORTED: Project inspection or a repository operation was aborted.
|
|
4545
|
+
* - ADAPTER_EXECUTION_FAILED: A runtime adapter failed or returned an invalid result.
|
|
4546
|
+
*/
|
|
4547
|
+
var inspectProject = async (input, options) => {
|
|
4548
|
+
const validatedInput = validateInput(input);
|
|
4549
|
+
const operationOptions = createCoreOperationOptionsSnapshot(options);
|
|
4550
|
+
const session = createRepositoryInspectionSession(validatedInput.repository, operationOptions.limits, validatedInput.signal);
|
|
4551
|
+
const universal = await inspectUniversalProject({
|
|
4552
|
+
session,
|
|
4553
|
+
...validatedInput.signal === void 0 ? {} : { signal: validatedInput.signal }
|
|
4554
|
+
}, operationOptions);
|
|
4555
|
+
if (universal.project === null) return freezeRecursively({
|
|
4556
|
+
diagnostics: universal.diagnostics,
|
|
4557
|
+
evidence: [],
|
|
4558
|
+
formatVersion: universal.formatVersion,
|
|
4559
|
+
project: null,
|
|
4560
|
+
valid: false
|
|
4561
|
+
});
|
|
4562
|
+
const availabilityDiagnostics = validateRuntimeAdapterAvailability(universal.runtimeLocations, universal.project.formatVersion, operationOptions);
|
|
4563
|
+
if (availabilityDiagnostics.length > 0) return freezeRecursively({
|
|
4564
|
+
diagnostics: availabilityDiagnostics,
|
|
4565
|
+
evidence: [],
|
|
4566
|
+
formatVersion: universal.formatVersion,
|
|
4567
|
+
project: null,
|
|
4568
|
+
valid: false
|
|
4569
|
+
});
|
|
4570
|
+
const adapterInspection = await inspectRuntimeAdapters(universal.project, session, operationOptions, validatedInput.signal);
|
|
4571
|
+
const valid = adapterInspection.diagnostics.length === 0;
|
|
4572
|
+
return freezeRecursively({
|
|
4573
|
+
diagnostics: adapterInspection.diagnostics,
|
|
4574
|
+
evidence: adapterInspection.evidence,
|
|
4575
|
+
formatVersion: universal.formatVersion,
|
|
4576
|
+
project: valid ? universal.project : null,
|
|
4577
|
+
valid
|
|
4578
|
+
});
|
|
4579
|
+
};
|
|
4580
|
+
//#endregion
|
|
4581
|
+
//#region src/core/index.ts
|
|
4582
|
+
/**
|
|
4583
|
+
* Creates one immutable Core instance from detached configuration snapshots.
|
|
4584
|
+
* @param options Optional runtime adapters and resource-limit overrides.
|
|
4585
|
+
* @returns A frozen Core instance safe for concurrent independent operations.
|
|
4586
|
+
* @throws
|
|
4587
|
+
* - DUPLICATE_ADAPTER_ID: A runtime adapter ID is registered more than once.
|
|
4588
|
+
* - RESERVED_ADAPTER_ID: A reserved runtime adapter ID was supplied.
|
|
4589
|
+
* - INVALID_ADAPTER_DEFINITION: A runtime adapter definition is invalid.
|
|
4590
|
+
* - INVALID_RESOURCE_LIMIT: A Core resource limit is invalid.
|
|
4591
|
+
*/
|
|
4592
|
+
var createCore = (options) => {
|
|
4593
|
+
const snapshot = normalizeCoreOptions(options);
|
|
4594
|
+
return freezeRecursively({
|
|
4595
|
+
calculateContentDigest: (input) => {
|
|
4596
|
+
return calculateContentDigest(input, createCoreOperationOptionsSnapshot(snapshot).limits);
|
|
4597
|
+
},
|
|
4598
|
+
inspectProject: (input) => inspectProject(input, createCoreOperationOptionsSnapshot(snapshot)),
|
|
4599
|
+
normalizeText: (input) => {
|
|
4600
|
+
return normalizeTextDocument(input, createCoreOperationOptionsSnapshot(snapshot).limits, "normalize-text");
|
|
4601
|
+
},
|
|
4602
|
+
parseDecision: (input) => parseDecisionDocument(input, createCoreOperationOptionsSnapshot(snapshot)),
|
|
4603
|
+
parseManifest: (input) => parseManifestDocument(input, createCoreOperationOptionsSnapshot(snapshot))
|
|
4604
|
+
});
|
|
4605
|
+
};
|
|
4606
|
+
//#endregion
|
|
4607
|
+
export { CoreConfigurationException, CoreOperationException, DEFAULT_CORE_RESOURCE_LIMITS, SUPPORTED_REPOSITORY_FORMAT_VERSIONS, createCore };
|
|
4608
|
+
|
|
4609
|
+
//# sourceMappingURL=index.js.map
|