ai-capability-framework 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/CONTRIBUTING.md +56 -0
  3. package/LICENSE +21 -0
  4. package/README.md +198 -0
  5. package/SECURITY.md +29 -0
  6. package/conformance/invalid/duplicate/capabilities/conformance.duplicate.one.yaml +55 -0
  7. package/conformance/invalid/duplicate/capabilities/conformance.duplicate.two.yaml +55 -0
  8. package/conformance/invalid/eval-results/conformance.results.failing.json +15 -0
  9. package/conformance/invalid/missing-reference/capabilities/conformance.missing_reference.prepare.yaml +58 -0
  10. package/conformance/invalid/schema/capabilities/conformance.invalid.missing_required.yaml +3 -0
  11. package/conformance/valid/capabilities/conformance.note.get.yaml +82 -0
  12. package/conformance/valid/decisions/conformance.note.get.denied_missing_permission.json +13 -0
  13. package/conformance/valid/entities/Note.yaml +18 -0
  14. package/conformance/valid/eval-results/conformance.results.passing.json +19 -0
  15. package/conformance/valid/evals/conformance.note.get.valid.yaml +33 -0
  16. package/conformance/valid/openai/context.no_permissions.json +6 -0
  17. package/dist/adapter-common.d.ts +62 -0
  18. package/dist/adapter-common.d.ts.map +1 -0
  19. package/dist/adapter-common.js +453 -0
  20. package/dist/ai-sdk.d.ts +5 -0
  21. package/dist/ai-sdk.d.ts.map +1 -0
  22. package/dist/ai-sdk.js +71 -0
  23. package/dist/anthropic-claude.d.ts +5 -0
  24. package/dist/anthropic-claude.d.ts.map +1 -0
  25. package/dist/anthropic-claude.js +62 -0
  26. package/dist/cli.d.ts +11 -0
  27. package/dist/cli.d.ts.map +1 -0
  28. package/dist/cli.js +360 -0
  29. package/dist/decision.d.ts +6 -0
  30. package/dist/decision.d.ts.map +1 -0
  31. package/dist/decision.js +237 -0
  32. package/dist/eval-runner.d.ts +6 -0
  33. package/dist/eval-runner.d.ts.map +1 -0
  34. package/dist/eval-runner.js +372 -0
  35. package/dist/gemini.d.ts +5 -0
  36. package/dist/gemini.d.ts.map +1 -0
  37. package/dist/gemini.js +64 -0
  38. package/dist/generated/manifest-types.d.ts +208 -0
  39. package/dist/generated/manifest-types.d.ts.map +1 -0
  40. package/dist/generated/manifest-types.js +1 -0
  41. package/dist/index.d.ts +15 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +13 -0
  44. package/dist/langchain.d.ts +5 -0
  45. package/dist/langchain.d.ts.map +1 -0
  46. package/dist/langchain.js +65 -0
  47. package/dist/loader.d.ts +4 -0
  48. package/dist/loader.d.ts.map +1 -0
  49. package/dist/loader.js +94 -0
  50. package/dist/mcp.d.ts +5 -0
  51. package/dist/mcp.d.ts.map +1 -0
  52. package/dist/mcp.js +82 -0
  53. package/dist/openai-responses.d.ts +6 -0
  54. package/dist/openai-responses.d.ts.map +1 -0
  55. package/dist/openai-responses.js +65 -0
  56. package/dist/registry.d.ts +6 -0
  57. package/dist/registry.d.ts.map +1 -0
  58. package/dist/registry.js +134 -0
  59. package/dist/semantic-kernel.d.ts +5 -0
  60. package/dist/semantic-kernel.d.ts.map +1 -0
  61. package/dist/semantic-kernel.js +79 -0
  62. package/dist/types.d.ts +400 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +1 -0
  65. package/dist/validator.d.ts +3 -0
  66. package/dist/validator.d.ts.map +1 -0
  67. package/dist/validator.js +105 -0
  68. package/docs/adapter-roadmap.md +179 -0
  69. package/docs/adapters.md +112 -0
  70. package/docs/api.md +115 -0
  71. package/docs/capability-manifests.md +47 -0
  72. package/docs/control-plane.md +59 -0
  73. package/docs/eval-runner.md +80 -0
  74. package/docs/evals.md +34 -0
  75. package/docs/getting-started.md +51 -0
  76. package/docs/host-responsibilities.md +23 -0
  77. package/docs/interoperability.md +32 -0
  78. package/docs/migration-0.1-to-1.0.md +22 -0
  79. package/docs/openai-responses.md +103 -0
  80. package/docs/release.md +75 -0
  81. package/docs/spec.md +194 -0
  82. package/examples/eval-results/public.results.passing.json +128 -0
  83. package/examples/scheduling/capabilities/scheduling.availability.get.yaml +91 -0
  84. package/examples/scheduling/capabilities/scheduling.invite.prepare.yaml +114 -0
  85. package/examples/scheduling/capabilities/scheduling.invite.send.yaml +97 -0
  86. package/examples/scheduling/decisions/scheduling.availability.get.select.json +14 -0
  87. package/examples/scheduling/decisions/scheduling.invite.prepare.allowed.json +20 -0
  88. package/examples/scheduling/decisions/scheduling.invite.send.allowed.json +22 -0
  89. package/examples/scheduling/decisions/scheduling.invite.send.denied_missing_approval.json +18 -0
  90. package/examples/scheduling/entities/AvailabilitySlot.yaml +26 -0
  91. package/examples/scheduling/entities/MeetingInvite.yaml +25 -0
  92. package/examples/scheduling/eval-results/scheduling.results.passing.json +59 -0
  93. package/examples/scheduling/evals/scheduling.availability.get.valid.yaml +38 -0
  94. package/examples/scheduling/evals/scheduling.invite.prepare.valid.yaml +44 -0
  95. package/examples/scheduling/evals/scheduling.invite.send.requires_approval.yaml +43 -0
  96. package/examples/scheduling/openai/context.scheduler.json +6 -0
  97. package/examples/support/capabilities/support.refund.commit_case.yaml +98 -0
  98. package/examples/support/capabilities/support.refund.prepare_case.yaml +116 -0
  99. package/examples/support/capabilities/support.ticket.get.yaml +85 -0
  100. package/examples/support/decisions/support.refund.commit_case.allowed.json +22 -0
  101. package/examples/support/decisions/support.refund.commit_case.denied_missing_approval.json +18 -0
  102. package/examples/support/decisions/support.refund.prepare_case.approval_required.json +19 -0
  103. package/examples/support/decisions/support.ticket.get.select.json +0 -0
  104. package/examples/support/entities/Order.yaml +23 -0
  105. package/examples/support/entities/Ticket.yaml +25 -0
  106. package/examples/support/eval-results/support.results.passing.json +68 -0
  107. package/examples/support/evals/support.refund.commit_case.requires_approval.yaml +42 -0
  108. package/examples/support/evals/support.refund.prepare_case.approval_required.yaml +45 -0
  109. package/examples/support/evals/support.refund.prepare_case.valid.yaml +43 -0
  110. package/examples/support/openai/context.support_agent.json +9 -0
  111. package/package.json +74 -0
  112. package/schemas/capability-manifest.schema.json +250 -0
  113. package/schemas/entity-manifest.schema.json +97 -0
  114. package/schemas/eval-case.schema.json +134 -0
  115. package/schemas/eval-result.schema.json +77 -0
