@ddlqhd/agent-sdk 0.1.0 → 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.
Files changed (49) hide show
  1. package/README.md +4 -2
  2. package/dist/{chunk-5QMA2YBY.cjs → chunk-6X7EYQLS.cjs} +782 -114
  3. package/dist/chunk-6X7EYQLS.cjs.map +1 -0
  4. package/dist/{chunk-NDSL7NPN.js → chunk-D3UZNLZO.js} +769 -71
  5. package/dist/chunk-D3UZNLZO.js.map +1 -0
  6. package/dist/{chunk-Q3SOMX26.js → chunk-EQ5CXH44.js} +772 -111
  7. package/dist/chunk-EQ5CXH44.js.map +1 -0
  8. package/dist/chunk-LOYIGOBZ.js +54 -0
  9. package/dist/chunk-LOYIGOBZ.js.map +1 -0
  10. package/dist/{chunk-OHXW2YM6.js → chunk-MEJHTQJM.js} +289 -166
  11. package/dist/chunk-MEJHTQJM.js.map +1 -0
  12. package/dist/chunk-NYZD3THB.cjs +1521 -0
  13. package/dist/chunk-NYZD3THB.cjs.map +1 -0
  14. package/dist/chunk-OZO7D77N.cjs +59 -0
  15. package/dist/chunk-OZO7D77N.cjs.map +1 -0
  16. package/dist/{chunk-JF5AJQMU.cjs → chunk-Z45DHTDX.cjs} +291 -170
  17. package/dist/chunk-Z45DHTDX.cjs.map +1 -0
  18. package/dist/cli/index.cjs +47 -39
  19. package/dist/cli/index.cjs.map +1 -1
  20. package/dist/cli/index.js +22 -14
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/{index-DPsZ1zat.d.ts → index-Cw3SfEAB.d.ts} +20 -34
  23. package/dist/{index-RTPmFjMp.d.cts → index-D2Qntkn_.d.cts} +20 -34
  24. package/dist/index.cjs +125 -89
  25. package/dist/index.d.cts +62 -22
  26. package/dist/index.d.ts +62 -22
  27. package/dist/index.js +4 -4
  28. package/dist/models/index.cjs +19 -15
  29. package/dist/models/index.d.cts +55 -6
  30. package/dist/models/index.d.ts +55 -6
  31. package/dist/models/index.js +2 -2
  32. package/dist/tools/index.cjs +53 -61
  33. package/dist/tools/index.d.cts +3 -3
  34. package/dist/tools/index.d.ts +3 -3
  35. package/dist/tools/index.js +2 -2
  36. package/dist/{types-C0aX_Qdp.d.cts → types-CWPAYWzr.d.cts} +307 -61
  37. package/dist/{types-C0aX_Qdp.d.ts → types-CWPAYWzr.d.ts} +307 -61
  38. package/package.json +25 -14
  39. package/dist/chunk-5QMA2YBY.cjs.map +0 -1
  40. package/dist/chunk-CNSGZVRN.cjs +0 -152
  41. package/dist/chunk-CNSGZVRN.cjs.map +0 -1
  42. package/dist/chunk-JF5AJQMU.cjs.map +0 -1
  43. package/dist/chunk-NDSL7NPN.js.map +0 -1
  44. package/dist/chunk-OHXW2YM6.js.map +0 -1
  45. package/dist/chunk-Q3SOMX26.js.map +0 -1
  46. package/dist/chunk-WH3APNQ5.js +0 -147
  47. package/dist/chunk-WH3APNQ5.js.map +0 -1
  48. package/dist/chunk-X35MHWXE.cjs +0 -817
  49. package/dist/chunk-X35MHWXE.cjs.map +0 -1
@@ -1,14 +1,358 @@
1
1
  #!/usr/bin/env node
2
- import { BaseModelAdapter, toolsToModelSchema } from './chunk-WH3APNQ5.js';
2
+ import { BaseModelAdapter, toolsToModelSchema } from './chunk-LOYIGOBZ.js';
3
+ import { randomUUID } from 'crypto';
3
4
 
