@modelprofile.com/flexharness-providers 8.0.0 → 8.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.
@@ -1,6 +1,4 @@
1
- import type { IOpenAiModelConnection } from '@modelprofile.com/flexharness-providers/openai';
2
- import type { IOpenAiChatGptAuthCredentials } from './interfaces.js';
3
1
  export * from './interfaces.js';
4
2
  export * from './smartai.auth.openai.js';
3
+ export * from './smartai.connection.openai.js';
5
4
  export * from './smartai.middleware.openai.js';
6
- export declare const createOpenAiChatGptModelConnection: (credentials: IOpenAiChatGptAuthCredentials) => IOpenAiModelConnection;
@@ -1,10 +1,5 @@
1
- import { createOpenAiChatGptProviderSettings } from './smartai.auth.openai.js';
2
- import { createOpenAiChatGptInstructionsMiddleware } from './smartai.middleware.openai.js';
3
1
  export * from './interfaces.js';
4
2
  export * from './smartai.auth.openai.js';
3
+ export * from './smartai.connection.openai.js';
5
4
  export * from './smartai.middleware.openai.js';
6
- export const createOpenAiChatGptModelConnection = (credentials) => ({
7
- settings: createOpenAiChatGptProviderSettings(credentials),
8
- middleware: createOpenAiChatGptInstructionsMiddleware(),
9
- });
10
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19vcGVuYWlfYXV0aC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUsbUNBQW1DLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMvRSxPQUFPLEVBQUUseUNBQXlDLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUMzRixjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYyxnQ0FBZ0MsQ0FBQztBQUUvQyxNQUFNLENBQUMsTUFBTSxrQ0FBa0MsR0FBRyxDQUFDLFdBQTBDLEVBQTBCLEVBQUUsQ0FBQyxDQUFDO0lBQ3pILFFBQVEsRUFBRSxtQ0FBbUMsQ0FBQyxXQUFXLENBQUM7SUFDMUQsVUFBVSxFQUFFLHlDQUF5QyxFQUFFO0NBQ3hELENBQUMsQ0FBQyJ9
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19vcGVuYWlfYXV0aC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYyxnQ0FBZ0MsQ0FBQztBQUMvQyxjQUFjLGdDQUFnQyxDQUFDIn0=
@@ -17,6 +17,30 @@ export interface IOpenAiChatGptAuthCredentials {
17
17
  tokenInfo?: IOpenAiChatGptTokenInfo;
18
18
  originator?: string;
19
19
  }
