@apolloyh/apollo-agent 0.1.10 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example CHANGED
@@ -16,3 +16,10 @@ ANTHROPIC_DEFAULT_SONNET_MODEL_NAME=glm-5.2
16
16
  # ANTHROPIC_TIMEOUT_MS=300000
17
17
  # ANTHROPIC_CACHE_TTL=5m
18
18
  # AGENT_SENDMAIL_PATH=/usr/sbin/sendmail
19
+
20
+ # OpenAI-compatible alternative
21
+ # APOLLO_LLM_PROTOCOL=openai
22
+ # OPENAI_API_KEY=
23
+ # OPENAI_BASE_URL=https://api.openai.com/v1
24
+ # OPENAI_MODEL=gpt-4o-mini
25
+ # OPENAI_MAX_TOKENS_PARAM=max_tokens
package/README.md CHANGED
@@ -55,6 +55,7 @@ Commands: `/help` · `/skill` · `/simplify` · `/verify` · `/debug` · `/agent
55
55
 
56
56
  - [完整使用指南](docs/guide.md)
57
57
  - [SDK 接入指南](docs/sdk.md)
58
+ - [npm 打包与发布手册](docs/npm-release.md)
58
59
  - [高级功能与恢复机制](docs/features-cc.md)
59
60
 
60
61
  ## SDK
package/dist/brand.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /** Product identity — single source of truth for display strings. */
2
2
  export declare const PRODUCT: {
3
3
  readonly name: "Apollo";
4
- readonly version: "0.1.10";
4
+ readonly version: "0.2.0";
5
5
  readonly tagline: "Your personal agent — answer first, act when needed.";
6
6
  readonly packageName: "@apolloyh/apollo-agent";
7
7
  readonly userAgent: "ApolloAgent/0.1";
package/dist/brand.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /** Product identity — single source of truth for display strings. */
2
2
  export const PRODUCT = {
3
3
  name: "Apollo",
4
- version: "0.1.10",
4
+ version: "0.2.0",
5
5
  tagline: "Your personal agent — answer first, act when needed.",
6
6
  packageName: "@apolloyh/apollo-agent",
7
7
  userAgent: "ApolloAgent/0.1",
package/dist/config.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { AgentConfig, LlmConfig, SubagentConfig } from "./types.js";
2
2
  /** Default Apollo Task roles. */
3
3
  export declare const DEFAULT_AGENTS: SubagentConfig[];
4
- export declare const MISSING_AUTH_TOKEN_MESSAGE = "Missing ANTHROPIC_AUTH_TOKEN. Add it to ~/.apollo/.env, .apollo/.env, .env, or your shell, then restart Apollo.";
4
+ export declare const MISSING_AUTH_TOKEN_MESSAGE = "Missing model API token. Set ANTHROPIC_AUTH_TOKEN or OPENAI_API_KEY, then restart Apollo.";
5
5
  export declare function ensureUserEnvFile(): {
6
6
  path: string;
7
7
  created: boolean;
@@ -14,7 +14,9 @@ export declare function ensureUserConfigExampleFile(): {
14
14
  path: string;
15
15
  created: boolean;
16
16
  };
17
- export declare function loadAgentConfig(configPath?: string): Promise<AgentConfig>;
17
+ export declare function loadAgentConfig(configPath?: string, options?: {
18
+ isolated?: boolean;
19
+ }): Promise<AgentConfig>;
18
20
  /**
19
21
  * Load an explicit env file, or discover project env then ~/.apollo/.env.
20
22
  * By default **overrides** existing shell vars so local agent config wins.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzE,iCAAiC;AACjC,eAAO,MAAM,cAAc,EAAE,cAAc,EAkC1C,CAAC;AAsBF,eAAO,MAAM,0BAA0B,oHAC4E,CAAC;AAwBpH,wBAAgB,iBAAiB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAatE;AAED,wBAAgB,oBAAoB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAazE;AAED,wBAAgB,2BAA2B,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAchF;AAED,wBAAsB,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAkD/E;AAiBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAwB3F;AAYD,wBAAgB,aAAa,CAAC,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,SAAS,CAkCrF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzE,iCAAiC;AACjC,eAAO,MAAM,cAAc,EAAE,cAAc,EAkC1C,CAAC;AAsBF,eAAO,MAAM,0BAA0B,8FACsD,CAAC;AA8B9F,wBAAgB,iBAAiB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAatE;AAED,wBAAgB,oBAAoB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAazE;AAED,wBAAgB,2BAA2B,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAchF;AAED,wBAAsB,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAkDrH;AAiBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAwB3F;AAYD,wBAAgB,aAAa,CAAC,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,SAAS,CAsCrF"}
package/dist/config.js CHANGED
@@ -55,7 +55,7 @@ const defaultConfig = {
55
55
  };
56
56
  const DEFAULT_CONFIG_PATH = ".apollo/config.json";
57
57
  const LEGACY_CONFIG_PATH = "agent.config.json";
58
- export const MISSING_AUTH_TOKEN_MESSAGE = "Missing ANTHROPIC_AUTH_TOKEN. Add it to ~/.apollo/.env, .apollo/.env, .env, or your shell, then restart Apollo.";
58
+ export const MISSING_AUTH_TOKEN_MESSAGE = "Missing model API token. Set ANTHROPIC_AUTH_TOKEN or OPENAI_API_KEY, then restart Apollo.";
59
59
  const USER_ENV_TEMPLATE = `# Apollo uses Anthropic-compatible environment variable names.