4
- // src/models/openai.ts
5
- var OPENAI_CAPABILITIES = {
6
- "gpt-4o": { contextLength: 128e3, maxOutputTokens: 16384 },
7
- "gpt-4o-mini": { contextLength: 128e3, maxOutputTokens: 16384 },
8
- "gpt-4-turbo": { contextLength: 128e3, maxOutputTokens: 4096 },
9
- "gpt-4": { contextLength: 8192, maxOutputTokens: 4096 },
10
- "gpt-3.5-turbo": { contextLength: 16385, maxOutputTokens: 4096 }
5
+ // src/models/default-capabilities.ts
6
+ var DEFAULT_ADAPTER_CAPABILITIES = {
7
+ contextLength: 2e5,
8
+ maxOutputTokens: 32e3
9
+ };
10
+
11
+ // src/core/logger.ts
12
+ var TRUTHY = /^(1|true|yes)$/i;
13
+ var DEFAULT_MAX_BODY_CHARS = 4e3;
14
+ var DEFAULT_REDACT_KEYS = [
15
+ "authorization",
16
+ "proxy-authorization",
17
+ "x-api-key",
18
+ "api-key",
19
+ "apikey",
20
+ "api_key",
21
+ "cookie",
22
+ "set-cookie",
23
+ "token",
24
+ "access_token",
25
+ "refresh_token",
26
+ "password",
27
+ "secret"
28
+ ];
29
+ var LEVEL_PRIORITY = {
30
+ debug: 10,
31
+ info: 20,
32
+ warn: 30,
33
+ error: 40,
34
+ silent: 99
11
35
  };
36
+ function parseEnvLogLevel(raw) {
37
+ switch ((raw ?? "").trim().toLowerCase()) {
38
+ case "debug":
39
+ case "info":
40
+ case "warn":
41
+ case "error":
42
+ case "silent":
43
+ return (raw ?? "").trim().toLowerCase();
44
+ default:
45
+ return void 0;
46
+ }
47
+ }
48
+ function parseBooleanEnv(name) {
49
+ const raw = process.env[name];
50
+ if (raw == null || raw === "") {
51
+ return void 0;
52
+ }
53
+ if (TRUTHY.test(raw.trim())) {
54
+ return true;
55
+ }
56
+ if (/^(0|false|no)$/i.test(raw.trim())) {
57
+ return false;
58
+ }
59
+ return void 0;
60
+ }
61
+ function parseNumericEnv(name) {
62
+ const raw = process.env[name];
63
+ if (raw == null || raw === "") {
64
+ return void 0;
65
+ }
66
+ const value = Number(raw);
67
+ return Number.isFinite(value) ? value : void 0;
68
+ }
69
+ function resolveSDKLogLevel(level, hasLogger = false) {
70
+ if (level != null) {
71
+ return level;
72
+ }
73
+ const fromEnv = parseEnvLogLevel(process.env.AGENT_SDK_LOG_LEVEL);
74
+ if (fromEnv != null) {
75
+ return fromEnv;
76
+ }
77
+ if (hasLogger) {
78
+ return "info";
79
+ }
80
+ return "silent";
81
+ }
82
+ function resolveLogRedaction(config) {
83
+ const envIncludeBodies = parseBooleanEnv("AGENT_SDK_LOG_BODIES");
84
+ const envIncludeToolArgs = parseBooleanEnv("AGENT_SDK_LOG_INCLUDE_TOOL_ARGS");
85
+ const envMaxBodyChars = parseNumericEnv("AGENT_SDK_LOG_MAX_BODY_CHARS");
86
+ return {
87
+ includeBodies: config?.includeBodies ?? envIncludeBodies ?? false,
88
+ includeToolArguments: config?.includeToolArguments ?? envIncludeToolArgs ?? false,
89
+ maxBodyChars: Math.max(
90
+ 0,
91
+ Math.floor(config?.maxBodyChars ?? envMaxBodyChars ?? DEFAULT_MAX_BODY_CHARS)
92
+ ),
93
+ redactKeys: [
94
+ ...DEFAULT_REDACT_KEYS,
95
+ ...config?.redactKeys ?? []
96
+ ]
97
+ };
98
+ }
99
+ function shouldEmitLog(configuredLevel, hasLogger, eventLevel) {
100
+ const effectiveLevel = resolveSDKLogLevel(configuredLevel, hasLogger);
101
+ return LEVEL_PRIORITY[eventLevel] >= LEVEL_PRIORITY[effectiveLevel];
102
+ }
103
+ function truncateString(value, maxChars) {
104
+ if (maxChars <= 0 || value.length <= maxChars) {
105
+ return value;
106
+ }
107
+ return `${value.slice(0, maxChars)}... [truncated ${value.length - maxChars} chars]`;
108
+ }
109
+ function isSensitiveKey(key, redaction) {
110
+ if (key == null || redaction == null) {
111
+ return false;
112
+ }
113
+ const normalized = key.toLowerCase();
114
+ return redaction.redactKeys.some((candidate) => candidate.toLowerCase() === normalized);
115
+ }
116
+ function sanitizeObjectEntries(entries, redaction) {
117
+ const output = {};
118
+ for (const [key, value] of entries) {
119
+ if (isSensitiveKey(key, redaction)) {
120
+ output[key] = "[REDACTED]";
121
+ continue;
122
+ }
123
+ if (key === "messages" && !redaction.includeBodies && Array.isArray(value)) {
124
+ output[key] = `[REDACTED_MESSAGES:${value.length}]`;
125
+ continue;
126
+ }
127
+ if ((key === "arguments" || key === "input") && !redaction.includeToolArguments) {
128
+ output[key] = "[REDACTED_TOOL_ARGUMENTS]";
129
+ continue;
130
+ }
131
+ output[key] = sanitizeForLogging(value, redaction, key);
132
+ }
133
+ return output;
134
+ }
135
+ function sanitizeForLogging(value, redaction, key) {
136
+ if (isSensitiveKey(key, redaction)) {
137
+ return "[REDACTED]";
138
+ }
139
+ if (typeof value === "string") {
140
+ if (!redaction.includeBodies && (key === "content" || key === "text" || key === "thinking")) {
141
+ return "[REDACTED_BODY]";
142
+ }
143
+ return truncateString(value, redaction.maxBodyChars);
144
+ }
145
+ if (typeof value === "number" || typeof value === "boolean" || value == null) {
146
+ return value;
147
+ }
148
+ if (Array.isArray(value)) {
149
+ if (!redaction.includeBodies && key === "messages") {
150
+ return `[REDACTED_MESSAGES:${value.length}]`;
151
+ }
152
+ return value.map((item) => sanitizeForLogging(item, redaction));
153
+ }
154
+ if (typeof value === "object") {
155
+ return sanitizeObjectEntries(Object.entries(value), redaction);
156
+ }
157
+ return String(value);
158
+ }
159
+ function formatSDKLog(event) {
160
+ const prefix = `[agent-sdk][${event.component}][${event.event}]`;
161
+ const details = [];
162
+ if (event.provider) details.push(`provider=${event.provider}`);
163
+ if (event.model) details.push(`model=${event.model}`);
164
+ if (event.sessionId) details.push(`sessionId=${event.sessionId}`);
165
+ if (event.iteration !== void 0) details.push(`iteration=${event.iteration}`);
166
+ if (event.statusCode !== void 0) details.push(`statusCode=${event.statusCode}`);
167
+ if (event.durationMs !== void 0) details.push(`durationMs=${event.durationMs}`);
168
+ if (event.toolName) details.push(`tool=${event.toolName}`);
169
+ if (event.requestId) details.push(`requestId=${event.requestId}`);
170
+ if (event.clientRequestId) details.push(`clientRequestId=${event.clientRequestId}`);
171
+ const suffix = details.length > 0 ? ` ${details.join(" ")}` : "";
172
+ return event.message ? `${prefix} ${event.message}${suffix}` : `${prefix}${suffix}`;
173
+ }
174
+ function consoleMethod(level) {
175
+ if (level === "error") return console.error.bind(console);
176
+ if (level === "warn") return console.warn.bind(console);
177
+ if (level === "info") return console.info.bind(console);
178
+ return console.debug.bind(console);
179
+ }
180
+ function createConsoleSDKLogger() {
181
+ const write = (level, event) => {
182
+ const line = formatSDKLog(event);
183
+ const logFn = consoleMethod(level);
184
+ if (event.metadata != null) {
185
+ logFn(line, event.metadata);
186
+ } else {
187
+ logFn(line);
188
+ }
189
+ };
190
+ return {
191
+ debug(event) {
192
+ write("debug", event);
193
+ },
194
+ info(event) {
195
+ write("info", event);
196
+ },
197
+ warn(event) {
198
+ write("warn", event);
199
+ },
200
+ error(event) {
201
+ write("error", event);
202
+ }
203
+ };
204
+ }
205
+ function emitSDKLog(args) {
206
+ if (!shouldEmitLog(args.logLevel, args.logger != null, args.level)) {
207
+ return;
208
+ }
209
+ const logger = args.logger ?? createConsoleSDKLogger();
210
+ const payload = {
211
+ source: "agent-sdk",
212
+ ...args.event
213
+ };
214
+ logger[args.level]?.(payload);
215
+ }
216
+ function extractProviderRequestId(headers) {
217
+ if (headers == null) {
218
+ return void 0;
219
+ }
220
+ return headers.get("x-request-id") ?? headers.get("request-id") ?? headers.get("x-amzn-requestid") ?? void 0;
221
+ }
222
+
223
+ // src/models/model-request-log.ts
224
+ function countMessages(body) {
225
+ if (body == null || typeof body !== "object" || !("messages" in body)) {
226
+ return void 0;
227
+ }
228
+ const messages = body.messages;
229
+ return Array.isArray(messages) ? messages.length : void 0;
230
+ }
231
+ function countTools(body) {
232
+ if (body == null || typeof body !== "object" || !("tools" in body)) {
233
+ return void 0;
234
+ }
235
+ const tools = body.tools;
236
+ return Array.isArray(tools) ? tools.length : void 0;
237
+ }
238
+ function buildRequestMetadata(body, params) {
239
+ const redaction = resolveLogRedaction(params?.redaction);
240
+ const metadata = {};
241
+ const messageCount = countMessages(body);
242
+ const toolCount = countTools(body);
243
+ if (messageCount !== void 0) metadata.messageCount = messageCount;
244
+ if (toolCount !== void 0) metadata.toolCount = toolCount;
245
+ if (redaction.includeBodies) {
246
+ metadata.requestBody = sanitizeForLogging(body, redaction);
247
+ }
248
+ return metadata;
249
+ }
250
+ function logModelRequestStart(context, body, extraMetadata) {
251
+ const state = {
252
+ clientRequestId: randomUUID(),
253
+ startedAt: Date.now()
254
+ };
255
+ emitSDKLog({
256
+ logger: context.params?.logger,
257
+ logLevel: context.params?.logLevel,
258
+ level: "info",
259
+ event: {
260
+ component: "model",
261
+ event: "model.request.start",
262
+ message: `Starting ${context.operation} request`,
263
+ provider: context.provider,
264
+ model: context.model,
265
+ operation: context.operation,
266
+ sessionId: context.params?.sessionId,
267
+ iteration: context.iteration,
268
+ clientRequestId: state.clientRequestId,
269
+ metadata: {
270
+ path: context.path,
271
+ ...buildRequestMetadata(body, context.params),
272
+ ...extraMetadata
273
+ }
274
+ }
275
+ });
276
+ return state;
277
+ }
278
+ function logModelRequestEnd(context, state, response, extraMetadata) {
279
+ emitSDKLog({
280
+ logger: context.params?.logger,
281
+ logLevel: context.params?.logLevel,
282
+ level: response.ok ? "info" : "warn",
283
+ event: {
284
+ component: "model",
285
+ event: response.ok ? "model.request.end" : "model.request.error",
286
+ message: response.ok ? "Model request completed" : "Model request returned error response",
287
+ provider: context.provider,
288
+ model: context.model,
289
+ operation: context.operation,
290
+ sessionId: context.params?.sessionId,
291
+ iteration: context.iteration,
292
+ clientRequestId: state.clientRequestId,
293
+ requestId: extractProviderRequestId(response.headers),
294
+ statusCode: response.status,
295
+ durationMs: Date.now() - state.startedAt,
296
+ metadata: {
297
+ path: context.path,
298
+ ...extraMetadata
299
+ }
300
+ }
301
+ });
302
+ }
303
+ function logModelRequestFailure(context, state, error, extraMetadata) {
304
+ const err = error instanceof Error ? error : new Error(String(error));
305
+ emitSDKLog({
306
+ logger: context.params?.logger,
307
+ logLevel: context.params?.logLevel,
308
+ level: err.name === "AbortError" ? "info" : "error",
309
+ event: {
310
+ component: "model",
311
+ event: err.name === "AbortError" ? "model.request.aborted" : "model.request.error",
312
+ message: err.name === "AbortError" ? "Model request aborted" : "Model request failed",
313
+ provider: context.provider,
314
+ model: context.model,
315
+ operation: context.operation,
316
+ sessionId: context.params?.sessionId,
317
+ iteration: context.iteration,
318
+ clientRequestId: state.clientRequestId,
319
+ durationMs: Date.now() - state.startedAt,
320
+ errorName: err.name,
321
+ errorMessage: err.message,
322
+ metadata: {
323
+ path: context.path,
324
+ ...extraMetadata
325
+ }
326
+ }
327
+ });
328
+ }
329
+ function logModelStreamParseError(context, rawChunk, error) {
330
+ const err = error instanceof Error ? error : new Error(String(error));
331
+ const redaction = resolveLogRedaction(context.params?.redaction);
332
+ emitSDKLog({
333
+ logger: context.params?.logger,
334
+ logLevel: context.params?.logLevel,
335
+ level: "warn",
336
+ event: {
337
+ component: "streaming",
338
+ event: "model.stream.parse_error",
339
+ message: "Failed to parse provider stream chunk",
340
+ provider: context.provider,
341
+ model: context.model,
342
+ operation: context.operation,
343
+ sessionId: context.params?.sessionId,
344
+ iteration: context.iteration,
345
+ errorName: err.name,
346
+ errorMessage: err.message,
347
+ metadata: {
348
+ path: context.path,
349
+ rawChunk: sanitizeForLogging(rawChunk, redaction)
350
+ }
351
+ }
352
+ });
353
+ }
354
+
355
+ // src/models/openai.ts
12
356
  var OpenAIAdapter = class extends BaseModelAdapter {
13
357
  name;
14
358
  apiKey;
@@ -25,11 +369,11 @@ var OpenAIAdapter = class extends BaseModelAdapter {
25
369
  throw new Error("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass apiKey in config.");
26
370
  }
27
371
  this.name = `openai/${this.model}`;
28
- this.capabilities = config.capabilities ?? OPENAI_CAPABILITIES[this.model] ?? { contextLength: 128e3, maxOutputTokens: 4096 };
372
+ this.capabilities = config.capabilities ?? DEFAULT_ADAPTER_CAPABILITIES;
29
373
  }
30
374
  async *stream(params) {
31
375
  const body = this.buildRequestBody(params, true);
32
- const response = await this.fetch("/chat/completions", body, params.signal);
376
+ const response = await this.fetch("/chat/completions", body, "stream", params);
33
377
  if (!response.ok) {
34
378
  const error = await response.text();
35
379
  throw new Error(`OpenAI API error: ${response.status} - ${error}`);
@@ -128,7 +472,18 @@ var OpenAIAdapter = class extends BaseModelAdapter {
128
472
  ...raw
129
473
  };
130
474
  }
131
- } catch {
475
+ } catch (error) {
476
+ logModelStreamParseError(
477
+ {
478
+ provider: "openai",
479
+ model: this.model,
480
+ path: "/chat/completions",
481
+ operation: "stream",
482
+ params
483
+ },
484
+ trimmed,
485
+ error
486
+ );
132
487
  }
133
488
  }
