@cassiomc1/forgeloop 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/AGENT_COMPATIBILITY.md +8 -0
  2. package/DOCS_INDEX.md +35 -3
  3. package/ENG/nodejs-backend-development-eng.md +2 -2
  4. package/ENG/sec-code-eng.md +7 -7
  5. package/EXECUTION_STATE.md +12 -0
  6. package/LOOP_ENGINEERING.md +28 -2
  7. package/ORCHESTRATOR_INTEGRATION.md +9 -5
  8. package/PROTOCOL_INTEGRATION.md +55 -2
  9. package/README.md +40 -25
  10. package/TERMINOLOGY.md +2 -0
  11. package/THREAT_MODEL.md +140 -1
  12. package/completions/_forgeloop +19 -1
  13. package/completions/forgeloop.bash +37 -1
  14. package/completions/forgeloop.fish +123 -1
  15. package/docs/ADVISORY_CONTEXT.md +25 -0
  16. package/docs/AGENT_BROWSER_ADAPTER.md +81 -0
  17. package/docs/AGENT_BROWSER_VERIFICATION.md +6 -0
  18. package/docs/AGENT_PROTOCOL_SUMMARY.md +27 -2
  19. package/docs/AGENT_SKILL.md +66 -0
  20. package/docs/ARTIFACT_REFERENCE.md +123 -0
  21. package/docs/AUDIT_UX.md +46 -0
  22. package/docs/BROWSER_VERIFICATION.md +136 -0
  23. package/docs/CLI_REFERENCE.md +366 -6
  24. package/docs/CODE_ATTESTATION.md +2 -2
  25. package/docs/DOCUMENTATION_GUIDE.md +32 -11
  26. package/docs/JEV_BENCHMARKS.md +31 -0
  27. package/docs/MODEL_ROUTING.md +37 -0
  28. package/docs/OPENSRC_ADAPTER.md +241 -0
  29. package/docs/PACKAGE_CONTENTS.md +35 -8
  30. package/docs/PROVIDERS.md +126 -0
  31. package/docs/PROVIDER_ARCHITECTURE.md +199 -0
  32. package/docs/RECIPES.md +9 -0
  33. package/docs/RELEASE_CHECKLIST.md +38 -5
  34. package/docs/SECURITY_REVIEW.md +71 -0
  35. package/docs/SEMANTIC_DECISION_PLANE.md +71 -0
  36. package/docs/TEST_INTELLIGENCE.md +29 -0
  37. package/docs/TEST_PRUNING.md +14 -0
  38. package/docs/TROUBLESHOOTING.md +198 -1
  39. package/docs/UNIVERSAL_INTEGRATION.md +31 -0
  40. package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +2 -2
  41. package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +5 -5
  42. package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +1 -1
  43. package/docs/assets/diagrams/forgeloop-engineering-flow.html +39 -26
  44. package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
  45. package/docs/assets/diagrams/forgeloop-engineering-flow.svg +26 -26
  46. package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +2 -1
  47. package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +5 -5
  48. package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +1 -1
  49. package/docs/diagrams/README.md +13 -9
  50. package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +1 -1
  51. package/docs/diagrams/forgeloop-engineering-flow.workflow.json +24 -19
  52. package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +1 -0
  53. package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +4 -4
  54. package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +4 -4
  55. package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +4 -4
  56. package/docs/documentation-manifest.json +750 -5
  57. package/docs/protocol-requirements.json +24 -0
  58. package/package.json +30 -3
  59. package/schemas/config.schema.json +14 -0
  60. package/schemas/context-plan.schema.json +18 -0
  61. package/schemas/semantic-decision.schema.json +46 -0
  62. package/schemas/test-utility.schema.json +44 -0
  63. package/scripts/CI_VALIDATORS.md +6 -6
  64. package/scripts/benchmark-jev.mjs +5 -0
  65. package/scripts/benchmark-test-intelligence.mjs +4 -0
  66. package/scripts/generate-agent-protocol-summary.mjs +4 -1
  67. package/scripts/generate-forgeloop-skill.mjs +133 -0
  68. package/scripts/jev-smoke.mjs +19 -0
  69. package/skills/forgeloop/README.md +9 -0
  70. package/skills/forgeloop/SKILL.md +77 -0
  71. package/skills/forgeloop/references/lifecycle.md +9 -0
  72. package/skills/forgeloop/references/recovery.md +7 -0
  73. package/skills/forgeloop/references/verification.md +7 -0
  74. package/src/adapters/agent-browser/assertions.js +47 -0
  75. package/src/adapters/agent-browser/commands.js +54 -0
  76. package/src/adapters/agent-browser/index.js +3 -0
  77. package/src/adapters/agent-browser/locator.js +40 -0
  78. package/src/adapters/agent-browser/process.js +215 -0
  79. package/src/adapters/agent-browser/provider.js +313 -0
  80. package/src/adapters/emulated-services/constants.js +24 -0
  81. package/src/adapters/emulated-services/index.js +7 -0
  82. package/src/adapters/emulated-services/process.js +162 -0
  83. package/src/adapters/emulated-services/provider.js +282 -0
  84. package/src/adapters/opensrc/normalize.js +90 -0
  85. package/src/adapters/opensrc/process.js +248 -0
  86. package/src/adapters/opensrc/provider.js +338 -0
  87. package/src/adapters/opensrc/search.js +264 -0
  88. package/src/adapters/typesafe/client.js +28 -0
  89. package/src/adapters/typesafe/engine.js +63 -0
  90. package/src/adapters/typesafe/normalize.js +41 -0
  91. package/src/cli.js +108 -0
  92. package/src/commands/checkpoint-revalidate.js +176 -0
  93. package/src/commands/context-plan.js +38 -0
  94. package/src/commands/contract-create.js +264 -0
  95. package/src/commands/contract-revise.js +236 -0
  96. package/src/commands/decision-show.js +14 -0
  97. package/src/commands/decision-status.js +22 -0
  98. package/src/commands/discover.js +41 -0
  99. package/src/commands/doctor.js +15 -0
  100. package/src/commands/gate-record.js +205 -0
  101. package/src/commands/gate-revalidate.js +137 -0
  102. package/src/commands/model-route.js +32 -0
  103. package/src/commands/route.js +146 -18
  104. package/src/commands/semantic-plan.js +17 -0
  105. package/src/commands/task-abandon.js +224 -0
  106. package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
  107. package/src/commands/task-repair-contract-bootstrap.js +263 -0
  108. package/src/commands/test-inventory.js +5 -0
  109. package/src/commands/test-prune-plan.js +5 -0
  110. package/src/commands/test-prune-probe.js +5 -0
  111. package/src/commands/test-utility.js +5 -0
  112. package/src/commands/validate-protocol.js +10 -1
  113. package/src/core/artifact-registry.js +24 -0
  114. package/src/core/audit-ux.js +514 -0
  115. package/src/core/browser-verification/constants.js +149 -0
  116. package/src/core/browser-verification/normalize.js +254 -0
  117. package/src/core/browser-verification/provider.js +519 -0
  118. package/src/core/browser-verification/service.js +115 -0
  119. package/src/core/checkpoint-revalidation.js +319 -0
  120. package/src/core/cli-command-definitions.js +241 -0
  121. package/src/core/command-executors.js +110 -0
  122. package/src/core/command-input.js +115 -43
  123. package/src/core/completion-artifacts.js +14 -5
  124. package/src/core/completion.js +4 -6
  125. package/src/core/config.js +3 -0
  126. package/src/core/context-compiler/budget.js +9 -0
  127. package/src/core/context-compiler/candidates.js +39 -0
  128. package/src/core/context-compiler/compiler.js +63 -0
  129. package/src/core/context-compiler/fingerprint.js +11 -0
  130. package/src/core/context-compiler/policy.js +13 -0
  131. package/src/core/context-compiler/result.js +23 -0
  132. package/src/core/contract-bootstrap-recovery.js +655 -0
  133. package/src/core/contract-revision.js +210 -0
  134. package/src/core/decision/artifact.js +69 -0
  135. package/src/core/decision/benchmarks.js +103 -0
  136. package/src/core/decision/cache.js +27 -0
  137. package/src/core/decision/constants.js +58 -0
  138. package/src/core/decision/cutover.js +34 -0
  139. package/src/core/decision/engine.js +22 -0
  140. package/src/core/decision/errors.js +68 -0
  141. package/src/core/decision/events.js +101 -0
  142. package/src/core/decision/freshness.js +19 -0
  143. package/src/core/decision/normalizers/index.js +115 -0
  144. package/src/core/decision/policy.js +18 -0
  145. package/src/core/decision/projection.js +16 -0
  146. package/src/core/decision/question-registry.js +201 -0
  147. package/src/core/decision/request.js +26 -0
  148. package/src/core/decision/resolver.js +130 -0
  149. package/src/core/decision/result.js +58 -0
  150. package/src/core/decision/service.js +156 -0
  151. package/src/core/decision/state-builder.js +65 -0
  152. package/src/core/decision/task-bindings.js +30 -0
  153. package/src/core/decision/test-provider.js +32 -0
  154. package/src/core/decision/thresholds.js +15 -0
  155. package/src/core/error-codes.js +278 -0
  156. package/src/core/events.js +226 -57
  157. package/src/core/evidence-readiness.js +9 -0
  158. package/src/core/execution-prerequisites.js +14 -0
  159. package/src/core/execution-profile.js +63 -38
  160. package/src/core/gate-provenance.js +124 -0
  161. package/src/core/integration-invocation-policy.js +27 -4
  162. package/src/core/integration-resources.js +86 -61
  163. package/src/core/model-router/constants.js +10 -0
  164. package/src/core/model-router/policy.js +103 -0
  165. package/src/core/model-router/router.js +37 -0
  166. package/src/core/next-action-model.js +58 -0
  167. package/src/core/next-action-phases.js +130 -42
  168. package/src/core/next-action-refresh.js +43 -9
  169. package/src/core/next-action-review-phase.js +7 -2
  170. package/src/core/next-action.js +35 -7
  171. package/src/core/phase.js +128 -10
  172. package/src/core/preflight-consistency.js +23 -9
  173. package/src/core/preflight-loaders.js +37 -5
  174. package/src/core/protocol-info.js +65 -0
  175. package/src/core/protocol.js +20 -0
  176. package/src/core/reconcile-closure.js +128 -52
  177. package/src/core/recovery-history.js +1 -0
  178. package/src/core/resumability.js +154 -44
  179. package/src/core/route-artifact.js +15 -1
  180. package/src/core/router.js +67 -1
  181. package/src/core/runtime-context.js +118 -61
  182. package/src/core/schema-validation.js +3 -0
  183. package/src/core/security-review/constants.js +64 -0
  184. package/src/core/security-review/normalize.js +245 -0
  185. package/src/core/security-review/provider.js +204 -0
  186. package/src/core/security-review/service.js +134 -0
  187. package/src/core/semantic-planning/constants.js +19 -0
  188. package/src/core/semantic-planning/projection.js +94 -0
  189. package/src/core/semantic-planning/service.js +15 -0
  190. package/src/core/sources.js +37 -0
  191. package/src/core/task-claim-state.js +201 -1
  192. package/src/core/task-conflict-inspection.js +31 -5
  193. package/src/core/task-paths.js +13 -0
  194. package/src/core/task-recovery.js +1 -0
  195. package/src/core/templates.js +3 -0
  196. package/src/core/test-intelligence/benchmarks.js +68 -0
  197. package/src/core/test-intelligence/inventory.js +73 -0
  198. package/src/core/test-intelligence/prune.js +90 -0
  199. package/src/core/test-intelligence/semantic-state.js +15 -0
  200. package/src/core/test-intelligence/service.js +40 -0
  201. package/src/core/test-intelligence/utility.js +50 -0
  202. package/src/core/trace.js +11 -7
  203. package/src/core/transaction.js +1 -0
  204. package/src/integration.d.ts +492 -0
  205. package/src/integration.js +54 -0
  206. package/src/providers/README.md +47 -0
  207. package/src/providers/capabilities.js +46 -0
  208. package/src/providers/errors.js +15 -0
  209. package/src/providers/index.js +29 -0
  210. package/src/providers/json-snapshot.js +105 -0
  211. package/src/providers/registry.js +152 -0
