klaus-agent 0.2.0 → 0.2.2
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/README.md +37 -11
- package/README.zh-CN.md +709 -0
- package/dist/core/agent-loop.d.ts +2 -1
- package/dist/core/agent-loop.js +5 -1
- package/dist/core/agent-loop.js.map +1 -1
- package/dist/core/agent.js +1 -0
- package/dist/core/agent.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/llm/provider.js +5 -11
- package/dist/llm/provider.js.map +1 -1
- package/dist/llm/types.d.ts +17 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai-codex.d.ts +8 -0
- package/dist/providers/openai-codex.js +334 -0
- package/dist/providers/openai-codex.js.map +1 -0
- package/dist/providers/openai-responses-shared.d.ts +36 -0
- package/dist/providers/openai-responses-shared.js +74 -0
- package/dist/providers/openai-responses-shared.js.map +1 -0
- package/dist/providers/openai-responses.d.ts +7 -0
- package/dist/providers/openai-responses.js +128 -0
- package/dist/providers/openai-responses.js.map +1 -0
- package/dist/providers/openai.js +1 -10
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/shared.d.ts +5 -1
- package/dist/providers/shared.js +21 -0
- package/dist/providers/shared.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
- package/src/core/agent-loop.ts +6 -1
- package/src/core/agent.ts +1 -0
- package/src/index.ts +4 -3
- package/src/llm/provider.ts +5 -12
- package/src/llm/types.ts +19 -0
- package/src/providers/openai-codex.ts +399 -0
- package/src/providers/openai-responses-shared.ts +97 -0
- package/src/providers/openai-responses.ts +152 -0
- package/src/providers/openai.ts +1 -8
- package/src/providers/shared.ts +20 -1
- package/src/types.ts +4 -0
- package/src/providers/index.ts +0 -6
- package/src/providers/kimi.ts +0 -12
- package/src/providers/minimax.ts +0 -12
- package/src/providers/volcengine.ts +0 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,sBAAsB;AAEtB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAW5B,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantMessageEvent, ThinkingLevel } from "../llm/types.js";
|
|
1
|
+
import type { AssistantMessageEvent, ModelCost, TokenUsage, ThinkingLevel, UsageCost } from "../llm/types.js";
|
|
2
2
|
export declare const RETRYABLE_PATTERNS: Record<string, string[]>;
|
|
3
3
|
export declare function isRetryableError(error: Error, patterns: string[]): boolean;
|
|
4
4
|
/**
|
|
@@ -8,3 +8,7 @@ export declare function isRetryableError(error: Error, patterns: string[]): bool
|
|
|
8
8
|
export declare function withRetry(streamOnce: () => AsyncIterable<AssistantMessageEvent>, retryablePatterns: string[], maxRetries?: number): AsyncIterable<AssistantMessageEvent>;
|
|
9
9
|
/** Maps ThinkingLevel to token budget (shared by Anthropic and Gemini). */
|
|
10
10
|
export declare function mapThinkingBudget(level?: ThinkingLevel): number | undefined;
|
|
11
|
+
/** Maps ThinkingLevel to OpenAI reasoning_effort (shared by OpenAI and OpenAI Responses). */
|
|
12
|
+
export declare function mapReasoningEffort(level?: ThinkingLevel): "low" | "medium" | "high" | undefined;
|
|
13
|
+
/** Calculate actual dollar cost from token counts and per-million-token pricing. */
|
|
14
|
+
export declare function calculateCost(cost: ModelCost | undefined, usage: TokenUsage): UsageCost | undefined;
|
package/dist/providers/shared.js
CHANGED
|
@@ -5,6 +5,7 @@ export const RETRYABLE_PATTERNS = {
|
|
|
5
5
|
anthropic: [...COMMON_RETRYABLE, "rate_limit", "overloaded", "529"],
|
|
6
6
|
openai: [...COMMON_RETRYABLE, "rate_limit"],
|
|
7
7
|
google: [...COMMON_RETRYABLE],
|
|
8
|
+
codex: [...COMMON_RETRYABLE, "rate_limit", "usage_limit", "overloaded"],
|
|
8
9
|
};
|
|
9
10
|
export function isRetryableError(error, patterns) {
|
|
10
11
|
return patterns.some((p) => error.message.includes(p));
|
|
@@ -46,4 +47,24 @@ export function mapThinkingBudget(level) {
|
|
|
46
47
|
};
|
|
47
48
|
return budgets[level];
|
|
48
49
|
}
|
|
50
|
+
/** Maps ThinkingLevel to OpenAI reasoning_effort (shared by OpenAI and OpenAI Responses). */
|
|
51
|
+
export function mapReasoningEffort(level) {
|
|
52
|
+
if (!level || level === "off")
|
|
53
|
+
return undefined;
|
|
54
|
+
if (level === "minimal" || level === "low")
|
|
55
|
+
return "low";
|
|
56
|
+
if (level === "medium")
|
|
57
|
+
return "medium";
|
|
58
|
+
return "high";
|
|
59
|
+
}
|
|
60
|
+
/** Calculate actual dollar cost from token counts and per-million-token pricing. */
|
|
61
|
+
export function calculateCost(cost, usage) {
|
|
62
|
+
if (!cost)
|
|
63
|
+
return undefined;
|
|
64
|
+
const input = (cost.input / 1_000_000) * usage.inputTokens;
|
|
65
|
+
const output = (cost.output / 1_000_000) * usage.outputTokens;
|
|
66
|
+
const cacheRead = ((cost.cacheRead ?? 0) / 1_000_000) * (usage.cacheReadTokens ?? 0);
|
|
67
|
+
const cacheWrite = ((cost.cacheWrite ?? 0) / 1_000_000) * (usage.cacheWriteTokens ?? 0);
|
|
68
|
+
return { input, output, cacheRead, cacheWrite, total: input + output + cacheRead + cacheWrite };
|
|
69
|
+
}
|
|
49
70
|
//# sourceMappingURL=shared.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/providers/shared.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAI/C,mDAAmD;AACnD,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;AAE7D,MAAM,CAAC,MAAM,kBAAkB,GAA6B;IAC1D,SAAS,EAAE,CAAC,GAAG,gBAAgB,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IACnE,MAAM,EAAE,CAAC,GAAG,gBAAgB,EAAE,YAAY,CAAC;IAC3C,MAAM,EAAE,CAAC,GAAG,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/providers/shared.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Message, UserMessage, AssistantMessage, ToolResultMessage, AssistantMessageEvent, TokenUsage, ThinkingLevel, ModelConfig, ContentBlock, TextContent, ImageContent, ToolCallBlock } from "./llm/types.js";
|
|
1
|
+
import type { Message, UserMessage, AssistantMessage, ToolResultMessage, AssistantMessageEvent, TokenUsage, ModelCost, UsageCost, ThinkingLevel, ModelConfig, ContentBlock, TextContent, ImageContent, ToolCallBlock } from "./llm/types.js";
|
|
2
2
|
import type { AgentTool, AgentToolResult, BeforeToolCallContext, BeforeToolCallResult, AfterToolCallContext, AfterToolCallResult } from "./tools/types.js";
|
|
3
3
|
import type { ApprovalRequest, ApprovalResponse, ApprovalConfig } from "./approval/types.js";
|
|
4
4
|
export interface CustomAgentMessages {
|
|
@@ -93,4 +93,4 @@ export interface AgentHooks {
|
|
|
93
93
|
beforeToolCall?: (ctx: BeforeToolCallContext) => Promise<BeforeToolCallResult | void>;
|
|
94
94
|
afterToolCall?: (ctx: AfterToolCallContext) => Promise<AfterToolCallResult | void>;
|
|
95
95
|
}
|
|
96
|
-
export type { Message, UserMessage, AssistantMessage, ToolResultMessage, AssistantMessageEvent, TokenUsage, ThinkingLevel, ModelConfig, ContentBlock, TextContent, ImageContent, ToolCallBlock, AgentTool, AgentToolResult, BeforeToolCallContext, BeforeToolCallResult, AfterToolCallContext, AfterToolCallResult, ApprovalRequest, ApprovalResponse, ApprovalConfig, };
|
|
96
|
+
export type { Message, UserMessage, AssistantMessage, ToolResultMessage, AssistantMessageEvent, TokenUsage, ModelCost, UsageCost, ThinkingLevel, ModelConfig, ContentBlock, TextContent, ImageContent, ToolCallBlock, AgentTool, AgentToolResult, BeforeToolCallContext, BeforeToolCallResult, AfterToolCallContext, AfterToolCallResult, ApprovalRequest, ApprovalResponse, ApprovalConfig, };
|
package/package.json
CHANGED
package/src/core/agent-loop.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
TokenUsage,
|
|
13
13
|
UserMessage,
|
|
14
14
|
ThinkingLevel,
|
|
15
|
+
ModelCost,
|
|
15
16
|
} from "../types.js";
|
|
16
17
|
import type { AgentTool } from "../tools/types.js";
|
|
17
18
|
import type { LLMProvider, LLMRequestOptions, ToolDefinition, AssistantMessageEvent } from "../llm/types.js";
|
|
@@ -24,6 +25,7 @@ import type { CompactionConfig } from "../compaction/types.js";
|
|
|
24
25
|
import { executeToolCalls, type ToolCallResult } from "../tools/executor.js";
|
|
25
26
|
import { estimateTokens, shouldCompact, findCutPoint } from "../compaction/compaction.js";
|
|
26
27
|
import { normalizeHistory } from "../injection/history-normalizer.js";
|
|
28
|
+
import { calculateCost } from "../providers/shared.js";
|
|
27
29
|
|
|
28
30
|
export interface AgentLoopConfig {
|
|
29
31
|
provider: LLMProvider;
|
|
@@ -48,6 +50,7 @@ export interface AgentLoopConfig {
|
|
|
48
50
|
injectionManager?: InjectionManager;
|
|
49
51
|
extensionRunner?: ExtensionRunner;
|
|
50
52
|
compaction?: CompactionConfig & { summarize?: (messages: AgentMessage[]) => Promise<string> };
|
|
53
|
+
modelCost?: ModelCost;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
function defaultConvertToLlm(messages: AgentMessage[]): Message[] {
|
|
@@ -284,7 +287,9 @@ export async function runAgentLoop(
|
|
|
284
287
|
if (event.type === "done") {
|
|
285
288
|
assistantMessage = event.message;
|
|
286
289
|
usage = event.usage;
|
|
287
|
-
|
|
290
|
+
const cost = calculateCost(config.modelCost, event.usage);
|
|
291
|
+
if (cost) usage.cost = cost;
|
|
292
|
+
onEvent({ type: "message_end", message: event.message, usage });
|
|
288
293
|
await extensionRunner?.emitSimple("message_end", { message: event.message });
|
|
289
294
|
} else if (event.type === "error") {
|
|
290
295
|
onEvent({ type: "error", error: event.error });
|
package/src/core/agent.ts
CHANGED
|
@@ -410,6 +410,7 @@ export class Agent {
|
|
|
410
410
|
hooks: this._config.hooks,
|
|
411
411
|
thinkingLevel: this._state.thinkingLevel,
|
|
412
412
|
capabilities: this._state.model.capabilities,
|
|
413
|
+
modelCost: this._state.model.cost,
|
|
413
414
|
signal: this._abortController.signal,
|
|
414
415
|
getSteeringMessages: () => {
|
|
415
416
|
const msgs = [...this._steeringQueue];
|
package/src/index.ts
CHANGED
|
@@ -80,10 +80,8 @@ export { ApprovalImpl } from "./approval/approval.js";
|
|
|
80
80
|
export { registerProvider, resolveProvider } from "./llm/provider.js";
|
|
81
81
|
export { AnthropicProvider } from "./providers/anthropic.js";
|
|
82
82
|
export { OpenAIProvider } from "./providers/openai.js";
|
|
83
|
+
export { OpenAIResponsesProvider } from "./providers/openai-responses.js";
|
|
83
84
|
export { GeminiProvider } from "./providers/google.js";
|
|
84
|
-
export { MiniMaxProvider } from "./providers/minimax.js";
|
|
85
|
-
export { KimiProvider } from "./providers/kimi.js";
|
|
86
|
-
export { VolcengineProvider } from "./providers/volcengine.js";
|
|
87
85
|
export { executeToolCalls } from "./tools/executor.js";
|
|
88
86
|
export { SessionManager } from "./session/session-manager.js";
|
|
89
87
|
export { buildSessionContext } from "./session/session-context-builder.js";
|
|
@@ -98,6 +96,7 @@ export { discoverSkills } from "./skills/discovery.js";
|
|
|
98
96
|
export { loadSkill, renderSkillTemplate } from "./skills/loader.js";
|
|
99
97
|
export { MCPAdapter } from "./tools/mcp-adapter.js";
|
|
100
98
|
export { estimateTokens, shouldCompact, findCutPoint } from "./compaction/compaction.js";
|
|
99
|
+
export { calculateCost } from "./providers/shared.js";
|
|
101
100
|
export { LLMSummarizer } from "./compaction/summarizer.js";
|
|
102
101
|
export { Wire } from "./wire/wire.js";
|
|
103
102
|
export { BackgroundTaskManager } from "./background/task-manager.js";
|
|
@@ -144,6 +143,8 @@ export type {
|
|
|
144
143
|
ToolResultMessage,
|
|
145
144
|
Message,
|
|
146
145
|
TokenUsage,
|
|
146
|
+
ModelCost,
|
|
147
|
+
UsageCost,
|
|
147
148
|
ContentBlock,
|
|
148
149
|
TextContent,
|
|
149
150
|
ImageContent,
|
package/src/llm/provider.ts
CHANGED
|
@@ -2,26 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
import { AnthropicProvider } from "../providers/anthropic.js";
|
|
4
4
|
import { OpenAIProvider } from "../providers/openai.js";
|
|
5
|
+
import { OpenAICodexProvider } from "../providers/openai-codex.js";
|
|
6
|
+
import { OpenAIResponsesProvider } from "../providers/openai-responses.js";
|
|
5
7
|
import { GeminiProvider } from "../providers/google.js";
|
|
6
|
-
import { MiniMaxProvider } from "../providers/minimax.js";
|
|
7
|
-
import { KimiProvider } from "../providers/kimi.js";
|
|
8
|
-
import { VolcengineProvider } from "../providers/volcengine.js";
|
|
9
8
|
import type { LLMProvider, LLMProviderFactory } from "./types.js";
|
|
10
9
|
|
|
11
10
|
const providers = new Map<string, LLMProviderFactory>();
|
|
12
11
|
|
|
13
|
-
// Built-in providers
|
|
12
|
+
// Built-in protocol providers
|
|
14
13
|
providers.set("anthropic", (c) => new AnthropicProvider(c.apiKey, c.baseUrl));
|
|
15
14
|
providers.set("openai", (c) => new OpenAIProvider(c.apiKey, c.baseUrl));
|
|
15
|
+
providers.set("openai-responses", (c) => new OpenAIResponsesProvider(c.apiKey, c.baseUrl));
|
|
16
|
+
providers.set("openai-codex", (c) => new OpenAICodexProvider(c.apiKey, c.baseUrl));
|
|
16
17
|
providers.set("google", (c) => new GeminiProvider(c.apiKey, c.baseUrl));
|
|
17
|
-
providers.set("minimax", (c) => new MiniMaxProvider(c.apiKey, c.baseUrl));
|
|
18
|
-
providers.set("kimi", (c) => new KimiProvider(c.apiKey, c.baseUrl));
|
|
19
|
-
providers.set("volcengine", (c) => new VolcengineProvider(c.apiKey, c.baseUrl));
|
|
20
|
-
|
|
21
|
-
// Protocol-compatible proxies — user provides baseUrl to connect any compatible service
|
|
22
|
-
providers.set("openai-compatible", (c) => new OpenAIProvider(c.apiKey, c.baseUrl));
|
|
23
|
-
providers.set("anthropic-compatible", (c) => new AnthropicProvider(c.apiKey, c.baseUrl));
|
|
24
|
-
providers.set("gemini-compatible", (c) => new GeminiProvider(c.apiKey, c.baseUrl));
|
|
25
18
|
|
|
26
19
|
export function registerProvider(name: string, factory: LLMProviderFactory): void {
|
|
27
20
|
providers.set(name, factory);
|
package/src/llm/types.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
// LLM abstraction types
|
|
2
2
|
|
|
3
|
+
/** Per-token pricing in $/million tokens. */
|
|
4
|
+
export interface ModelCost {
|
|
5
|
+
input: number;
|
|
6
|
+
output: number;
|
|
7
|
+
cacheRead?: number;
|
|
8
|
+
cacheWrite?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Calculated cost in actual dollars for a single request. */
|
|
12
|
+
export interface UsageCost {
|
|
13
|
+
input: number;
|
|
14
|
+
output: number;
|
|
15
|
+
cacheRead: number;
|
|
16
|
+
cacheWrite: number;
|
|
17
|
+
total: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
3
20
|
export interface ModelConfig {
|
|
4
21
|
provider: string;
|
|
5
22
|
model: string;
|
|
@@ -10,6 +27,7 @@ export interface ModelConfig {
|
|
|
10
27
|
vision?: boolean;
|
|
11
28
|
thinking?: boolean;
|
|
12
29
|
};
|
|
30
|
+
cost?: ModelCost;
|
|
13
31
|
}
|
|
14
32
|
|
|
15
33
|
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
@@ -84,6 +102,7 @@ export interface TokenUsage {
|
|
|
84
102
|
totalTokens: number;
|
|
85
103
|
cacheReadTokens?: number;
|
|
86
104
|
cacheWriteTokens?: number;
|
|
105
|
+
cost?: UsageCost;
|
|
87
106
|
}
|
|
88
107
|
|
|
89
108
|
// --- Streaming events ---
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
// OpenAI Codex LLM provider
|
|
2
|
+
// Connects to ChatGPT's Codex backend via Responses API format.
|
|
3
|
+
// Requires a ChatGPT OAuth JWT token as apiKey.
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
LLMProvider,
|
|
7
|
+
LLMRequestOptions,
|
|
8
|
+
AssistantMessageEvent,
|
|
9
|
+
AssistantMessage,
|
|
10
|
+
AssistantContentBlock,
|
|
11
|
+
TokenUsage,
|
|
12
|
+
ThinkingLevel,
|
|
13
|
+
Message,
|
|
14
|
+
ToolDefinition,
|
|
15
|
+
} from "../llm/types.js";
|
|
16
|
+
import { platform, release, arch } from "node:os";
|
|
17
|
+
import { mapMessages, mapTools } from "./openai-responses-shared.js";
|
|
18
|
+
import type { ResponseInput, ResponseTool } from "./openai-responses-shared.js";
|
|
19
|
+
|
|
20
|
+
// --- Configuration ---
|
|
21
|
+
|
|
22
|
+
const DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
|
|
23
|
+
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
24
|
+
const MAX_RETRIES = 3;
|
|
25
|
+
const BASE_DELAY_MS = 1000;
|
|
26
|
+
|
|
27
|
+
// --- Types ---
|
|
28
|
+
|
|
29
|
+
interface CodexRequestBody {
|
|
30
|
+
model: string;
|
|
31
|
+
store: boolean;
|
|
32
|
+
stream: boolean;
|
|
33
|
+
instructions: string;
|
|
34
|
+
input: ResponseInput;
|
|
35
|
+
tools?: ResponseTool[];
|
|
36
|
+
tool_choice: "auto";
|
|
37
|
+
parallel_tool_calls: boolean;
|
|
38
|
+
temperature?: number;
|
|
39
|
+
reasoning?: { effort: string; summary: string };
|
|
40
|
+
text: { verbosity: string };
|
|
41
|
+
include: string[];
|
|
42
|
+
prompt_cache_key?: string;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// --- Provider ---
|
|
47
|
+
|
|
48
|
+
export class OpenAICodexProvider implements LLMProvider {
|
|
49
|
+
private apiKey: string;
|
|
50
|
+
private baseUrl: string;
|
|
51
|
+
|
|
52
|
+
constructor(apiKey?: string, baseUrl?: string) {
|
|
53
|
+
this.apiKey = apiKey || process.env.OPENAI_CODEX_TOKEN || "";
|
|
54
|
+
this.baseUrl = baseUrl || DEFAULT_BASE_URL;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async *stream(options: LLMRequestOptions): AsyncIterable<AssistantMessageEvent> {
|
|
58
|
+
yield* this._streamOnce(options);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private async *_streamOnce(options: LLMRequestOptions): AsyncIterable<AssistantMessageEvent> {
|
|
62
|
+
const { model, systemPrompt, messages, tools, thinkingLevel, signal } = options;
|
|
63
|
+
|
|
64
|
+
if (!this.apiKey) {
|
|
65
|
+
throw new Error("No API key for openai-codex provider. Set OPENAI_CODEX_TOKEN or pass apiKey.");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const accountId = extractAccountId(this.apiKey);
|
|
69
|
+
const body = buildRequestBody(model, systemPrompt, messages, tools, thinkingLevel);
|
|
70
|
+
const headers = buildHeaders(accountId, this.apiKey);
|
|
71
|
+
const url = resolveCodexUrl(this.baseUrl);
|
|
72
|
+
|
|
73
|
+
// Fetch with retry for rate limits / transient errors
|
|
74
|
+
let response: Response | undefined;
|
|
75
|
+
let lastError: Error | undefined;
|
|
76
|
+
|
|
77
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
78
|
+
if (signal?.aborted) throw new Error("Request was aborted");
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
response = await fetch(url, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers,
|
|
84
|
+
body: JSON.stringify(body),
|
|
85
|
+
signal,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (response.ok) break;
|
|
89
|
+
|
|
90
|
+
const errorText = await response.text();
|
|
91
|
+
if (attempt < MAX_RETRIES && isRetryableStatus(response.status, errorText)) {
|
|
92
|
+
await sleep(BASE_DELAY_MS * 2 ** attempt, signal);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw new Error(parseErrorMessage(response.status, errorText));
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (err instanceof Error && (err.name === "AbortError" || err.message === "Request was aborted")) {
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
102
|
+
if (attempt < MAX_RETRIES && !lastError.message.includes("usage limit")) {
|
|
103
|
+
await sleep(BASE_DELAY_MS * 2 ** attempt, signal);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
throw lastError;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!response?.ok) throw lastError ?? new Error("Failed after retries");
|
|
111
|
+
if (!response.body) throw new Error("No response body");
|
|
112
|
+
|
|
113
|
+
// Parse SSE stream and map to AssistantMessageEvent
|
|
114
|
+
const contentBlocks: AssistantContentBlock[] = [];
|
|
115
|
+
const toolCalls = new Map<string, { id: string; name: string; args: string; callId: string }>();
|
|
116
|
+
let usage: TokenUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
117
|
+
|
|
118
|
+
for await (const event of mapCodexEvents(parseSSE(response))) {
|
|
119
|
+
const type = event.type as string;
|
|
120
|
+
|
|
121
|
+
// Text delta
|
|
122
|
+
if (type === "response.output_text.delta") {
|
|
123
|
+
const delta = (event as any).delta as string;
|
|
124
|
+
if (delta) {
|
|
125
|
+
if (contentBlocks.length === 0 || contentBlocks[contentBlocks.length - 1].type !== "text") {
|
|
126
|
+
contentBlocks.push({ type: "text", text: "" });
|
|
127
|
+
}
|
|
128
|
+
const block = contentBlocks[contentBlocks.length - 1];
|
|
129
|
+
if (block.type === "text") block.text += delta;
|
|
130
|
+
yield { type: "text", text: delta };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Reasoning/thinking summary delta
|
|
135
|
+
if (type === "response.reasoning_summary_text.delta") {
|
|
136
|
+
const delta = (event as any).delta as string;
|
|
137
|
+
if (delta) {
|
|
138
|
+
if (contentBlocks.length === 0 || contentBlocks[contentBlocks.length - 1].type !== "thinking") {
|
|
139
|
+
contentBlocks.push({ type: "thinking", thinking: "" });
|
|
140
|
+
}
|
|
141
|
+
const block = contentBlocks[contentBlocks.length - 1];
|
|
142
|
+
if (block.type === "thinking") block.thinking += delta;
|
|
143
|
+
yield { type: "thinking", thinking: delta };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// New output item (function call start)
|
|
148
|
+
if (type === "response.output_item.added") {
|
|
149
|
+
const item = (event as any).item;
|
|
150
|
+
if (item?.type === "function_call") {
|
|
151
|
+
const id = item.id || item.call_id || `call_${toolCalls.size}`;
|
|
152
|
+
const callId = item.call_id || id;
|
|
153
|
+
const name = item.name || "";
|
|
154
|
+
toolCalls.set(id, { id, name, args: "", callId });
|
|
155
|
+
contentBlocks.push({ type: "tool_call", id: callId, name, input: {} });
|
|
156
|
+
yield { type: "tool_call_start", id: callId, name };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Function call arguments delta
|
|
161
|
+
if (type === "response.function_call_arguments.delta") {
|
|
162
|
+
const itemId = (event as any).item_id;
|
|
163
|
+
const delta = (event as any).delta as string;
|
|
164
|
+
if (itemId && delta) {
|
|
165
|
+
const entry = toolCalls.get(itemId);
|
|
166
|
+
if (entry) {
|
|
167
|
+
entry.args += delta;
|
|
168
|
+
yield { type: "tool_call_delta", id: entry.callId, input: delta };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Response completed — extract usage
|
|
174
|
+
if (type === "response.completed") {
|
|
175
|
+
const resp = (event as any).response;
|
|
176
|
+
if (resp?.usage) {
|
|
177
|
+
usage = {
|
|
178
|
+
inputTokens: resp.usage.input_tokens ?? 0,
|
|
179
|
+
outputTokens: resp.usage.output_tokens ?? 0,
|
|
180
|
+
totalTokens: (resp.usage.input_tokens ?? 0) + (resp.usage.output_tokens ?? 0),
|
|
181
|
+
cacheReadTokens: resp.usage.input_tokens_details?.cached_tokens,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Finalize tool call inputs
|
|
188
|
+
for (const [, entry] of toolCalls) {
|
|
189
|
+
const block = contentBlocks.find((b) => b.type === "tool_call" && b.id === entry.callId);
|
|
190
|
+
if (block && block.type === "tool_call") {
|
|
191
|
+
try {
|
|
192
|
+
block.input = JSON.parse(entry.args || "{}");
|
|
193
|
+
} catch {
|
|
194
|
+
block.input = {};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const message: AssistantMessage = { role: "assistant", content: contentBlocks };
|
|
200
|
+
yield { type: "done", message, usage };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// --- JWT ---
|
|
205
|
+
|
|
206
|
+
function extractAccountId(token: string): string {
|
|
207
|
+
try {
|
|
208
|
+
const parts = token.split(".");
|
|
209
|
+
if (parts.length !== 3) throw new Error("Invalid JWT");
|
|
210
|
+
// JWT uses base64url encoding; convert to standard base64 for atob
|
|
211
|
+
const b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
212
|
+
const payload = JSON.parse(atob(b64));
|
|
213
|
+
const accountId = payload?.[JWT_CLAIM_PATH]?.chatgpt_account_id;
|
|
214
|
+
if (!accountId) throw new Error("No chatgpt_account_id in token");
|
|
215
|
+
return accountId;
|
|
216
|
+
} catch {
|
|
217
|
+
throw new Error("Failed to extract accountId from Codex JWT token");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// --- Request building ---
|
|
222
|
+
|
|
223
|
+
function resolveCodexUrl(baseUrl: string): string {
|
|
224
|
+
const normalized = baseUrl.replace(/\/+$/, "");
|
|
225
|
+
if (normalized.endsWith("/codex/responses")) return normalized;
|
|
226
|
+
if (normalized.endsWith("/codex")) return `${normalized}/responses`;
|
|
227
|
+
return `${normalized}/codex/responses`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function buildHeaders(accountId: string, token: string): Record<string, string> {
|
|
231
|
+
const userAgent = `klaus-agent (${platform()} ${release()}; ${arch()})`;
|
|
232
|
+
return {
|
|
233
|
+
"Authorization": `Bearer ${token}`,
|
|
234
|
+
"Content-Type": "application/json",
|
|
235
|
+
"Accept": "text/event-stream",
|
|
236
|
+
"chatgpt-account-id": accountId,
|
|
237
|
+
"originator": "klaus",
|
|
238
|
+
"OpenAI-Beta": "responses=experimental",
|
|
239
|
+
"User-Agent": userAgent,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function buildRequestBody(
|
|
244
|
+
model: string,
|
|
245
|
+
systemPrompt: string,
|
|
246
|
+
messages: Message[],
|
|
247
|
+
tools?: ToolDefinition[],
|
|
248
|
+
thinkingLevel?: ThinkingLevel,
|
|
249
|
+
): CodexRequestBody {
|
|
250
|
+
const body: CodexRequestBody = {
|
|
251
|
+
model,
|
|
252
|
+
store: false,
|
|
253
|
+
stream: true,
|
|
254
|
+
instructions: systemPrompt,
|
|
255
|
+
input: mapMessages(messages),
|
|
256
|
+
text: { verbosity: "medium" },
|
|
257
|
+
include: ["reasoning.encrypted_content"],
|
|
258
|
+
tool_choice: "auto",
|
|
259
|
+
parallel_tool_calls: true,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
if (tools?.length) {
|
|
263
|
+
body.tools = mapTools(tools);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const effort = mapReasoningEffort(model, thinkingLevel);
|
|
267
|
+
if (effort) {
|
|
268
|
+
body.reasoning = { effort, summary: "auto" };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return body;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
function mapReasoningEffort(modelId: string, level?: ThinkingLevel): string | undefined {
|
|
276
|
+
if (!level || level === "off") return undefined;
|
|
277
|
+
|
|
278
|
+
let effort: string = level;
|
|
279
|
+
// Clamp per model, following pi-mono's logic
|
|
280
|
+
const id = modelId.includes("/") ? modelId.split("/").pop()! : modelId;
|
|
281
|
+
if ((id.startsWith("gpt-5.2") || id.startsWith("gpt-5.3") || id.startsWith("gpt-5.4")) && effort === "minimal") {
|
|
282
|
+
effort = "low";
|
|
283
|
+
}
|
|
284
|
+
if (id === "gpt-5.1" && effort === "xhigh") effort = "high";
|
|
285
|
+
if (id === "gpt-5.1-codex-mini") {
|
|
286
|
+
effort = effort === "high" || effort === "xhigh" ? "high" : "medium";
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return effort;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// --- SSE parsing ---
|
|
293
|
+
|
|
294
|
+
async function* parseSSE(response: Response): AsyncGenerator<Record<string, unknown>> {
|
|
295
|
+
if (!response.body) return;
|
|
296
|
+
|
|
297
|
+
const reader = response.body.getReader();
|
|
298
|
+
const decoder = new TextDecoder();
|
|
299
|
+
let buffer = "";
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
while (true) {
|
|
303
|
+
const { done, value } = await reader.read();
|
|
304
|
+
if (done) break;
|
|
305
|
+
buffer += decoder.decode(value, { stream: true });
|
|
306
|
+
|
|
307
|
+
let idx = buffer.indexOf("\n\n");
|
|
308
|
+
while (idx !== -1) {
|
|
309
|
+
const chunk = buffer.slice(0, idx);
|
|
310
|
+
buffer = buffer.slice(idx + 2);
|
|
311
|
+
|
|
312
|
+
const dataLines = chunk
|
|
313
|
+
.split("\n")
|
|
314
|
+
.filter((l) => l.startsWith("data:"))
|
|
315
|
+
.map((l) => l.slice(5).trim());
|
|
316
|
+
|
|
317
|
+
if (dataLines.length > 0) {
|
|
318
|
+
const data = dataLines.join("\n").trim();
|
|
319
|
+
if (data && data !== "[DONE]") {
|
|
320
|
+
try {
|
|
321
|
+
yield JSON.parse(data);
|
|
322
|
+
} catch { /* skip malformed JSON */ }
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
idx = buffer.indexOf("\n\n");
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
} finally {
|
|
329
|
+
try { await reader.cancel(); } catch { /* ignore */ }
|
|
330
|
+
try { reader.releaseLock(); } catch { /* ignore */ }
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// --- Event mapping ---
|
|
335
|
+
|
|
336
|
+
async function* mapCodexEvents(
|
|
337
|
+
events: AsyncIterable<Record<string, unknown>>,
|
|
338
|
+
): AsyncGenerator<Record<string, unknown>> {
|
|
339
|
+
for await (const event of events) {
|
|
340
|
+
const type = typeof event.type === "string" ? event.type : undefined;
|
|
341
|
+
if (!type) continue;
|
|
342
|
+
|
|
343
|
+
if (type === "error") {
|
|
344
|
+
const code = (event as any).code || "";
|
|
345
|
+
const message = (event as any).message || "";
|
|
346
|
+
throw new Error(`Codex error: ${message || code || JSON.stringify(event)}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (type === "response.failed") {
|
|
350
|
+
const msg = (event as any).response?.error?.message;
|
|
351
|
+
throw new Error(msg || "Codex response failed");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Normalize completion events
|
|
355
|
+
if (type === "response.done" || type === "response.completed" || type === "response.incomplete") {
|
|
356
|
+
yield { ...event, type: "response.completed" };
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
yield event;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// --- Error handling ---
|
|
365
|
+
|
|
366
|
+
function isRetryableStatus(status: number, errorText: string): boolean {
|
|
367
|
+
if (status === 429 || status === 500 || status === 502 || status === 503 || status === 504) return true;
|
|
368
|
+
return /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(errorText);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function parseErrorMessage(status: number, raw: string): string {
|
|
372
|
+
try {
|
|
373
|
+
const parsed = JSON.parse(raw) as {
|
|
374
|
+
error?: { code?: string; type?: string; message?: string; plan_type?: string; resets_at?: number };
|
|
375
|
+
};
|
|
376
|
+
const err = parsed?.error;
|
|
377
|
+
if (err) {
|
|
378
|
+
if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(err.code || err.type || "")) {
|
|
379
|
+
const plan = err.plan_type ? ` (${err.plan_type.toLowerCase()} plan)` : "";
|
|
380
|
+
const mins = err.resets_at
|
|
381
|
+
? Math.max(0, Math.round((err.resets_at * 1000 - Date.now()) / 60000))
|
|
382
|
+
: undefined;
|
|
383
|
+
const when = mins !== undefined ? ` Try again in ~${mins} min.` : "";
|
|
384
|
+
return `ChatGPT usage limit reached${plan}.${when}`.trim();
|
|
385
|
+
}
|
|
386
|
+
return err.message || raw;
|
|
387
|
+
}
|
|
388
|
+
} catch { /* not JSON */ }
|
|
389
|
+
return `Codex request failed (${status}): ${raw.slice(0, 200)}`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
393
|
+
return new Promise((resolve, reject) => {
|
|
394
|
+
if (signal?.aborted) { reject(new Error("Request was aborted")); return; }
|
|
395
|
+
const onAbort = () => { clearTimeout(timeout); reject(new Error("Request was aborted")); };
|
|
396
|
+
const timeout = setTimeout(() => { signal?.removeEventListener("abort", onAbort); resolve(); }, ms);
|
|
397
|
+
signal?.addEventListener("abort", onAbort);
|
|
398
|
+
});
|
|
399
|
+
}
|