134
489
  }
@@ -150,7 +505,7 @@ var OpenAIAdapter = class extends BaseModelAdapter {
150
505
  }
151
506
  async complete(params) {
152
507
  const body = this.buildRequestBody(params, false);
153
- const response = await this.fetch("/chat/completions", body);
508
+ const response = await this.fetch("/chat/completions", body, "complete", params);
154
509
  if (!response.ok) {
155
510
  const error = await response.text();
156
511
  throw new Error(`OpenAI API error: ${response.status} - ${error}`);
@@ -181,13 +536,14 @@ var OpenAIAdapter = class extends BaseModelAdapter {
181
536
  }
182
537
  buildRequestBody(params, stream) {
183
538
  const messages = this.transformMessages(params.messages);
539
+ const defaultMaxTokens = this.capabilities?.maxOutputTokens ?? DEFAULT_ADAPTER_CAPABILITIES.maxOutputTokens;
184
540
  const body = {
185
541
  model: this.model,
186
542
  messages,
187
543
  stream,
188
544
  ...stream && { stream_options: { include_usage: true } },
189
545
  ...params.temperature !== void 0 && { temperature: params.temperature },
190
- ...params.maxTokens !== void 0 && { max_tokens: params.maxTokens },
546
+ max_tokens: params.maxTokens ?? defaultMaxTokens,
191
547
  ...params.stopSequences && { stop: params.stopSequences }
192
548
  };
193
549
  if (params.tools && params.tools.length > 0) {
@@ -198,20 +554,55 @@ var OpenAIAdapter = class extends BaseModelAdapter {
198
554
  }
199
555
  return body;
200
556
  }
201
- async fetch(path, body, signal) {
557
+ async fetch(path, body, operation, params) {
558
+ const requestLog = logModelRequestStart({
559
+ provider: "openai",
560
+ model: this.model,
561
+ path,
562
+ operation,
563
+ params
564
+ }, body);
202
565
  const headers = {
203
566
  "Content-Type": "application/json",
204
- "Authorization": `Bearer ${this.apiKey}`
567
+ "Authorization": `Bearer ${this.apiKey}`,
568
+ "X-Client-Request-Id": requestLog.clientRequestId
205
569
  };
206
570
  if (this.organization) {
207
571
  headers["OpenAI-Organization"] = this.organization;
208
572
  }
209
- return globalThis.fetch(`${this.baseUrl}${path}`, {
210
- method: "POST",
211
- headers,
212
- body: JSON.stringify(body),
213
- signal
214
- });
573
+ try {
574
+ const response = await globalThis.fetch(`${this.baseUrl}${path}`, {
575
+ method: "POST",
576
+ headers,
577
+ body: JSON.stringify(body),
578
+ signal: params.signal
579
+ });
580
+ logModelRequestEnd(
581
+ {
582
+ provider: "openai",
583
+ model: this.model,
584
+ path,
585
+ operation,
586
+ params
587
+ },
588
+ requestLog,
589
+ response
590
+ );
591
+ return response;
592
+ } catch (error) {
593
+ logModelRequestFailure(
594
+ {
595
+ provider: "openai",
596
+ model: this.model,
597
+ path,
598
+ operation,
599
+ params
600
+ },
601
+ requestLog,
602
+ error
603
+ );
604
+ throw error;
605
+ }
215
606
  }
216
607
  safeParseJSON(str) {
217
608
  try {
@@ -226,33 +617,114 @@ function createOpenAI(config) {
226
617
  }
227
618
 
228
619
  // src/models/anthropic.ts
229
- var ANTHROPIC_CAPABILITIES = {
230
- "claude-sonnet-4-20250514": { contextLength: 2e5, maxOutputTokens: 16384 },
231
- "claude-haiku": { contextLength: 2e5, maxOutputTokens: 8192 },
232
- "claude-3-5-sonnet-20241022": { contextLength: 2e5, maxOutputTokens: 8192 },
233
- "claude-3-haiku-20240307": { contextLength: 2e5, maxOutputTokens: 4096 }
620
+ var DEFAULT_FETCH_RETRY = {
621
+ maxAttempts: 2,
622
+ baseDelayMs: 200,
623
+ maxDelayMs: 2e3
234
624
  };
625
+ function normalizeFetchRetry(options) {
626
+ if (options == null) {
627
+ return { ...DEFAULT_FETCH_RETRY };
628
+ }
629
+ const maxAttempts = Math.max(1, Math.floor(options.maxAttempts ?? DEFAULT_FETCH_RETRY.maxAttempts));
630
+ const baseDelayMs = Math.max(0, options.baseDelayMs ?? DEFAULT_FETCH_RETRY.baseDelayMs);
631
+ const maxDelayMs = Math.max(baseDelayMs, options.maxDelayMs ?? DEFAULT_FETCH_RETRY.maxDelayMs);
632
+ return { maxAttempts, baseDelayMs, maxDelayMs };
633
+ }
634
+ function isAbortError(e) {
635
+ if (e instanceof DOMException && e.name === "AbortError") {
636
+ return true;
637
+ }
638
+ return typeof e === "object" && e !== null && e.name === "AbortError";
639
+ }
640
+ function isRetriableFetchError(e) {
641
+ if (isAbortError(e)) {
642
+ return false;
643
+ }
644
+ if (e instanceof TypeError) {
645
+ return true;
646
+ }
647
+ const cause = typeof e === "object" && e !== null && "cause" in e ? e.cause : void 0;
648
+ const code = cause?.code;
649
+ return code === "ECONNRESET" || code === "ETIMEDOUT" || code === "EPIPE" || code === "UND_ERR_SOCKET";
650
+ }
651
+ function isRetriableHttpStatus(status) {
652
+ return status === 429 || status === 502 || status === 503 || status === 504;
653
+ }
654
+ function parseRetryAfterMs(header) {
655
+ if (header == null || header === "") {
656
+ return void 0;
657
+ }
658
+ const trimmed = header.trim();
659
+ const asNum = Number(trimmed);
660
+ if (Number.isFinite(asNum) && asNum >= 0) {
661
+ return asNum * 1e3;
662
+ }
663
+ const parsed = Date.parse(trimmed);
664
+ if (!Number.isNaN(parsed)) {
665
+ const delta = parsed - Date.now();
666
+ return delta > 0 ? delta : 0;
667
+ }
668
+ return void 0;
669
+ }
670
+ function computeBackoffMs(attemptIndex, baseDelayMs, maxDelayMs) {
671
+ const exp = Math.min(maxDelayMs, baseDelayMs * 2 ** attemptIndex);
672
+ const jitter = 0.5 + Math.random() * 0.5;
673
+ return Math.min(maxDelayMs, Math.floor(exp * jitter));
674
+ }
675
+ async function delay(ms, signal) {
676
+ if (ms <= 0) {
677
+ return;
678
+ }
679
+ await new Promise((resolve, reject) => {
680
+ if (signal?.aborted) {
681
+ reject(new DOMException("The operation was aborted.", "AbortError"));
682
+ return;
683
+ }
684
+ const id = setTimeout(() => {
685
+ if (signal) {
686
+ signal.removeEventListener("abort", onAbort);
687
+ }
688
+ resolve();
689
+ }, ms);
690
+ const onAbort = () => {
691
+ clearTimeout(id);
692
+ reject(new DOMException("The operation was aborted.", "AbortError"));
693
+ };
694
+ signal?.addEventListener("abort", onAbort, { once: true });
695
+ });
696
+ }
697
+ async function drainResponseBody(response) {
698
+ try {
699
+ await response.arrayBuffer();
700
+ } catch {
701
+ }
702
+ }
235
703
  var AnthropicAdapter = class extends BaseModelAdapter {
236
704
  name;
237
705
  apiKey;
238
706
  baseUrl;
239
707
  model;
240
708
  version;
709
+ requestMetadata;
710
+ fetchRetry;
241
711
  constructor(config = {}) {
242
712
  super();
243
713
  this.apiKey = config.apiKey || process.env.ANTHROPIC_API_KEY || "";
244
714
  this.baseUrl = config.baseUrl || process.env.ANTHROPIC_BASE_URL || "https://api.anthropic.com";
245
715
  this.model = config.model || "claude-sonnet-4-20250514";
246
716
  this.version = config.version || "2023-06-01";
717
+ this.requestMetadata = config.metadata;
718
+ this.fetchRetry = normalizeFetchRetry(config.fetchRetry);
247
719
  if (!this.apiKey) {
248
720
  throw new Error("Anthropic API key is required. Set ANTHROPIC_API_KEY environment variable or pass apiKey in config.");
249
721
  }
250
722
  this.name = `anthropic/${this.model}`;
251
- this.capabilities = config.capabilities ?? ANTHROPIC_CAPABILITIES[this.model] ?? { contextLength: 2e5, maxOutputTokens: 4096 };
723
+ this.capabilities = config.capabilities ?? DEFAULT_ADAPTER_CAPABILITIES;
252
724
  }
253
725
  async *stream(params) {
254
726
  const body = this.buildRequestBody(params, true);
255
- const response = await this.fetch("/v1/messages", body, params.signal);
727
+ const response = await this.fetch("/v1/messages", body, "stream", params);
256
728
  if (!response.ok) {
257
729
  const error = await response.text();
258
730
  throw new Error(`Anthropic API error: ${response.status} - ${error}`);
@@ -350,6 +822,7 @@ var AnthropicAdapter = class extends BaseModelAdapter {
350
822
  currentToolCall = null;
351
823
  }
352
824
  if (currentThinkingBlock) {
825
+ yield { type: "thinking_block_end", ...raw };
353
826
  currentThinkingBlock = null;
354
827
  }
355
828
  break;
@@ -391,7 +864,18 @@ var AnthropicAdapter = class extends BaseModelAdapter {
391
864
  }
392
865
  break;
393
866
  }
394
- } catch {
867
+ } catch (error) {
868
+ logModelStreamParseError(
869
+ {
870
+ provider: "anthropic",
871
+ model: this.model,
872
+ path: "/v1/messages",
873
+ operation: "stream",
874
+ params
875
+ },
876
+ jsonStr,
877
+ error
878
+ );
395
879
  }
396
880
  }
397
881
  }
@@ -402,7 +886,7 @@ var AnthropicAdapter = class extends BaseModelAdapter {
402
886
  }
403
887
  async complete(params) {
404
888
  const body = this.buildRequestBody(params, false);
405
- const response = await this.fetch("/v1/messages", body);
889
+ const response = await this.fetch("/v1/messages", body, "complete", params);
406
890
  if (!response.ok) {
407
891
  const error = await response.text();
408
892
  throw new Error(`Anthropic API error: ${response.status} - ${error}`);
@@ -440,9 +924,10 @@ var AnthropicAdapter = class extends BaseModelAdapter {
440
924
  buildRequestBody(params, stream) {
441
925
  const { system, messages } = this.extractSystemMessage(params.messages);
442
926
  const transformedMessages = this.transformAnthropicMessages(messages);
927
+ const defaultMaxTokens = this.capabilities?.maxOutputTokens ?? DEFAULT_ADAPTER_CAPABILITIES.maxOutputTokens;
443
928
  const body = {
444
929
  model: this.model,
445
- max_tokens: params.maxTokens || 4096,
930
+ max_tokens: params.maxTokens ?? defaultMaxTokens,
446
931
  messages: transformedMessages,
447
932
  stream,
448
933
  ...system && { system },
@@ -455,8 +940,48 @@ var AnthropicAdapter = class extends BaseModelAdapter {
455
940
  input_schema: tool.parameters
456
941
  }));
457
942
  }
943
+ const mergedMetadata = this.mergeAnthropicMetadata(params);
944
+ if (mergedMetadata && Object.keys(mergedMetadata).length > 0) {
945
+ body.metadata = mergedMetadata;
946
+ }
458
947
  return body;
459
948
  }
949
+ /**
950
+ * Build Messages API `metadata`: `sessionId` → `user_id`, merged with resolved adapter `metadata` (dict or fn).
951
+ * Config `metadata` keys override `user_id` when duplicated.
952
+ */
953
+ mergeAnthropicMetadata(params) {
954
+ const extra = this.resolveMetadataExtra(params);
955
+ const hasSession = params.sessionId !== void 0 && params.sessionId !== "";
956
+ if (!hasSession && extra === void 0) {
957
+ return void 0;
958
+ }
959
+ const merged = {};
960
+ if (hasSession) {
961
+ merged.user_id = params.sessionId;
962
+ }
963
+ if (extra !== void 0) {
964
+ Object.assign(merged, extra);
965
+ }
966
+ return Object.keys(merged).length > 0 ? merged : void 0;
967
+ }
968
+ resolveMetadataExtra(params) {
969
+ const raw = this.requestMetadata;
970
+ if (raw == null) {
971
+ return void 0;
972
+ }
973
+ if (typeof raw === "function") {
974
+ const v = raw(params);
975
+ if (typeof v !== "object" || v === null || Array.isArray(v) || Object.keys(v).length === 0) {
976
+ return void 0;
977
+ }
978
+ return { ...v };
979
+ }
980
+ if (typeof raw === "object" && !Array.isArray(raw) && Object.keys(raw).length > 0) {
981
+ return { ...raw };
982
+ }
983
+ return void 0;
984
+ }
460
985
  extractSystemMessage(messages) {
461
986
  const systemMessages = messages.filter((m) => m.role === "system");
462
987
  const otherMessages = messages.filter((m) => m.role !== "system");
@@ -511,8 +1036,23 @@ var AnthropicAdapter = class extends BaseModelAdapter {
511
1036
  return transformed;
512
1037
  });
513
1038
  }
514
- async fetch(path, body, signal) {
515
- return globalThis.fetch(`${this.baseUrl}${path}`, {
1039
+ /**
1040
+ * 发起 POST;按 `fetchRetry` 对网络错误与 429/502/503/504 重试(不含响应体已开始消费后的 SSE 读失败)。
1041
+ */
1042
+ async fetch(path, body, operation, params) {
1043
+ const requestLog = logModelRequestStart(
1044
+ {
1045
+ provider: "anthropic",
1046
+ model: this.model,
1047
+ path,
1048
+ operation,
1049
+ params
1050
+ },
1051
+ body,
1052
+ { httpMaxAttempts: this.fetchRetry.maxAttempts }
1053
+ );
1054
+ const url = `${this.baseUrl}${path}`;
1055
+ const init = {
516
1056
  method: "POST",
517
1057
  headers: {
518
1058
  "Content-Type": "application/json",
@@ -520,8 +1060,104 @@ var AnthropicAdapter = class extends BaseModelAdapter {
520
1060
  "anthropic-version": this.version
521
1061
  },
522
1062
  body: JSON.stringify(body),
523
- signal
524
- });
1063
+ signal: params.signal
1064
+ };
1065
+ for (let attempt = 0; attempt < this.fetchRetry.maxAttempts; attempt++) {
1066
+ const httpAttemptMeta = {
1067
+ httpAttempt: attempt + 1,
1068
+ httpMaxAttempts: this.fetchRetry.maxAttempts
1069
+ };
1070
+ if (params.signal?.aborted) {
1071
+ logModelRequestFailure(
1072
+ {
1073
+ provider: "anthropic",
1074
+ model: this.model,
1075
+ path,
1076
+ operation,
1077
+ params
1078
+ },
1079
+ requestLog,
1080
+ new DOMException("The operation was aborted.", "AbortError"),
1081
+ { httpMaxAttempts: this.fetchRetry.maxAttempts }
1082
+ );
1083
+ throw new DOMException("The operation was aborted.", "AbortError");
1084
+ }
1085
+ try {
1086
+ const response = await globalThis.fetch(url, init);
1087
+ if (response.ok) {
1088
+ logModelRequestEnd(
1089
+ {
1090
+ provider: "anthropic",
1091
+ model: this.model,
1092
+ path,
1093
+ operation,
1094
+ params
1095
+ },
1096
+ requestLog,
1097
+ response,
1098
+ httpAttemptMeta
1099
+ );
1100
+ return response;
1101
+ }
1102
+ const canRetryHttp = attempt < this.fetchRetry.maxAttempts - 1 && isRetriableHttpStatus(response.status);
1103
+ if (canRetryHttp) {
1104
+ await drainResponseBody(response);
1105
+ const fromHeader = parseRetryAfterMs(response.headers.get("Retry-After"));
1106
+ const backoff = computeBackoffMs(attempt, this.fetchRetry.baseDelayMs, this.fetchRetry.maxDelayMs);
1107
+ const waitMs = fromHeader != null ? Math.min(fromHeader, this.fetchRetry.maxDelayMs) : backoff;
1108
+ await delay(waitMs, params.signal);
1109
+ continue;
1110
+ }
1111
+ logModelRequestEnd(
1112
+ {
1113
+ provider: "anthropic",
1114
+ model: this.model,
1115
+ path,
1116
+ operation,
1117
+ params
1118
+ },
1119
+ requestLog,
1120
+ response,
1121
+ httpAttemptMeta
1122
+ );
1123
+ return response;
1124
+ } catch (e) {
1125
+ if (isAbortError(e) || params.signal?.aborted) {
1126
+ logModelRequestFailure(
1127
+ {
1128
+ provider: "anthropic",
1129
+ model: this.model,
1130
+ path,
1131
+ operation,
1132
+ params
1133
+ },
1134
+ requestLog,
1135
+ e,
1136
+ httpAttemptMeta
1137
+ );
1138
+ throw e;
1139
+ }
1140
+ if (attempt < this.fetchRetry.maxAttempts - 1 && isRetriableFetchError(e)) {
1141
+ const backoff = computeBackoffMs(attempt, this.fetchRetry.baseDelayMs, this.fetchRetry.maxDelayMs);
1142
+ await delay(backoff, params.signal);
1143
+ continue;
1144
+ }
1145
+ logModelRequestFailure(
1146
+ {
1147
+ provider: "anthropic",
1148
+ model: this.model,
1149
+ path,
1150
+ operation,
1151
+ params
1152
+ },
1153
+ requestLog,
1154
+ e,
1155
+ httpAttemptMeta
1156
+ );
1157
+ throw e;
1158
+ }
1159
+ }
1160
+ throw new Error("Anthropic fetch: unexpected retry loop exit");
525
1161
  }
526
1162
  safeParseJSON(str) {
527
1163
  try {
@@ -536,12 +1172,6 @@ function createAnthropic(config) {
536
1172
  }
537
1173
 
538
1174
  // src/models/ollama.ts
539
- var OLLAMA_CAPABILITIES = {
540
- "qwen3.5:0.8b": { contextLength: 32768, maxOutputTokens: 4096 },
541
- "minimax-m2.7:cloud": { contextLength: 128e3, maxOutputTokens: 16384 },
542
- "nemotron-3-super:cloud": { contextLength: 128e3, maxOutputTokens: 16384 },
543
- "glm-5:cloud": { contextLength: 128e3, maxOutputTokens: 16384 }
544
- };
545
1175
  function ollamaStreamChunksFromChatData(data, parseToolArguments, nextToolCallId) {
546
1176
  const chunks = [];
547
1177
  const msg = data.message;
@@ -596,11 +1226,11 @@ var OllamaAdapter = class extends BaseModelAdapter {
596
1226
  this.model = config.model || "qwen3.5:0.8b";
597
1227
  this.think = config.think;
598
1228
  this.name = `ollama/${this.model}`;
599
- this.capabilities = config.capabilities ?? OLLAMA_CAPABILITIES[this.model] ?? { contextLength: 4096, maxOutputTokens: 2048 };
1229
+ this.capabilities = config.capabilities ?? DEFAULT_ADAPTER_CAPABILITIES;
600
1230
  }
601
1231
  async *stream(params) {
602
1232
  const body = this.buildRequestBody(params, true);
603
- const response = await this.fetch("/api/chat", body, params.signal);
1233
+ const response = await this.fetch("/api/chat", body, "stream", params);
604
1234
  if (!response.ok) {
605
1235
  const error = await response.text();
606
1236
  throw new Error(`Ollama API error: ${response.status} - ${error}`);
@@ -654,7 +1284,18 @@ var OllamaAdapter = class extends BaseModelAdapter {
654
1284
  }
655
1285
  yield { type: "done", ...raw };
656
1286
  }
657
- } catch {
1287
+ } catch (error) {
1288
+ logModelStreamParseError(
1289
+ {
1290
+ provider: "ollama",
1291
+ model: this.model,
1292
+ path: "/api/chat",
1293
+ operation: "stream",
1294
+ params
1295
+ },
1296
+ trimmed,
1297
+ error
1298
+ );
658
1299
  }
659
1300
  }
660
1301
  }
@@ -664,7 +1305,7 @@ var OllamaAdapter = class extends BaseModelAdapter {
664
1305
  }
665
1306
  async complete(params) {
666
1307
  const body = this.buildRequestBody(params, false);
667
- const response = await this.fetch("/api/chat", body);
1308
+ const response = await this.fetch("/api/chat", body, "complete", params);
668
1309
  if (!response.ok) {
669
1310
  const error = await response.text();
670
1311
  throw new Error(`Ollama API error: ${response.status} - ${error}`);
@@ -744,11 +1385,18 @@ var OllamaAdapter = class extends BaseModelAdapter {
744
1385
  });
745
1386
  }
746
1387
  buildRequestBody(params, stream) {
1388
+ const defaultMaxTokens = this.capabilities?.maxOutputTokens ?? DEFAULT_ADAPTER_CAPABILITIES.maxOutputTokens;
1389
+ const options = {
1390
+ num_predict: params.maxTokens ?? defaultMaxTokens
1391
+ };
1392
+ if (params.temperature !== void 0) {
1393
+ options.temperature = params.temperature;
1394
+ }
747
1395
  const body = {
748
1396
  model: this.model,
749
1397
  messages: this.transformMessages(params.messages),
750
1398
  stream,
751
- ...params.temperature !== void 0 && { options: { temperature: params.temperature } }
1399
+ options
752
1400
  };
753
1401
  if (this.think !== void 0) {
754
1402
  body.think = this.think;
@@ -761,47 +1409,97 @@ var OllamaAdapter = class extends BaseModelAdapter {
761
1409
  }
762
1410
  return body;
763
1411
  }
764
- async fetch(path, body, signal) {
765
- return globalThis.fetch(`${this.baseUrl}${path}`, {
766
- method: "POST",
767
- headers: {
768
- "Content-Type": "application/json"
769
- },
770
- body: JSON.stringify(body),
771
- signal
772
- });
1412
+ async fetch(path, body, operation, params) {
1413
+ const requestLog = logModelRequestStart({
1414
+ provider: "ollama",
1415
+ model: this.model,
1416
+ path,
1417
+ operation,
1418
+ params
1419
+ }, body);
1420
+ try {
1421
+ const response = await globalThis.fetch(`${this.baseUrl}${path}`, {
1422
+ method: "POST",
1423
+ headers: {
1424
+ "Content-Type": "application/json"
1425
+ },
1426
+ body: JSON.stringify(body),
1427
+ signal: params.signal
1428
+ });
1429
+ logModelRequestEnd(
1430
+ {
1431
+ provider: "ollama",
1432
+ model: this.model,
1433
+ path,
1434
+ operation,
1435
+ params
1436
+ },
1437
+ requestLog,
1438
+ response
1439
+ );
1440
+ return response;
1441
+ } catch (error) {
1442
+ logModelRequestFailure(
1443
+ {
1444
+ provider: "ollama",
1445
+ model: this.model,
1446
+ path,
1447
+ operation,
1448
+ params
1449
+ },
1450
+ requestLog,
1451
+ error
1452
+ );
1453
+ throw error;
1454
+ }
773
1455
  }
774
1456
  };
775
1457
  function createOllama(config) {
776
1458
  return new OllamaAdapter(config);
777
1459
  }
778
1460
 
1461
+ // src/core/process-env-merge.ts
1462
+ function mergeProcessEnv(overrides) {
1463
+ const base = {};
1464
+ for (const [key, value] of Object.entries(process.env)) {
1465
+ if (typeof value === "string") {
1466
+ base[key] = value;
1467
+ }
1468
+ }
1469
+ return overrides ? { ...base, ...overrides } : base;
1470
+ }
1471
+ function mergeMcpStdioEnv(agentEnv, serverEnv) {
1472
+ const merged = mergeProcessEnv(agentEnv);
1473
+ return serverEnv ? { ...merged, ...serverEnv } : merged;
1474
+ }
1475
+
779
1476
  // src/models/index.ts
780
- function createModel(config) {
781
- switch (config.provider) {
1477
+ function createModel(modelConfig, agentEnv) {
1478
+ const merged = mergeProcessEnv(agentEnv);
1479
+ switch (modelConfig.provider) {
782
1480
  case "openai":
783
1481
  return new OpenAIAdapter({
784
- apiKey: config.apiKey,
785
- baseUrl: config.baseUrl,
786
- model: config.model
1482
+ apiKey: modelConfig.apiKey || merged.OPENAI_API_KEY || "",
1483
+ baseUrl: modelConfig.baseUrl || merged.OPENAI_BASE_URL,
1484
+ model: modelConfig.model,
1485
+ organization: merged.OPENAI_ORG_ID
787
1486
  });
788
1487
  case "anthropic":
789
1488
  return new AnthropicAdapter({
790
- apiKey: config.apiKey,
791
- baseUrl: config.baseUrl,
792
- model: config.model
1489
+ apiKey: modelConfig.apiKey || merged.ANTHROPIC_API_KEY || "",
1490
+ baseUrl: modelConfig.baseUrl || merged.ANTHROPIC_BASE_URL,
1491
+ model: modelConfig.model
793
1492
  });
794
1493
  case "ollama":
795
1494
  return new OllamaAdapter({
796
- baseUrl: config.baseUrl,
797
- model: config.model,
798
- think: config.think
1495
+ baseUrl: modelConfig.baseUrl || merged.OLLAMA_BASE_URL,
1496
+ model: modelConfig.model,
1497
+ think: modelConfig.think
799
1498
  });
800
- default:
801
- throw new Error(`Unknown model provider: ${config.provider}`);
802
1499
  }
1500
+ throw new Error(`Unknown model provider: ${modelConfig.provider}`);
803
1501
  }
804
1502
 
805
- export { AnthropicAdapter, OllamaAdapter, OpenAIAdapter, createAnthropic, createModel, createOllama, createOpenAI, ollamaMessageContentToApiString, ollamaStreamChunksFromChatData };
806
- //# sourceMappingURL=chunk-NDSL7NPN.js.map
807
- //# sourceMappingURL=chunk-NDSL7NPN.js.map
1503
+ export { AnthropicAdapter, DEFAULT_ADAPTER_CAPABILITIES, OllamaAdapter, OpenAIAdapter, createAnthropic, createConsoleSDKLogger, createModel, createOllama, createOpenAI, emitSDKLog, formatSDKLog, mergeMcpStdioEnv, mergeProcessEnv, ollamaMessageContentToApiString, ollamaStreamChunksFromChatData };
1504
+ //# sourceMappingURL=chunk-D3UZNLZO.js.map
1505
+ //# sourceMappingURL=chunk-D3UZNLZO.js.map