@kernl-sdk/ai 0.4.4 → 0.4.6

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,4 +1,4 @@
1
1
 
2
- > @kernl-sdk/ai@0.4.4 build /home/runner/work/kernl/kernl/packages/providers/ai
2
+ > @kernl-sdk/ai@0.4.6 build /home/runner/work/kernl/kernl/packages/providers/ai
3
3
  > tsc && tsc-alias --resolve-full-paths
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @kernl/ai
2
2
 
3
+ ## 0.4.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [296c377]
8
+ - kernl@0.12.4
9
+
10
+ ## 0.4.5
11
+
12
+ ### Patch Changes
13
+
14
+ - 098dd36: Fix OpenAI OAuth by passing `store: false` via model settings. AISDKLanguageModel now accepts optional default settings that are merged with per-request settings. Remove Anthropic OAuth support (blocked server-side).
15
+
3
16
  ## 0.4.4
4
17
 
5
18
  ### Patch Changes
@@ -1,14 +1,19 @@
1
1
  import type { LanguageModelV3 } from "@ai-sdk/provider";
2
- import type { LanguageModel, LanguageModelRequest, LanguageModelResponse, LanguageModelStreamEvent } from "@kernl-sdk/protocol";
2
+ import type { LanguageModel, LanguageModelRequest, LanguageModelRequestSettings, LanguageModelResponse, LanguageModelStreamEvent } from "@kernl-sdk/protocol";
3
3
  /**
4
4
  * LanguageModel adapter for the AI SDK LanguageModelV3.
5
5
  */