20
+ export interface IOpenAiChatGptAccessIdentity {
21
+ /** Exact workspace claim expected on every resolved access token. */
22
+ readonly accountId: string | undefined;
23
+ /** Exact residency claim expected on every resolved access token. */
24
+ readonly isFedrampAccount: boolean;
25
+ }
26
+ export interface IOpenAiChatGptAccessRequest {
27
+ /** The provider request signal. A resolver should pass it through to its authority. */
28
+ readonly signal: AbortSignal;
29
+ }
30
+ /** Access-only result. Refresh and ID tokens are not part of the request-time contract. */
31
+ export interface IOpenAiChatGptResolvedAccess {
32
+ readonly accessToken: string;
33
+ }
34
+ export interface IOpenAiChatGptDynamicModelConnectionOptions {
35
+ /** Pins the account and residency boundary for the lifetime of the connection. */
36
+ readonly expectedIdentity: IOpenAiChatGptAccessIdentity;
37
+ /** Stable provider originator header. Defaults to `smartai`. */
38
+ readonly originator?: string;
39
+ /** Resolves current access immediately before every provider network request. */
40
+ readonly resolveAccess: (request: IOpenAiChatGptAccessRequest) => Promise<IOpenAiChatGptResolvedAccess>;
41
+ /** Optional underlying transport. Defaults to the current global fetch. */
42
+ readonly fetch?: typeof fetch;
43
+ }
20
44
  export interface IOpenAiChatGptTokenData extends IOpenAiChatGptAuthCredentials {
21
45
  refreshToken: string;
22
46
  tokenInfo: IOpenAiChatGptTokenInfo;
@@ -0,0 +1,4 @@
1
+ import type { IOpenAiModelConnection } from '@modelprofile.com/flexharness-providers/openai';
2
+ import type { IOpenAiChatGptAuthCredentials, IOpenAiChatGptDynamicModelConnectionOptions } from './interfaces.js';
3
+ export declare const createOpenAiChatGptModelConnection: (credentialsArg: IOpenAiChatGptAuthCredentials) => IOpenAiModelConnection;
4
+ export declare const createOpenAiChatGptDynamicModelConnection: (optionsArg: IOpenAiChatGptDynamicModelConnectionOptions) => IOpenAiModelConnection;
@@ -0,0 +1,98 @@
1
+ import { createOpenAiChatGptProviderSettings, OPENAI_CHATGPT_CODEX_BASE_URL, OpenAiChatGptAuthError, } from './smartai.auth.openai.js';
2
+ import { createOpenAiChatGptInstructionsMiddleware } from './smartai.middleware.openai.js';
3
+ const codexBaseUrl = new URL(OPENAI_CHATGPT_CODEX_BASE_URL);
4
+ function assertCodexRequestUrl(normalizedUrlArg) {
5
+ const url = new URL(normalizedUrlArg);
6
+ const isCodexPath = url.pathname === codexBaseUrl.pathname
7
+ || url.pathname.startsWith(`${codexBaseUrl.pathname}/`);
8
+ if (url.origin !== codexBaseUrl.origin
9
+ || url.username !== ''
10
+ || url.password !== ''
11
+ || !isCodexPath) {
12
+ throw new OpenAiChatGptAuthError('OpenAI ChatGPT inference URL is outside the Codex endpoint.');
13
+ }
14
+ }
15
+ function requireExpectedIdentity(optionsArg) {
16
+ const expectedIdentity = optionsArg.expectedIdentity;
17
+ if (!expectedIdentity
18
+ || (expectedIdentity.accountId !== undefined
19
+ && (typeof expectedIdentity.accountId !== 'string' || expectedIdentity.accountId.length === 0))
20
+ || typeof expectedIdentity.isFedrampAccount !== 'boolean') {
21
+ throw new OpenAiChatGptAuthError('OpenAI ChatGPT expected access identity is invalid.');
22
+ }
23
+ return Object.freeze({
24
+ accountId: expectedIdentity.accountId,
25
+ isFedrampAccount: expectedIdentity.isFedrampAccount,
26
+ });
27
+ }
28
+ function assertExpectedIdentity(settingsArg, expectedIdentityArg) {
29
+ const accountId = settingsArg.headers['ChatGPT-Account-ID'];
30
+ const isFedrampAccount = settingsArg.headers['X-OpenAI-Fedramp'] === 'true';
31
+ if (accountId !== expectedIdentityArg.accountId
32
+ || isFedrampAccount !== expectedIdentityArg.isFedrampAccount) {
33
+ throw new OpenAiChatGptAuthError('OpenAI ChatGPT resolved access identity does not match the connection.');
34
+ }
35
+ }
36
+ function applyAccessHeaders(headersArg, settingsArg) {
37
+ headersArg.set('Authorization', `Bearer ${settingsArg.apiKey}`);
38
+ headersArg.set('originator', settingsArg.headers.originator);
39
+ for (const name of ['ChatGPT-Account-ID', 'X-OpenAI-Fedramp']) {
40
+ const value = settingsArg.headers[name];
41
+ if (value === undefined) {
42
+ headersArg.delete(name);
43
+ }
44
+ else {
45
+ headersArg.set(name, value);
46
+ }
47
+ }
48
+ }
49
+ export const createOpenAiChatGptModelConnection = (credentialsArg) => ({
50
+ settings: createOpenAiChatGptProviderSettings(credentialsArg),
51
+ middleware: createOpenAiChatGptInstructionsMiddleware(),
52
+ });
53
+ export const createOpenAiChatGptDynamicModelConnection = (optionsArg) => {
54
+ if (!optionsArg || typeof optionsArg.resolveAccess !== 'function') {
55
+ throw new OpenAiChatGptAuthError('OpenAI ChatGPT access resolver is required.');
56
+ }
57
+ const expectedIdentity = requireExpectedIdentity(optionsArg);
58
+ const resolveAccess = optionsArg.resolveAccess;
59
+ const originator = optionsArg.originator;
60
+ const transport = optionsArg.fetch ?? globalThis.fetch;
61
+ if (typeof transport !== 'function') {
62
+ throw new OpenAiChatGptAuthError('fetch is not available for OpenAI ChatGPT inference.');
63
+ }
64
+ const fetchWithCurrentAccess = async (inputArg, initArg) => {
65
+ const request = new Request(inputArg, {
66
+ ...initArg,
67
+ redirect: 'error',
68
+ });
69
+ assertCodexRequestUrl(request.url);
70
+ request.signal.throwIfAborted();
71
+ const access = await resolveAccess({ signal: request.signal });
72
+ request.signal.throwIfAborted();
73
+ const settings = createOpenAiChatGptProviderSettings({
74
+ accessToken: access.accessToken,
75
+ originator,
76
+ });
77
+ if (settings.baseURL !== OPENAI_CHATGPT_CODEX_BASE_URL) {
78
+ throw new OpenAiChatGptAuthError('OpenAI ChatGPT inference endpoint is invalid.');
79
+ }
80
+ assertExpectedIdentity(settings, expectedIdentity);
81
+ const headers = new Headers(request.headers);
82
+ applyAccessHeaders(headers, settings);
83
+ const authenticatedRequest = new Request(request, {
84
+ headers,
85
+ });
86
+ authenticatedRequest.signal.throwIfAborted();
87
+ return transport(authenticatedRequest);
88
+ };
89
+ return {
90
+ settings: {
91
+ apiKey: '',
92
+ baseURL: OPENAI_CHATGPT_CODEX_BASE_URL,
93
+ fetch: fetchWithCurrentAccess,
94
+ },
95
+ middleware: createOpenAiChatGptInstructionsMiddleware(),
96
+ };
97
+ };
98
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhaS5jb25uZWN0aW9uLm9wZW5haS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX29wZW5haV9hdXRoL3NtYXJ0YWkuY29ubmVjdGlvbi5vcGVuYWkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBS0EsT0FBTyxFQUNMLG1DQUFtQyxFQUNuQyw2QkFBNkIsRUFDN0Isc0JBQXNCLEdBQ3ZCLE1BQU0sMEJBQTBCLENBQUM7QUFDbEMsT0FBTyxFQUFFLHlDQUF5QyxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFM0YsTUFBTSxZQUFZLEdBQUcsSUFBSSxHQUFHLENBQUMsNkJBQTZCLENBQUMsQ0FBQztBQUU1RCxTQUFTLHFCQUFxQixDQUFDLGdCQUF3QjtJQUNyRCxNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3RDLE1BQU0sV0FBVyxHQUFHLEdBQUcsQ0FBQyxRQUFRLEtBQUssWUFBWSxDQUFDLFFBQVE7V0FDckQsR0FBRyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsR0FBRyxZQUFZLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQztJQUMxRCxJQUNFLEdBQUcsQ0FBQyxNQUFNLEtBQUssWUFBWSxDQUFDLE1BQU07V0FDL0IsR0FBRyxDQUFDLFFBQVEsS0FBSyxFQUFFO1dBQ25CLEdBQUcsQ0FBQyxRQUFRLEtBQUssRUFBRTtXQUNuQixDQUFDLFdBQVcsRUFDZixDQUFDO1FBQ0QsTUFBTSxJQUFJLHNCQUFzQixDQUFDLDZEQUE2RCxDQUFDLENBQUM7SUFDbEcsQ0FBQztBQUNILENBQUM7QUFFRCxTQUFTLHVCQUF1QixDQUFDLFVBQXVEO0lBSXRGLE1BQU0sZ0JBQWdCLEdBQUcsVUFBVSxDQUFDLGdCQUFnQixDQUFDO0lBQ3JELElBQ0UsQ0FBQyxnQkFBZ0I7V0FDZCxDQUNELGdCQUFnQixDQUFDLFNBQVMsS0FBSyxTQUFTO2VBQ3JDLENBQUMsT0FBTyxnQkFBZ0IsQ0FBQyxTQUFTLEtBQUss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
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "Task Venture Capital GmbH",
3
3
  "license": "MIT",
4
4
  "name": "@modelprofile.com/flexharness-providers",
5
- "version": "8.0.0",
5
+ "version": "8.1.0",
6
6
  "type": "module",
7
7
  "description": "Model providers, authentication, account management, media and research integrations in one package.",
8
8
  "main": "./dist_ts_providers/index.js",
@@ -75,7 +75,7 @@
75
75
  "@ai-sdk/provider": "^4.0.8",
76
76
  "@ai-sdk/xai": "^4.0.44",
77
77
  "@anthropic-ai/sdk": "0.95.2",
78
- "@modelprofile.com/flexharness-models": "8.0.0",
78
+ "@modelprofile.com/flexharness-models": "8.1.0",
79
79
  "ai": "^7.0.79",
80
80
  "openai": "^6.37.0",
81
81
  "proper-lockfile": "^4.1.2"
package/readme.md CHANGED
@@ -36,7 +36,11 @@ Provider SDKs are installed together. Authentication, file access and vendor med
36
36
  are separate entrypoints and are not imported by the provider factory root. For
37
37
  ChatGPT authentication, pass
38
38
  `connection: createOpenAiChatGptModelConnection(credentials)` from `/auth` to the
39
- OpenAI model options. API-key inference needs no authentication setup.
39
+ OpenAI model options. Delegated runtimes use
40
+ `createOpenAiChatGptDynamicModelConnection({ expectedIdentity, resolveAccess })`
41
+ instead; it resolves current access at every provider HTTP request while keeping
42
+ account and residency identity fixed for the connection. API-key inference
43
+ needs no authentication setup.
40
44
 
41
45
  The package includes the applicable OpenAI Codex license and third-party notices.
42
46
 
@@ -1,12 +1,4 @@
1
- import type { IOpenAiModelConnection } from '@modelprofile.com/flexharness-providers/openai';
2
- import type { IOpenAiChatGptAuthCredentials } from './interfaces.js';
3
- import { createOpenAiChatGptProviderSettings } from './smartai.auth.openai.js';
4
- import { createOpenAiChatGptInstructionsMiddleware } from './smartai.middleware.openai.js';
5
1
  export * from './interfaces.js';
6
2
  export * from './smartai.auth.openai.js';
3
+ export * from './smartai.connection.openai.js';
7
4
  export * from './smartai.middleware.openai.js';
8
-
9
- export const createOpenAiChatGptModelConnection = (credentials: IOpenAiChatGptAuthCredentials): IOpenAiModelConnection => ({
10
- settings: createOpenAiChatGptProviderSettings(credentials),
11
- middleware: createOpenAiChatGptInstructionsMiddleware(),
12
- });
@@ -19,6 +19,36 @@ export interface IOpenAiChatGptAuthCredentials {
19
19
  originator?: string;
20
20
  }
21
21
 
22
+ export interface IOpenAiChatGptAccessIdentity {
23
+ /** Exact workspace claim expected on every resolved access token. */
24
+ readonly accountId: string | undefined;
25
+ /** Exact residency claim expected on every resolved access token. */
26
+ readonly isFedrampAccount: boolean;
27
+ }
28
+
29
+ export interface IOpenAiChatGptAccessRequest {
30
+ /** The provider request signal. A resolver should pass it through to its authority. */
31
+ readonly signal: AbortSignal;
32
+ }
33
+
34
+ /** Access-only result. Refresh and ID tokens are not part of the request-time contract. */
35
+ export interface IOpenAiChatGptResolvedAccess {
36
+ readonly accessToken: string;
37
+ }
38
+
39
+ export interface IOpenAiChatGptDynamicModelConnectionOptions {
40
+ /** Pins the account and residency boundary for the lifetime of the connection. */
41
+ readonly expectedIdentity: IOpenAiChatGptAccessIdentity;
42
+ /** Stable provider originator header. Defaults to `smartai`. */
43
+ readonly originator?: string;
44
+ /** Resolves current access immediately before every provider network request. */
45
+ readonly resolveAccess: (
46
+ request: IOpenAiChatGptAccessRequest,
47
+ ) => Promise<IOpenAiChatGptResolvedAccess>;
48
+ /** Optional underlying transport. Defaults to the current global fetch. */
49
+ readonly fetch?: typeof fetch;
50
+ }
51
+
22
52
  export interface IOpenAiChatGptTokenData extends IOpenAiChatGptAuthCredentials {
23
53
  refreshToken: string;
24
54
  tokenInfo: IOpenAiChatGptTokenInfo;
@@ -23,6 +23,14 @@ the current access token. The token must contain an unexpired `exp` claim;
23
23
  supplied `accountId`, `isFedrampAccount`, and `tokenInfo` must agree with its
24
24
  claims. A saved ID-token snapshot cannot override current access-token facts.
25
25
 
26
+ Use `createOpenAiChatGptDynamicModelConnection()` for delegated access owned by
27
+ an external authority. Pin its exact account and FedRAMP identity, then provide
28
+ an async `resolveAccess({ signal })` callback. The connection resolves and
29
+ validates current access immediately before every provider HTTP request, so
30
+ multi-step tool runs do not retain the token that admitted the run. Resolution,
31
+ identity, expiry, endpoint, redirect, and abort failures stop before the
32
+ underlying transport is called.
33
+
26
34
  ## Issue Reporting and Security
27
35
 
28
36
  Report issues and security concerns through [community.foss.global](https://community.foss.global).
@@ -0,0 +1,143 @@
1
+ import type { IOpenAiModelConnection } from '@modelprofile.com/flexharness-providers/openai';
2
+ import type {
3
+ IOpenAiChatGptAuthCredentials,
4
+ IOpenAiChatGptDynamicModelConnectionOptions,
5
+ } from './interfaces.js';
6
+ import {
7
+ createOpenAiChatGptProviderSettings,
8
+ OPENAI_CHATGPT_CODEX_BASE_URL,
9
+ OpenAiChatGptAuthError,
10
+ } from './smartai.auth.openai.js';
11
+ import { createOpenAiChatGptInstructionsMiddleware } from './smartai.middleware.openai.js';
12
+
13
+ const codexBaseUrl = new URL(OPENAI_CHATGPT_CODEX_BASE_URL);
14
+
15
+ function assertCodexRequestUrl(normalizedUrlArg: string): void {
16
+ const url = new URL(normalizedUrlArg);
17
+ const isCodexPath = url.pathname === codexBaseUrl.pathname
18
+ || url.pathname.startsWith(`${codexBaseUrl.pathname}/`);
19
+ if (
20
+ url.origin !== codexBaseUrl.origin
21
+ || url.username !== ''
22
+ || url.password !== ''
23
+ || !isCodexPath
24
+ ) {
25
+ throw new OpenAiChatGptAuthError('OpenAI ChatGPT inference URL is outside the Codex endpoint.');
26
+ }
27
+ }
28
+
29
+ function requireExpectedIdentity(optionsArg: IOpenAiChatGptDynamicModelConnectionOptions): {
30
+ accountId: string | undefined;
31
+ isFedrampAccount: boolean;
32
+ } {
33
+ const expectedIdentity = optionsArg.expectedIdentity;
34
+ if (
35
+ !expectedIdentity
36
+ || (
37
+ expectedIdentity.accountId !== undefined
38
+ && (typeof expectedIdentity.accountId !== 'string' || expectedIdentity.accountId.length === 0)
39
+ )
40
+ || typeof expectedIdentity.isFedrampAccount !== 'boolean'
41
+ ) {
42
+ throw new OpenAiChatGptAuthError('OpenAI ChatGPT expected access identity is invalid.');
43
+ }
44
+ return Object.freeze({
45
+ accountId: expectedIdentity.accountId,
46
+ isFedrampAccount: expectedIdentity.isFedrampAccount,
47
+ });
48
+ }
49
+
50
+ function assertExpectedIdentity(
51
+ settingsArg: ReturnType<typeof createOpenAiChatGptProviderSettings>,
52
+ expectedIdentityArg: Readonly<{
53
+ accountId: string | undefined;
54
+ isFedrampAccount: boolean;
55
+ }>,
56
+ ): void {
57
+ const accountId = settingsArg.headers['ChatGPT-Account-ID'];
58
+ const isFedrampAccount = settingsArg.headers['X-OpenAI-Fedramp'] === 'true';
59
+ if (
60
+ accountId !== expectedIdentityArg.accountId
61
+ || isFedrampAccount !== expectedIdentityArg.isFedrampAccount
62
+ ) {
63
+ throw new OpenAiChatGptAuthError(
64
+ 'OpenAI ChatGPT resolved access identity does not match the connection.',
65
+ );
66
+ }
67
+ }
68
+
69
+ function applyAccessHeaders(
70
+ headersArg: Headers,
71
+ settingsArg: ReturnType<typeof createOpenAiChatGptProviderSettings>,
72
+ ): void {
73
+ headersArg.set('Authorization', `Bearer ${settingsArg.apiKey}`);
74
+ headersArg.set('originator', settingsArg.headers.originator);
75
+
76
+ for (const name of ['ChatGPT-Account-ID', 'X-OpenAI-Fedramp']) {
77
+ const value = settingsArg.headers[name];
78
+ if (value === undefined) {
79
+ headersArg.delete(name);
80
+ } else {
81
+ headersArg.set(name, value);
82
+ }
83
+ }
84
+ }
85
+
86
+ export const createOpenAiChatGptModelConnection = (
87
+ credentialsArg: IOpenAiChatGptAuthCredentials,
88
+ ): IOpenAiModelConnection => ({
89
+ settings: createOpenAiChatGptProviderSettings(credentialsArg),
90
+ middleware: createOpenAiChatGptInstructionsMiddleware(),
91
+ });
92
+
93
+ export const createOpenAiChatGptDynamicModelConnection = (
94
+ optionsArg: IOpenAiChatGptDynamicModelConnectionOptions,
95
+ ): IOpenAiModelConnection => {
96
+ if (!optionsArg || typeof optionsArg.resolveAccess !== 'function') {
97
+ throw new OpenAiChatGptAuthError('OpenAI ChatGPT access resolver is required.');
98
+ }
99
+ const expectedIdentity = requireExpectedIdentity(optionsArg);
100
+ const resolveAccess = optionsArg.resolveAccess;
101
+ const originator = optionsArg.originator;
102
+ const transport = optionsArg.fetch ?? globalThis.fetch;
103
+ if (typeof transport !== 'function') {
104
+ throw new OpenAiChatGptAuthError('fetch is not available for OpenAI ChatGPT inference.');
105
+ }
106
+
107
+ const fetchWithCurrentAccess: typeof fetch = async (inputArg, initArg) => {
108
+ const request = new Request(inputArg, {
109
+ ...initArg,
110
+ redirect: 'error',
111
+ });
112
+ assertCodexRequestUrl(request.url);
113
+ request.signal.throwIfAborted();
114
+
115
+ const access = await resolveAccess({ signal: request.signal });
116
+ request.signal.throwIfAborted();
117
+ const settings = createOpenAiChatGptProviderSettings({
118
+ accessToken: access.accessToken,
119
+ originator,
120
+ });
121
+ if (settings.baseURL !== OPENAI_CHATGPT_CODEX_BASE_URL) {
122
+ throw new OpenAiChatGptAuthError('OpenAI ChatGPT inference endpoint is invalid.');
123
+ }
124
+ assertExpectedIdentity(settings, expectedIdentity);
125
+
126
+ const headers = new Headers(request.headers);
127
+ applyAccessHeaders(headers, settings);
128
+ const authenticatedRequest = new Request(request, {
129
+ headers,
130
+ });
131
+ authenticatedRequest.signal.throwIfAborted();
132
+ return transport(authenticatedRequest);
133
+ };
134
+
135
+ return {
136
+ settings: {
137
+ apiKey: '',
138
+ baseURL: OPENAI_CHATGPT_CODEX_BASE_URL,
139
+ fetch: fetchWithCurrentAccess,
140
+ },
141
+ middleware: createOpenAiChatGptInstructionsMiddleware(),
142
+ };
143
+ };
@@ -11,6 +11,11 @@ pnpm add @modelprofile.com/flexharness-providers
11
11
  See the [toolbox usage and migration guide](https://code.foss.global/modelprofile.com/flexharness#readme)
12
12
  for composition examples and the SmartAI/SmartAgent migration map.
13
13
 
14
+ ChatGPT connections come from the `/auth` subpath. Use the dynamic connection
15
+ factory for delegated authority access so every provider HTTP request resolves
16
+ a current token while the connection retains its pinned account and residency
17
+ identity.
18
+
14
19
  ## Issue Reporting and Security
15
20
 
16
21
  Report issues and security concerns through [community.foss.global](https://community.foss.global).
@@ -36,7 +36,11 @@ Provider SDKs are installed together. Authentication, file access and vendor med
36
36
  are separate entrypoints and are not imported by the provider factory root. For
37
37
  ChatGPT authentication, pass
38
38
  `connection: createOpenAiChatGptModelConnection(credentials)` from `/auth` to the
39
- OpenAI model options. API-key inference needs no authentication setup.
39
+ OpenAI model options. Delegated runtimes use
40
+ `createOpenAiChatGptDynamicModelConnection({ expectedIdentity, resolveAccess })`
41
+ instead; it resolves current access at every provider HTTP request while keeping
42
+ account and residency identity fixed for the connection. API-key inference
43
+ needs no authentication setup.
40
44
 
41
45
  The package includes the applicable OpenAI Codex license and third-party notices.
42
46