60
60
  # Add your provider token below, then restart Apollo.
61
61
  ANTHROPIC_AUTH_TOKEN=
@@ -63,6 +63,12 @@ ANTHROPIC_AUTH_TOKEN=
63
63
  # Optional provider settings:
64
64
  # ANTHROPIC_BASE_URL=https://api.anthropic.com
65
65
  # ANTHROPIC_MODEL=claude-haiku-4-5
66
+
67
+ # OpenAI-compatible alternative:
68
+ # APOLLO_LLM_PROTOCOL=openai
69
+ # OPENAI_API_KEY=
70
+ # OPENAI_BASE_URL=https://api.openai.com/v1
71
+ # OPENAI_MODEL=gpt-4o-mini
66
72
  `;
67
73
  const USER_CONFIG_TEMPLATE = `${JSON.stringify({
68
74
  systemPrompt: "",
@@ -122,12 +128,12 @@ export function ensureUserConfigExampleFile() {
122
128
  throw error;
123
129
  }
124
130
  }
125
- export async function loadAgentConfig(configPath) {
131
+ export async function loadAgentConfig(configPath, options = {}) {
126
132
  const userConfigPath = path.join(os.homedir(), ".apollo", "config.json");
127
133
  const projectConfigPath = [DEFAULT_CONFIG_PATH, LEGACY_CONFIG_PATH].find(fsSync.existsSync);
128
134
  const layers = [
129
- ...(fsSync.existsSync(userConfigPath) ? [{ file: userConfigPath, projectScoped: false }] : []),
130
- ...(projectConfigPath ? [{ file: projectConfigPath, projectScoped: true }] : []),
135
+ ...(!options.isolated && fsSync.existsSync(userConfigPath) ? [{ file: userConfigPath, projectScoped: false }] : []),
136
+ ...(!options.isolated && projectConfigPath ? [{ file: projectConfigPath, projectScoped: true }] : []),
131
137
  ...(configPath ? [{ file: configPath, projectScoped: true }] : []),
132
138
  ];
133
139
  const seen = new Set();
@@ -223,13 +229,15 @@ function unwrapEnvValue(value) {
223
229
  return value;
224
230
  }
225
231
  export function loadLlmConfig(options = {}) {
226
- const authToken = process.env.ANTHROPIC_AUTH_TOKEN;
227
- const baseUrl = process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com";
228
- const model = process.env.ANTHROPIC_MODEL ||
229
- process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL ||
230
- process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME ||
231
- process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME ||
232
- "claude-haiku-4-5";
232
+ const requestedProtocol = process.env.APOLLO_LLM_PROTOCOL ?? (process.env.OPENAI_API_KEY && !process.env.ANTHROPIC_AUTH_TOKEN ? "openai" : "anthropic");
233
+ if (requestedProtocol !== "anthropic" && requestedProtocol !== "openai")
234
+ throw new Error("APOLLO_LLM_PROTOCOL must be anthropic or openai.");
235
+ const protocol = requestedProtocol;
236
+ const authToken = protocol === "openai" ? process.env.OPENAI_API_KEY : process.env.ANTHROPIC_AUTH_TOKEN;
237
+ const baseUrl = protocol === "openai" ? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1" : process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com";
238
+ const model = protocol === "openai"
239
+ ? process.env.OPENAI_MODEL ?? "gpt-4o-mini"
240
+ : process.env.ANTHROPIC_MODEL || process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL || process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME || process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME || "claude-haiku-4-5";
233
241
  if (!authToken && !options.allowMissingAuth)
234
242
  throw new Error(MISSING_AUTH_TOKEN_MESSAGE);
235
243
  const cacheTtl = process.env.ANTHROPIC_CACHE_TTL ?? "5m";
@@ -237,25 +245,30 @@ export function loadLlmConfig(options = {}) {
237
245
  throw new Error("ANTHROPIC_CACHE_TTL must be 5m or 1h.");
238
246
  }
239
247
  const providerDefaultMaxTokens = /(?:^glm-|bigmodel\.cn)/i.test(`${model} ${baseUrl}`) ? 128 * 1024 : 4096;
240
- const maxTokens = positiveNumberEnv("ANTHROPIC_MAX_TOKENS", providerDefaultMaxTokens, true);
241
- const timeoutMs = positiveNumberEnv("ANTHROPIC_TIMEOUT_MS", 300000, true);
248
+ const maxTokens = positiveNumberEnv(protocol === "openai" ? "OPENAI_MAX_TOKENS" : "ANTHROPIC_MAX_TOKENS", providerDefaultMaxTokens, true);
249
+ const timeoutMs = positiveNumberEnv(protocol === "openai" ? "OPENAI_TIMEOUT_MS" : "ANTHROPIC_TIMEOUT_MS", 300000, true);
250
+ const openaiMaxTokensParam = process.env.OPENAI_MAX_TOKENS_PARAM ?? (/^(?:o[134]-|gpt-5)/i.test(model) ? "max_completion_tokens" : "max_tokens");
251
+ if (openaiMaxTokensParam !== "max_tokens" && openaiMaxTokensParam !== "max_completion_tokens")
252
+ throw new Error("OPENAI_MAX_TOKENS_PARAM must be max_tokens or max_completion_tokens.");
242
253
  let parsedBaseUrl;
243
254
  try {
244
255
  parsedBaseUrl = new URL(baseUrl);
245
256
  }
246
257
  catch {
247
- throw new Error("ANTHROPIC_BASE_URL must be a valid HTTP or HTTPS URL.");
258
+ throw new Error("Model base URL must be a valid HTTP or HTTPS URL.");
248
259
  }
249
260
  if (parsedBaseUrl.protocol !== "http:" && parsedBaseUrl.protocol !== "https:") {
250
- throw new Error("ANTHROPIC_BASE_URL must be a valid HTTP or HTTPS URL.");
261
+ throw new Error("Model base URL must be a valid HTTP or HTTPS URL.");
251
262
  }
252
263
  return {
264
+ protocol,
253
265
  authToken: authToken ?? "",
254
266
  baseUrl,
255
267
  model,
256
268
  maxTokens,
257
269
  timeoutMs,
258
270
  cacheTtl,
271
+ openaiMaxTokensParam,
259
272
  };
260
273
  }
261
274
  function positiveNumberEnv(name, fallback, integer) {
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ let liveStatus;
21
21
  let runningTaskCount = 0;
22
22
  let activeVerb = "Thinking";
23
23
  let assistantResponseStarted = false;
24
+ let liveThinkingPreview = "";
24
25
  /** Prevent double green/red outcome lines in one user turn */
25
26
  let outcomePrintedThisTurn = false;
26
27
  let pastedTextSequence = 0;
@@ -97,31 +98,19 @@ async function main() {
97
98
  // never reaches the interactive `runtime` binding). Also lets SIGINT close whichever is active.
98
99
  let activeRuntime;
99
100
  const traceSink = (event) => {
100
- // Collect process into collapsible Thought (always, even when quiet)
101
+ let completedThought = null;
102
+ // Collect only model thinking tokens into the collapsible Thought.
101
103
  if (event.type === "llm_request") {
102
104
  thoughtFold.beginTurn();
103
- thoughtFold.note(`turn ${event.turn} · ${event.toolCount} tools available`);
104
105
  }
105
- else if (event.type === "llm_retry") {
106
- thoughtFold.note(`retry ${event.attempt} · ${event.mode} · ${event.reason}`);
107
- }
108
- else if (event.type === "tool_call") {
109
- thoughtFold.noteTool(event.tool);
110
- }
111
- else if (event.type === "tool_result") {
112
- thoughtFold.note(`${event.tool} ${event.isError ? "failed" : "ok"}`);
113
- }
114
- else if (event.type === "task_start") {
115
- thoughtFold.note(`Task(${event.subagentType}): ${event.description}`);
116
- }
117
- else if (event.type === "task_finish") {
118
- thoughtFold.note(`Task(${event.subagentType}) finished`);
106
+ else if (event.type === "thinking_delta") {
107
+ thoughtFold.appendThinking(event.text);
119
108
  }
120
109
  else if (event.type === "assistant_delta") {
121
- thoughtFold.finalizeAndPrint();
110
+ completedThought = thoughtFold.finalizeAndPrint();
122
111
  }
123
112
  else if (event.type === "llm_response" && event.stopReason !== "tool_use") {
124
- thoughtFold.finalizeAndPrint();
113
+ completedThought = thoughtFold.finalizeAndPrint();
125
114
  }
126
115
  if (event.type === "workflow_status") {
127
116
  const bar = workflowStatusBar({
@@ -166,6 +155,8 @@ async function main() {
166
155
  if (renderBeforeStatus)
167
156
  rendererSink(event);
168
157
  updateLiveStatus(event, interactiveMode);
158
+ if (interactiveMode && completedThought)
159
+ thoughtFold.printPreview(completedThought);
169
160
  taskActivity.record(event);
170
161
  if (!renderBeforeStatus)
171
162
  rendererSink(event);
@@ -513,10 +504,6 @@ async function main() {
513
504
  stdout.write(notes.map((n) => `- ${n.id} ${n.title}\n ${n.content.slice(0, 120).replace(/\n/g, " ")}`).join("\n") + "\n");
514
505
  continue;
515
506
  }
516
- if (text === "/thought" || text === "/think" || text === "/t") {
517
- thoughtFold.toggle();
518
- continue;
519
- }
520
507
  if (text.startsWith("/verbose")) {
521
508
  const value = text.split(/\s+/)[1];
522
509
  traceOptions.verbose = value === "on" ? true : value === "off" ? false : !traceOptions.verbose;
@@ -756,8 +743,19 @@ function updateLiveStatus(event, enabled) {
756
743
  switch (event.type) {
757
744
  case "llm_request":
758
745
  assistantResponseStarted = false;
746
+ liveThinkingPreview = "";
759
747
  startLiveStatus(runningTaskCount > 0 ? formatRunningTasks() : activeVerb);
760
748
  break;
749
+ case "thinking_delta": {
750
+ liveThinkingPreview = stripTerminalEscapes(`${liveThinkingPreview}${event.text}`)
751
+ .replace(/\s+/g, " ")
752
+ .trimStart()
753
+ .slice(0, 1000);
754
+ if (liveStatus)
755
+ liveStatus.text = "Thinking";
756
+ paintLiveStatus();
757
+ break;
758
+ }
761
759
  case "llm_retry":
762
760
  startLiveStatus(event.mode === "after-compaction"
763
761
  ? "Retrying after compaction"
@@ -1038,6 +1036,9 @@ async function readCliLine(options) {
1038
1036
  const liveStatusLine = renderLiveStatusLine(options.color);
1039
1037
  if (liveStatusLine)
1040
1038
  lines.push(liveStatusLine);
1039
+ if (liveStatus?.text === "Thinking" && liveThinkingPreview) {
1040
+ lines.push(...renderLiveThinkingLines(liveThinkingPreview, options.color));
1041
+ }
1041
1042
  lines.push(renderInputBorder("top", options.color));
1042
1043
  const view = renderInputView(buffer, cursor, inputContentWidth(), pastedTextBlocks);
1043
1044
  const idleQueue = options.queueMode && !buffer;
@@ -1614,6 +1615,37 @@ function renderLiveStatusLine(useColor) {
1614
1615
  : `${liveStatus.text}...`;
1615
1616
  return formatWaitLine({ frame: liveStatus.frame, label, elapsed, useColor });
1616
1617
  }
1618
+ function renderLiveThinkingLines(value, useColor) {
1619
+ const prefixWidth = 4;
1620
+ const width = Math.max(24, Math.min((stdout.columns ?? 100) - prefixWidth, 96));
1621
+ const lines = [];
1622
+ let current = "";
1623
+ let currentWidth = 0;
1624
+ let consumed = 0;
1625
+ const characters = Array.from(value);
1626
+ for (const char of characters) {
1627
+ const charWidth = displayLength(char);
1628
+ if (current && currentWidth + charWidth > width) {
1629
+ lines.push(current);
1630
+ if (lines.length === 3)
1631
+ break;
1632
+ current = "";
1633
+ currentWidth = 0;
1634
+ }
1635
+ current += char;
1636
+ currentWidth += charWidth;
1637
+ consumed += 1;
1638
+ }
1639
+ if (lines.length < 3 && current)
1640
+ lines.push(current);
1641
+ if (consumed < characters.length && lines.length) {
1642
+ const last = truncateDisplay(lines[lines.length - 1], Math.max(1, width - 1)).replace(/…$/, "");
1643
+ lines[lines.length - 1] = `${last}…`;
1644
+ }
1645
+ const dim = useColor ? "\x1b[90m" : "";
1646
+ const reset = useColor ? "\x1b[0m" : "";
1647
+ return lines.map((line, index) => `${dim}${index === 0 ? " └ " : " "}${line}${reset}`);
1648
+ }
1617
1649
  function renderInputBorder(position, useColor) {
1618
1650
  const terminalWidth = Math.max(48, Math.min(stdout.columns ?? 100, 100));
1619
1651
  const left = position === "top" ? "╭" : "╰";
@@ -1641,7 +1673,7 @@ function renderWelcomeBanner(options) {
1641
1673
  `${dim}trace${reset} ${options.quiet ? "quiet" : "verbose"}`,
1642
1674
  `${dim}approval${reset} ${options.yolo ? `${green}yolo${reset}` : "ask"}`,
1643
1675
  "",
1644
- `${dim}Type / to search · /thought expands Thought · /help${reset}`,
1676
+ `${dim}Type / to search · /help${reset}`,
1645
1677
  "",
1646
1678
  "",
1647
1679
  ].join("\n");
@@ -18,29 +18,40 @@ export type ToolInputDelta = {
18
18
  tool?: string;
19
19
  detail?: string;
20
20
  };
21
+ export type ThinkingDelta = {
22
+ text: string;
23
+ };
24
+ export type CreateMessageInput = {
25
+ system: string;
26
+ messages: Message[];
27
+ tools: AnthropicTool[];
28
+ stream?: boolean;
29
+ signal?: AbortSignal;
30
+ onTextDelta?: (text: string) => void;
31
+ onThinkingDelta?: (event: ThinkingDelta) => void;
32
+ onToolInputDelta?: (event: ToolInputDelta) => void;
33
+ };
34
+ export interface LlmClient {
35
+ createMessage(input: CreateMessageInput): Promise<AnthropicResponse>;
36
+ }
21
37
  export declare class MalformedStreamError extends Error {
22
38
  constructor(message: string, cause?: unknown);
23
39
  }
24
40
  export declare class MalformedToolInputError extends MalformedStreamError {
25
41
  constructor(tool: string, bytes: number, cause: unknown);
26
42
  }
27
- export declare class AnthropicApiError extends Error {
43
+ export declare class ProviderApiError extends Error {
28
44
  readonly status: number;
29
45
  readonly responseBody: string;
30
46
  readonly responseHeaders: Headers;
31
47
  constructor(status: number, responseBody: string, responseHeaders: Headers);
32
48
  }
49
+ export declare class AnthropicApiError extends ProviderApiError {
50
+ name: string;
51
+ }
33
52
  export declare class AnthropicClient {
34
53
  private readonly config;
35
54
  constructor(config: LlmConfig);
36
- createMessage(input: {
37
- system: string;
38
- messages: Message[];
39
- tools: AnthropicTool[];
40
- stream?: boolean;
41
- signal?: AbortSignal;
42
- onTextDelta?: (text: string) => void;
43
- onToolInputDelta?: (event: ToolInputDelta) => void;
44
- }): Promise<AnthropicResponse>;
55
+ createMessage(input: CreateMessageInput): Promise<AnthropicResponse>;
45
56
  }
46
57
  //# sourceMappingURL=anthropic.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/llm/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEnF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B,EAAE,MAAM,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAAC;AAMF,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAI7C;AAED,qBAAa,uBAAwB,SAAQ,oBAAoB;gBACnD,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAIxD;AAED,qBAAa,iBAAkB,SAAQ,KAAK;aAExB,MAAM,EAAE,MAAM;aACd,YAAY,EAAE,MAAM;aACpB,eAAe,EAAE,OAAO;gBAFxB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,OAAO;CAK3C;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAExC,aAAa,CAAC,KAAK,EAAE;QACzB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,KAAK,EAAE,aAAa,EAAE,CAAC;QACvB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QACrC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;KACpD,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAqC/B"}
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/llm/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEnF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B,EAAE,MAAM,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAAC;AAOF,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/E,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAC7C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACjD,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CACpD,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACtE;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAI7C;AAED,qBAAa,uBAAwB,SAAQ,oBAAoB;gBACnD,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAIxD;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,MAAM,EAAE,MAAM;aACd,YAAY,EAAE,MAAM;aACpB,eAAe,EAAE,OAAO;gBAFxB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,OAAO;CAK3C;AAED,qBAAa,iBAAkB,SAAQ,gBAAgB;IAC5C,IAAI,SAAuB;CACrC;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAExC,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAsC3E"}
@@ -10,18 +10,21 @@ export class MalformedToolInputError extends MalformedStreamError {
10
10
  this.name = "MalformedToolInputError";
11
11
  }
12
12
  }
13
- export class AnthropicApiError extends Error {
13
+ export class ProviderApiError extends Error {
14
14
  status;
15
15
  responseBody;
16
16
  responseHeaders;
17
17
  constructor(status, responseBody, responseHeaders) {
18
- super(`Anthropic API error ${status}: ${responseBody}`);
18
+ super(`Provider API error ${status}: ${responseBody}`);
19
19
  this.status = status;
20
20
  this.responseBody = responseBody;
21
21
  this.responseHeaders = responseHeaders;
22
- this.name = "AnthropicApiError";
22
+ this.name = "ProviderApiError";
23
23
  }
24
24
  }
25
+ export class AnthropicApiError extends ProviderApiError {
26
+ name = "AnthropicApiError";
27
+ }
25
28
  export class AnthropicClient {
26
29
  config;
27
30
  constructor(config) {
@@ -48,6 +51,7 @@ export class AnthropicClient {
48
51
  cache_control: this.config.cacheTtl === "1h"
49
52
  ? { type: "ephemeral", ttl: "1h" }
50
53
  : { type: "ephemeral" },
54
+ ...(supportsThinking(this.config) ? { thinking: { type: "enabled" } } : {}),
51
55
  stream: input.stream ?? false,
52
56
  }),
53
57
  signal: input.signal
@@ -59,12 +63,12 @@ export class AnthropicClient {
59
63
  throw new AnthropicApiError(response.status, errorText, response.headers);
60
64
  }
61
65
  if (input.stream) {
62
- return readStreamingResponse(response, input.onTextDelta, input.onToolInputDelta);
66
+ return readStreamingResponse(response, input.onTextDelta, input.onToolInputDelta, input.onThinkingDelta);
63
67
  }
64
68
  return (await response.json());
65
69
  }
66
70
  }
67
- async function readStreamingResponse(response, onTextDelta, onToolInputDelta) {
71
+ async function readStreamingResponse(response, onTextDelta, onToolInputDelta, onThinkingDelta) {
68
72
  if (!response.body)
69
73
  throw new Error("Anthropic API returned an empty stream body.");
70
74
  const reader = response.body.getReader();
@@ -100,7 +104,7 @@ async function readStreamingResponse(response, onTextDelta, onToolInputDelta) {
100
104
  stopReason = reason;
101
105
  }, (part) => {
102
106
  usage = mergeUsage(usage, part);
103
- }, onTextDelta, onToolInputDelta);
107
+ }, onTextDelta, onToolInputDelta, onThinkingDelta);
104
108
  }
105
109
  separator = buffer.match(/\r?\n\r?\n/);
106
110
  }
@@ -119,7 +123,7 @@ async function readStreamingResponse(response, onTextDelta, onToolInputDelta) {
119
123
  stopReason = reason;
120
124
  }, (part) => {
121
125
  usage = mergeUsage(usage, part);
122
- }, onTextDelta, onToolInputDelta);
126
+ }, onTextDelta, onToolInputDelta, onThinkingDelta);
123
127
  }
124
128
  }
125
129
  if (blocks.size === 0) {
@@ -161,7 +165,7 @@ function parseStreamEvent(data) {
161
165
  throw new MalformedStreamError("Model returned malformed JSON in the event stream.", error);
162
166
  }
163
167
  }
164
- function applyStreamEvent(event, blocks, setResponseId, setStopReason, setUsage, onTextDelta, onToolInputDelta) {
168
+ function applyStreamEvent(event, blocks, setResponseId, setStopReason, setUsage, onTextDelta, onToolInputDelta, onThinkingDelta) {
165
169
  if (event.type === "message_start") {
166
170
  const message = event.message;
167
171
  if (message?.id)
@@ -186,6 +190,13 @@ function applyStreamEvent(event, blocks, setResponseId, setStopReason, setUsage,
186
190
  if (contentBlock.type === "text") {
187
191
  blocks.set(index, { type: "text", text: contentBlock.text ?? "" });
188
192
  }
193
+ else if (contentBlock.type === "thinking") {
194
+ blocks.set(index, {
195
+ type: "thinking",
196
+ thinking: contentBlock.thinking ?? "",
197
+ signature: contentBlock.signature,
198
+ });
199
+ }
189
200
  else if (contentBlock.type === "tool_use") {
190
201
  const block = {
191
202
  type: "tool_use",
@@ -203,12 +214,26 @@ function applyStreamEvent(event, blocks, setResponseId, setStopReason, setUsage,
203
214
  const index = Number(event.index);
204
215
  const block = blocks.get(index);
205
216
  const delta = event.delta;
206
- if (!block || !delta)
217
+ if (!delta)
218
+ return;
219
+ const publicThinking = delta.type === "summary_delta"
220
+ ? delta.summary
221
+ : delta.type === "reasoning_delta" || delta.type === "reasoning_summary_delta"
222
+ ? (delta.reasoning_summary ?? delta.reasoning)
223
+ : delta.type === "thinking_delta"
224
+ ? delta.thinking
225
+ : undefined;
226
+ if (publicThinking)
227
+ onThinkingDelta?.({ text: publicThinking });
228
+ if (!block)
207
229
  return;
208
230
  if (block.type === "text" && delta.type === "text_delta" && delta.text) {
209
231
  block.text += delta.text;
210
232
  onTextDelta?.(delta.text);
211
233
  }
234
+ else if (block.type === "thinking" && delta.type === "thinking_delta" && delta.thinking) {
235
+ block.thinking += delta.thinking;
236
+ }
212
237
  else if (block.type === "tool_use" && delta.type === "input_json_delta" && delta.partial_json) {
213
238
  block.inputJson += delta.partial_json;
214
239
  block.detail ??= partialToolDetail(block.inputJson);
@@ -258,7 +283,7 @@ function mergeUsage(current, part) {
258
283
  }, { ...next });
259
284
  }
260
285
  function finalizeStreamBlock(block) {
261
- if (block.type === "text")
286
+ if (block.type === "text" || block.type === "thinking")
262
287
  return block;
263
288
  let input = {};
264
289
  if (block.inputJson.trim()) {
@@ -276,3 +301,6 @@ function finalizeStreamBlock(block) {
276
301
  input,
277
302
  };
278
303
  }
304
+ function supportsThinking(config) {
305
+ return /(?:^glm-|bigmodel\.cn)/i.test(`${config.model} ${config.baseUrl}`);
306
+ }
@@ -0,0 +1,8 @@
1
+ import type { LlmConfig } from "../types.js";
2
+ import { type AnthropicResponse, type CreateMessageInput, type LlmClient } from "./anthropic.js";
3
+ export declare class OpenAIClient implements LlmClient {
4
+ private readonly config;
5
+ constructor(config: LlmConfig);
6
+ createMessage(input: CreateMessageInput): Promise<AnthropicResponse>;
7
+ }
8
+ //# sourceMappingURL=openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/llm/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2C,SAAS,EAAW,MAAM,aAAa,CAAC;AAC/F,OAAO,EAIL,KAAK,iBAAiB,EAEtB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAKxB,qBAAa,YAAa,YAAW,SAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAExC,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAmB3E"}