6
6
  export declare class AISDKLanguageModel implements LanguageModel {
7
7
  private model;
8
+ private settings?;
8
9
  readonly spec: "1.0";
9
10
  readonly provider: string;
10
11
  readonly modelId: string;
11
- constructor(model: LanguageModelV3);
12
+ /**
13
+ * @param model - The underlying AI SDK model
14
+ * @param settings - Default settings to apply to every request (overridden by per-request settings)
15
+ */
16
+ constructor(model: LanguageModelV3, settings?: Partial<LanguageModelRequestSettings> | undefined);
12
17
  /**
13
18
  * Get a response from the model.
14
19
  */
@@ -1 +1 @@
1
- {"version":3,"file":"language-model.d.ts","sourceRoot":"","sources":["../src/language-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAU7B;;GAEG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IAK1C,OAAO,CAAC,KAAK;IAJzB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEL,KAAK,EAAE,eAAe;IAK1C;;OAEG;IACG,QAAQ,CACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAiBjC;;OAEG;IACI,MAAM,CACX,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,wBAAwB,CAAC;CAkF3C"}
1
+ {"version":3,"file":"language-model.d.ts","sourceRoot":"","sources":["../src/language-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,4BAA4B,EAC5B,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAU7B;;GAEG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IAUpD,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ,CAAC;IAVnB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;OAGG;gBAEO,KAAK,EAAE,eAAe,EACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,YAAA;IAM1D;;OAEG;IACG,QAAQ,CACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAkBjC;;OAEG;IACI,MAAM,CACX,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,wBAAwB,CAAC;CAmF3C"}
@@ -10,11 +10,17 @@ import { normalizeProvider } from "./util.js";
10
10
  */
11
11
  export class AISDKLanguageModel {
12
12
  model;
13
+ settings;
13
14
  spec = "1.0";
14
15
  provider;
15
16
  modelId;
16
- constructor(model) {
17
+ /**
18
+ * @param model - The underlying AI SDK model
19
+ * @param settings - Default settings to apply to every request (overridden by per-request settings)
20
+ */
21
+ constructor(model, settings) {
17
22
  this.model = model;
23
+ this.settings = settings;
18
24
  this.provider = normalizeProvider(model.provider);
19
25
  this.modelId = model.modelId;
20
26
  }
@@ -24,14 +30,15 @@ export class AISDKLanguageModel {
24
30
  async generate(request) {
25
31
  const messages = request.input.map(MESSAGE.encode);
26
32
  const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
27
- const settings = MODEL_SETTINGS.encode(request.settings);
33
+ const merged = { ...this.settings, ...request.settings };
34
+ const settings = MODEL_SETTINGS.encode(merged);
28
35
  const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
29
36
  const result = await this.model.doGenerate({
30
37
  prompt: messages,
31
38
  tools,
32
- ...settings,
33
39
  responseFormat,
34
40
  abortSignal: request.abort,
41
+ ...settings,
35
42
  });
36
43
  return MODEL_RESPONSE.decode(result);
37
44
  }
@@ -41,14 +48,15 @@ export class AISDKLanguageModel {
41
48
  async *stream(request) {
42
49
  const messages = request.input.map(MESSAGE.encode);
43
50
  const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
44
- const settings = MODEL_SETTINGS.encode(request.settings);
51
+ const merged = { ...this.settings, ...request.settings };
52
+ const settings = MODEL_SETTINGS.encode(merged);
45
53
  const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
46
54
  const stream = await this.model.doStream({
47
55
  prompt: messages,
48
56
  tools,
49
- ...settings,
50
57
  responseFormat,
51
58
  abortSignal: request.abort,
59
+ ...settings,
52
60
  });
53
61
  // text + reasoning buffers for delta accumulation
54
62
  const tbuf = {};
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/oauth/openai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAsDtD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,sBAAsB,IAE1D,OAAO,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,OAAO,WAAW,KACjB,OAAO,CAAC,QAAQ,CAAC,CAgErB"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/oauth/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AA2CtD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,sBAAsB,IAE1D,OAAO,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,OAAO,WAAW,KACjB,OAAO,CAAC,QAAQ,CAAC,CAyCrB"}
@@ -1,15 +1,6 @@
1
- import { appendFileSync } from "node:fs";
2
1
  const TOKEN_URL = "https://auth.openai.com/oauth/token";
3
2
  const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
4
3
  const CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
5
- function debug(msg) {
6
- try {
7
- appendFileSync("/tmp/popcorn-debug.log", `${new Date().toISOString()} [oauth/fetch] ${msg}\n`);
8
- }
9
- catch {
10
- // ignore
11
- }
12
- }
13
4
  /**
14
5
  * Refresh OpenAI OAuth tokens.
15
6
  */
@@ -52,23 +43,12 @@ export function createOAuthFetch(creds) {
52
43
  if (creds.accountId) {
53
44
  headers.set("ChatGPT-Account-Id", creds.accountId);
54
45
  }
55
- // Debug: log request
56
- const url = typeof input === "string"
57
- ? input
58
- : input instanceof URL
59
- ? input.toString()
60
- : input.url;
61
- debug(`Request to: ${url}`);
62
- debug(`Redirecting to: ${CODEX_ENDPOINT}`);
63
46
  // Transform request body for Codex API
64
47
  // Codex requires "instructions" field instead of developer/system role in input
65
- // Codex also requires "store: false"
66
48
  let body = init?.body;
67
49
  if (body && typeof body === "string") {
68
50
  try {
69
51
  const parsed = JSON.parse(body);
70
- // Codex requires store: false
71
- parsed.store = false;
72
52
  // Extract developer/system message as instructions
73
53
  if (parsed.input && Array.isArray(parsed.input)) {
74
54
  const devIdx = parsed.input.findIndex((m) => m.role === "developer" || m.role === "system");
@@ -79,19 +59,11 @@ export function createOAuthFetch(creds) {
79
59
  }
80
60
  }
81
61
  body = JSON.stringify(parsed);
82
- debug(`Transformed body: ${body.slice(0, 500)}`);
83
62
  }
84
63
  catch {
85
- debug(`Failed to transform body`);
64
+ // ignore parse errors
86
65
  }
87
66
  }
88
- const response = await fetch(CODEX_ENDPOINT, { ...init, headers, body });
89
- // Debug: log response
90
- debug(`Response status: ${response.status}`);
91
- if (!response.ok) {
92
- const text = await response.clone().text();
93
- debug(`Error response: ${text.slice(0, 1000)}`);
94
- }
95
- return response;
67
+ return fetch(CODEX_ENDPOINT, { ...init, headers, body });
96
68
  };
97
69
  }
@@ -1,6 +1,5 @@
1
1
  import { anthropic as _anthropic } from "@ai-sdk/anthropic";
2
2
  import { AISDKLanguageModel } from "../language-model.js";
3
- import type { OAuthCredentials } from "../oauth/types.js";
4
3
  /**
5
4
  * Anthropic model IDs (derived from @ai-sdk/anthropic).
6
5
  */
@@ -9,10 +8,8 @@ export type AnthropicModelId = Parameters<typeof _anthropic>[0];
9
8
  * Options for creating a custom Anthropic provider.
10
9
  */
11
10
  export interface AnthropicProviderOptions {
12
- /** API key for standard authentication */
11
+ /** API key for authentication */
13
12
  apiKey?: string;
14
- /** OAuth credentials for Claude Pro/Max authentication */
15
- oauth?: OAuthCredentials;
16
13
  /** Custom base URL */
17
14
  baseURL?: string;
18
15
  /** Custom headers */
@@ -21,24 +18,11 @@ export interface AnthropicProviderOptions {
21
18
  /**
22
19
  * Create a custom Anthropic provider with explicit credentials.
23
20
  *
24
- * @example API key auth
21
+ * @example
25
22
  * ```ts
26
23
  * const anthropic = createAnthropic({ apiKey: "sk-..." });
27
24
  * const model = anthropic("claude-sonnet-4-5");
28
25
  * ```
29
- *
30
- * @example OAuth auth (Claude Pro/Max)
31
- * ```ts
32
- * const anthropic = createAnthropic({
33
- * oauth: {
34
- * accessToken: "...",
35
- * refreshToken: "...",
36
- * expiresAt: Date.now() + 3600000,
37
- * onRefresh: (tokens) => saveTokens(tokens),
38
- * }
39
- * });
40
- * const model = anthropic("claude-sonnet-4-5");
41
- * ```
42
26
  */
43
27
  export declare function createAnthropic(options?: AnthropicProviderOptions): (modelId: AnthropicModelId) => AISDKLanguageModel;
44
28
  /**
@@ -54,5 +38,4 @@ export declare function createAnthropic(options?: AnthropicProviderOptions): (mo
54
38
  * ```
55
39
  */
56
40
  export declare function anthropic(modelId: AnthropicModelId): AISDKLanguageModel;
57
- export type { OAuthCredentials } from "../oauth/types.js";
58
41
  //# sourceMappingURL=anthropic.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,IAAI,UAAU,EAExB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,wBAA6B,IAQ5D,SAAS,gBAAgB,wBAElC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,sBAGlD;AAGD,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,IAAI,UAAU,EAExB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,wBAA6B,IAO5D,SAAS,gBAAgB,wBAElC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,sBAGlD"}
@@ -1,34 +1,19 @@
1
1
  import { anthropic as _anthropic, createAnthropic as _createAnthropic, } from "@ai-sdk/anthropic";
2
2
  import { AISDKLanguageModel } from "../language-model.js";
3
- import { createOAuthFetch } from "../oauth/anthropic.js";
4
3
  /**
5
4
  * Create a custom Anthropic provider with explicit credentials.
6
5
  *
7
- * @example API key auth
6
+ * @example
8
7
  * ```ts
9
8
  * const anthropic = createAnthropic({ apiKey: "sk-..." });
10
9
  * const model = anthropic("claude-sonnet-4-5");
11
10
  * ```
12
- *
13
- * @example OAuth auth (Claude Pro/Max)
14
- * ```ts
15
- * const anthropic = createAnthropic({
16
- * oauth: {
17
- * accessToken: "...",
18
- * refreshToken: "...",
19
- * expiresAt: Date.now() + 3600000,
20
- * onRefresh: (tokens) => saveTokens(tokens),
21
- * }
22
- * });
23
- * const model = anthropic("claude-sonnet-4-5");
24
- * ```
25
11
  */
26
12
  export function createAnthropic(options = {}) {
27
13
  const provider = _createAnthropic({
28
- apiKey: options.oauth ? undefined : options.apiKey,
14
+ apiKey: options.apiKey,
29
15
  baseURL: options.baseURL,
30
16
  headers: options.headers,
31
- fetch: options.oauth ? createOAuthFetch(options.oauth) : undefined,
32
17
  });
33
18
  return (modelId) => new AISDKLanguageModel(provider(modelId));
34
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,IAAI,OAAO,EAElB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,qBAA0B,IAQtD,SAAS,aAAa,wBAC/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,sBAG5C;AAGD,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,IAAI,OAAO,EAElB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,qBAA0B,IAatD,SAAS,aAAa,wBAE/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,sBAG5C;AAGD,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -33,7 +33,11 @@ export function createOpenAI(options = {}) {
33
33
  headers: options.headers,
34
34
  fetch: options.oauth ? createOAuthFetch(options.oauth) : undefined,
35
35
  });
36
- return (modelId) => new AISDKLanguageModel(provider(modelId));
36
+ // OAuth requires store: false - Codex endpoint doesn't persist items
37
+ const settings = options.oauth
38
+ ? { providerOptions: { openai: { store: false } } }
39
+ : undefined;
40
+ return (modelId) => new AISDKLanguageModel(provider(modelId), settings);
37
41
  }
38
42
  /**
39
43
  * Create a kernl-compatible OpenAI language model.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernl-sdk/ai",
3
- "version": "0.4.4",
3
+ "version": "0.4.6",
4
4
  "description": "Vercel AI SDK adapter for kernl",
5
5
  "keywords": [
6
6
  "kernl",
@@ -71,7 +71,7 @@
71
71
  "@kernl-sdk/protocol": "0.5.1",
72
72
  "@kernl-sdk/retrieval": "0.1.10",
73
73
  "@kernl-sdk/shared": "^0.4.0",
74
- "kernl": "0.12.3"
74
+ "kernl": "0.12.4"
75
75
  },
76
76
  "scripts": {
77
77
  "build": "tsc && tsc-alias --resolve-full-paths",
@@ -3,6 +3,7 @@ import type { LanguageModelV3 } from "@ai-sdk/provider";
3
3
  import type {
4
4
  LanguageModel,
5
5
  LanguageModelRequest,
6
+ LanguageModelRequestSettings,
6
7
  LanguageModelResponse,
7
8
  LanguageModelStreamEvent,
8
9
  } from "@kernl-sdk/protocol";
@@ -23,7 +24,14 @@ export class AISDKLanguageModel implements LanguageModel {
23
24
  readonly provider: string;
24
25
  readonly modelId: string;
25
26
 
26
- constructor(private model: LanguageModelV3) {
27
+ /**
28
+ * @param model - The underlying AI SDK model
29
+ * @param settings - Default settings to apply to every request (overridden by per-request settings)
30
+ */
31
+ constructor(
32
+ private model: LanguageModelV3,
33
+ private settings?: Partial<LanguageModelRequestSettings>,
34
+ ) {
27
35
  this.provider = normalizeProvider(model.provider);
28
36
  this.modelId = model.modelId;
29
37
  }
@@ -36,15 +44,16 @@ export class AISDKLanguageModel implements LanguageModel {
36
44
  ): Promise<LanguageModelResponse> {
37
45
  const messages = request.input.map(MESSAGE.encode);
38
46
  const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
39
- const settings = MODEL_SETTINGS.encode(request.settings);
47
+ const merged = { ...this.settings, ...request.settings };
48
+ const settings = MODEL_SETTINGS.encode(merged);
40
49
  const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
41
50
 
42
51
  const result = await this.model.doGenerate({
43
52
  prompt: messages,
44
53
  tools,
45
- ...settings,
46
54
  responseFormat,
47
55
  abortSignal: request.abort,
56
+ ...settings,
48
57
  });
49
58
 
50
59
  return MODEL_RESPONSE.decode(result);
@@ -58,15 +67,16 @@ export class AISDKLanguageModel implements LanguageModel {
58
67
  ): AsyncIterable<LanguageModelStreamEvent> {
59
68
  const messages = request.input.map(MESSAGE.encode);
60
69
  const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
61
- const settings = MODEL_SETTINGS.encode(request.settings);
70
+ const merged = { ...this.settings, ...request.settings };
71
+ const settings = MODEL_SETTINGS.encode(merged);
62
72
  const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
63
73
 
64
74
  const stream = await this.model.doStream({
65
75
  prompt: messages,
66
76
  tools,
67
- ...settings,
68
77
  responseFormat,
69
78
  abortSignal: request.abort,
79
+ ...settings,
70
80
  });
71
81
 
72
82
  // text + reasoning buffers for delta accumulation
@@ -1,21 +1,9 @@
1
- import { appendFileSync } from "node:fs";
2
1
  import type { OpenAIOAuthCredentials } from "./types";
3
2
 
4
3
  const TOKEN_URL = "https://auth.openai.com/oauth/token";
5
4
  const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
6
5
  const CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
7
6
 
8
- function debug(msg: string) {
9
- try {
10
- appendFileSync(
11
- "/tmp/popcorn-debug.log",
12
- `${new Date().toISOString()} [oauth/fetch] ${msg}\n`,
13
- );
14
- } catch {
15
- // ignore
16
- }
17
- }
18
-
19
7
  interface TokenResponse {
20
8
  access_token: string;
21
9
  refresh_token: string;
@@ -75,31 +63,18 @@ export function createOAuthFetch(creds: OpenAIOAuthCredentials) {
75
63
  headers.set("ChatGPT-Account-Id", creds.accountId);
76
64
  }
77
65
 
78
- // Debug: log request
79
- const url =
80
- typeof input === "string"
81
- ? input
82
- : input instanceof URL
83
- ? input.toString()
84
- : input.url;
85
- debug(`Request to: ${url}`);
86
- debug(`Redirecting to: ${CODEX_ENDPOINT}`);
87
-
88
66
  // Transform request body for Codex API
89
67
  // Codex requires "instructions" field instead of developer/system role in input
90
- // Codex also requires "store: false"
91
68
  let body = init?.body;
92
69
  if (body && typeof body === "string") {
93
70
  try {
94
71
  const parsed = JSON.parse(body);
95
72
 
96
- // Codex requires store: false
97
- parsed.store = false;
98
-
99
73
  // Extract developer/system message as instructions
100
74
  if (parsed.input && Array.isArray(parsed.input)) {
101
75
  const devIdx = parsed.input.findIndex(
102
- (m: any) => m.role === "developer" || m.role === "system",
76
+ (m: Record<string, unknown>) =>
77
+ m.role === "developer" || m.role === "system",
103
78
  );
104
79
  if (devIdx !== -1) {
105
80
  const devMsg = parsed.input[devIdx];
@@ -109,21 +84,11 @@ export function createOAuthFetch(creds: OpenAIOAuthCredentials) {
109
84
  }
110
85
 
111
86
  body = JSON.stringify(parsed);
112
- debug(`Transformed body: ${body.slice(0, 500)}`);
113
87
  } catch {
114
- debug(`Failed to transform body`);
88
+ // ignore parse errors
115
89
  }
116
90
  }
117
91
 
118
- const response = await fetch(CODEX_ENDPOINT, { ...init, headers, body });
119
-
120
- // Debug: log response
121
- debug(`Response status: ${response.status}`);
122
- if (!response.ok) {
123
- const text = await response.clone().text();
124
- debug(`Error response: ${text.slice(0, 1000)}`);
125
- }
126
-
127
- return response;
92
+ return fetch(CODEX_ENDPOINT, { ...init, headers, body });
128
93
  };
129
94
  }
@@ -3,8 +3,6 @@ import {
3
3
  createAnthropic as _createAnthropic,
4
4
  } from "@ai-sdk/anthropic";
5
5
  import { AISDKLanguageModel } from "../language-model";
6
- import { createOAuthFetch } from "../oauth/anthropic";
7
- import type { OAuthCredentials } from "../oauth/types";
8
6
 
9
7
  /**
10
8
  * Anthropic model IDs (derived from @ai-sdk/anthropic).
@@ -15,10 +13,8 @@ export type AnthropicModelId = Parameters<typeof _anthropic>[0];
15
13
  * Options for creating a custom Anthropic provider.
16
14
  */
17
15
  export interface AnthropicProviderOptions {
18
- /** API key for standard authentication */
16
+ /** API key for authentication */
19
17
  apiKey?: string;
20
- /** OAuth credentials for Claude Pro/Max authentication */
21
- oauth?: OAuthCredentials;
22
18
  /** Custom base URL */
23
19
  baseURL?: string;
24
20
  /** Custom headers */
@@ -28,31 +24,17 @@ export interface AnthropicProviderOptions {
28
24
  /**
29
25
  * Create a custom Anthropic provider with explicit credentials.
30
26
  *
31
- * @example API key auth
27
+ * @example
32
28
  * ```ts
33
29
  * const anthropic = createAnthropic({ apiKey: "sk-..." });
34
30
  * const model = anthropic("claude-sonnet-4-5");
35
31
  * ```
36
- *
37
- * @example OAuth auth (Claude Pro/Max)
38
- * ```ts
39
- * const anthropic = createAnthropic({
40
- * oauth: {
41
- * accessToken: "...",
42
- * refreshToken: "...",
43
- * expiresAt: Date.now() + 3600000,
44
- * onRefresh: (tokens) => saveTokens(tokens),
45
- * }
46
- * });
47
- * const model = anthropic("claude-sonnet-4-5");
48
- * ```
49
32
  */
50
33
  export function createAnthropic(options: AnthropicProviderOptions = {}) {
51
34
  const provider = _createAnthropic({
52
- apiKey: options.oauth ? undefined : options.apiKey,
35
+ apiKey: options.apiKey,
53
36
  baseURL: options.baseURL,
54
37
  headers: options.headers,
55
- fetch: options.oauth ? createOAuthFetch(options.oauth) : undefined,
56
38
  });
57
39
 
58
40
  return (modelId: AnthropicModelId) =>
@@ -76,7 +58,4 @@ export function anthropic(modelId: AnthropicModelId) {
76
58
  return new AISDKLanguageModel(model);
77
59
  }
78
60
 
79
- // Re-export types
80
- export type { OAuthCredentials } from "../oauth/types";
81
-
82
61
  // Note: Anthropic does not currently support embeddings
@@ -59,7 +59,13 @@ export function createOpenAI(options: OpenAIProviderOptions = {}) {
59
59
  fetch: options.oauth ? createOAuthFetch(options.oauth) : undefined,
60
60
  });
61
61
 
62
- return (modelId: OpenAIModelId) => new AISDKLanguageModel(provider(modelId));
62
+ // OAuth requires store: false - Codex endpoint doesn't persist items
63
+ const settings = options.oauth
64
+ ? { providerOptions: { openai: { store: false } } }
65
+ : undefined;
66
+
67
+ return (modelId: OpenAIModelId) =>
68
+ new AISDKLanguageModel(provider(modelId), settings);
63
69
  }
64
70
 
65
71
  /**
@@ -1,8 +0,0 @@
1
- import type { OAuthCredentials } from "./types.js";
2
- /**
3
- * Create a fetch wrapper for Anthropic OAuth.
4
- *
5
- * Uses the standard Anthropic API with OAuth bearer token.
6
- */
7
- export declare function createOAuthFetch(creds: OAuthCredentials): (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
8
- //# sourceMappingURL=anthropic.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/oauth/anthropic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAoDhD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,IAEpD,OAAO,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,OAAO,WAAW,KACjB,OAAO,CAAC,QAAQ,CAAC,CAwBrB"}
@@ -1,65 +0,0 @@
1
- import { appendFileSync } from "node:fs";
2
- const TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
3
- const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
4
- function debug(msg) {
5
- try {
6
- appendFileSync("/tmp/popcorn-debug.log", `${new Date().toISOString()} [oauth/anthropic] ${msg}\n`);
7
- }
8
- catch {
9
- // ignore
10
- }
11
- }
12
- /**
13
- * Refresh Anthropic OAuth tokens.
14
- */
15
- async function refresh(creds) {
16
- debug(`Refreshing token...`);
17
- const res = await fetch(TOKEN_URL, {
18
- method: "POST",
19
- headers: { "Content-Type": "application/json" },
20
- body: JSON.stringify({
21
- grant_type: "refresh_token",
22
- refresh_token: creds.refreshToken,
23
- client_id: CLIENT_ID,
24
- }),
25
- });
26
- if (!res.ok) {
27
- throw new Error(`Token refresh failed: ${res.status}`);
28
- }
29
- const data = (await res.json());
30
- creds.accessToken = data.access_token;
31
- creds.refreshToken = data.refresh_token;
32
- creds.expiresAt = Date.now() + data.expires_in * 1000;
33
- creds.onRefresh?.({
34
- accessToken: creds.accessToken,
35
- refreshToken: creds.refreshToken,
36
- expiresAt: creds.expiresAt,
37
- });
38
- debug(`Token refreshed successfully`);
39
- }
40
- /**
41
- * Create a fetch wrapper for Anthropic OAuth.
42
- *
43
- * Uses the standard Anthropic API with OAuth bearer token.
44
- */
45
- export function createOAuthFetch(creds) {
46
- return async (input, init) => {
47
- // Refresh if expired (with 30s buffer)
48
- if (Date.now() >= creds.expiresAt - 30_000) {
49
- await refresh(creds);
50
- }
51
- const headers = new Headers(init?.headers);
52
- headers.set("Authorization", `Bearer ${creds.accessToken}`);
53
- // Required beta header for OAuth
54
- headers.set("anthropic-beta", "oauth-2025-04-20");
55
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
56
- debug(`Request to: ${url}`);
57
- const response = await fetch(input, { ...init, headers });
58
- debug(`Response status: ${response.status}`);
59
- if (!response.ok) {
60
- const text = await response.clone().text();
61
- debug(`Error response: ${text.slice(0, 1000)}`);
62
- }
63
- return response;
64
- };
65
- }
@@ -1,87 +0,0 @@
1
- import { appendFileSync } from "node:fs";
2
- import type { OAuthCredentials } from "./types";
3
-
4
- const TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
5
- const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
6
-
7
- function debug(msg: string) {
8
- try {
9
- appendFileSync("/tmp/popcorn-debug.log", `${new Date().toISOString()} [oauth/anthropic] ${msg}\n`);
10
- } catch {
11
- // ignore
12
- }
13
- }
14
-
15
- interface TokenResponse {
16
- access_token: string;
17
- refresh_token: string;
18
- expires_in: number;
19
- }
20
-
21
- /**
22
- * Refresh Anthropic OAuth tokens.
23
- */
24
- async function refresh(creds: OAuthCredentials): Promise<void> {
25
- debug(`Refreshing token...`);
26
- const res = await fetch(TOKEN_URL, {
27
- method: "POST",
28
- headers: { "Content-Type": "application/json" },
29
- body: JSON.stringify({
30
- grant_type: "refresh_token",
31
- refresh_token: creds.refreshToken,
32
- client_id: CLIENT_ID,
33
- }),
34
- });
35
-
36
- if (!res.ok) {
37
- throw new Error(`Token refresh failed: ${res.status}`);
38
- }
39
-
40
- const data = (await res.json()) as TokenResponse;
41
-
42
- creds.accessToken = data.access_token;
43
- creds.refreshToken = data.refresh_token;
44
- creds.expiresAt = Date.now() + data.expires_in * 1000;
45
-
46
- creds.onRefresh?.({
47
- accessToken: creds.accessToken,
48
- refreshToken: creds.refreshToken,
49
- expiresAt: creds.expiresAt,
50
- });
51
- debug(`Token refreshed successfully`);
52
- }
53
-
54
- /**
55
- * Create a fetch wrapper for Anthropic OAuth.
56
- *
57
- * Uses the standard Anthropic API with OAuth bearer token.
58
- */
59
- export function createOAuthFetch(creds: OAuthCredentials) {
60
- return async (
61
- input: string | URL | Request,
62
- init?: RequestInit,
63
- ): Promise<Response> => {
64
- // Refresh if expired (with 30s buffer)
65
- if (Date.now() >= creds.expiresAt - 30_000) {
66
- await refresh(creds);
67
- }
68
-
69
- const headers = new Headers(init?.headers);
70
- headers.set("Authorization", `Bearer ${creds.accessToken}`);
71
- // Required beta header for OAuth
72
- headers.set("anthropic-beta", "oauth-2025-04-20");
73
-
74
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
75
- debug(`Request to: ${url}`);
76
-
77
- const response = await fetch(input, { ...init, headers });
78
-
79
- debug(`Response status: ${response.status}`);
80
- if (!response.ok) {
81
- const text = await response.clone().text();
82
- debug(`Error response: ${text.slice(0, 1000)}`);
83
- }
84
-
85
- return response;
86
- };
87
- }