ai-capability-framework 1.0.3 → 1.1.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/CHANGELOG.md +42 -0
- package/README.md +38 -30
- package/ROADMAP.md +14 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +32 -1
- package/dist/discovery/bindings.d.ts +7 -0
- package/dist/discovery/bindings.d.ts.map +1 -0
- package/dist/discovery/bindings.js +24 -0
- package/dist/discovery/budget.d.ts +17 -0
- package/dist/discovery/budget.d.ts.map +1 -0
- package/dist/discovery/budget.js +77 -0
- package/dist/discovery/configuration.d.ts +10 -0
- package/dist/discovery/configuration.d.ts.map +1 -0
- package/dist/discovery/configuration.js +63 -0
- package/dist/discovery/corpus.d.ts +8 -0
- package/dist/discovery/corpus.d.ts.map +1 -0
- package/dist/discovery/corpus.js +64 -0
- package/dist/discovery/diagnostics.d.ts +15 -0
- package/dist/discovery/diagnostics.d.ts.map +1 -0
- package/dist/discovery/diagnostics.js +11 -0
- package/dist/discovery/discoverer.d.ts +23 -0
- package/dist/discovery/discoverer.d.ts.map +1 -0
- package/dist/discovery/discoverer.js +261 -0
- package/dist/discovery/embedding-configuration.d.ts +12 -0
- package/dist/discovery/embedding-configuration.d.ts.map +1 -0
- package/dist/discovery/embedding-configuration.js +81 -0
- package/dist/discovery/embedding-types.d.ts +75 -0
- package/dist/discovery/embedding-types.d.ts.map +1 -0
- package/dist/discovery/embedding-types.js +1 -0
- package/dist/discovery/embeddings.d.ts +38 -0
- package/dist/discovery/embeddings.d.ts.map +1 -0
- package/dist/discovery/embeddings.js +71 -0
- package/dist/discovery/evaluation-validation.d.ts +8 -0
- package/dist/discovery/evaluation-validation.d.ts.map +1 -0
- package/dist/discovery/evaluation-validation.js +19 -0
- package/dist/discovery/evaluation.d.ts +84 -0
- package/dist/discovery/evaluation.d.ts.map +1 -0
- package/dist/discovery/evaluation.js +310 -0
- package/dist/discovery/export.d.ts +14 -0
- package/dist/discovery/export.d.ts.map +1 -0
- package/dist/discovery/export.js +81 -0
- package/dist/discovery/facade.d.ts +82 -0
- package/dist/discovery/facade.d.ts.map +1 -0
- package/dist/discovery/facade.js +266 -0
- package/dist/discovery/fingerprint.d.ts +71 -0
- package/dist/discovery/fingerprint.d.ts.map +1 -0
- package/dist/discovery/fingerprint.js +67 -0
- package/dist/discovery/index-builder.d.ts +3 -0
- package/dist/discovery/index-builder.d.ts.map +1 -0
- package/dist/discovery/index-builder.js +129 -0
- package/dist/discovery/index-validation.d.ts +12 -0
- package/dist/discovery/index-validation.d.ts.map +1 -0
- package/dist/discovery/index-validation.js +117 -0
- package/dist/discovery/index.d.ts +32 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +18 -0
- package/dist/discovery/json-snapshot.d.ts +3 -0
- package/dist/discovery/json-snapshot.d.ts.map +1 -0
- package/dist/discovery/json-snapshot.js +11 -0
- package/dist/discovery/lexical.d.ts +12 -0
- package/dist/discovery/lexical.d.ts.map +1 -0
- package/dist/discovery/lexical.js +26 -0
- package/dist/discovery/metadata.d.ts +4 -0
- package/dist/discovery/metadata.d.ts.map +1 -0
- package/dist/discovery/metadata.js +33 -0
- package/dist/discovery/ranking.d.ts +7 -0
- package/dist/discovery/ranking.d.ts.map +1 -0
- package/dist/discovery/ranking.js +68 -0
- package/dist/discovery/replay.d.ts +50 -0
- package/dist/discovery/replay.d.ts.map +1 -0
- package/dist/discovery/replay.js +175 -0
- package/dist/discovery/retrieval-policy.d.ts +5 -0
- package/dist/discovery/retrieval-policy.d.ts.map +1 -0
- package/dist/discovery/retrieval-policy.js +10 -0
- package/dist/discovery/router.d.ts +39 -0
- package/dist/discovery/router.d.ts.map +1 -0
- package/dist/discovery/router.js +98 -0
- package/dist/discovery/selection-attempt.d.ts +20 -0
- package/dist/discovery/selection-attempt.d.ts.map +1 -0
- package/dist/discovery/selection-attempt.js +39 -0
- package/dist/discovery/selection-cards.d.ts +12 -0
- package/dist/discovery/selection-cards.d.ts.map +1 -0
- package/dist/discovery/selection-cards.js +79 -0
- package/dist/discovery/selection-configuration.d.ts +18 -0
- package/dist/discovery/selection-configuration.d.ts.map +1 -0
- package/dist/discovery/selection-configuration.js +71 -0
- package/dist/discovery/selection-response.d.ts +13 -0
- package/dist/discovery/selection-response.d.ts.map +1 -0
- package/dist/discovery/selection-response.js +47 -0
- package/dist/discovery/selection-types.d.ts +18 -0
- package/dist/discovery/selection-types.d.ts.map +1 -0
- package/dist/discovery/selection-types.js +1 -0
- package/dist/discovery/selection.d.ts +6 -0
- package/dist/discovery/selection.d.ts.map +1 -0
- package/dist/discovery/selection.js +145 -0
- package/dist/discovery/types.d.ts +291 -0
- package/dist/discovery/types.d.ts.map +1 -0
- package/dist/discovery/types.js +1 -0
- package/dist/discovery/usage.d.ts +2 -0
- package/dist/discovery/usage.d.ts.map +1 -0
- package/dist/discovery/usage.js +1 -0
- package/dist/discovery/vectors.d.ts +5 -0
- package/dist/discovery/vectors.d.ts.map +1 -0
- package/dist/discovery/vectors.js +43 -0
- package/dist/discovery/versions.d.ts +37 -0
- package/dist/discovery/versions.d.ts.map +1 -0
- package/dist/discovery/versions.js +36 -0
- package/dist/evals-live/live-eval-runner.d.ts +1 -0
- package/dist/evals-live/live-eval-runner.d.ts.map +1 -1
- package/dist/evals-live/live-eval-runner.js +12 -1
- package/dist/evals-live/types.d.ts +3 -0
- package/dist/evals-live/types.d.ts.map +1 -1
- package/dist/generated/manifest-types.d.ts +3655 -0
- package/dist/generated/manifest-types.d.ts.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +8 -0
- package/dist/mcp-server/server.d.ts.map +1 -1
- package/dist/mcp-server/server.js +11 -2
- package/dist/mcp-server/types.d.ts +4 -0
- package/dist/mcp-server/types.d.ts.map +1 -1
- package/dist/observability/redaction.d.ts.map +1 -1
- package/dist/observability/redaction.js +6 -2
- package/dist/observability/types.d.ts +1 -1
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/openai/index.d.ts +2 -0
- package/dist/openai/index.d.ts.map +1 -1
- package/dist/openai/index.js +1 -0
- package/dist/openai/responses-runner.d.ts +1 -1
- package/dist/openai/responses-runner.d.ts.map +1 -1
- package/dist/openai/responses-runner.js +134 -7
- package/dist/openai/selection-client.d.ts +28 -0
- package/dist/openai/selection-client.d.ts.map +1 -0
- package/dist/openai/selection-client.js +95 -0
- package/dist/openai/tool-loop.js +1 -1
- package/dist/openai/types.d.ts +19 -2
- package/dist/openai/types.d.ts.map +1 -1
- package/dist/replay/discovery-snapshot.d.ts +10 -0
- package/dist/replay/discovery-snapshot.d.ts.map +1 -0
- package/dist/replay/discovery-snapshot.js +17 -0
- package/dist/replay/recorder.d.ts.map +1 -1
- package/dist/replay/recorder.js +4 -0
- package/dist/replay/redaction.js +5 -1
- package/dist/replay/replayer.d.ts.map +1 -1
- package/dist/replay/replayer.js +14 -1
- package/dist/replay/types.d.ts +3 -0
- package/dist/replay/types.d.ts.map +1 -1
- package/dist/replay/validation.d.ts.map +1 -1
- package/dist/replay/validation.js +6 -0
- package/dist/runtime/bounded-json.d.ts +4 -0
- package/dist/runtime/bounded-json.d.ts.map +1 -0
- package/dist/runtime/bounded-json.js +74 -0
- package/dist/runtime/eligibility.d.ts +4 -0
- package/dist/runtime/eligibility.d.ts.map +1 -0
- package/dist/runtime/eligibility.js +128 -0
- package/dist/runtime/index.d.ts +5 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/provider-attempt.d.ts +11 -0
- package/dist/runtime/provider-attempt.d.ts.map +1 -0
- package/dist/runtime/provider-attempt.js +221 -0
- package/dist/runtime/provider-run.d.ts +10 -0
- package/dist/runtime/provider-run.d.ts.map +1 -0
- package/dist/runtime/provider-run.js +123 -0
- package/dist/runtime/provider-stop.d.ts +9 -0
- package/dist/runtime/provider-stop.d.ts.map +1 -0
- package/dist/runtime/provider-stop.js +28 -0
- package/dist/runtime/provider-types.d.ts +189 -0
- package/dist/runtime/provider-types.d.ts.map +1 -0
- package/dist/runtime/provider-types.js +1 -0
- package/dist/runtime/provider-usage.d.ts +9 -0
- package/dist/runtime/provider-usage.d.ts.map +1 -0
- package/dist/runtime/provider-usage.js +69 -0
- package/dist/runtime/router.d.ts.map +1 -1
- package/dist/runtime/router.js +3 -118
- package/dist/runtime/types.d.ts +9 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/validator.d.ts +3 -0
- package/dist/validator.d.ts.map +1 -1
- package/dist/validator.js +32 -0
- package/docs/api/public-api-policy.md +10 -0
- package/docs/api/stable-workflow.md +58 -0
- package/docs/api.md +7 -0
- package/docs/capability-manifests.md +24 -0
- package/docs/development.md +22 -0
- package/docs/discovery.md +186 -0
- package/docs/evals/discovery-live.md +199 -0
- package/docs/evals/discovery-qualification.md +158 -0
- package/docs/evals/overview.md +9 -0
- package/docs/getting-started/installation.md +11 -12
- package/docs/getting-started/openai-quickstart.md +16 -5
- package/docs/getting-started/provider-neutral-quickstart.md +247 -88
- package/docs/getting-started/quickstart.md +8 -30
- package/docs/index.md +10 -0
- package/docs/release.md +29 -5
- package/docs/runtime.md +27 -0
- package/docs/start-here.md +23 -134
- package/examples/12-capability-discovery-selection/README.md +346 -0
- package/examples/12-capability-discovery-selection/customer-cases.mjs +52 -0
- package/examples/12-capability-discovery-selection/customer-harness.mjs +91 -0
- package/examples/12-capability-discovery-selection/customer-request-example.mjs +320 -0
- package/examples/12-capability-discovery-selection/discovery-example.mjs +175 -0
- package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.suite.json +7887 -0
- package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v2.suite.json +8167 -0
- package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v3.suite.json +8342 -0
- package/examples/12-capability-discovery-selection/evaluation/application.mjs +305 -0
- package/examples/12-capability-discovery-selection/evaluation/campaign.mjs +248 -0
- package/examples/12-capability-discovery-selection/evaluation/clients.mjs +433 -0
- package/examples/12-capability-discovery-selection/evaluation/continuation.mjs +208 -0
- package/examples/12-capability-discovery-selection/evaluation/durable-budget.mjs +420 -0
- package/examples/12-capability-discovery-selection/evaluation/freeze.mjs +9 -0
- package/examples/12-capability-discovery-selection/evaluation/harness.mjs +618 -0
- package/examples/12-capability-discovery-selection/evaluation/mock-host.mjs +260 -0
- package/examples/12-capability-discovery-selection/evaluation/preflight.mjs +115 -0
- package/examples/12-capability-discovery-selection/evaluation/qualification-workflows.mjs +87 -0
- package/examples/12-capability-discovery-selection/evaluation/regrade.mjs +281 -0
- package/examples/12-capability-discovery-selection/evaluation/report.mjs +517 -0
- package/examples/12-capability-discovery-selection/evaluation/runner.mjs +613 -0
- package/examples/12-capability-discovery-selection/evaluation/saved-campaign.mjs +385 -0
- package/examples/12-capability-discovery-selection/evaluation/wording.mjs +115 -0
- package/examples/12-capability-discovery-selection/evaluation/workflow-application.mjs +378 -0
- package/examples/12-capability-discovery-selection/evaluation/workflow-cases.mjs +90 -0
- package/examples/12-capability-discovery-selection/evaluation-example.mjs +154 -0
- package/examples/12-capability-discovery-selection/hybrid-example.mjs +174 -0
- package/examples/12-capability-discovery-selection/mock-embeddings.mjs +101 -0
- package/examples/12-capability-discovery-selection/mock-selection.mjs +78 -0
- package/examples/12-capability-discovery-selection/pipeline-example.mjs +496 -0
- package/examples/12-capability-discovery-selection/retrieval-cases.mjs +31 -0
- package/examples/12-capability-discovery-selection/run-campaign-mock.mjs +40 -0
- package/examples/12-capability-discovery-selection/run-customer-mock.mjs +15 -0
- package/examples/12-capability-discovery-selection/run-evaluation-live.mjs +10 -0
- package/examples/12-capability-discovery-selection/run-evaluation-mock.mjs +3 -0
- package/examples/12-capability-discovery-selection/run-evaluation-regrade.mjs +71 -0
- package/examples/12-capability-discovery-selection/run-hybrid-mock.mjs +3 -0
- package/examples/12-capability-discovery-selection/run-mock.mjs +3 -0
- package/examples/12-capability-discovery-selection/run-pipeline-mock.mjs +2 -0
- package/examples/12-capability-discovery-selection/run-selector-mock.mjs +3 -0
- package/examples/12-capability-discovery-selection/selection-example.mjs +88 -0
- package/package.json +17 -4
- package/schemas/discovery/embedding-index.schema.json +71 -0
- package/schemas/discovery/eval-observations.schema.json +409 -0
- package/schemas/discovery/eval-suite.schema.json +375 -0
- package/schemas/discovery/metadata.schema.json +30 -0
- package/schemas/discovery/selection-response.schema.json +47 -0
- package/schemas/replay/discovery-snapshot.schema.json +335 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** Bounds strict JSON while traversing, before allocating a complete serialization or clone. */
|
|
2
|
+
export function boundedJsonSnapshot(value, maxBytes) {
|
|
3
|
+
let bytes = 0;
|
|
4
|
+
const ancestors = new Set();
|
|
5
|
+
const add = (text) => {
|
|
6
|
+
bytes += Buffer.byteLength(text);
|
|
7
|
+
if (bytes > maxBytes)
|
|
8
|
+
throw invalid();
|
|
9
|
+
return text;
|
|
10
|
+
};
|
|
11
|
+
const walk = (entry, depth) => {
|
|
12
|
+
if (depth > 32)
|
|
13
|
+
throw invalid();
|
|
14
|
+
if (entry === null || typeof entry === "boolean" || typeof entry === "number" && Number.isFinite(entry)) {
|
|
15
|
+
return add(JSON.stringify(entry));
|
|
16
|
+
}
|
|
17
|
+
if (typeof entry === "string") {
|
|
18
|
+
if (entry.length > maxBytes)
|
|
19
|
+
throw invalid();
|
|
20
|
+
return add(JSON.stringify(entry));
|
|
21
|
+
}
|
|
22
|
+
if (typeof entry !== "object" || !entry || ancestors.has(entry) || Object.getOwnPropertySymbols(entry).length)
|
|
23
|
+
throw invalid();
|
|
24
|
+
const array = Array.isArray(entry);
|
|
25
|
+
if (array ? entry.length > maxBytes : ![Object.prototype, null].includes(Object.getPrototypeOf(entry)))
|
|
26
|
+
throw invalid();
|
|
27
|
+
ancestors.add(entry);
|
|
28
|
+
try {
|
|
29
|
+
const descriptors = Object.getOwnPropertyDescriptors(entry);
|
|
30
|
+
const keys = Object.keys(descriptors);
|
|
31
|
+
if (keys.length > maxBytes)
|
|
32
|
+
throw invalid();
|
|
33
|
+
if (array) {
|
|
34
|
+
if (keys.length !== entry.length + 1)
|
|
35
|
+
throw invalid();
|
|
36
|
+
const pieces = [add("[")];
|
|
37
|
+
for (let index = 0; index < entry.length; index++) {
|
|
38
|
+
const descriptor = descriptors[String(index)];
|
|
39
|
+
if (!descriptor || !descriptor.enumerable || !("value" in descriptor))
|
|
40
|
+
throw invalid();
|
|
41
|
+
if (index)
|
|
42
|
+
pieces.push(add(","));
|
|
43
|
+
pieces.push(walk(descriptor.value, depth + 1));
|
|
44
|
+
}
|
|
45
|
+
pieces.push(add("]"));
|
|
46
|
+
return pieces.join("");
|
|
47
|
+
}
|
|
48
|
+
const pieces = [add("{")];
|
|
49
|
+
for (const [index, key] of keys.sort().entries()) {
|
|
50
|
+
const descriptor = descriptors[key];
|
|
51
|
+
if (!descriptor.enumerable || !("value" in descriptor))
|
|
52
|
+
throw invalid();
|
|
53
|
+
if (index)
|
|
54
|
+
pieces.push(add(","));
|
|
55
|
+
pieces.push(add(JSON.stringify(key)), add(":"), walk(descriptor.value, depth + 1));
|
|
56
|
+
}
|
|
57
|
+
pieces.push(add("}"));
|
|
58
|
+
return pieces.join("");
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
ancestors.delete(entry);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return JSON.parse(walk(value, 0));
|
|
65
|
+
}
|
|
66
|
+
export function deepFreeze(value) {
|
|
67
|
+
if (value && typeof value === "object") {
|
|
68
|
+
for (const entry of Object.values(value))
|
|
69
|
+
deepFreeze(entry);
|
|
70
|
+
Object.freeze(value);
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
function invalid() { return new Error("Bounded finite JSON without accessors or cycles is required."); }
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CapabilityEligibilityResult, CapabilityRouteRequest } from "./types.js";
|
|
2
|
+
/** Enumerates the full eligible registry without scoring or count truncation. */
|
|
3
|
+
export declare function filterEligibleCapabilities(request: CapabilityRouteRequest): CapabilityEligibilityResult;
|
|
4
|
+
//# sourceMappingURL=eligibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../src/runtime/eligibility.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAsB,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAU1G,iFAAiF;AACjF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,sBAAsB,GAAG,2BAA2B,CAUvG"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { isRestrictedCapability } from "../adapter-common.js";
|
|
2
|
+
import { readOnlyBlocks } from "../controls/index.js";
|
|
3
|
+
const riskRank = {
|
|
4
|
+
none: 0,
|
|
5
|
+
low: 1,
|
|
6
|
+
medium: 2,
|
|
7
|
+
high: 3,
|
|
8
|
+
critical: 4
|
|
9
|
+
};
|
|
10
|
+
/** Enumerates the full eligible registry without scoring or count truncation. */
|
|
11
|
+
export function filterEligibleCapabilities(request) {
|
|
12
|
+
const eligible = [];
|
|
13
|
+
const excluded = [];
|
|
14
|
+
const maxRiskTier = request.maxRiskTier ?? request.builtContext.runtimeContext.autonomy.maxRiskTier;
|
|
15
|
+
for (const capability of request.registry.capabilities) {
|
|
16
|
+
const reason = exclusionReason(request, capability, maxRiskTier);
|
|
17
|
+
if (reason)
|
|
18
|
+
excluded.push({ capabilityId: capability.manifest.id, reason });
|
|
19
|
+
else
|
|
20
|
+
eligible.push(capability);
|
|
21
|
+
}
|
|
22
|
+
return { eligible, excluded };
|
|
23
|
+
}
|
|
24
|
+
function exclusionReason(request, loadedCapability, maxRiskTier) {
|
|
25
|
+
const capability = loadedCapability.manifest;
|
|
26
|
+
if (request.includeCapabilityIds && !request.includeCapabilityIds.includes(capability.id)) {
|
|
27
|
+
return "Capability was not explicitly included.";
|
|
28
|
+
}
|
|
29
|
+
if (request.excludeCapabilityIds?.includes(capability.id)) {
|
|
30
|
+
return "Capability was explicitly excluded.";
|
|
31
|
+
}
|
|
32
|
+
const statusReason = statusExclusionReason(request, capability);
|
|
33
|
+
if (statusReason) {
|
|
34
|
+
return statusReason;
|
|
35
|
+
}
|
|
36
|
+
if (riskRank[capability.risk_tier] > riskRank[maxRiskTier]) {
|
|
37
|
+
return `Capability risk tier ${capability.risk_tier} exceeds ${maxRiskTier}.`;
|
|
38
|
+
}
|
|
39
|
+
if (isRestrictedCapability(capability) && !request.includeRestricted) {
|
|
40
|
+
return "Restricted side-effect capability was excluded.";
|
|
41
|
+
}
|
|
42
|
+
const controlsReason = controlsExclusionReason(request, loadedCapability);
|
|
43
|
+
if (controlsReason) {
|
|
44
|
+
return controlsReason;
|
|
45
|
+
}
|
|
46
|
+
const contextReason = requiredContextReason(capability, request.builtContext.runtimeContext);
|
|
47
|
+
if (contextReason) {
|
|
48
|
+
return contextReason;
|
|
49
|
+
}
|
|
50
|
+
const permissionReason = permissionExclusionReason(request, capability);
|
|
51
|
+
if (permissionReason) {
|
|
52
|
+
return permissionReason;
|
|
53
|
+
}
|
|
54
|
+
if (request.allowedDomains && (!capability.domain || !request.allowedDomains.includes(capability.domain))) {
|
|
55
|
+
return "Capability domain was not allowed.";
|
|
56
|
+
}
|
|
57
|
+
if (request.allowedCapabilityTypes && !request.allowedCapabilityTypes.includes(capability.capability_type)) {
|
|
58
|
+
return "Capability type was not allowed.";
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function controlsExclusionReason(request, loadedCapability) {
|
|
63
|
+
if (!request.controls) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const decision = request.controls.evaluate({
|
|
67
|
+
capability: loadedCapability,
|
|
68
|
+
capabilityId: loadedCapability.manifest.id,
|
|
69
|
+
domain: loadedCapability.manifest.domain,
|
|
70
|
+
operation: "export",
|
|
71
|
+
registry: request.registry,
|
|
72
|
+
riskTier: loadedCapability.manifest.risk_tier,
|
|
73
|
+
runtimeContext: request.builtContext.runtimeContext
|
|
74
|
+
});
|
|
75
|
+
if (decision.status === "denied") {
|
|
76
|
+
return decision.reasons[0]?.message ?? "Capability was denied by runtime controls.";
|
|
77
|
+
}
|
|
78
|
+
if (decision.status === "read_only" && readOnlyBlocks({
|
|
79
|
+
capability: loadedCapability,
|
|
80
|
+
capabilityId: loadedCapability.manifest.id,
|
|
81
|
+
operation: "export",
|
|
82
|
+
runtimeContext: request.builtContext.runtimeContext
|
|
83
|
+
})) {
|
|
84
|
+
return decision.reasons[0]?.message ?? "Read-only controls exclude this capability.";
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function statusExclusionReason(request, capability) {
|
|
89
|
+
switch (capability.status) {
|
|
90
|
+
case "active":
|
|
91
|
+
return null;
|
|
92
|
+
case "disabled":
|
|
93
|
+
return request.includeDisabledForTests ? null : "Disabled capability was excluded.";
|
|
94
|
+
case "deprecated":
|
|
95
|
+
return request.includeDeprecated ? null : "Deprecated capability was excluded.";
|
|
96
|
+
case "draft":
|
|
97
|
+
return request.includeDraft ? null : "Draft capability was excluded.";
|
|
98
|
+
case "experimental":
|
|
99
|
+
return request.includeExperimental ? null : "Experimental capability was excluded.";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function requiredContextReason(capability, context) {
|
|
103
|
+
if (capability.authorization.requires_user_context && !hasText(context.subject.userId)) {
|
|
104
|
+
return "Capability requires user context.";
|
|
105
|
+
}
|
|
106
|
+
if (capability.authorization.tenant_scoped && !hasText(context.account.tenantId)) {
|
|
107
|
+
return "Capability requires tenant context.";
|
|
108
|
+
}
|
|
109
|
+
if (!hasText(context.account.accountId)) {
|
|
110
|
+
return "Runtime account context is required.";
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
function permissionExclusionReason(request, capability) {
|
|
115
|
+
const override = request.capabilityPermissions?.[capability.id];
|
|
116
|
+
if (override && !override.allowed) {
|
|
117
|
+
return override.reason ?? "Capability permission adapter denied access.";
|
|
118
|
+
}
|
|
119
|
+
const permissionSet = new Set([
|
|
120
|
+
...request.builtContext.runtimeContext.subject.permissions,
|
|
121
|
+
...(override?.permissions ?? [])
|
|
122
|
+
]);
|
|
123
|
+
const missingPermission = capability.authorization.permissions.find((permission) => !permissionSet.has(permission));
|
|
124
|
+
return missingPermission ? `Missing permission ${missingPermission}.` : null;
|
|
125
|
+
}
|
|
126
|
+
function hasText(value) {
|
|
127
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
128
|
+
}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export { buildRuntimeContext, DefaultContextBuilder, StaticAuthPlatformAdapter,
|
|
|
8
8
|
export { DefaultPolicyBroker } from "./policy.js";
|
|
9
9
|
export { DefaultRedactionPolicy } from "./redaction.js";
|
|
10
10
|
export { DefaultCapabilityRouter, formatCapabilitySliceForModel } from "./router.js";
|
|
11
|
+
export { filterEligibleCapabilities } from "./eligibility.js";
|
|
11
12
|
export { InMemoryApprovalStore, InMemoryIdempotencyStore, InMemoryPreparedActionStore } from "./stores.js";
|
|
12
|
-
export type { AicfActionLifecycleManagerOptions, AicfActionState, AicfActionVerificationState, AicfAccountContext, AicfApprovalDecision, AicfApprovalRequirement, AicfApprovalStore, AicfAuditEvent, AicfAuditSink, AicfAuthPlatformAdapter, AicfAutonomyContext, AicfBuiltContext, AicfCapabilityHandler, AicfCapabilityRouter, AicfCommitActionInput, AicfCommittedAction, AicfCommitResult, AicfContextBuilder, AicfContextBuilderInput, AicfContextItem, AicfHostPolicyHook, AicfIdempotencyStore, AicfPolicyBroker, AicfPolicyDecision, AicfPolicyDecisionStatus, AicfPolicyEvaluationInput, AicfPolicyReason, AicfPrepareActionInput, AicfPreparedAction, AicfPreparedActionPreview, AicfPreparedActionUpdateStateInput, AicfPreparedActionVerification, AicfPreparedActionStore, AicfRedaction, AicfRedactionPolicy, AicfRecordApprovalInput, AicfRuntimeContext, AicfRuntimeControls, AicfRuntimeEnvironment, AicfRuntimeErrorCode, AicfRuntimeEnvelopeError, AicfRuntimeToolResultEnvelope, AicfRuntimeUserInput, AicfRuntimeWarning, AicfSubjectContext, AicfToolExecutionRequest, AicfToolExecutorOptions, AicfToolResultOperation, AicfToolResultStatus, AicfVerificationResult, AicfWorkflowContext, BuildRuntimeContextInput, CapabilityRouteRequest, CapabilitySlice, CapabilitySliceItem, CreateToolEnvelopeInput, JsonObject, JsonValue, LoadedCapabilityManifest, ManifestRegistry, ModelSafeEnvelopeOptions, RuntimeCapabilitySlice } from "./types.js";
|
|
13
|
+
export type { AicfActionLifecycleManagerOptions, AicfActionState, AicfActionVerificationState, AicfAccountContext, AicfApprovalDecision, AicfApprovalRequirement, AicfApprovalStore, AicfAuditEvent, AicfAuditSink, AicfAuthPlatformAdapter, AicfAutonomyContext, AicfBuiltContext, AicfCapabilityHandler, AicfCapabilityRouter, AicfCommitActionInput, AicfCommittedAction, AicfCommitResult, AicfContextBuilder, AicfContextBuilderInput, AicfContextItem, AicfHostPolicyHook, AicfIdempotencyStore, AicfPolicyBroker, AicfPolicyDecision, AicfPolicyDecisionStatus, AicfPolicyEvaluationInput, AicfPolicyReason, AicfPrepareActionInput, AicfPreparedAction, AicfPreparedActionPreview, AicfPreparedActionUpdateStateInput, AicfPreparedActionVerification, AicfPreparedActionStore, AicfRedaction, AicfRedactionPolicy, AicfRecordApprovalInput, AicfRuntimeContext, AicfRuntimeControls, AicfRuntimeEnvironment, AicfRuntimeErrorCode, AicfRuntimeEnvelopeError, AicfRuntimeToolResultEnvelope, AicfRuntimeUserInput, AicfRuntimeWarning, AicfSubjectContext, AicfToolExecutionRequest, AicfToolExecutorOptions, AicfToolResultOperation, AicfToolResultStatus, AicfVerificationResult, AicfWorkflowContext, BuildRuntimeContextInput, CapabilityRouteRequest, CapabilityEligibilityResult, CapabilitySlice, CapabilitySliceItem, CreateToolEnvelopeInput, JsonObject, JsonValue, LoadedCapabilityManifest, ManifestRegistry, ModelSafeEnvelopeOptions, RuntimeCapabilitySlice } from "./types.js";
|
|
14
|
+
export { createAicfProviderRun } from "./provider-run.js";
|
|
15
|
+
export type { AicfProviderRun, CreateAicfProviderRunOptions, ProviderRunSnapshot, ProviderAttemptInput, ProviderAttemptResult, ProviderBudgetScope, ProviderBudgetLedger, ProviderUsage, ModelUsage, UsageQuantity, ProviderModelConfiguration, ProviderTraceMetadata } from "./provider-types.js";
|
|
16
|
+
export type { ProviderDiagnostic, ProviderStage, ProviderInvocation, ProviderUsageTotals, ProviderStageUsage, ProviderAttemptReservation, ProviderAttemptSettlement, ProviderBudgetReservation, ProviderFailureCode } from "./provider-types.js";
|
|
13
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,6BAA6B,EAC7B,uBAAuB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,2BAA2B,EAC5B,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,iCAAiC,EACjC,eAAe,EACf,2BAA2B,EAC3B,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,EACzB,kCAAkC,EAClC,8BAA8B,EAC9B,uBAAuB,EACvB,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,6BAA6B,EAC7B,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,UAAU,EACV,SAAS,EACT,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,6BAA6B,EAC7B,uBAAuB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,2BAA2B,EAC5B,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,iCAAiC,EACjC,eAAe,EACf,2BAA2B,EAC3B,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,EACzB,kCAAkC,EAClC,8BAA8B,EAC9B,uBAAuB,EACvB,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,6BAA6B,EAC7B,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,2BAA2B,EAC3B,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,UAAU,EACV,SAAS,EACT,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EAAE,eAAe,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACnS,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,kBAAkB,EAC1G,0BAA0B,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/runtime/index.js
CHANGED
|
@@ -8,4 +8,6 @@ export { buildRuntimeContext, DefaultContextBuilder, StaticAuthPlatformAdapter,
|
|
|
8
8
|
export { DefaultPolicyBroker } from "./policy.js";
|
|
9
9
|
export { DefaultRedactionPolicy } from "./redaction.js";
|
|
10
10
|
export { DefaultCapabilityRouter, formatCapabilitySliceForModel } from "./router.js";
|
|
11
|
+
export { filterEligibleCapabilities } from "./eligibility.js";
|
|
11
12
|
export { InMemoryApprovalStore, InMemoryIdempotencyStore, InMemoryPreparedActionStore } from "./stores.js";
|
|
13
|
+
export { createAicfProviderRun } from "./provider-run.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BudgetUsage } from "../controls/types.js";
|
|
2
|
+
import type { ProviderAttemptInput, ProviderAttemptResult, ProviderBudgetScope, ProviderStageUsage } from "./provider-types.js";
|
|
3
|
+
export interface ProviderAttemptObserver {
|
|
4
|
+
attemptId?: string;
|
|
5
|
+
update(receipt: ProviderStageUsage): void;
|
|
6
|
+
accountingFailed(): void;
|
|
7
|
+
projectedUsage(requested: BudgetUsage): BudgetUsage;
|
|
8
|
+
}
|
|
9
|
+
/** A single attempt. The completion remains observed after the caller's cancellation race. */
|
|
10
|
+
export declare function executeProviderAttempt<T, C extends string>(input: ProviderAttemptInput<T, C>, budget: ProviderBudgetScope, observer?: ProviderAttemptObserver): Promise<ProviderAttemptResult<T, C>>;
|
|
11
|
+
//# sourceMappingURL=provider-attempt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-attempt.d.ts","sourceRoot":"","sources":["../../src/runtime/provider-attempt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EACV,oBAAoB,EAAE,qBAAqB,EAAE,mBAAmB,EAChE,kBAAkB,EACnB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,WAAW,uBAAuB;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,gBAAgB,IAAI,IAAI,CAAC;IACzB,cAAc,CAAC,SAAS,EAAE,WAAW,GAAG,WAAW,CAAC;CACrD;AAED,8FAA8F;AAC9F,wBAAsB,sBAAsB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,EAC9D,KAAK,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,mBAAmB,EAAE,QAAQ,CAAC,EAAE,uBAAuB,GACjG,OAAO,CAAC,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAqItC"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
|
+
import { stopGate, stopReason } from "./provider-stop.js";
|
|
3
|
+
import { aggregateProviderUsage, knownUsage, ownValue, usageQuantities, validReportedUsage } from "./provider-usage.js";
|
|
4
|
+
/** A single attempt. The completion remains observed after the caller's cancellation race. */
|
|
5
|
+
export async function executeProviderAttempt(input, budget, observer) {
|
|
6
|
+
const request = Object.freeze({ ...input, configuration: Object.freeze({ ...input.configuration }) });
|
|
7
|
+
if (!request.runId?.trim() || !request.requestId?.trim() || !Number.isFinite(request.deadlineAt)
|
|
8
|
+
|| !validConfiguration(request.configuration, request.stage)
|
|
9
|
+
|| !positive(request.attempt ?? 1)
|
|
10
|
+
|| !positive(request.maxInputTokens) || !["embedding_build", "query_embedding"].includes(request.stage) && !positive(request.maxOutputTokens)
|
|
11
|
+
|| request.maxOutputTokens !== undefined && !positive(request.maxOutputTokens)
|
|
12
|
+
|| typeof request.mock !== "boolean" || !Number.isSafeInteger(budget.policy.maxProviderCallsPerRun) || budget.policy.maxProviderCallsPerRun < 0
|
|
13
|
+
|| !budget.ledger || typeof budget.ledger.reserve !== "function" || typeof budget.ledger.settle !== "function"
|
|
14
|
+
|| request.maxEstimatedCostPerCall !== undefined && !nonnegative(request.maxEstimatedCostPerCall)
|
|
15
|
+
|| budget.policy.maxEstimatedCostPerRun !== undefined && request.maxEstimatedCostPerCall === undefined) {
|
|
16
|
+
throw new Error("Provider attempts require valid invocation bounds, token limits and a finite shared budget.");
|
|
17
|
+
}
|
|
18
|
+
const started = performance.now();
|
|
19
|
+
let attempted = false;
|
|
20
|
+
let reasonCode;
|
|
21
|
+
let progress = { usage: {}, providerAttempts: 1, completedResponses: 0 };
|
|
22
|
+
const attempt = request.attempt ?? 1;
|
|
23
|
+
const receipt = (outcome, pending = false) => ({
|
|
24
|
+
...(observer?.attemptId ? { attemptId: observer.attemptId } : {}), ...(reasonCode ? { reasonCode } : {}),
|
|
25
|
+
stage: request.stage, configuration: request.configuration, mock: request.mock, outcome, pending, cacheHits: 0,
|
|
26
|
+
providerAttempts: progress.providerAttempts, completedResponses: progress.completedResponses,
|
|
27
|
+
retryAttempts: Math.max(0, progress.providerAttempts - 1), repairAttempts: request.stage === "selection_repair" ? 1 : 0,
|
|
28
|
+
elapsedMs: performance.now() - started, ...usageQuantities(progress.usage, progress.providerAttempts === 0 && progress.completedResponses === 0)
|
|
29
|
+
});
|
|
30
|
+
const usage = (outcome, pending = false) => {
|
|
31
|
+
const entry = receipt(outcome, pending);
|
|
32
|
+
if (attempted)
|
|
33
|
+
observer?.update(entry);
|
|
34
|
+
return aggregateProviderUsage(attempted ? [entry] : [], performance.now() - started);
|
|
35
|
+
};
|
|
36
|
+
const failed = (code, pending = false, issues = []) => {
|
|
37
|
+
reasonCode = code;
|
|
38
|
+
return { code, issues, usage: usage(progress.completedResponses ? "discarded"
|
|
39
|
+
: code === "cancelled" || code === "deadline_exceeded" ? "cancelled" : "failed", pending) };
|
|
40
|
+
};
|
|
41
|
+
const requestedUsage = { providerCalls: 1, inputTokens: request.maxInputTokens,
|
|
42
|
+
...(request.maxOutputTokens === undefined ? {} : { outputTokens: request.maxOutputTokens }), retries: 0,
|
|
43
|
+
...(request.maxEstimatedCostPerCall === undefined ? {} : { estimatedCost: request.maxEstimatedCostPerCall }) };
|
|
44
|
+
const guard = () => {
|
|
45
|
+
const stop = stopReason(request);
|
|
46
|
+
if (stop)
|
|
47
|
+
return stop;
|
|
48
|
+
if (budget.policy.maxProviderCallsPerRun === 0)
|
|
49
|
+
return "budget_denied";
|
|
50
|
+
const code = request.guard?.();
|
|
51
|
+
if (code)
|
|
52
|
+
return code;
|
|
53
|
+
if (request.controls?.evaluate({ operation: "provider_call", providerId: request.configuration.provider,
|
|
54
|
+
model: request.configuration.model, runtimeContext: request.runtimeContext,
|
|
55
|
+
usage: observer?.projectedUsage(requestedUsage) ?? requestedUsage }).status === "denied")
|
|
56
|
+
return "control_denied";
|
|
57
|
+
return undefined;
|
|
58
|
+
};
|
|
59
|
+
try {
|
|
60
|
+
const code = guard();
|
|
61
|
+
if (code)
|
|
62
|
+
return failed(code);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
observer?.accountingFailed();
|
|
66
|
+
return failed("accounting_unavailable");
|
|
67
|
+
}
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
const gate = stopGate(request, controller);
|
|
70
|
+
const settle = async (id, outcome, noAttempt = false) => {
|
|
71
|
+
try {
|
|
72
|
+
await budget.ledger.settle(id, { outcome, usage: noAttempt ? {} : { ...progress.usage },
|
|
73
|
+
providerAttempts: noAttempt ? 0 : progress.providerAttempts, completedResponses: noAttempt ? 0 : progress.completedResponses,
|
|
74
|
+
elapsedMs: performance.now() - started });
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
observer?.accountingFailed();
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
try {
|
|
83
|
+
const reservation = Promise.resolve().then(() => budget.ledger.reserve({ runId: request.runId, requestId: request.requestId,
|
|
84
|
+
deadlineAt: request.deadlineAt, signal: controller.signal, stage: request.stage, attempt,
|
|
85
|
+
configuration: request.configuration, policy: budget.policy, requestedUsage
|
|
86
|
+
})).then(value => ({ value }), () => ({ error: true }));
|
|
87
|
+
const reserved = await Promise.race([reservation, gate.promise]);
|
|
88
|
+
if (typeof reserved === "string") {
|
|
89
|
+
void reservation.then(async (result) => {
|
|
90
|
+
if ("error" in result)
|
|
91
|
+
observer?.accountingFailed();
|
|
92
|
+
if ("value" in result && result.value.status === "reserved")
|
|
93
|
+
await settle(result.value.reservationId, "cancelled", true);
|
|
94
|
+
}).catch(() => observer?.accountingFailed());
|
|
95
|
+
return failed(reserved);
|
|
96
|
+
}
|
|
97
|
+
if ("error" in reserved || !reserved.value || !["denied", "reserved"].includes(reserved.value.status)) {
|
|
98
|
+
observer?.accountingFailed();
|
|
99
|
+
return failed("accounting_unavailable");
|
|
100
|
+
}
|
|
101
|
+
if (reserved.value.status === "denied")
|
|
102
|
+
return failed("budget_denied");
|
|
103
|
+
const id = reserved.value.reservationId;
|
|
104
|
+
if (typeof id !== "string" || !id.trim()) {
|
|
105
|
+
observer?.accountingFailed();
|
|
106
|
+
return failed("accounting_unavailable");
|
|
107
|
+
}
|
|
108
|
+
let before;
|
|
109
|
+
try {
|
|
110
|
+
before = guard();
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
before = "accounting_unavailable";
|
|
114
|
+
observer?.accountingFailed();
|
|
115
|
+
}
|
|
116
|
+
if (before) {
|
|
117
|
+
const settled = await Promise.race([settle(id, "cancelled", true), gate.promise]);
|
|
118
|
+
return failed(settled === false ? "accounting_unavailable" : before);
|
|
119
|
+
}
|
|
120
|
+
attempted = true;
|
|
121
|
+
observer?.update(receipt("completed", true));
|
|
122
|
+
const completion = (async () => {
|
|
123
|
+
let code;
|
|
124
|
+
let value;
|
|
125
|
+
let issues = [];
|
|
126
|
+
try {
|
|
127
|
+
const response = await request.invoke({ signal: controller.signal, deadlineAt: request.deadlineAt, attempt, beforeProviderCall: () => {
|
|
128
|
+
const changed = guard();
|
|
129
|
+
if (changed)
|
|
130
|
+
throw new ProviderPreflightError(changed);
|
|
131
|
+
} });
|
|
132
|
+
progress.completedResponses = 1;
|
|
133
|
+
progress.usage = knownUsage(ownValue(response, "usage"));
|
|
134
|
+
const reported = ownValue(response, "providerAttempts");
|
|
135
|
+
if (Number.isSafeInteger(reported) && reported >= 0)
|
|
136
|
+
progress.providerAttempts = Math.max(1, reported);
|
|
137
|
+
if (reported !== 1 || !validReportedUsage(ownValue(response, "usage"))
|
|
138
|
+
|| progress.usage.inputTokens !== undefined && progress.usage.inputTokens > request.maxInputTokens
|
|
139
|
+
|| request.maxOutputTokens !== undefined && progress.usage.outputTokens !== undefined && progress.usage.outputTokens > request.maxOutputTokens)
|
|
140
|
+
code = request.protocolError ?? "provider_unavailable";
|
|
141
|
+
else {
|
|
142
|
+
const validated = request.validate(response);
|
|
143
|
+
value = validated.value;
|
|
144
|
+
code = validated.code;
|
|
145
|
+
issues = validated.issues ?? [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (!progress.completedResponses) {
|
|
150
|
+
progress.usage = knownUsage(ownValue(error, "usage"));
|
|
151
|
+
const reported = ownValue(error, "providerAttempts"), responses = ownValue(error, "completedResponses");
|
|
152
|
+
if (Number.isSafeInteger(reported) && reported >= 0)
|
|
153
|
+
progress.providerAttempts = reported;
|
|
154
|
+
if (Number.isSafeInteger(responses) && responses >= 0)
|
|
155
|
+
progress.completedResponses = responses;
|
|
156
|
+
progress.providerAttempts = Math.max(progress.providerAttempts, progress.completedResponses);
|
|
157
|
+
code = error instanceof ProviderPreflightError ? error.code : "provider_unavailable";
|
|
158
|
+
}
|
|
159
|
+
else
|
|
160
|
+
code = request.protocolError ?? "provider_unavailable";
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
if (request.estimateCost && progress.usage.estimatedCost === undefined && progress.providerAttempts > 0) {
|
|
164
|
+
const cost = request.estimateCost(Object.freeze({ ...progress.usage }), request.configuration);
|
|
165
|
+
if (cost !== undefined && !nonnegative(cost)) {
|
|
166
|
+
code = "accounting_unavailable";
|
|
167
|
+
observer?.accountingFailed();
|
|
168
|
+
}
|
|
169
|
+
else if (cost !== undefined)
|
|
170
|
+
progress.usage.estimatedCost = cost;
|
|
171
|
+
}
|
|
172
|
+
const changed = guard();
|
|
173
|
+
if (changed)
|
|
174
|
+
code = changed;
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
code = "accounting_unavailable";
|
|
178
|
+
observer?.accountingFailed();
|
|
179
|
+
}
|
|
180
|
+
const stop = stopReason(request);
|
|
181
|
+
if (stop)
|
|
182
|
+
code = stop;
|
|
183
|
+
const outcome = code ? progress.completedResponses ? "discarded" : stop ? "cancelled" : "failed" : "completed";
|
|
184
|
+
if (!await settle(id, outcome))
|
|
185
|
+
code = "accounting_unavailable";
|
|
186
|
+
try {
|
|
187
|
+
const changed = guard();
|
|
188
|
+
if (changed)
|
|
189
|
+
code = changed;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
code = "accounting_unavailable";
|
|
193
|
+
observer?.accountingFailed();
|
|
194
|
+
}
|
|
195
|
+
return code ? failed(code, false, issues) : { value, issues, usage: usage("completed") };
|
|
196
|
+
})().catch(() => { observer?.accountingFailed(); return failed("accounting_unavailable"); });
|
|
197
|
+
const finished = await Promise.race([completion, gate.promise]);
|
|
198
|
+
return typeof finished === "string" ? failed(finished, true) : finished;
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
gate.dispose();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function positive(value) { return typeof value === "number" && Number.isSafeInteger(value) && value > 0; }
|
|
205
|
+
function nonnegative(value) { return typeof value === "number" && Number.isFinite(value) && value >= 0; }
|
|
206
|
+
function validConfiguration(value, stage) {
|
|
207
|
+
const embedding = ["embedding_build", "query_embedding"].includes(stage);
|
|
208
|
+
return [value.provider, value.model, value.configurationId].every(item => typeof item === "string" && !!item.trim())
|
|
209
|
+
&& (value.revision === undefined || typeof value.revision === "string" && !!value.revision.trim())
|
|
210
|
+
&& Object.keys(value).every(key => ["provider", "model", "configurationId", "revision", ...(embedding ? ["dimensions"] : [])].includes(key));
|
|
211
|
+
}
|
|
212
|
+
class ProviderPreflightError extends Error {
|
|
213
|
+
code;
|
|
214
|
+
providerAttempts = 0;
|
|
215
|
+
completedResponses = 0;
|
|
216
|
+
usage = {};
|
|
217
|
+
constructor(code) {
|
|
218
|
+
super("Provider preflight denied the attempt.");
|
|
219
|
+
this.code = code;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AicfProviderRun, CreateAicfProviderRunOptions, ProviderTraceMetadata } from "./provider-types.js";
|
|
2
|
+
export declare function isAicfProviderRun(value: unknown): value is AicfProviderRun;
|
|
3
|
+
/** One in-process run journal; enforcement remains in the supplied reservation ledger. */
|
|
4
|
+
export declare function createAicfProviderRun(options: CreateAicfProviderRunOptions): AicfProviderRun;
|
|
5
|
+
export declare function assertProviderRunScope(run: AicfProviderRun, runId: string, budget?: {
|
|
6
|
+
ledger: unknown;
|
|
7
|
+
policy: unknown;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare function sanitizeProviderTraceMetadata(metadata: ProviderTraceMetadata): Record<string, import("../types.js").JsonValue>;
|
|
10
|
+
//# sourceMappingURL=provider-run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-run.d.ts","sourceRoot":"","sources":["../../src/runtime/provider-run.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EAAE,4BAA4B,EACzB,qBAAqB,EAC1C,MAAM,qBAAqB,CAAC;AAI7B,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED,0FAA0F;AAC1F,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,eAAe,CAsE5F;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAG/H;AAGD,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,aAAa,EAAE,SAAS,CAAC,CAkB9H"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { performance } from "node:perf_hooks";
|
|
3
|
+
import { executeProviderAttempt } from "./provider-attempt.js";
|
|
4
|
+
import { aggregateProviderUsage, freezeProviderValue } from "./provider-usage.js";
|
|
5
|
+
const issued = new WeakSet();
|
|
6
|
+
export function isAicfProviderRun(value) {
|
|
7
|
+
return !!value && typeof value === "object" && issued.has(value);
|
|
8
|
+
}
|
|
9
|
+
/** One in-process run journal; enforcement remains in the supplied reservation ledger. */
|
|
10
|
+
export function createAicfProviderRun(options) {
|
|
11
|
+
if (!options.runId?.trim() || !Number.isFinite(options.deadlineAt) || !options.budget?.ledger
|
|
12
|
+
|| typeof options.budget.ledger.reserve !== "function" || typeof options.budget.ledger.settle !== "function"
|
|
13
|
+
|| !Number.isSafeInteger(options.budget.policy.maxProviderCallsPerRun) || options.budget.policy.maxProviderCallsPerRun < 1
|
|
14
|
+
|| options.signal !== undefined && !(options.signal instanceof AbortSignal))
|
|
15
|
+
throw new Error("A provider run requires a run ID, deadline and finite budget.");
|
|
16
|
+
const runId = options.runId, deadlineAt = options.deadlineAt, signal = options.signal;
|
|
17
|
+
const budget = Object.freeze({ ledger: options.budget.ledger, policy: freezeProviderValue(structuredClone(options.budget.policy)) });
|
|
18
|
+
const sourcePolicy = options.budget.policy;
|
|
19
|
+
const started = performance.now();
|
|
20
|
+
const records = new Map();
|
|
21
|
+
const diagnostics = [];
|
|
22
|
+
const events = [];
|
|
23
|
+
let sequence = 0, broken = false;
|
|
24
|
+
const diagnostic = (code) => { if (!diagnostics.some(item => item.code === code) && diagnostics.length < 16)
|
|
25
|
+
diagnostics.push({ code, message: "A managed provider-run boundary reported a failure." }); };
|
|
26
|
+
const record = (type, requestId, metadata) => {
|
|
27
|
+
if (events.length >= 256) {
|
|
28
|
+
diagnostic("trace_overflow");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const event = freezeProviderValue({ type, runId: bounded(runId), requestId: bounded(requestId), timestamp: new Date().toISOString(),
|
|
32
|
+
attributes: sanitizeProviderTraceMetadata(metadata) });
|
|
33
|
+
events.push(event);
|
|
34
|
+
try {
|
|
35
|
+
void Promise.resolve(options.traceSink?.emit(event)).catch(() => diagnostic("trace_sink_failed"));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
diagnostic("trace_sink_failed");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const snapshot = () => {
|
|
42
|
+
const stages = [...records.values()];
|
|
43
|
+
const elapsedMs = performance.now() - started;
|
|
44
|
+
return freezeProviderValue({ runId, usage: aggregateProviderUsage(stages, elapsedMs), elapsedMs,
|
|
45
|
+
attemptElapsedMs: stages.reduce((sum, item) => sum + item.elapsedMs, 0), pendingAttempts: stages.filter(item => item.pending).length,
|
|
46
|
+
diagnostics: diagnostics.map(item => ({ ...item })), events: [...events] });
|
|
47
|
+
};
|
|
48
|
+
const run = {
|
|
49
|
+
runId, deadlineAt, signal, budget, record, snapshot,
|
|
50
|
+
async attempt(input) {
|
|
51
|
+
if (input.runId !== undefined && input.runId !== runId || input.runtimeContext && input.runtimeContext.runId !== runId)
|
|
52
|
+
throw new Error("Provider run ID mismatch.");
|
|
53
|
+
if (!isDeepStrictEqual(sourcePolicy, budget.policy)) {
|
|
54
|
+
broken = true;
|
|
55
|
+
diagnostic("accounting_unavailable");
|
|
56
|
+
}
|
|
57
|
+
const attemptId = runId + ":" + ++sequence;
|
|
58
|
+
const signals = [signal, input.signal].filter((item) => !!item);
|
|
59
|
+
let lastPending = true;
|
|
60
|
+
return executeProviderAttempt({ ...input, runId, deadlineAt: Math.min(deadlineAt, input.deadlineAt ?? Infinity),
|
|
61
|
+
signal: signals.length ? AbortSignal.any(signals) : undefined,
|
|
62
|
+
guard: () => broken ? "accounting_unavailable" : input.guard?.()
|
|
63
|
+
}, budget, {
|
|
64
|
+
attemptId,
|
|
65
|
+
accountingFailed() { broken = true; diagnostic("accounting_unavailable"); },
|
|
66
|
+
projectedUsage(requested) {
|
|
67
|
+
const stages = [...records.values()];
|
|
68
|
+
const result = { ...requested, runtimeMs: performance.now() - started };
|
|
69
|
+
for (const [key, quantity] of [["inputTokens", "inputTokens"], ["outputTokens", "outputTokens"], ["estimatedCost", "estimatedCost"]])
|
|
70
|
+
result[key] = (requested[key] ?? 0) + stages.reduce((sum, item) => sum + item[quantity].knownSubtotal, 0);
|
|
71
|
+
result.providerCalls = (requested.providerCalls ?? 0) + stages.reduce((sum, item) => sum + item.providerAttempts, 0);
|
|
72
|
+
// The current pending record already represents this attempt after reservation.
|
|
73
|
+
if (records.has(attemptId))
|
|
74
|
+
result.providerCalls -= records.get(attemptId).providerAttempts;
|
|
75
|
+
return result;
|
|
76
|
+
},
|
|
77
|
+
update(receipt) {
|
|
78
|
+
const first = !records.has(attemptId);
|
|
79
|
+
records.set(attemptId, freezeProviderValue({ ...receipt, attemptId }));
|
|
80
|
+
if (first || lastPending && !receipt.pending)
|
|
81
|
+
record(first ? "provider.attempt.start" : "provider.attempt.end", input.requestId, {
|
|
82
|
+
attemptId, stage: receipt.stage, configurationId: receipt.configuration.configurationId, provider: receipt.configuration.provider,
|
|
83
|
+
model: receipt.configuration.model, mock: receipt.mock, outcome: receipt.outcome, reasonCode: receipt.reasonCode, pending: receipt.pending,
|
|
84
|
+
providerAttempts: receipt.providerAttempts, completedResponses: receipt.completedResponses, elapsedMs: receipt.elapsedMs,
|
|
85
|
+
inputTokens: receipt.inputTokens, outputTokens: receipt.outputTokens, totalTokens: receipt.totalTokens,
|
|
86
|
+
cachedInputTokens: receipt.cachedInputTokens, reasoningTokens: receipt.reasoningTokens, estimatedCost: receipt.estimatedCost
|
|
87
|
+
});
|
|
88
|
+
lastPending = receipt.pending;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
issued.add(run);
|
|
94
|
+
return Object.freeze(run);
|
|
95
|
+
}
|
|
96
|
+
export function assertProviderRunScope(run, runId, budget) {
|
|
97
|
+
if (!isAicfProviderRun(run) || run.runId !== runId || budget && (budget.ledger !== run.budget.ledger
|
|
98
|
+
|| !isDeepStrictEqual(budget.policy, run.budget.policy)))
|
|
99
|
+
throw new Error("The provider run and budget scope conflict.");
|
|
100
|
+
}
|
|
101
|
+
function bounded(value) { return value.slice(0, 160); }
|
|
102
|
+
export function sanitizeProviderTraceMetadata(metadata) {
|
|
103
|
+
const result = {};
|
|
104
|
+
const strings = ["stage", "outcome", "reasonCode", "queryId", "configurationId", "provider", "model", "fingerprint", "corpusFingerprint",
|
|
105
|
+
"indexFingerprint", "retrievalPolicyFingerprint", "eligibilityFingerprint", "attemptId"];
|
|
106
|
+
for (const key of strings) {
|
|
107
|
+
const value = Object.getOwnPropertyDescriptor(metadata, key)?.value;
|
|
108
|
+
if (typeof value === "string")
|
|
109
|
+
result[key] = bounded(value);
|
|
110
|
+
}
|
|
111
|
+
for (const key of ["candidateCount", "taskCount", "providerAttempts", "completedResponses", "elapsedMs", "pending", "mock"]) {
|
|
112
|
+
const value = Object.getOwnPropertyDescriptor(metadata, key)?.value;
|
|
113
|
+
if (typeof value === "boolean" || typeof value === "number" && Number.isFinite(value) && value >= 0)
|
|
114
|
+
result[key] = value;
|
|
115
|
+
}
|
|
116
|
+
for (const key of ["inputTokens", "outputTokens", "totalTokens", "cachedInputTokens", "reasoningTokens", "estimatedCost"]) {
|
|
117
|
+
const value = Object.getOwnPropertyDescriptor(metadata, key)?.value;
|
|
118
|
+
if (value && typeof value.complete === "boolean" && Number.isFinite(value.knownSubtotal) && value.knownSubtotal >= 0
|
|
119
|
+
&& (value.total === null || Number.isFinite(value.total) && value.total >= 0))
|
|
120
|
+
result[key] = { total: value.total, knownSubtotal: value.knownSubtotal, complete: value.complete };
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProviderInvocation } from "./provider-types.js";
|
|
2
|
+
export declare function stopReason(request: Pick<ProviderInvocation, "signal" | "deadlineAt">): "cancelled" | "deadline_exceeded" | undefined;
|
|
3
|
+
export declare function stopGate(request: ProviderInvocation & {
|
|
4
|
+
deadlineAt: number;
|
|
5
|
+
}, controller: AbortController): {
|
|
6
|
+
promise: Promise<"cancelled" | "deadline_exceeded">;
|
|
7
|
+
dispose: () => void;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=provider-stop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-stop.d.ts","sourceRoot":"","sources":["../../src/runtime/provider-stop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,wBAAgB,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,YAAY,CAAC,GAAG,WAAW,GAAG,mBAAmB,GAAG,SAAS,CAIpI;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,kBAAkB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,EAAE,UAAU,EAAE,eAAe;;;EAczG"}
|