@agent-surface/compiler 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-BNIUE5RA.js +746 -0
- package/dist/chunk-BNIUE5RA.js.map +1 -0
- package/dist/compile-RPDITMVH.js +7 -0
- package/dist/compile-RPDITMVH.js.map +1 -0
- package/dist/index.js +12 -664
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,668 +1,16 @@
|
|
|
1
|
-
// src/canonical.ts
|
|
2
|
-
import { createHash } from "crypto";
|
|
3
|
-
function normalize(value) {
|
|
4
|
-
if (Array.isArray(value)) return value.map(normalize);
|
|
5
|
-
if (typeof value !== "object" || value === null) return value;
|
|
6
|
-
return Object.fromEntries(
|
|
7
|
-
Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => [key, normalize(entry)])
|
|
8
|
-
);
|
|
9
|
-
}
|
|
10
|
-
function canonicalJson(value, pretty = false) {
|
|
11
|
-
return `${JSON.stringify(normalize(value), null, pretty ? 2 : void 0)}
|
|
12
|
-
`;
|
|
13
|
-
}
|
|
14
|
-
function sha256(value) {
|
|
15
|
-
return createHash("sha256").update(value).digest("hex");
|
|
16
|
-
}
|
|
17
|
-
function manifestPayload(manifest) {
|
|
18
|
-
const { hash: _hash, ...payload } = manifest;
|
|
19
|
-
return payload;
|
|
20
|
-
}
|
|
21
|
-
function computeManifestHash(manifest) {
|
|
22
|
-
return sha256(canonicalJson(manifestPayload(manifest)));
|
|
23
|
-
}
|
|
24
|
-
function canonicalManifestJson(manifest) {
|
|
25
|
-
return canonicalJson(manifest, true);
|
|
26
|
-
}
|
|
27
|
-
function verifyManifest(manifest) {
|
|
28
|
-
if (computeManifestHash(manifest) !== manifest.hash) {
|
|
29
|
-
throw new Error(`capability contract hash mismatch: ${manifest.hash}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// src/plugin.ts
|
|
34
|
-
import { existsSync, readFileSync } from "fs";
|
|
35
|
-
import { dirname, isAbsolute, join, relative as relative2, resolve as resolve2, sep } from "path";
|
|
36
1
|
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
var WRAPPERS = /* @__PURE__ */ new Set([
|
|
50
|
-
"observationContract",
|
|
51
|
-
"actionContract",
|
|
52
|
-
"fromJsonSchema",
|
|
53
|
-
"defineAgentComponentContract",
|
|
54
|
-
"defineAgentProcedureContract",
|
|
55
|
-
"defineExternalAgentToolContract"
|
|
56
|
-
]);
|
|
57
|
-
function sourceKind(id) {
|
|
58
|
-
if (/\.tsx(?:\?|$)/.test(id)) return ts.ScriptKind.TSX;
|
|
59
|
-
if (/\.jsx(?:\?|$)/.test(id)) return ts.ScriptKind.JSX;
|
|
60
|
-
if (/\.js(?:\?|$)/.test(id)) return ts.ScriptKind.JS;
|
|
61
|
-
return ts.ScriptKind.TS;
|
|
62
|
-
}
|
|
63
|
-
function unwrap(node) {
|
|
64
|
-
while (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isTypeAssertionExpression(node) || ts.isSatisfiesExpression(node) || ts.isNonNullExpression(node)) {
|
|
65
|
-
node = node.expression;
|
|
66
|
-
}
|
|
67
|
-
return node;
|
|
68
|
-
}
|
|
69
|
-
function propertyName(node) {
|
|
70
|
-
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) {
|
|
71
|
-
return node.text;
|
|
72
|
-
}
|
|
73
|
-
if (ts.isComputedPropertyName(node) && ts.isStringLiteral(unwrap(node.expression))) {
|
|
74
|
-
return unwrap(node.expression).text;
|
|
75
|
-
}
|
|
76
|
-
return void 0;
|
|
77
|
-
}
|
|
78
|
-
function calleeName(node) {
|
|
79
|
-
node = unwrap(node);
|
|
80
|
-
if (ts.isIdentifier(node)) return node.text;
|
|
81
|
-
if (ts.isPropertyAccessExpression(node)) return node.name.text;
|
|
82
|
-
if (ts.isElementAccessExpression(node) && node.argumentExpression && ts.isStringLiteral(unwrap(node.argumentExpression))) {
|
|
83
|
-
return unwrap(node.argumentExpression).text;
|
|
84
|
-
}
|
|
85
|
-
return void 0;
|
|
86
|
-
}
|
|
87
|
-
function normalizedOrigin(root, id) {
|
|
88
|
-
const clean = id.replace(/^\0/, "virtual:").split("?")[0];
|
|
89
|
-
if (clean.startsWith("virtual:")) return clean;
|
|
90
|
-
const path = clean.startsWith("/@fs/") ? clean.slice(4) : clean;
|
|
91
|
-
return posix.normalize(relative(root, resolve(path)).split("\\").join("/"));
|
|
92
|
-
}
|
|
93
|
-
var StaticEvaluator = class {
|
|
94
|
-
constructor(file) {
|
|
95
|
-
this.file = file;
|
|
96
|
-
const visit = (node) => {
|
|
97
|
-
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
|
98
|
-
this.constants.set(node.name.text, node.initializer);
|
|
99
|
-
}
|
|
100
|
-
ts.forEachChild(node, visit);
|
|
101
|
-
};
|
|
102
|
-
visit(file);
|
|
103
|
-
}
|
|
104
|
-
file;
|
|
105
|
-
constants = /* @__PURE__ */ new Map();
|
|
106
|
-
active = /* @__PURE__ */ new Set();
|
|
107
|
-
evaluate(input, where) {
|
|
108
|
-
const node = unwrap(input);
|
|
109
|
-
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
|
|
110
|
-
if (ts.isNumericLiteral(node)) return Number(node.text);
|
|
111
|
-
if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
|
|
112
|
-
if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
|
|
113
|
-
if (node.kind === ts.SyntaxKind.NullKeyword) return null;
|
|
114
|
-
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken) {
|
|
115
|
-
const value = this.evaluate(node.operand, where);
|
|
116
|
-
if (typeof value === "number") return -value;
|
|
117
|
-
}
|
|
118
|
-
if (ts.isArrayLiteralExpression(node)) {
|
|
119
|
-
return node.elements.map((entry, index) => {
|
|
120
|
-
if (ts.isSpreadElement(entry)) {
|
|
121
|
-
const spread = this.evaluate(entry.expression, `${where}[${index}]`);
|
|
122
|
-
if (!Array.isArray(spread)) throw this.failure(entry, `${where}: array spread is not static`);
|
|
123
|
-
return spread;
|
|
124
|
-
}
|
|
125
|
-
return this.evaluate(entry, `${where}[${index}]`);
|
|
126
|
-
}).flat();
|
|
127
|
-
}
|
|
128
|
-
if (ts.isObjectLiteralExpression(node)) {
|
|
129
|
-
const result = {};
|
|
130
|
-
for (const property of node.properties) {
|
|
131
|
-
if (ts.isSpreadAssignment(property)) {
|
|
132
|
-
const spread = this.evaluate(property.expression, `${where} spread`);
|
|
133
|
-
if (typeof spread !== "object" || spread === null || Array.isArray(spread)) {
|
|
134
|
-
throw this.failure(property, `${where}: object spread is not static`);
|
|
135
|
-
}
|
|
136
|
-
Object.assign(result, spread);
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (ts.isShorthandPropertyAssignment(property)) {
|
|
140
|
-
result[property.name.text] = this.evaluate(property.name, `${where}.${property.name.text}`);
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (!ts.isPropertyAssignment(property)) {
|
|
144
|
-
throw this.failure(property, `${where}: methods/accessors are runtime code`);
|
|
145
|
-
}
|
|
146
|
-
const name = propertyName(property.name);
|
|
147
|
-
if (name === void 0) throw this.failure(property.name, `${where}: computed key is not static`);
|
|
148
|
-
result[name] = this.evaluate(property.initializer, `${where}.${name}`);
|
|
149
|
-
}
|
|
150
|
-
return result;
|
|
151
|
-
}
|
|
152
|
-
if (ts.isIdentifier(node)) {
|
|
153
|
-
if (node.text === "undefined") throw this.failure(node, `${where}: undefined is not serializable`);
|
|
154
|
-
if (node.text === "emptyObjectSchema") {
|
|
155
|
-
return { type: "object", properties: {}, additionalProperties: false };
|
|
156
|
-
}
|
|
157
|
-
const value = this.constants.get(node.text);
|
|
158
|
-
if (!value) throw this.failure(node, `${where}: imported/dynamic value "${node.text}" is not static`);
|
|
159
|
-
if (this.active.has(node.text)) throw this.failure(node, `${where}: cyclic static value "${node.text}"`);
|
|
160
|
-
this.active.add(node.text);
|
|
161
|
-
try {
|
|
162
|
-
return this.evaluate(value, where);
|
|
163
|
-
} finally {
|
|
164
|
-
this.active.delete(node.text);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (ts.isCallExpression(node)) {
|
|
168
|
-
const name = calleeName(node.expression);
|
|
169
|
-
if (name && WRAPPERS.has(name) && node.arguments[0]) {
|
|
170
|
-
return this.evaluate(node.arguments[0], where);
|
|
171
|
-
}
|
|
172
|
-
throw this.failure(node, `${where}: call "${name ?? "<computed>"}" is not a compiler macro`);
|
|
173
|
-
}
|
|
174
|
-
if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) {
|
|
175
|
-
const owner = this.evaluate(node.expression, where);
|
|
176
|
-
const name = ts.isPropertyAccessExpression(node) ? node.name.text : node.argumentExpression && ts.isStringLiteral(unwrap(node.argumentExpression)) ? unwrap(node.argumentExpression).text : void 0;
|
|
177
|
-
if (name !== void 0 && typeof owner === "object" && owner !== null && !Array.isArray(owner)) {
|
|
178
|
-
const value = owner[name];
|
|
179
|
-
if (value !== void 0) return value;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
throw this.failure(node, `${where}: expression is dynamic or non-serializable`);
|
|
183
|
-
}
|
|
184
|
-
failure(node, message) {
|
|
185
|
-
const position = this.file.getLineAndCharacterOfPosition(node.getStart(this.file));
|
|
186
|
-
return new Error(`${this.file.fileName}:${position.line + 1}:${position.character + 1}: ${message}`);
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
function record(value, where) {
|
|
190
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
191
|
-
throw new Error(`${where} must be a static object`);
|
|
192
|
-
}
|
|
193
|
-
return value;
|
|
194
|
-
}
|
|
195
|
-
function string(value, where) {
|
|
196
|
-
if (typeof value !== "string" || value.length === 0) throw new Error(`${where} must be a string`);
|
|
197
|
-
return value;
|
|
198
|
-
}
|
|
199
|
-
function policies(value) {
|
|
200
|
-
if (value === void 0) return void 0;
|
|
201
|
-
if (!Array.isArray(value)) throw new Error("policies must be a static array");
|
|
202
|
-
return value.map((entry) => record(entry, "policy"));
|
|
203
|
-
}
|
|
204
|
-
function combine(inherited, local) {
|
|
205
|
-
const values = [...inherited ?? [], ...local ?? []];
|
|
206
|
-
if (values.length === 0) return void 0;
|
|
207
|
-
const unique = new Map(values.map((value) => [canonicalJson(value), value]));
|
|
208
|
-
return [...unique.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([, value]) => value);
|
|
209
|
-
}
|
|
210
|
-
function entryHash(entry) {
|
|
211
|
-
return sha256(canonicalJson(entry));
|
|
212
|
-
}
|
|
213
|
-
function declarationVariable(call) {
|
|
214
|
-
let node = call;
|
|
215
|
-
while (node.parent && (ts.isParenthesizedExpression(node.parent) || ts.isAsExpression(node.parent) || ts.isSatisfiesExpression(node.parent))) {
|
|
216
|
-
node = node.parent;
|
|
217
|
-
}
|
|
218
|
-
return node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name) ? node.parent.name.text : void 0;
|
|
219
|
-
}
|
|
220
|
-
function isMacroCall(call, names, namespaces, aliases) {
|
|
221
|
-
const expression = unwrap(call.expression);
|
|
222
|
-
let imported;
|
|
223
|
-
if (ts.isIdentifier(expression)) imported = aliases.get(expression.text);
|
|
224
|
-
else if (ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.expression)) {
|
|
225
|
-
if (namespaces.has(expression.expression.text)) imported = expression.name.text;
|
|
226
|
-
} else if (ts.isElementAccessExpression(expression) && ts.isIdentifier(expression.expression) && namespaces.has(expression.expression.text) && expression.argumentExpression && ts.isStringLiteral(unwrap(expression.argumentExpression))) {
|
|
227
|
-
imported = unwrap(expression.argumentExpression).text;
|
|
228
|
-
}
|
|
229
|
-
if (imported === COMPONENT_MACRO) return "component";
|
|
230
|
-
if (imported === PROCEDURE_MACRO) return "procedure";
|
|
231
|
-
if (imported === EXTERNAL_MACRO) return "external";
|
|
232
|
-
return void 0;
|
|
233
|
-
}
|
|
234
|
-
function extractModule(options) {
|
|
235
|
-
const file = ts.createSourceFile(options.id, options.code, ts.ScriptTarget.Latest, true, sourceKind(options.id));
|
|
236
|
-
const namespaces = /* @__PURE__ */ new Set();
|
|
237
|
-
const aliases = /* @__PURE__ */ new Map();
|
|
238
|
-
for (const statement of file.statements) {
|
|
239
|
-
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
240
|
-
if (statement.moduleSpecifier.text !== CORE || !statement.importClause) continue;
|
|
241
|
-
const bindings = statement.importClause.namedBindings;
|
|
242
|
-
if (bindings && ts.isNamespaceImport(bindings)) namespaces.add(bindings.name.text);
|
|
243
|
-
if (bindings && ts.isNamedImports(bindings)) {
|
|
244
|
-
for (const binding of bindings.elements) {
|
|
245
|
-
aliases.set(binding.name.text, binding.propertyName?.text ?? binding.name.text);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
let changed = true;
|
|
250
|
-
while (changed) {
|
|
251
|
-
changed = false;
|
|
252
|
-
const visitAlias = (node) => {
|
|
253
|
-
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
|
254
|
-
const init = unwrap(node.initializer);
|
|
255
|
-
let source;
|
|
256
|
-
if (ts.isIdentifier(init)) source = aliases.get(init.text);
|
|
257
|
-
else if (ts.isPropertyAccessExpression(init) && ts.isIdentifier(init.expression) && namespaces.has(init.expression.text)) {
|
|
258
|
-
source = init.name.text;
|
|
259
|
-
}
|
|
260
|
-
if (source && !aliases.has(node.name.text)) {
|
|
261
|
-
aliases.set(node.name.text, source);
|
|
262
|
-
changed = true;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
ts.forEachChild(node, visitAlias);
|
|
266
|
-
};
|
|
267
|
-
visitAlias(file);
|
|
268
|
-
}
|
|
269
|
-
const macroNames = /* @__PURE__ */ new Set([COMPONENT_MACRO, PROCEDURE_MACRO, EXTERNAL_MACRO]);
|
|
270
|
-
if (![...aliases.values()].some((name) => macroNames.has(name)) && namespaces.size === 0) {
|
|
271
|
-
return { code: options.code, entries: [] };
|
|
272
|
-
}
|
|
273
|
-
const evaluator = new StaticEvaluator(file);
|
|
274
|
-
const entries = [];
|
|
275
|
-
const injections = [];
|
|
276
|
-
const origin = normalizedOrigin(options.root, options.id);
|
|
277
|
-
const visit = (node) => {
|
|
278
|
-
if (ts.isCallExpression(node)) {
|
|
279
|
-
const macro = isMacroCall(node, macroNames, namespaces, aliases);
|
|
280
|
-
if (macro) {
|
|
281
|
-
if (!node.arguments[0]) throw new Error(`${origin}: contract declaration needs an argument`);
|
|
282
|
-
const variable = declarationVariable(node);
|
|
283
|
-
if (!variable) throw new Error(`${origin}: contract declaration must initialize a named module constant`);
|
|
284
|
-
const declarationId = `${origin}#${variable}`;
|
|
285
|
-
const definition = record(evaluator.evaluate(node.arguments[0], declarationId), declarationId);
|
|
286
|
-
const emitted = [];
|
|
287
|
-
if (macro === "component") {
|
|
288
|
-
const type = string(definition.type, `${declarationId}.type`);
|
|
289
|
-
const inheritedPolicies = policies(definition.policies);
|
|
290
|
-
const inheritedTags = definition.tags;
|
|
291
|
-
const groups = [
|
|
292
|
-
["observations", "observation", "read"],
|
|
293
|
-
["actions", "action", void 0]
|
|
294
|
-
];
|
|
295
|
-
for (const [groupName, kind, defaultEffect] of groups) {
|
|
296
|
-
const groupValue = definition[groupName];
|
|
297
|
-
if (groupValue === void 0) continue;
|
|
298
|
-
const group = record(groupValue, `${declarationId}.${groupName}`);
|
|
299
|
-
for (const [name, rawMember] of Object.entries(group)) {
|
|
300
|
-
const member = record(rawMember, `${declarationId}.${groupName}.${name}`);
|
|
301
|
-
const capabilityPolicies = combine(inheritedPolicies, policies(member.policies));
|
|
302
|
-
const capabilityTags = combine(inheritedTags, member.tags);
|
|
303
|
-
const base = {
|
|
304
|
-
declarationId,
|
|
305
|
-
capabilityId: `view:${type}.${name}`,
|
|
306
|
-
kind,
|
|
307
|
-
description: string(member.description, `${declarationId}.${name}.description`),
|
|
308
|
-
effect: string(member.effect ?? defaultEffect, `${declarationId}.${name}.effect`),
|
|
309
|
-
...member.input ? { inputSchema: member.input } : {},
|
|
310
|
-
...member.output ? { outputSchema: member.output } : {},
|
|
311
|
-
...member.confirmation ? { confirmation: member.confirmation } : {},
|
|
312
|
-
...capabilityPolicies ? { policies: capabilityPolicies } : {},
|
|
313
|
-
...capabilityTags ? { tags: capabilityTags } : {},
|
|
314
|
-
origin
|
|
315
|
-
};
|
|
316
|
-
emitted.push({ ...base, contractHash: entryHash(base), targets: [options.target] });
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
} else {
|
|
320
|
-
const id = string(definition.id, `${declarationId}.id`);
|
|
321
|
-
const base = {
|
|
322
|
-
declarationId,
|
|
323
|
-
capabilityId: id,
|
|
324
|
-
kind: macro === "procedure" ? "procedure" : "external",
|
|
325
|
-
description: string(definition.description, `${declarationId}.description`),
|
|
326
|
-
effect: string(definition.effect, `${declarationId}.effect`),
|
|
327
|
-
...definition.input ? { inputSchema: definition.input } : {},
|
|
328
|
-
...definition.output ? { outputSchema: definition.output } : {},
|
|
329
|
-
...definition.confirmation ? { confirmation: definition.confirmation } : {},
|
|
330
|
-
...policies(definition.policies) ? { policies: policies(definition.policies) } : {},
|
|
331
|
-
...definition.tags ? { tags: definition.tags } : {},
|
|
332
|
-
origin
|
|
333
|
-
};
|
|
334
|
-
emitted.push({ ...base, contractHash: entryHash(base), targets: [options.target] });
|
|
335
|
-
}
|
|
336
|
-
entries.push(...emitted);
|
|
337
|
-
if (node.arguments.length < 2) {
|
|
338
|
-
if (macro === "component") {
|
|
339
|
-
const capabilities = {};
|
|
340
|
-
for (const entry of emitted) {
|
|
341
|
-
capabilities[entry.capabilityId] = {
|
|
342
|
-
manifestHash: options.placeholder,
|
|
343
|
-
declarationId: entry.declarationId,
|
|
344
|
-
capabilityId: entry.capabilityId,
|
|
345
|
-
contractHash: entry.contractHash
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
const provenance = {
|
|
349
|
-
manifestHash: options.placeholder,
|
|
350
|
-
declarationId,
|
|
351
|
-
capabilities
|
|
352
|
-
};
|
|
353
|
-
injections.push({ position: node.arguments.end, text: `, ${JSON.stringify(provenance)}` });
|
|
354
|
-
} else {
|
|
355
|
-
const entry = emitted[0];
|
|
356
|
-
const token = {
|
|
357
|
-
manifestHash: options.placeholder,
|
|
358
|
-
declarationId,
|
|
359
|
-
capabilityId: entry.capabilityId,
|
|
360
|
-
contractHash: entry.contractHash
|
|
361
|
-
};
|
|
362
|
-
injections.push({ position: node.arguments.end, text: `, ${JSON.stringify(token)}` });
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
ts.forEachChild(node, visit);
|
|
368
|
-
};
|
|
369
|
-
visit(file);
|
|
370
|
-
let code = options.code;
|
|
371
|
-
for (const injection of injections.sort((a, b) => b.position - a.position)) {
|
|
372
|
-
code = `${code.slice(0, injection.position)}${injection.text}${code.slice(injection.position)}`;
|
|
373
|
-
}
|
|
374
|
-
return { code, entries };
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// src/plugin.ts
|
|
378
|
-
var COMPILER_VERSION = "0.17.0";
|
|
379
|
-
var CONTRACT_FILE = "agent-surface.contract.json";
|
|
380
|
-
var VIRTUAL_CONTRACT_ID = "virtual:agent-surface-contract";
|
|
381
|
-
var RESOLVED_VIRTUAL_CONTRACT_ID = `\0${VIRTUAL_CONTRACT_ID}`;
|
|
382
|
-
function mergeEntries(entries) {
|
|
383
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
384
|
-
for (const entry of entries) {
|
|
385
|
-
const key = `${entry.declarationId}\0${entry.capabilityId}`;
|
|
386
|
-
const current = byKey.get(key);
|
|
387
|
-
if (!current) {
|
|
388
|
-
byKey.set(key, { ...entry, targets: [...entry.targets] });
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
if (current.contractHash !== entry.contractHash) {
|
|
392
|
-
throw new Error(`contract "${entry.declarationId}" / "${entry.capabilityId}" differs across build targets`);
|
|
393
|
-
}
|
|
394
|
-
current.targets = [.../* @__PURE__ */ new Set([...current.targets, ...entry.targets])].sort();
|
|
395
|
-
}
|
|
396
|
-
return [...byKey.values()].sort(
|
|
397
|
-
(a, b) => `${a.declarationId}\0${a.capabilityId}`.localeCompare(`${b.declarationId}\0${b.capabilityId}`)
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
function validateEntries(entries) {
|
|
401
|
-
for (const entry of entries) {
|
|
402
|
-
if (entry.kind !== "external" && !parseCapabilityId(entry.capabilityId)) {
|
|
403
|
-
throw new Error(`invalid compiled capability id "${entry.capabilityId}"`);
|
|
404
|
-
}
|
|
405
|
-
for (const [label, schema] of [["input", entry.inputSchema], ["output", entry.outputSchema]]) {
|
|
406
|
-
if (!schema) continue;
|
|
407
|
-
const result = validateJsonSchemaDocument(schema, DEFAULT_LIMITS);
|
|
408
|
-
if (!result.ok) {
|
|
409
|
-
throw new Error(`${entry.declarationId} ${label} schema: ${result.reason}`);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
function nearestPackage(id) {
|
|
415
|
-
if (id.startsWith("\0") || !isAbsolute(id)) return void 0;
|
|
416
|
-
let dir = dirname(id.split("?")[0]);
|
|
417
|
-
while (dir !== dirname(dir)) {
|
|
418
|
-
const packagePath = join(dir, "package.json");
|
|
419
|
-
if (existsSync(packagePath)) {
|
|
420
|
-
const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
421
|
-
if (!pkg.name) return void 0;
|
|
422
|
-
return {
|
|
423
|
-
name: pkg.name,
|
|
424
|
-
dir,
|
|
425
|
-
packagePath,
|
|
426
|
-
...pkg.agentSurface?.contract ? { sidecar: resolve2(dir, pkg.agentSurface.contract) } : {}
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
dir = dirname(dir);
|
|
430
|
-
}
|
|
431
|
-
return void 0;
|
|
432
|
-
}
|
|
433
|
-
function isDependencyModule(id) {
|
|
434
|
-
return id.split("?")[0].includes(`${sep}node_modules${sep}`);
|
|
435
|
-
}
|
|
436
|
-
function readSidecar(path) {
|
|
437
|
-
const bytes = readFileSync(path);
|
|
438
|
-
const manifest = JSON.parse(bytes.toString("utf8"));
|
|
439
|
-
verifyManifest(manifest);
|
|
440
|
-
return { manifest, digest: sha256(bytes) };
|
|
441
|
-
}
|
|
442
|
-
function authorizeContributions(contributions, policy) {
|
|
443
|
-
const allow = new Map((policy?.allow ?? []).map((entry) => [entry.package, entry]));
|
|
444
|
-
const unauthorized = [];
|
|
445
|
-
const mismatched = [];
|
|
446
|
-
const attributions = [];
|
|
447
|
-
for (const contribution of contributions) {
|
|
448
|
-
const approved = allow.get(contribution.package);
|
|
449
|
-
if (!approved) {
|
|
450
|
-
unauthorized.push(
|
|
451
|
-
` ${contribution.package} (${contribution.route}: ${contribution.source})
|
|
452
|
-
{ "package": "${contribution.package}", "digest": "${contribution.contractDigest}" }`
|
|
453
|
-
);
|
|
454
|
-
continue;
|
|
455
|
-
}
|
|
456
|
-
if (approved.digest !== contribution.contractDigest) {
|
|
457
|
-
mismatched.push(
|
|
458
|
-
` ${contribution.package}: approved ${approved.digest}, this build computed ${contribution.contractDigest}`
|
|
459
|
-
);
|
|
460
|
-
continue;
|
|
461
|
-
}
|
|
462
|
-
attributions.push({
|
|
463
|
-
package: contribution.package,
|
|
464
|
-
source: contribution.source,
|
|
465
|
-
route: contribution.route,
|
|
466
|
-
contractDigest: contribution.contractDigest,
|
|
467
|
-
authorization: { mode: "pinned", expectedDigest: approved.digest }
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
if (unauthorized.length > 0) {
|
|
471
|
-
throw new Error(
|
|
472
|
-
`unauthorized external capability contract(s). A dependency cannot contribute capabilities until the consumer approves it. Add to externalContracts.allow:
|
|
473
|
-
${unauthorized.join("\n")}`
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
if (mismatched.length > 0) {
|
|
477
|
-
throw new Error(
|
|
478
|
-
`external capability contract digest mismatch. The dependency changed what it contributes; review the change before updating the approved digest:
|
|
479
|
-
${mismatched.join("\n")}`
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
return attributions.sort((a, b) => a.package.localeCompare(b.package));
|
|
483
|
-
}
|
|
484
|
-
function agentSurface(options = {}) {
|
|
485
|
-
const target = options.target ?? "web-production";
|
|
486
|
-
const placeholder = `__AGENT_SURFACE_MANIFEST_HASH_${Math.random().toString(36).slice(2)}__`;
|
|
487
|
-
const manifestPlaceholder = `__AGENT_SURFACE_MANIFEST_${Math.random().toString(36).slice(2)}__`;
|
|
488
|
-
let root = process.cwd();
|
|
489
|
-
let entries = [];
|
|
490
|
-
let sourceEntries = /* @__PURE__ */ new Map();
|
|
491
|
-
let manifest;
|
|
492
|
-
return {
|
|
493
|
-
name: "agent-surface:compiler",
|
|
494
|
-
apply: "build",
|
|
495
|
-
enforce: "pre",
|
|
496
|
-
configResolved(config) {
|
|
497
|
-
root = config.root;
|
|
498
|
-
},
|
|
499
|
-
resolveId(id) {
|
|
500
|
-
return id === VIRTUAL_CONTRACT_ID ? RESOLVED_VIRTUAL_CONTRACT_ID : null;
|
|
501
|
-
},
|
|
502
|
-
load(id) {
|
|
503
|
-
if (id !== RESOLVED_VIRTUAL_CONTRACT_ID) return null;
|
|
504
|
-
return `import { createCapabilityAuthority } from "@agent-surface/core";
|
|
505
|
-
const manifest = ${manifestPlaceholder};
|
|
506
|
-
export { manifest };
|
|
507
|
-
export default createCapabilityAuthority(manifest);`;
|
|
508
|
-
},
|
|
509
|
-
buildStart() {
|
|
510
|
-
entries = [];
|
|
511
|
-
sourceEntries = /* @__PURE__ */ new Map();
|
|
512
|
-
manifest = void 0;
|
|
513
|
-
},
|
|
514
|
-
transform(code, id) {
|
|
515
|
-
if (id.includes("/node_modules/@agent-surface/") || /\/packages\/(?:core|react|orpc|testing|webmcp|cli|compiler)\//.test(id)) {
|
|
516
|
-
return null;
|
|
517
|
-
}
|
|
518
|
-
if (/\buseAgentComponent\s*\(\s*\{/.test(code)) {
|
|
519
|
-
throw new Error(`${id}: inline useAgentComponent registration is unsupported; bind a compiled contract`);
|
|
520
|
-
}
|
|
521
|
-
if (/\b(?:useAgentAction|useAgentObservation)\s*\(/.test(code)) {
|
|
522
|
-
throw new Error(`${id}: granular runtime capability construction is unsupported by the compiled contract`);
|
|
523
|
-
}
|
|
524
|
-
if (/\bdefineAgentComponent\s*\(/.test(code) || /\bregistry\s*\.\s*register\s*\(\s*\{/.test(code)) {
|
|
525
|
-
throw new Error(`${id}: raw capability registration bypasses the compiled exposure ceiling`);
|
|
526
|
-
}
|
|
527
|
-
if (!code.includes("defineAgentComponentContract") && !code.includes("defineAgentProcedureContract") && !code.includes("defineExternalAgentToolContract")) {
|
|
528
|
-
return null;
|
|
529
|
-
}
|
|
530
|
-
const extracted = extractModule({ code, id, root, target, placeholder });
|
|
531
|
-
const owner = isDependencyModule(id) ? nearestPackage(id) : void 0;
|
|
532
|
-
if (owner) {
|
|
533
|
-
const existing = sourceEntries.get(owner.name) ?? [];
|
|
534
|
-
existing.push(...extracted.entries);
|
|
535
|
-
sourceEntries.set(owner.name, existing);
|
|
536
|
-
} else {
|
|
537
|
-
entries.push(...extracted.entries);
|
|
538
|
-
}
|
|
539
|
-
return extracted.code === code ? null : { code: extracted.code, map: null };
|
|
540
|
-
},
|
|
541
|
-
buildEnd(error) {
|
|
542
|
-
if (error) return;
|
|
543
|
-
const contributions = [];
|
|
544
|
-
const seenSidecars = /* @__PURE__ */ new Set();
|
|
545
|
-
for (const id of this.getModuleIds()) {
|
|
546
|
-
if (!isDependencyModule(id)) continue;
|
|
547
|
-
const owner = nearestPackage(id);
|
|
548
|
-
if (!owner?.sidecar || seenSidecars.has(owner.sidecar)) continue;
|
|
549
|
-
seenSidecars.add(owner.sidecar);
|
|
550
|
-
const { manifest: external, digest } = readSidecar(owner.sidecar);
|
|
551
|
-
contributions.push({
|
|
552
|
-
package: owner.name,
|
|
553
|
-
source: relative2(root, owner.sidecar).split(sep).join("/"),
|
|
554
|
-
route: "sidecar",
|
|
555
|
-
contractDigest: digest,
|
|
556
|
-
entries: external.capabilities
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
for (const allowed of options.externalContracts?.allow ?? []) {
|
|
560
|
-
if (!allowed.path) continue;
|
|
561
|
-
const path = isAbsolute(allowed.path) ? allowed.path : resolve2(root, allowed.path);
|
|
562
|
-
if (seenSidecars.has(path)) continue;
|
|
563
|
-
seenSidecars.add(path);
|
|
564
|
-
const owner = nearestPackage(path);
|
|
565
|
-
if (owner && owner.name !== allowed.package) {
|
|
566
|
-
throw new Error(
|
|
567
|
-
`external contract "${allowed.path}" belongs to "${owner.name}", not the approved "${allowed.package}"`
|
|
568
|
-
);
|
|
569
|
-
}
|
|
570
|
-
const { manifest: external, digest } = readSidecar(path);
|
|
571
|
-
contributions.push({
|
|
572
|
-
package: owner?.name ?? allowed.package,
|
|
573
|
-
source: allowed.path,
|
|
574
|
-
route: "sidecar",
|
|
575
|
-
contractDigest: digest,
|
|
576
|
-
entries: external.capabilities
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
for (const [name, extracted] of [...sourceEntries].sort(([a], [b]) => a.localeCompare(b))) {
|
|
580
|
-
if (extracted.length === 0) continue;
|
|
581
|
-
const sorted = mergeEntries(extracted);
|
|
582
|
-
contributions.push({
|
|
583
|
-
package: name,
|
|
584
|
-
source: sorted[0]?.origin ?? name,
|
|
585
|
-
route: "source",
|
|
586
|
-
contractDigest: sha256(canonicalJson(sorted)),
|
|
587
|
-
entries: sorted
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
const externalContracts = authorizeContributions(contributions, options.externalContracts);
|
|
591
|
-
const externalEntries = contributions.flatMap((contribution) => contribution.entries);
|
|
592
|
-
const capabilities = mergeEntries([...entries, ...externalEntries]);
|
|
593
|
-
validateEntries(capabilities);
|
|
594
|
-
const targets = [...new Set(capabilities.flatMap((entry) => entry.targets))].sort();
|
|
595
|
-
const payload = {
|
|
596
|
-
formatVersion: 5,
|
|
597
|
-
compilerVersion: COMPILER_VERSION,
|
|
598
|
-
targets,
|
|
599
|
-
capabilities,
|
|
600
|
-
externalContracts,
|
|
601
|
-
completeness: { status: "proven" }
|
|
602
|
-
};
|
|
603
|
-
manifest = { ...payload, hash: computeManifestHash(payload) };
|
|
604
|
-
options.onManifest?.(manifest);
|
|
605
|
-
},
|
|
606
|
-
renderChunk(code) {
|
|
607
|
-
if (!manifest || !code.includes(placeholder) && !code.includes(manifestPlaceholder)) return null;
|
|
608
|
-
return {
|
|
609
|
-
code: code.replaceAll(placeholder, manifest.hash).replaceAll(manifestPlaceholder, JSON.stringify(manifest)),
|
|
610
|
-
map: null
|
|
611
|
-
};
|
|
612
|
-
},
|
|
613
|
-
generateBundle() {
|
|
614
|
-
if (!manifest || options.emit === false) return;
|
|
615
|
-
this.emitFile({
|
|
616
|
-
type: "asset",
|
|
617
|
-
fileName: options.fileName ?? CONTRACT_FILE,
|
|
618
|
-
source: canonicalManifestJson(manifest)
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// src/compile.ts
|
|
625
|
-
import { resolve as resolve3 } from "path";
|
|
626
|
-
import { build } from "vite";
|
|
627
|
-
async function compileCapabilityContract(options = {}) {
|
|
628
|
-
let manifest;
|
|
629
|
-
const root = resolve3(options.root ?? process.cwd());
|
|
630
|
-
const result = await build({
|
|
631
|
-
...options.vite ?? {},
|
|
632
|
-
root,
|
|
633
|
-
...options.configFile ? { configFile: resolve3(root, options.configFile) } : {},
|
|
634
|
-
logLevel: options.vite?.logLevel ?? "silent",
|
|
635
|
-
build: {
|
|
636
|
-
...options.vite?.build ?? {},
|
|
637
|
-
write: false
|
|
638
|
-
},
|
|
639
|
-
plugins: [
|
|
640
|
-
...options.vite?.plugins ?? [],
|
|
641
|
-
agentSurface({
|
|
642
|
-
target: options.target,
|
|
643
|
-
emit: false,
|
|
644
|
-
...options.externalContracts ? { externalContracts: options.externalContracts } : {},
|
|
645
|
-
onManifest: (value) => {
|
|
646
|
-
manifest = value;
|
|
647
|
-
}
|
|
648
|
-
})
|
|
649
|
-
]
|
|
650
|
-
});
|
|
651
|
-
const outputs = Array.isArray(result) ? result : [result];
|
|
652
|
-
for (const output of outputs) {
|
|
653
|
-
if (!("output" in output)) continue;
|
|
654
|
-
const asset = output.output.find(
|
|
655
|
-
(entry) => entry.type === "asset" && entry.fileName === "agent-surface.contract.json"
|
|
656
|
-
);
|
|
657
|
-
if (asset?.type === "asset") {
|
|
658
|
-
const source = typeof asset.source === "string" ? asset.source : new TextDecoder().decode(asset.source);
|
|
659
|
-
manifest = JSON.parse(source);
|
|
660
|
-
break;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
if (!manifest) throw new Error("compiler did not produce a capability contract");
|
|
664
|
-
return manifest;
|
|
665
|
-
}
|
|
2
|
+
COMPILER_VERSION,
|
|
3
|
+
CONTRACT_FILE,
|
|
4
|
+
VIRTUAL_CONTRACT_ID,
|
|
5
|
+
agentSurface,
|
|
6
|
+
canonicalJson,
|
|
7
|
+
canonicalManifestJson,
|
|
8
|
+
compileCapabilityContract,
|
|
9
|
+
computeManifestHash,
|
|
10
|
+
manifestPayload,
|
|
11
|
+
sha256,
|
|
12
|
+
verifyManifest
|
|
13
|
+
} from "./chunk-BNIUE5RA.js";
|
|
666
14
|
export {
|
|
667
15
|
COMPILER_VERSION,
|
|
668
16
|
CONTRACT_FILE,
|