@@ -0,0 +1,46 @@
1
+ export const PROVIDER_KINDS = Object.freeze([
2
+ "ADVISORY_CONTEXT",
3
+ "VERIFICATION_EXECUTION",
4
+ "BROWSER_VERIFICATION",
5
+ "SECURITY_REVIEW",
6
+ "PRESENTATION",
7
+ ]);
8
+
9
+ export const PROVIDER_CAPABILITIES = Object.freeze({
10
+ ADVISORY_CONTEXT: Object.freeze({
11
+ lifecycleAuthority: false,
12
+ completionAuthority: false,
13
+ executableAuthority: false,
14
+ evidenceAuthority: false,
15
+ }),
16
+ VERIFICATION_EXECUTION: Object.freeze({
17
+ lifecycleAuthority: false,
18
+ completionAuthority: false,
19
+ canProduceExecutionObservation: true,
20
+ evidenceAuthority: false,
21
+ }),
22
+ BROWSER_VERIFICATION: Object.freeze({
23
+ lifecycleAuthority: false,
24
+ completionAuthority: false,
25
+ evidenceRequiresForgeLoopValidation: true,
26
+ evidenceAuthority: false,
27
+ }),
28
+ SECURITY_REVIEW: Object.freeze({
29
+ lifecycleAuthority: false,
30
+ completionAuthority: false,
31
+ evidenceRequiresForgeLoopValidation: true,
32
+ evidenceAuthority: false,
33
+ }),
34
+ PRESENTATION: Object.freeze({
35
+ lifecycleAuthority: false,
36
+ completionAuthority: false,
37
+ executableAuthority: false,
38
+ evidenceAuthority: false,
39
+ mayMutateProtocolState: false,
40
+ }),
41
+ });
42
+
43
+ export function capabilityFor(kind) {
44
+ if (!PROVIDER_KINDS.includes(kind)) return null;
45
+ return PROVIDER_CAPABILITIES[kind] ?? null;
46
+ }
@@ -0,0 +1,15 @@
1
+ export const E_PROVIDER_INVALID = "E_PROVIDER_INVALID";
2
+ export const E_PROVIDER_UNAVAILABLE = "E_PROVIDER_UNAVAILABLE";
3
+ export const E_PROVIDER_TIMEOUT = "E_PROVIDER_TIMEOUT";
4
+ export const E_PROVIDER_OUTPUT_INVALID = "E_PROVIDER_OUTPUT_INVALID";
5
+ export const E_PROVIDER_OUTPUT_LIMIT = "E_PROVIDER_OUTPUT_LIMIT";
6
+ export const E_PROVIDER_CAPABILITY_UNSUPPORTED = "E_PROVIDER_CAPABILITY_UNSUPPORTED";
7
+ export const E_PROVIDER_AUTHORITY_ESCALATION = "E_PROVIDER_AUTHORITY_ESCALATION";
8
+ export const E_PROVIDER_EXECUTION_FAILED = "E_PROVIDER_EXECUTION_FAILED";
9
+
10
+ export function providerError(code, message) {
11
+ const error = new Error(message);
12
+ error.name = "ProviderError";
13
+ error.code = code;
14
+ return error;
15
+ }
@@ -0,0 +1,29 @@
1
+ import { PROVIDER_KINDS, PROVIDER_CAPABILITIES, capabilityFor } from "./capabilities.js";
2
+ import {
3
+ E_PROVIDER_INVALID,
4
+ E_PROVIDER_UNAVAILABLE,
5
+ E_PROVIDER_TIMEOUT,
6
+ E_PROVIDER_OUTPUT_INVALID,
7
+ E_PROVIDER_OUTPUT_LIMIT,
8
+ E_PROVIDER_CAPABILITY_UNSUPPORTED,
9
+ E_PROVIDER_AUTHORITY_ESCALATION,
10
+ E_PROVIDER_EXECUTION_FAILED,
11
+ providerError,
12
+ } from "./errors.js";
13
+ import { createProviderRegistry } from "./registry.js";
14
+
15
+ export {
16
+ PROVIDER_KINDS,
17
+ PROVIDER_CAPABILITIES,
18
+ capabilityFor,
19
+ E_PROVIDER_INVALID,
20
+ E_PROVIDER_UNAVAILABLE,
21
+ E_PROVIDER_TIMEOUT,
22
+ E_PROVIDER_OUTPUT_INVALID,
23
+ E_PROVIDER_OUTPUT_LIMIT,
24
+ E_PROVIDER_CAPABILITY_UNSUPPORTED,
25
+ E_PROVIDER_AUTHORITY_ESCALATION,
26
+ E_PROVIDER_EXECUTION_FAILED,
27
+ providerError,
28
+ createProviderRegistry,
29
+ };
@@ -0,0 +1,105 @@
1
+ import { types } from "node:util";
2
+ import { deepFreeze } from "../core/portable-context.js";
3
+ import {
4
+ E_PROVIDER_AUTHORITY_ESCALATION,
5
+ E_PROVIDER_INVALID,
6
+ E_PROVIDER_OUTPUT_INVALID,
7
+ E_PROVIDER_OUTPUT_LIMIT,
8
+ providerError,
9
+ } from "./errors.js";
10
+
11
+ const AUTHORITY_KEYS = new Set([
12
+ "lifecycleAuthority", "completionAuthority", "evidenceAuthority",
13
+ "executableAuthority", "installAuthority",
14
+ ]);
15
+
16
+ export function isPlainObject(value) {
17
+ if (value === null || typeof value !== "object" || types.isProxy(value) || Array.isArray(value)) return false;
18
+ const prototype = Object.getPrototypeOf(value);
19
+ return prototype === Object.prototype || prototype === null;
20
+ }
21
+
22
+ function serializeSnapshot(value, maxBytes, invalidCode) {
23
+ const ancestors = new WeakSet();
24
+ let bytes = 0;
25
+ let nodes = 0;
26
+ const invalid = () => { throw providerError(invalidCode, "Provider payload must contain only plain JSON data without accessors"); };
27
+ const limit = () => { throw providerError(E_PROVIDER_OUTPUT_LIMIT, "Provider payload exceeds its byte or structural limit"); };
28
+ function charge(text) {
29
+ bytes += Buffer.byteLength(text, "utf8");
30
+ if (bytes > maxBytes) limit();
31
+ return text;
32
+ }
33
+ function string(value) {
34
+ if (value.length > maxBytes - bytes) limit();
35
+ return charge(JSON.stringify(value));
36
+ }
37
+ function visit(value, depth) {
38
+ nodes += 1;
39
+ if (depth > 64 || nodes > 100_000) limit();
40
+ if (value === null) return charge("null");
41
+ if (typeof value === "string") return string(value);
42
+ if (typeof value === "boolean") return charge(String(value));
43
+ if (typeof value === "number" && Number.isFinite(value)) return charge(JSON.stringify(value));
44
+ if (typeof value !== "object" || types.isProxy(value)) invalid();
45
+ return visitContainer(value, depth);
46
+ }
47
+ function assertContainer(value, array) {
48
+ const valid = array ? Object.getPrototypeOf(value) === Array.prototype : isPlainObject(value);
49
+ if (!valid) invalid();
50
+ }
51
+ function visitContainer(value, depth) {
52
+ const array = Array.isArray(value);
53
+ assertContainer(value, array);
54
+ if (ancestors.has(value)) invalid();
55
+ ancestors.add(value);
56
+ const keys = Reflect.ownKeys(value);
57
+ if (keys.length > 100_000 || keys.length > maxBytes) limit();
58
+ const parts = [];
59
+ charge(array ? "[]" : "{}");
60
+ const length = array ? Object.getOwnPropertyDescriptor(value, "length").value : 0;
61
+ if (array && keys.length !== length + 1) invalid();
62
+ let index = 0;
63
+ for (const key of keys) {
64
+ if (array && key === "length") continue;
65
+ if (typeof key !== "string") invalid();
66
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
67
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) invalid();
68
+ if (array && key !== String(index)) invalid();
69
+ if (index > 0) charge(",");
70
+ const prefix = array ? "" : string(key) + charge(":");
71
+ parts.push(prefix + visit(descriptor.value, depth + 1));
72
+ index += 1;
73
+ }
74
+ if (array && index !== length) invalid();
75
+ ancestors.delete(value);
76
+ return array ? `[${parts.join(",")}]` : `{${parts.join(",")}}`;
77
+ }
78
+ return visit(value, 0);
79
+ }
80
+
81
+ function assertAuthorityFree(value) {
82
+ if (value === null || typeof value !== "object") return;
83
+ for (const [key, child] of Object.entries(value)) {
84
+ if (AUTHORITY_KEYS.has(key) && child === true) {
85
+ throw providerError(E_PROVIDER_AUTHORITY_ESCALATION, "Provider result declares forbidden authority");
86
+ }
87
+ assertAuthorityFree(child);
88
+ }
89
+ }
90
+
91
+ export function normalizeProviderInput(value, maxBytes) {
92
+ return deepFreeze(JSON.parse(serializeSnapshot(value, maxBytes, E_PROVIDER_INVALID)));
93
+ }
94
+
95
+ export function normalizeProviderResult(value, maxBytes) {
96
+ if (!isPlainObject(value)) {
97
+ throw providerError(E_PROVIDER_OUTPUT_INVALID, "Provider result must be a plain JSON object");
98
+ }
99
+ const normalized = JSON.parse(serializeSnapshot(value, maxBytes, E_PROVIDER_OUTPUT_INVALID));
100
+ assertAuthorityFree(normalized);
101
+ if (normalized.status === "COMPLETE") {
102
+ throw providerError(E_PROVIDER_AUTHORITY_ESCALATION, "Provider result cannot assign lifecycle completion");
103
+ }
104
+ return deepFreeze(normalized);
105
+ }
@@ -0,0 +1,152 @@
1
+ import { performance } from "node:perf_hooks";
2
+ import { types } from "node:util";
3
+ import { capabilityFor } from "./capabilities.js";
4
+ import { isPlainObject, normalizeProviderInput, normalizeProviderResult } from "./json-snapshot.js";
5
+ import {
6
+ E_PROVIDER_EXECUTION_FAILED,
7
+ E_PROVIDER_INVALID,
8
+ E_PROVIDER_TIMEOUT,
9
+ E_PROVIDER_UNAVAILABLE,
10
+ providerError,
11
+ } from "./errors.js";
12
+
13
+ const PROVIDER_ID_REGEX = /^[a-z0-9][a-z0-9_-]{0,63}$/;
14
+ const REGISTRY_MAX_PROVIDERS = 256;
15
+
16
+ function assertEntry(entry, key) {
17
+ if (!isPlainObject(entry)) {
18
+ throw providerError(E_PROVIDER_INVALID, "Provider must be a plain object");
19
+ }
20
+ for (const descriptor of Object.values(Object.getOwnPropertyDescriptors(entry))) {
21
+ if (!("value" in descriptor)) throw providerError(E_PROVIDER_INVALID, "Provider accessors are not supported");
22
+ }
23
+ if (typeof entry.id !== "string" || !PROVIDER_ID_REGEX.test(entry.id) || entry.id !== key) {
24
+ throw providerError(E_PROVIDER_INVALID, "Provider identity does not match the registry key");
25
+ }
26
+ if (entry.version !== undefined && (typeof entry.version !== "string" || entry.version.trim() === "" || entry.version.length > 64)) {
27
+ throw providerError(E_PROVIDER_INVALID, "Provider version must be a non-empty string of at most 64 characters");
28
+ }
29
+ if (!capabilityFor(entry.kind)) {
30
+ throw providerError(E_PROVIDER_INVALID, "Provider declares an unsupported kind");
31
+ }
32
+ if (entry.capability !== undefined && entry.capability !== capabilityFor(entry.kind)) {
33
+ throw providerError(E_PROVIDER_INVALID, "Provider capability metadata contradicts its kind");
34
+ }
35
+ if (typeof entry.operation !== "function") {
36
+ throw providerError(E_PROVIDER_INVALID, "Provider must implement operation(input, context)");
37
+ }
38
+ }
39
+
40
+ function boundedInteger(value, fallback, maximum) {
41
+ const resolved = value === undefined ? fallback : value;
42
+ if (!Number.isInteger(resolved) || resolved <= 0 || resolved > maximum) {
43
+ throw providerError(E_PROVIDER_INVALID, "Provider limits must be positive bounded integers");
44
+ }
45
+ return resolved;
46
+ }
47
+
48
+ function normalizeOptions(options) {
49
+ if (!isPlainObject(options)) throw providerError(E_PROVIDER_INVALID, "Provider options must be a plain object");
50
+ const descriptors = Object.getOwnPropertyDescriptors(options);
51
+ for (const [key, descriptor] of Object.entries(descriptors)) {
52
+ if (!["timeoutMs", "maxInputBytes", "maxResultBytes"].includes(key) || !("value" in descriptor)) {
53
+ throw providerError(E_PROVIDER_INVALID, "Unsupported provider option");
54
+ }
55
+ }
56
+ for (const key of Reflect.ownKeys(descriptors)) {
57
+ if (typeof key !== "string") throw providerError(E_PROVIDER_INVALID, "Unsupported provider option");
58
+ }
59
+ return {
60
+ timeoutMs: boundedInteger(descriptors.timeoutMs?.value, 10_000, 300_000),
61
+ maxInputBytes: boundedInteger(descriptors.maxInputBytes?.value, 262_144, 4_194_304),
62
+ maxResultBytes: boundedInteger(descriptors.maxResultBytes?.value, 262_144, 4_194_304),
63
+ };
64
+ }
65
+
66
+ function callProvider(operation, normalize) {
67
+ let returned;
68
+ try {
69
+ returned = operation();
70
+ } catch {
71
+ throw providerError(E_PROVIDER_EXECUTION_FAILED, "Provider execution failed");
72
+ }
73
+ if (types.isPromise(returned) && !types.isProxy(returned)) {
74
+ return Promise.prototype.then.call(returned, normalize, () => {
75
+ throw providerError(E_PROVIDER_EXECUTION_FAILED, "Provider execution failed");
76
+ });
77
+ }
78
+ return normalize(returned);
79
+ }
80
+
81
+ async function withProviderDeadline(providerId, timeoutMs, operation) {
82
+ const controller = new AbortController();
83
+ const context = Object.freeze({ signal: controller.signal, timeoutMs, providerId });
84
+ const expires = performance.now() + timeoutMs;
85
+ const timeoutError = providerError(E_PROVIDER_TIMEOUT, "Provider invocation exceeded its deadline");
86
+ const checkpoint = () => {
87
+ if (controller.signal.aborted || performance.now() >= expires) {
88
+ controller.abort();
89
+ throw timeoutError;
90
+ }
91
+ };
92
+ let timer;
93
+ const timeout = new Promise((_, reject) => {
94
+ timer = setTimeout(() => {
95
+ reject(timeoutError);
96
+ controller.abort();
97
+ }, timeoutMs);
98
+ });
99
+ try {
100
+ return await Promise.race([
101
+ timeout,
102
+ Promise.resolve().then(() => operation(context, checkpoint)),
103
+ ]);
104
+ } finally {
105
+ clearTimeout(timer);
106
+ }
107
+ }
108
+
109
+ export function createProviderRegistry({ providers = {} } = {}) {
110
+ if (!isPlainObject(providers)) throw providerError(E_PROVIDER_INVALID, "Provider registry requires an object map");
111
+ const map = new Map();
112
+ for (const key of Reflect.ownKeys(providers)) {
113
+ if (typeof key !== "string" || !PROVIDER_ID_REGEX.test(key)) {
114
+ throw providerError(E_PROVIDER_INVALID, "Invalid provider registry key");
115
+ }
116
+ if (map.size >= REGISTRY_MAX_PROVIDERS) throw providerError(E_PROVIDER_INVALID, "Too many providers");
117
+ const descriptor = Object.getOwnPropertyDescriptor(providers, key);
118
+ if (!("value" in descriptor) || !descriptor.enumerable) throw providerError(E_PROVIDER_INVALID, "Invalid provider registry entry");
119
+ const entry = descriptor.value;
120
+ if (typeof entry !== "function") assertEntry(entry, key);
121
+ map.set(key, entry);
122
+ }
123
+ return Object.freeze({
124
+ get(id) { return map.get(id) ?? null; },
125
+ has(id) { return map.has(id); },
126
+ list() { return [...map.keys()].sort(); },
127
+ async invoke(id, input = {}, options = {}) {
128
+ const { timeoutMs, maxInputBytes, maxResultBytes } = normalizeOptions(options);
129
+ if (!map.has(id)) throw providerError(E_PROVIDER_UNAVAILABLE, "Provider is not registered");
130
+ const payload = normalizeProviderInput(input, maxInputBytes);
131
+ const entry = map.get(id);
132
+ return withProviderDeadline(id, timeoutMs, async (context, checkpoint) => {
133
+ checkpoint();
134
+ const validate = provider => {
135
+ checkpoint();
136
+ assertEntry(provider, id);
137
+ return Object.freeze({ provider });
138
+ };
139
+ const { provider } = typeof entry === "function"
140
+ ? await callProvider(() => entry(context), validate)
141
+ : validate(entry);
142
+ checkpoint();
143
+ return callProvider(() => provider.operation(payload, context), result => {
144
+ checkpoint();
145
+ const normalized = normalizeProviderResult(result, maxResultBytes);
146
+ checkpoint();
147
+ return normalized;
148
+ });
149
+ });
150
+ },
151
+ });
152
+ }