@@ -0,0 +1,62 @@
1
+ import { adapterToolDescription, buildAdapterToolset, isRecord, parseAdapterToolCall, toAdapterToolName } from "./adapter-common.js";
2
+ const defaultNamePrefix = "aicf_";
3
+ const maxToolNameLength = 64;
4
+ export function buildAnthropicClaudeTools(registry, options) {
5
+ return buildAdapterToolset({
6
+ adapterName: "Anthropic Claude",
7
+ buildTool: ({ loadedCapability, normalizedInputSchema, toolName }) => ({
8
+ description: adapterToolDescription(loadedCapability.manifest),
9
+ input_schema: normalizedInputSchema,
10
+ name: toolName,
11
+ strict: true
12
+ }),
13
+ context: options.context,
14
+ defaultNamePrefix,
15
+ includeRestricted: options.includeRestricted,
16
+ maxToolNameLength,
17
+ namePrefix: options.namePrefix,
18
+ registry
19
+ });
20
+ }
21
+ export function parseAnthropicClaudeToolUse(toolset, toolUse) {
22
+ const shapeError = validateAnthropicToolUse(toolUse);
23
+ if (shapeError) {
24
+ return {
25
+ diagnostics: [{
26
+ code: "invalid_tool_call",
27
+ message: shapeError,
28
+ path: "tool_use"
29
+ }],
30
+ valid: false
31
+ };
32
+ }
33
+ return parseAdapterToolCall({
34
+ argumentsPath: "tool_use.input",
35
+ bindings: toolset.bindings,
36
+ callId: toolUse.id,
37
+ rawArguments: toolUse.input,
38
+ toolName: toolUse.name,
39
+ toolNamePath: "tool_use.name"
40
+ });
41
+ }
42
+ export function toAnthropicClaudeToolName(capabilityId, options = {}) {
43
+ return toAdapterToolName(capabilityId, {
44
+ maxLength: maxToolNameLength,
45
+ namePrefix: options.namePrefix ?? defaultNamePrefix
46
+ });
47
+ }
48
+ function validateAnthropicToolUse(toolUse) {
49
+ if (!isRecord(toolUse)) {
50
+ return "Anthropic Claude tool_use must be an object.";
51
+ }
52
+ if (toolUse.type !== undefined && toolUse.type !== "tool_use") {
53
+ return "Anthropic Claude tool_use type must be tool_use when present.";
54
+ }
55
+ if (typeof toolUse.name !== "string" || toolUse.name.length === 0) {
56
+ return "Anthropic Claude tool_use name is required.";
57
+ }
58
+ if (!isRecord(toolUse.input)) {
59
+ return "Anthropic Claude tool_use input must be a JSON object.";
60
+ }
61
+ return null;
62
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ interface WritableLike {
3
+ write(message: string): unknown;
4
+ }
5
+ export interface CliRunOptions {
6
+ stderr?: WritableLike;
7
+ stdout?: WritableLike;
8
+ }
9
+ export declare function runCli(argv: string[], options?: CliRunOptions): Promise<number>;
10
+ export {};
11
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAkBA,UAAU,YAAY;IACpB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AA4BD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqIzF"}
package/dist/cli.js ADDED
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { buildAiSdkTools } from "./ai-sdk.js";
6
+ import { buildAnthropicClaudeTools } from "./anthropic-claude.js";
7
+ import { decideCapability } from "./decision.js";
8
+ import { formatEvalSuiteResult, loadEvalResults, runEvalSuite } from "./eval-runner.js";
9
+ import { buildGeminiFunctionDeclarations } from "./gemini.js";
10
+ import { buildLangChainToolDescriptors } from "./langchain.js";
11
+ import { loadManifests } from "./loader.js";
12
+ import { buildMcpToolDescriptors } from "./mcp.js";
13
+ import { buildOpenAIResponsesTools } from "./openai-responses.js";
14
+ import { buildRegistry, formatInspection, inspectRegistry } from "./registry.js";
15
+ import { buildSemanticKernelFunctions } from "./semantic-kernel.js";
16
+ import { validateManifests } from "./validator.js";
17
+ const adapterCommands = new Set([
18
+ "ai-sdk-tools",
19
+ "anthropic-tools",
20
+ "gemini-tools",
21
+ "langchain-tools",
22
+ "mcp-tools",
23
+ "openai-tools",
24
+ "semantic-kernel-functions"
25
+ ]);
26
+ export async function runCli(argv, options = {}) {
27
+ const stdout = options.stdout ?? process.stdout;
28
+ const stderr = options.stderr ?? process.stderr;
29
+ const [command] = argv;
30
+ if (!command || command === "help" || command === "--help" || command === "-h") {
31
+ stdout.write(helpText());
32
+ return command ? 0 : 1;
33
+ }
34
+ if (command !== "validate" && command !== "inspect" && command !== "decide" && command !== "eval" && !adapterCommands.has(command)) {
35
+ stderr.write(`Unknown command "${command}".\n\n${helpText()}`);
36
+ return 1;
37
+ }
38
+ const parsedArgs = parseCommandArgs(argv.slice(1));
39
+ if (parsedArgs.error) {
40
+ stderr.write(`${parsedArgs.error}\n\n${helpText()}`);
41
+ return 1;
42
+ }
43
+ const targetPath = parsedArgs.targetPath ?? "examples";
44
+ const loadResult = await loadManifests({ path: targetPath });
45
+ const validation = validateManifests(loadResult.manifests);
46
+ const errors = [...loadResult.errors, ...validation.errors];
47
+ if (errors.length > 0) {
48
+ stderr.write(formatDiagnostics(errors));
49
+ return 1;
50
+ }
51
+ const registry = buildRegistry(loadResult.manifests);
52
+ if (command === "validate") {
53
+ stdout.write(`Validated ${loadResult.manifests.length} manifest(s).\n`);
54
+ return 0;
55
+ }
56
+ if (command === "decide") {
57
+ if (!parsedArgs.requestPath) {
58
+ stderr.write("Missing required --request <decision.json>.\n");
59
+ return 1;
60
+ }
61
+ const request = await readDecisionRequest(parsedArgs.requestPath);
62
+ if (!request.ok) {
63
+ stderr.write(`${request.error}\n`);
64
+ return 1;
65
+ }
66
+ const requestError = validateDecisionRequestShape(request.value);
67
+ if (requestError) {
68
+ stderr.write(`${requestError}\n`);
69
+ return 1;
70
+ }
71
+ if (!registry.capabilityById.has(request.value.capabilityId)) {
72
+ stderr.write(`Unknown capability id "${request.value.capabilityId}".\n`);
73
+ return 1;
74
+ }
75
+ stdout.write(`${JSON.stringify(decideCapability(registry, request.value), null, 2)}\n`);
76
+ return 0;
77
+ }
78
+ if (adapterCommands.has(command)) {
79
+ if (!parsedArgs.contextPath) {
80
+ stderr.write("Missing required --context <context.json>.\n");
81
+ return 1;
82
+ }
83
+ const context = await readToolContext(parsedArgs.contextPath);
84
+ if (!context.ok) {
85
+ stderr.write(`${context.error}\n`);
86
+ return 1;
87
+ }
88
+ const contextError = validateToolContextShape(context.value, adapterLabel(command));
89
+ if (contextError) {
90
+ stderr.write(`${contextError}\n`);
91
+ return 1;
92
+ }
93
+ const toolset = buildAdapterCliToolset(command, registry, {
94
+ context: context.value,
95
+ includeRestricted: parsedArgs.includeRestricted
96
+ });
97
+ const fatalDiagnostics = toolset.diagnostics.filter((diagnostic) => diagnostic.code === "tool_name_collision");
98
+ if (fatalDiagnostics.length > 0) {
99
+ stderr.write(formatDiagnostics(fatalDiagnostics));
100
+ return 1;
101
+ }
102
+ stdout.write(`${JSON.stringify(toolset, null, 2)}\n`);
103
+ if (adapterExportedCount(toolset) === 0) {
104
+ stderr.write(`No ${adapterLabel(command)} tools were exportable.\n`);
105
+ return 1;
106
+ }
107
+ return 0;
108
+ }
109
+ if (command === "eval") {
110
+ if (!parsedArgs.resultsPath) {
111
+ stderr.write("Missing required --results <results.json>.\n");
112
+ return 1;
113
+ }
114
+ if (parsedArgs.format !== "text" && parsedArgs.format !== "json") {
115
+ stderr.write("Eval format must be text or json.\n");
116
+ return 1;
117
+ }
118
+ const evalResults = await loadEvalResults(parsedArgs.resultsPath);
119
+ if (evalResults.errors.length > 0) {
120
+ stderr.write(formatDiagnostics(evalResults.errors));
121
+ return 1;
122
+ }
123
+ const suite = runEvalSuite(registry, evalResults.results);
124
+ if (parsedArgs.format === "json") {
125
+ stdout.write(`${JSON.stringify(suite, null, 2)}\n`);
126
+ }
127
+ else {
128
+ stdout.write(formatEvalSuiteResult(suite));
129
+ }
130
+ return suite.passed ? 0 : 1;
131
+ }
132
+ stdout.write(formatInspection(inspectRegistry(registry)));
133
+ return 0;
134
+ }
135
+ function helpText() {
136
+ return [
137
+ "Usage: aicf <command> [path]",
138
+ "",
139
+ "Commands:",
140
+ " decide <path> --request <file> Evaluate a decision request.",
141
+ " eval <path> --results <file> [--format text|json] Run deterministic evals.",
142
+ " ai-sdk-tools <path> --context <file> Export Vercel AI SDK tool descriptors.",
143
+ " anthropic-tools <path> --context <file> Export Anthropic Claude tool definitions.",
144
+ " gemini-tools <path> --context <file> Export Gemini function declarations.",
145
+ " langchain-tools <path> --context <file> Export LangChain/LangGraph tool descriptors.",
146
+ " mcp-tools <path> --context <file> Export Model Context Protocol tool descriptors.",
147
+ " openai-tools <path> --context <file> Export OpenAI Responses function tools.",
148
+ " semantic-kernel-functions <path> --context <file> Export Semantic Kernel function metadata.",
149
+ " validate [path] Validate AICF manifests. Defaults to examples.",
150
+ " inspect [path] Print a registry summary. Defaults to examples.",
151
+ ""
152
+ ].join("\n");
153
+ }
154
+ function parseCommandArgs(args) {
155
+ let contextPath;
156
+ let format = "text";
157
+ let includeRestricted = false;
158
+ let requestPath;
159
+ let resultsPath;
160
+ let targetPath;
161
+ for (let index = 0; index < args.length; index += 1) {
162
+ const arg = args[index];
163
+ if (arg === "--request") {
164
+ const nextArg = args[index + 1];
165
+ if (!nextArg) {
166
+ return { error: "Missing value for --request." };
167
+ }
168
+ requestPath = nextArg;
169
+ index += 1;
170
+ continue;
171
+ }
172
+ if (arg === "--results") {
173
+ const nextArg = args[index + 1];
174
+ if (!nextArg) {
175
+ return { error: "Missing value for --results." };
176
+ }
177
+ resultsPath = nextArg;
178
+ index += 1;
179
+ continue;
180
+ }
181
+ if (arg === "--format") {
182
+ const nextArg = args[index + 1];
183
+ if (!nextArg) {
184
+ return { error: "Missing value for --format." };
185
+ }
186
+ format = nextArg;
187
+ index += 1;
188
+ continue;
189
+ }
190
+ if (arg === "--context") {
191
+ const nextArg = args[index + 1];
192
+ if (!nextArg) {
193
+ return { error: "Missing value for --context." };
194
+ }
195
+ contextPath = nextArg;
196
+ index += 1;
197
+ continue;
198
+ }
199
+ if (arg === "--include-restricted") {
200
+ includeRestricted = true;
201
+ continue;
202
+ }
203
+ if (arg?.startsWith("--")) {
204
+ return { error: `Unknown option "${arg}".` };
205
+ }
206
+ if (targetPath) {
207
+ return { error: `Unexpected argument "${arg}".` };
208
+ }
209
+ targetPath = arg;
210
+ }
211
+ return {
212
+ contextPath,
213
+ format,
214
+ includeRestricted,
215
+ requestPath,
216
+ resultsPath,
217
+ targetPath
218
+ };
219
+ }
220
+ async function readDecisionRequest(filePath) {
221
+ try {
222
+ const content = await readFile(filePath, "utf8");
223
+ return {
224
+ ok: true,
225
+ value: JSON.parse(content)
226
+ };
227
+ }
228
+ catch (error) {
229
+ return {
230
+ error: error instanceof Error ? error.message : "Unable to read decision request.",
231
+ ok: false
232
+ };
233
+ }
234
+ }
235
+ async function readToolContext(filePath) {
236
+ try {
237
+ const content = await readFile(filePath, "utf8");
238
+ return {
239
+ ok: true,
240
+ value: JSON.parse(content)
241
+ };
242
+ }
243
+ catch (error) {
244
+ return {
245
+ error: error instanceof Error ? error.message : "Unable to read tool context.",
246
+ ok: false
247
+ };
248
+ }
249
+ }
250
+ function validateDecisionRequestShape(value) {
251
+ if (typeof value !== "object" || value === null) {
252
+ return "Decision request must be a JSON object.";
253
+ }
254
+ if (typeof value.capabilityId !== "string" || value.capabilityId.length === 0) {
255
+ return "Decision request requires capabilityId.";
256
+ }
257
+ if (value.operation !== "select" && value.operation !== "prepare" && value.operation !== "commit") {
258
+ return "Decision request operation must be select, prepare, or commit.";
259
+ }
260
+ if (typeof value.context !== "object" || value.context === null) {
261
+ return "Decision request requires context.";
262
+ }
263
+ if (!Array.isArray(value.context.permissions) || value.context.permissions.some((permission) => typeof permission !== "string")) {
264
+ return "Decision request context.permissions must be an array of strings.";
265
+ }
266
+ if (!["A0", "A1", "A2", "A3", "A4", "A5"].includes(value.context.autonomyTier)) {
267
+ return "Decision request context.autonomyTier must be A0, A1, A2, A3, A4, or A5.";
268
+ }
269
+ return null;
270
+ }
271
+ function validateToolContextShape(value, label) {
272
+ if (typeof value !== "object" || value === null) {
273
+ return `${label} tool context must be a JSON object.`;
274
+ }
275
+ if (!Array.isArray(value.permissions) || value.permissions.some((permission) => typeof permission !== "string")) {
276
+ return `${label} tool context.permissions must be an array of strings.`;
277
+ }
278
+ if (!["A0", "A1", "A2", "A3", "A4", "A5"].includes(value.autonomyTier)) {
279
+ return `${label} tool context.autonomyTier must be A0, A1, A2, A3, A4, or A5.`;
280
+ }
281
+ if (value.userId !== undefined && typeof value.userId !== "string") {
282
+ return `${label} tool context.userId must be a string when present.`;
283
+ }
284
+ if (value.tenantId !== undefined && typeof value.tenantId !== "string") {
285
+ return `${label} tool context.tenantId must be a string when present.`;
286
+ }
287
+ return null;
288
+ }
289
+ function buildAdapterCliToolset(command, registry, options) {
290
+ switch (command) {
291
+ case "ai-sdk-tools":
292
+ return buildAiSdkTools(registry, options);
293
+ case "anthropic-tools":
294
+ return buildAnthropicClaudeTools(registry, options);
295
+ case "gemini-tools":
296
+ return buildGeminiFunctionDeclarations(registry, options);
297
+ case "langchain-tools":
298
+ return buildLangChainToolDescriptors(registry, options);
299
+ case "mcp-tools":
300
+ return buildMcpToolDescriptors(registry, options);
301
+ case "semantic-kernel-functions":
302
+ return buildSemanticKernelFunctions(registry, options);
303
+ case "openai-tools":
304
+ return buildOpenAIResponsesTools(registry, options);
305
+ }
306
+ }
307
+ function adapterExportedCount(toolset) {
308
+ if (Array.isArray(toolset.tools)) {
309
+ return toolset.tools.length;
310
+ }
311
+ if (toolset.tools && typeof toolset.tools === "object") {
312
+ return Object.keys(toolset.tools).length;
313
+ }
314
+ if (toolset.functionDeclarations) {
315
+ return toolset.functionDeclarations.length;
316
+ }
317
+ if (toolset.functions) {
318
+ return toolset.functions.length;
319
+ }
320
+ return 0;
321
+ }
322
+ function adapterLabel(command) {
323
+ switch (command) {
324
+ case "ai-sdk-tools":
325
+ return "AI SDK";
326
+ case "anthropic-tools":
327
+ return "Anthropic Claude";
328
+ case "gemini-tools":
329
+ return "Gemini";
330
+ case "langchain-tools":
331
+ return "LangChain";
332
+ case "mcp-tools":
333
+ return "MCP";
334
+ case "openai-tools":
335
+ return "OpenAI";
336
+ case "semantic-kernel-functions":
337
+ return "Semantic Kernel";
338
+ }
339
+ }
340
+ function formatDiagnostics(errors) {
341
+ return errors
342
+ .map((error) => {
343
+ const kind = error.kind ? ` ${error.kind}` : "";
344
+ const id = error.id ? ` ${error.id}` : "";
345
+ return `${error.path}:${kind}${id}: ${error.message}`;
346
+ })
347
+ .join("\n")
348
+ .concat("\n");
349
+ }
350
+ if (isDirectCliRun()) {
351
+ const exitCode = await runCli(process.argv.slice(2));
352
+ process.exitCode = exitCode;
353
+ }
354
+ function isDirectCliRun() {
355
+ const invokedPath = process.argv[1];
356
+ if (!invokedPath) {
357
+ return false;
358
+ }
359
+ return path.resolve(invokedPath) === fileURLToPath(import.meta.url);
360
+ }
@@ -0,0 +1,6 @@
1
+ import type { CapabilityManifest } from "./generated/manifest-types.js";
2
+ import type { DecisionRequest, DecisionResult, LifecycleEvaluation, LoadedCapabilityManifest, ManifestRegistry, PolicyEvaluation } from "./types.js";
3
+ export declare function decideCapability(registry: ManifestRegistry, request: DecisionRequest): DecisionResult;
4
+ export declare function evaluatePolicy(capability: LoadedCapabilityManifest | CapabilityManifest, request: DecisionRequest): PolicyEvaluation;
5
+ export declare function evaluateLifecycle(capability: LoadedCapabilityManifest | CapabilityManifest, request: DecisionRequest): LifecycleEvaluation;
6
+ //# sourceMappingURL=decision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision.d.ts","sourceRoot":"","sources":["../src/decision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,KAAK,EAIV,eAAe,EACf,cAAc,EAEd,mBAAmB,EACnB,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAWpB,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,eAAe,GACvB,cAAc,CAuBhB;AAED,wBAAgB,cAAc,CAC5B,UAAU,EAAE,wBAAwB,GAAG,kBAAkB,EACzD,OAAO,EAAE,eAAe,GACvB,gBAAgB,CAsGlB;AAED,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,wBAAwB,GAAG,kBAAkB,EACzD,OAAO,EAAE,eAAe,GACvB,mBAAmB,CA6BrB"}
@@ -0,0 +1,237 @@
1
+ const autonomyRank = {
2
+ A0: 0,
3
+ A1: 1,
4
+ A2: 2,
5
+ A3: 3,
6
+ A4: 4,
7
+ A5: 5
8
+ };
9
+ export function decideCapability(registry, request) {
10
+ const loadedCapability = registry.capabilityById.get(request.capabilityId);
11
+ if (!loadedCapability) {
12
+ const reasons = [{
13
+ code: "capability_not_found",
14
+ message: `Capability "${request.capabilityId}" was not found.`
15
+ }];
16
+ return buildDecisionResult(request, "denied", {
17
+ reasons,
18
+ requiredApprovals: [],
19
+ status: "denied"
20
+ }, {
21
+ reasons: [],
22
+ status: "allowed"
23
+ });
24
+ }
25
+ const policy = evaluatePolicy(loadedCapability, request);
26
+ const lifecycle = evaluateLifecycle(loadedCapability, request);
27
+ const status = combineStatuses(policy.status, lifecycle.status);
28
+ return buildDecisionResult(request, status, policy, lifecycle);
29
+ }
30
+ export function evaluatePolicy(capability, request) {
31
+ const manifest = unwrapCapability(capability);
32
+ const reasons = [];
33
+ const requiredApprovals = [];
34
+ for (const permission of manifest.authorization.permissions) {
35
+ if (!request.context.permissions.includes(permission)) {
36
+ reasons.push({
37
+ code: "missing_permission",
38
+ message: `Missing required permission "${permission}".`
39
+ });
40
+ }
41
+ }
42
+ const maxAutonomyTier = manifest.policy.max_autonomy_tier ?? manifest.autonomy_tier;
43
+ if (autonomyRank[request.context.autonomyTier] > autonomyRank[manifest.autonomy_tier]) {
44
+ reasons.push({
45
+ code: "autonomy_exceeded",
46
+ message: `Request autonomy tier ${request.context.autonomyTier} exceeds capability tier ${manifest.autonomy_tier}.`
47
+ });
48
+ }
49
+ if (autonomyRank[request.context.autonomyTier] > autonomyRank[maxAutonomyTier]) {
50
+ reasons.push({
51
+ code: "autonomy_exceeded",
52
+ message: `Request autonomy tier ${request.context.autonomyTier} exceeds policy max tier ${maxAutonomyTier}.`
53
+ });
54
+ }
55
+ if (request.operation !== "select") {
56
+ for (const rule of manifest.policy.deny_if ?? []) {
57
+ const fact = readFact(request.facts?.[rule.rule]);
58
+ if (!fact.known) {
59
+ reasons.push({
60
+ code: "missing_fact",
61
+ message: `Deny rule "${rule.rule}" could not be evaluated and failed closed.`,
62
+ rule: rule.rule
63
+ });
64
+ continue;
65
+ }
66
+ if (fact.value) {
67
+ reasons.push({
68
+ code: "deny_rule_matched",
69
+ message: fact.reason ?? rule.reason,
70
+ rule: rule.rule
71
+ });
72
+ }
73
+ }
74
+ for (const rule of manifest.policy.approval_required_if ?? []) {
75
+ if (doesRuleMatch(rule, request.args ?? {})) {
76
+ const approvalReason = {
77
+ code: "approval_required",
78
+ message: rule.reason,
79
+ rule: rule.rule
80
+ };
81
+ if (request.operation === "commit" && !isApproved(request)) {
82
+ reasons.push(approvalReason);
83
+ }
84
+ else {
85
+ requiredApprovals.push(approvalReason);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ if (request.operation === "commit" && manifest.policy.approval_required && !isApproved(request)) {
91
+ reasons.push({
92
+ code: "approval_required",
93
+ message: "Capability commit requires approval."
94
+ });
95
+ }
96
+ if (request.operation === "commit" && manifest.idempotency?.required && !request.idempotencyKey) {
97
+ reasons.push({
98
+ code: "idempotency_required",
99
+ message: "Capability commit requires an idempotency key."
100
+ });
101
+ }
102
+ if (reasons.length > 0) {
103
+ return {
104
+ reasons,
105
+ requiredApprovals,
106
+ status: "denied"
107
+ };
108
+ }
109
+ if (requiredApprovals.length > 0 && !isApproved(request)) {
110
+ return {
111
+ reasons,
112
+ requiredApprovals,
113
+ status: "approval_required"
114
+ };
115
+ }
116
+ return {
117
+ reasons,
118
+ requiredApprovals,
119
+ status: "allowed"
120
+ };
121
+ }
122
+ export function evaluateLifecycle(capability, request) {
123
+ const manifest = unwrapCapability(capability);
124
+ const reasons = [];
125
+ if (request.operation === "select") {
126
+ return {
127
+ reasons,
128
+ status: "allowed"
129
+ };
130
+ }
131
+ if (request.operation === "prepare" && !manifest.lifecycle.prepare) {
132
+ reasons.push({
133
+ code: "lifecycle_not_supported",
134
+ message: `Capability "${manifest.id}" does not support prepare.`
135
+ });
136
+ }
137
+ if (request.operation === "commit" && !manifest.lifecycle.commit) {
138
+ reasons.push({
139
+ code: "lifecycle_not_supported",
140
+ message: `Capability "${manifest.id}" does not support commit.`
141
+ });
142
+ }
143
+ return {
144
+ reasons,
145
+ status: reasons.length > 0 ? "denied" : "allowed"
146
+ };
147
+ }
148
+ function buildDecisionResult(request, status, policy, lifecycle) {
149
+ const reasons = [...policy.reasons, ...policy.requiredApprovals, ...lifecycle.reasons];
150
+ const audit = {
151
+ capabilityId: request.capabilityId,
152
+ operation: request.operation,
153
+ reasons,
154
+ status
155
+ };
156
+ if (request.idempotencyKey) {
157
+ audit.idempotencyKey = request.idempotencyKey;
158
+ }
159
+ return {
160
+ audit,
161
+ capabilityId: request.capabilityId,
162
+ diagnostics: [],
163
+ lifecycle,
164
+ operation: request.operation,
165
+ policy,
166
+ reasons,
167
+ requiredApprovals: policy.requiredApprovals,
168
+ status
169
+ };
170
+ }
171
+ function combineStatuses(...statuses) {
172
+ if (statuses.includes("denied"))
173
+ return "denied";
174
+ if (statuses.includes("approval_required"))
175
+ return "approval_required";
176
+ return "allowed";
177
+ }
178
+ function unwrapCapability(capability) {
179
+ return "manifest" in capability ? capability.manifest : capability;
180
+ }
181
+ function readFact(fact) {
182
+ if (typeof fact === "boolean") {
183
+ return {
184
+ known: true,
185
+ value: fact
186
+ };
187
+ }
188
+ if (typeof fact === "object" && fact !== null) {
189
+ return {
190
+ known: true,
191
+ reason: fact.reason,
192
+ value: fact.value
193
+ };
194
+ }
195
+ return {
196
+ known: false,
197
+ value: false
198
+ };
199
+ }
200
+ function doesRuleMatch(rule, args) {
201
+ if (!rule.field || !rule.operator) {
202
+ return false;
203
+ }
204
+ const value = getFieldValue(rule.field, args);
205
+ switch (rule.operator) {
206
+ case "eq":
207
+ return value === rule.value;
208
+ case "neq":
209
+ return value !== rule.value;
210
+ case "gt":
211
+ return typeof value === "number" && typeof rule.value === "number" && value > rule.value;
212
+ case "gte":
213
+ return typeof value === "number" && typeof rule.value === "number" && value >= rule.value;
214
+ case "lt":
215
+ return typeof value === "number" && typeof rule.value === "number" && value < rule.value;
216
+ case "lte":
217
+ return typeof value === "number" && typeof rule.value === "number" && value <= rule.value;
218
+ case "in":
219
+ return Array.isArray(rule.value) && rule.value.includes(value);
220
+ case "not_in":
221
+ return Array.isArray(rule.value) && !rule.value.includes(value);
222
+ case "exists":
223
+ return value !== undefined;
224
+ }
225
+ }
226
+ function getFieldValue(field, args) {
227
+ const normalizedField = field.startsWith("args.") ? field.slice("args.".length) : field;
228
+ return normalizedField.split(".").reduce((current, segment) => {
229
+ if (typeof current !== "object" || current === null) {
230
+ return undefined;
231
+ }
232
+ return current[segment];
233
+ }, args);
234
+ }
235
+ function isApproved(request) {
236
+ return request.approval?.approved === true && Boolean(request.approval.approvalId);
237
+ }
@@ -0,0 +1,6 @@
1
+ import type { EvalCandidateResult, EvalCase, EvalCaseResult, EvalSuiteResult, LoadedEvalCase, LoadEvalResultsResult, ManifestRegistry, RunEvalSuiteOptions } from "./types.js";
2
+ export declare function loadEvalResults(resultPath: string): Promise<LoadEvalResultsResult>;
3
+ export declare function runEvalSuite(registry: ManifestRegistry, candidates: EvalCandidateResult[], options?: RunEvalSuiteOptions): EvalSuiteResult;
4
+ export declare function scoreEvalCase(evalCase: LoadedEvalCase | EvalCase, candidate: EvalCandidateResult, registry: ManifestRegistry): EvalCaseResult;
5
+ export declare function formatEvalSuiteResult(result: EvalSuiteResult): string;
6
+ //# sourceMappingURL=eval-runner.d.ts.map