@faapi/agent 6.30.0 → 6.32.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/dist/index.js CHANGED
@@ -14,6 +14,13 @@ var LLMProviderError = class extends Error {
14
14
  status;
15
15
  /** 响应体摘要(前 500 字符,便于诊断) */
16
16
  body;
17
+ /**
18
+ * 实际发起的 HTTP 尝试次数(≥1,含失败尝试)
19
+ *
20
+ * 重试耗尽抛出时由重试循环回填(构造时未知,故非 readonly);
21
+ * 首次尝试即失败的确定性错误(4xx 直抛)为 1,未回填时视为 1。
22
+ */
23
+ attempts;
17
24
  constructor(message, options) {
18
25
  super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
19
26
  this.name = "LLMProviderError";
@@ -21,6 +28,12 @@ var LLMProviderError = class extends Error {
21
28
  this.body = options?.body;
22
29
  }
23
30
  };
31
+ var LLMTimeoutError = class extends LLMProviderError {
32
+ constructor(message, options) {
33
+ super(message, options);
34
+ this.name = "LLMTimeoutError";
35
+ }
36
+ };
24
37
  function createOpenAIProvider(config) {
25
38
  const baseURL = (config.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "");
26
39
  const apiKey = config.apiKey;
@@ -69,13 +82,19 @@ function createOpenAIProvider(config) {
69
82
  if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
70
83
  return headers;
71
84
  }
72
- function classifyAbortError(err, externalSignal) {
85
+ function resolveTimeoutMs(request) {
86
+ if (typeof request.timeoutMs === "number") return request.timeoutMs;
87
+ return typeof config.timeoutMs === "number" ? config.timeoutMs : void 0;
88
+ }
89
+ function classifyAbortError(err, externalSignal, timeoutMs) {
73
90
  if (externalSignal?.aborted) {
74
91
  return new AgentAbortError();
75
92
  }
76
93
  const errName = err?.name;
77
94
  if (errName === "AbortError" || errName === "TimeoutError") {
78
- return new LLMProviderError(`LLM request timed out after ${config.timeoutMs}ms`);
95
+ return new LLMTimeoutError(
96
+ timeoutMs !== void 0 ? `LLM request timed out after ${timeoutMs}ms` : "LLM request timed out"
97
+ );
79
98
  }
80
99
  const reason = err instanceof Error ? err.message : String(err);
81
100
  return new LLMProviderError(`Network error: ${reason}`, { cause: err });
@@ -84,11 +103,11 @@ function createOpenAIProvider(config) {
84
103
  const name = err?.name;
85
104
  return name === "AbortError" || name === "TimeoutError";
86
105
  }
87
- async function safeFetch(url, init, externalSignal) {
106
+ async function safeFetch(url, init, externalSignal, timeoutMs) {
88
107
  try {
89
108
  return await fetch(url, init);
90
109
  } catch (err) {
91
- throw classifyAbortError(err, externalSignal);
110
+ throw classifyAbortError(err, externalSignal, timeoutMs);
92
111
  }
93
112
  }
94
113
  function isRetryable(err) {
@@ -106,17 +125,23 @@ function createOpenAIProvider(config) {
106
125
  }
107
126
  function effectiveSignal(request) {
108
127
  const { signal } = request;
109
- const timeoutMs = typeof config.timeoutMs === "number" ? config.timeoutMs : void 0;
128
+ const timeoutMs = resolveTimeoutMs(request);
110
129
  if (!signal && !timeoutMs) return void 0;
111
130
  const signals = [];
112
131
  if (signal) signals.push(signal);
113
132
  if (timeoutMs) signals.push(AbortSignal.timeout(timeoutMs));
114
133
  return signals.length === 1 ? signals[0] : AbortSignal.any(signals);
115
134
  }
116
- async function fetchOkWithRetry(url, baseInit, request, maxRetries2) {
135
+ function resolveMaxRetries(request) {
136
+ if (typeof request.maxRetries === "number") return Math.max(0, request.maxRetries);
137
+ return maxRetries;
138
+ }
139
+ async function fetchOkWithRetry(url, baseInit, request) {
140
+ const maxAttemptsRetries = resolveMaxRetries(request);
141
+ const timeoutMs = resolveTimeoutMs(request);
117
142
  let lastErr;
118
143
  let lastResponse;
119
- for (let attempt = 0; attempt <= maxRetries2; attempt++) {
144
+ for (let attempt = 0; attempt <= maxAttemptsRetries; attempt++) {
120
145
  if (request.signal?.aborted) {
121
146
  throw new AgentAbortError();
122
147
  }
@@ -126,13 +151,13 @@ function createOpenAIProvider(config) {
126
151
  const init = { ...baseInit, signal: effectiveSignal(request) };
127
152
  let response;
128
153
  try {
129
- response = await safeFetch(url, init, request.signal);
154
+ response = await safeFetch(url, init, request.signal, timeoutMs);
130
155
  } catch (err2) {
131
156
  if (!isRetryable(err2)) throw err2;
132
157
  lastErr = err2;
133
158
  continue;
134
159
  }
135
- if (response.ok) return response;
160
+ if (response.ok) return { response, attempts: attempt + 1 };
136
161
  let excerpt = "<body unreadable>";
137
162
  try {
138
163
  excerpt = (await response.text()).slice(0, 500);
@@ -146,6 +171,9 @@ function createOpenAIProvider(config) {
146
171
  lastErr = err;
147
172
  lastResponse = response;
148
173
  }
174
+ if (lastErr instanceof LLMProviderError) {
175
+ lastErr.attempts = maxAttemptsRetries + 1;
176
+ }
149
177
  throw lastErr;
150
178
  }
151
179
  async function complete(request) {
@@ -156,13 +184,14 @@ function createOpenAIProvider(config) {
156
184
  headers: buildHeaders(),
157
185
  body: JSON.stringify(body)
158
186
  };
159
- const response = await fetchOkWithRetry(url, baseInit, request, maxRetries);
187
+ const timeoutMs = resolveTimeoutMs(request);
188
+ const { response, attempts } = await fetchOkWithRetry(url, baseInit, request);
160
189
  let bodyText;
161
190
  try {
162
191
  bodyText = await response.text();
163
192
  } catch (err) {
164
193
  if (!isAbortLike(err)) throw err;
165
- throw classifyAbortError(err, request.signal);
194
+ throw classifyAbortError(err, request.signal, timeoutMs);
166
195
  }
167
196
  let json;
168
197
  try {
@@ -192,7 +221,8 @@ function createOpenAIProvider(config) {
192
221
  return {
193
222
  message,
194
223
  stopReason: mapStopReason(choice.finish_reason ?? void 0),
195
- usage: mapUsage(json.usage)
224
+ usage: mapUsage(json.usage),
225
+ attempts
196
226
  };
197
227
  }
198
228
  async function* stream(request) {
@@ -204,7 +234,8 @@ function createOpenAIProvider(config) {
204
234
  headers: buildHeaders(),
205
235
  body: JSON.stringify(body)
206
236
  };
207
- const response = await fetchOkWithRetry(url, baseInit, request, maxRetries);
237
+ const timeoutMs = resolveTimeoutMs(request);
238
+ const { response } = await fetchOkWithRetry(url, baseInit, request);
208
239
  if (!response.body) {
209
240
  throw new LLMProviderError("Response body is null (streaming unsupported)", {
210
241
  status: response.status
@@ -281,7 +312,7 @@ function createOpenAIProvider(config) {
281
312
  yield finalizeStreamChunk(accumulators, finishReason, usage);
282
313
  } catch (err) {
283
314
  if (!isAbortLike(err)) throw err;
284
- throw classifyAbortError(err, request.signal);
315
+ throw classifyAbortError(err, request.signal, timeoutMs);
285
316
  } finally {
286
317
  try {
287
318
  await reader.cancel();
@@ -433,6 +464,9 @@ function createProvider(config) {
433
464
  }
434
465
  }
435
466
 
467
+ // src/agent.ts
468
+ import { readResource, subAgentToolName } from "@faapi/faapi";
469
+
436
470
  // src/trace.ts
437
471
  function isTracingToolResult(value) {
438
472
  return typeof value === "object" && value !== null && value.__trace === true;
@@ -958,7 +992,6 @@ async function* reactLoopStream(input, config) {
958
992
  }
959
993
 
960
994
  // src/agent.ts
961
- import { readResource, subAgentToolName } from "@faapi/faapi";
962
995
  var DEFAULT_MAX_AGENT_DEPTH = 3;
963
996
  var DEFAULT_SUBAGENT_INPUT_DESCRIPTION = "\u6D3E\u53D1\u7ED9\u8BE5 agent \u7684\u4EFB\u52A1\u4EA4\u63A5\u5355\uFF08\u81EA\u7136\u8BED\u8A00\uFF09\uFF1A\u5199\u6E05\u4EFB\u52A1\u76EE\u6807\u3001\u5FC5\u8981\u7684\u4E0A\u4E0B\u6587\u4FE1\u606F\u4E0E\u5BF9\u4EA7\u51FA\u7ED3\u679C\u7684\u8981\u6C42";
964
997
  function extractSubAgentUserInput(args) {
@@ -984,6 +1017,64 @@ function materializeProvider(external) {
984
1017
  'options.provider must be an LlmConfig object (with a string "provider" field) or an LLMProvider instance (with complete/stream methods)'
985
1018
  );
986
1019
  }
1020
+ function resolveModelKey(key, deps, fallbackKey) {
1021
+ const effectiveKey = key ?? fallbackKey;
1022
+ if (effectiveKey === void 0) {
1023
+ throw new AgentError(
1024
+ 'No LLM provider resolved: pass options.model (a provider key / "provider/model" / model name from config.agent.llms), options.provider (external provider), or declare model in the agent config'
1025
+ );
1026
+ }
1027
+ const byProviderKey = deps.providers.get(effectiveKey);
1028
+ if (byProviderKey) {
1029
+ const llmConfig = deps.llms[effectiveKey];
1030
+ const firstModel = llmConfig ? Object.keys(llmConfig.models)[0] : void 0;
1031
+ return {
1032
+ provider: byProviderKey,
1033
+ providerName: effectiveKey,
1034
+ model: firstModel ?? fallbackKey
1035
+ };
1036
+ }
1037
+ if (effectiveKey.includes("/")) {
1038
+ const slashIdx = effectiveKey.indexOf("/");
1039
+ const providerName = effectiveKey.slice(0, slashIdx);
1040
+ const modelName = effectiveKey.slice(slashIdx + 1);
1041
+ const provider = deps.providers.get(providerName);
1042
+ if (!provider) {
1043
+ throw new AgentError(
1044
+ `Unknown provider "${providerName}" in model key "${effectiveKey}". Declare it in config.agent.llms, or pass options.provider to use an external provider.`
1045
+ );
1046
+ }
1047
+ const llmConfig = deps.llms[providerName];
1048
+ if (!llmConfig || !llmConfig.models[modelName]) {
1049
+ throw new AgentError(
1050
+ `Model "${modelName}" not found in provider "${providerName}". Declare it in config.agent.llms.${providerName}.models.`
1051
+ );
1052
+ }
1053
+ return { provider, providerName, model: modelName };
1054
+ }
1055
+ const matches = [];
1056
+ for (const [providerName, provider] of deps.providers) {
1057
+ const llmConfig = deps.llms[providerName];
1058
+ if (llmConfig && llmConfig.models[effectiveKey]) {
1059
+ matches.push({ provider, providerName });
1060
+ }
1061
+ }
1062
+ if (matches.length === 1) {
1063
+ return {
1064
+ provider: matches[0].provider,
1065
+ providerName: matches[0].providerName,
1066
+ model: effectiveKey
1067
+ };
1068
+ }
1069
+ if (matches.length > 1) {
1070
+ throw new AgentError(
1071
+ `Model "${effectiveKey}" is ambiguous (found in providers: ${matches.map((m) => m.providerName).join(", ")}). Use "provider/model" to disambiguate.`
1072
+ );
1073
+ }
1074
+ throw new AgentError(
1075
+ `Model "${effectiveKey}" not found in any provider. Declare it in config.agent.llms.*.models, or pass options.provider to use an external provider.`
1076
+ );
1077
+ }
987
1078
  function validateResumeHistory(messages) {
988
1079
  messages.forEach((message, index) => {
989
1080
  if (!VALID_MESSAGE_ROLES.has(message.role)) {
@@ -1286,72 +1377,14 @@ var Agent = class _Agent {
1286
1377
  return { provider, model };
1287
1378
  }
1288
1379
  /**
1289
- * 解析 `options.model` 字符串 key → provider + model
1290
- *
1291
- * 规则见 [agentHandle.md](./agentHandle.md) 的「`options.model` 字符串 key 解析规则」。
1292
- * 无默认 provider——`key` 未传时用 agent 元数据 `config.model` 作为缺省 key;
1293
- * 两者皆无抛 `AgentError`(要求调用方传 `options.model` 或 `options.provider`)。
1294
- * 1. 精确匹配 `deps.providers` 的 key → 该 provider + 其 `models` 第一个 key
1295
- * (该 provider 未声明 `models` 时回落 `meta.model`)
1296
- * 2. 含 `/` → `provider/model` 形式,`deps.providers.get(provider)` + 该 model
1297
- * (要求该 model 在 `deps.llms[provider].models` 里)
1298
- * 3. 不含 `/` 且非 provider key → 在所有 provider 的 `models` 里按 model 名查找
1299
- * - 唯一 → 该 provider + 该 model
1300
- * - 多个 → 抛 `AgentError`(要求用 `provider/model` 消歧)
1301
- * - 无 → 抛 `AgentError`
1380
+ * 解析 `options.model` 字符串 key → provider + model(模块级 [resolveModelKey] 的
1381
+ * Agent 方法包装——meta.model 作缺省 key,与 [lightComplete](./lightComplete.md) 共用同一解析实现)
1302
1382
  *
1303
1383
  * @throws {AgentError} key 与 `meta.model` 均缺省;key 解析失败(provider/model
1304
1384
  * 不存在或歧义)
1305
1385
  */
1306
1386
  resolveModelKey(key, meta) {
1307
- const effectiveKey = key ?? meta.model;
1308
- if (effectiveKey === void 0) {
1309
- throw new AgentError(
1310
- 'No LLM provider resolved: pass options.model (a provider key / "provider/model" / model name from config.agent.llms), options.provider (external provider), or declare model in the agent config'
1311
- );
1312
- }
1313
- const byProviderKey = this.deps.providers.get(effectiveKey);
1314
- if (byProviderKey) {
1315
- const llmConfig = this.deps.llms[effectiveKey];
1316
- const firstModel = llmConfig ? Object.keys(llmConfig.models)[0] : void 0;
1317
- return { provider: byProviderKey, model: firstModel ?? meta.model };
1318
- }
1319
- if (effectiveKey.includes("/")) {
1320
- const slashIdx = effectiveKey.indexOf("/");
1321
- const providerName = effectiveKey.slice(0, slashIdx);
1322
- const modelName = effectiveKey.slice(slashIdx + 1);
1323
- const provider = this.deps.providers.get(providerName);
1324
- if (!provider) {
1325
- throw new AgentError(
1326
- `Unknown provider "${providerName}" in model key "${effectiveKey}". Declare it in config.agent.llms, or pass options.provider to use an external provider.`
1327
- );
1328
- }
1329
- const llmConfig = this.deps.llms[providerName];
1330
- if (!llmConfig || !llmConfig.models[modelName]) {
1331
- throw new AgentError(
1332
- `Model "${modelName}" not found in provider "${providerName}". Declare it in config.agent.llms.${providerName}.models.`
1333
- );
1334
- }
1335
- return { provider, model: modelName };
1336
- }
1337
- const matches = [];
1338
- for (const [providerName, provider] of this.deps.providers) {
1339
- const llmConfig = this.deps.llms[providerName];
1340
- if (llmConfig && llmConfig.models[effectiveKey]) {
1341
- matches.push({ provider, providerName });
1342
- }
1343
- }
1344
- if (matches.length === 1) {
1345
- return { provider: matches[0].provider, model: effectiveKey };
1346
- }
1347
- if (matches.length > 1) {
1348
- throw new AgentError(
1349
- `Model "${effectiveKey}" is ambiguous (found in providers: ${matches.map((m) => m.providerName).join(", ")}). Use "provider/model" to disambiguate.`
1350
- );
1351
- }
1352
- throw new AgentError(
1353
- `Model "${effectiveKey}" not found in any provider. Declare it in config.agent.llms.*.models, or pass options.provider to use an external provider.`
1354
- );
1387
+ return resolveModelKey(key, this.deps, meta.model);
1355
1388
  }
1356
1389
  /**
1357
1390
  * 组装 LLM 可见 tool 列表(OpenAI chat completions 规范形)
@@ -1364,9 +1397,15 @@ var Agent = class _Agent {
1364
1397
  * - `resolveToolSchema` 提供 → 用其 `jsonSchema`
1365
1398
  * - 未提供 / tool 无 `inputTypeName` → 自由 schema `{ type: 'object' }`
1366
1399
  *
1367
- * sub-agent 的 `function.parameters` 为显式单字段 `input` schema(string,必填)——
1368
- * 严格遵循 JSON schema 的模型对无属性 `{ type: 'object' }` 只回 `{}`,派发上下文
1369
- * 传不进子代理;description 用 sub 元数据 `inputDescription`,未声明用默认文案。
1400
+ * sub-agent 的 `function.parameters` 按「派发入参 schema 声明」二选一:
1401
+ * - **富 schema 模式**——sub 元数据声明 `inputTypeName`(handler.ts 顶层 `Input`
1402
+ * 导出)且 `resolveAgentInputSchema` 已接线 → 用解析出的 JSON Schema(结构性
1403
+ * 交接单,字段 JSDoc 即主控可见参数描述)。声明了但解析为 `undefined` 是产物
1404
+ * 异常(dev/prod 全量生成下 zod.js 不可能合法缺失),抛 `AgentError` 不静默降级
1405
+ * - **单字段 `input` 模式**——未声明 `Input` 或 resolver 未接线 → 显式单字段
1406
+ * schema(string,必填)——严格遵循 JSON schema 的模型对无属性 `{ type: 'object' }`
1407
+ * 只回 `{}`,派发上下文传不进子代理;description 用 sub 元数据 `inputDescription`,
1408
+ * 未声明用默认文案
1370
1409
  */
1371
1410
  async buildToolDefinitions(agentName) {
1372
1411
  const definitions = /* @__PURE__ */ new Map();
@@ -1390,16 +1429,7 @@ var Agent = class _Agent {
1390
1429
  function: {
1391
1430
  name,
1392
1431
  description: subAgent.description,
1393
- parameters: {
1394
- type: "object",
1395
- properties: {
1396
- input: {
1397
- type: "string",
1398
- description: subAgent.inputDescription ?? DEFAULT_SUBAGENT_INPUT_DESCRIPTION
1399
- }
1400
- },
1401
- required: ["input"]
1402
- }
1432
+ parameters: await this.getSubAgentParameters(subAgent)
1403
1433
  }
1404
1434
  });
1405
1435
  }
@@ -1407,6 +1437,39 @@ var Agent = class _Agent {
1407
1437
  const filtered = this.deps.config?.filterTools?.(defs, this.deps.ctx);
1408
1438
  return filtered ?? defs;
1409
1439
  }
1440
+ /**
1441
+ * 解析 sub-agent 派发工具的 parameters(富 schema / 单字段 input 二选一)
1442
+ *
1443
+ * 富 schema 判定需要完整元数据的 `inputTypeName`(`AgentCore` 不含)——经
1444
+ * `getAgentEntry` 查询;未声明或 `resolveAgentInputSchema` 未接线时返回单字段
1445
+ * `input` schema(历史行为,完全向后兼容)。`inputDescription` 仍从 `AgentCore`
1446
+ * 读取(LLM 可见字段的既定来源,DB skill 同样可声明)。
1447
+ *
1448
+ * @throws {AgentError} 声明了 `inputTypeName` 且 resolver 已接线但解析为
1449
+ * `undefined`(zod.js 缺失/损坏的产物异常,不静默退回单字段模式)
1450
+ */
1451
+ async getSubAgentParameters(subAgent) {
1452
+ const entry = this.deps.getAgentEntry(subAgent.name);
1453
+ if (!entry?.inputTypeName || !this.deps.resolveAgentInputSchema) {
1454
+ return {
1455
+ type: "object",
1456
+ properties: {
1457
+ input: {
1458
+ type: "string",
1459
+ description: subAgent.inputDescription ?? DEFAULT_SUBAGENT_INPUT_DESCRIPTION
1460
+ }
1461
+ },
1462
+ required: ["input"]
1463
+ };
1464
+ }
1465
+ const schemaRes = await this.deps.resolveAgentInputSchema(entry);
1466
+ if (!schemaRes) {
1467
+ throw new AgentError(
1468
+ `Sub-agent "${subAgent.name}" declares input schema "${entry.inputTypeName}" but its zod.js artifact is missing or broken (run \`faapi build\` / restart \`faapi dev\` to regenerate)`
1469
+ );
1470
+ }
1471
+ return schemaRes.jsonSchema;
1472
+ }
1410
1473
  /**
1411
1474
  * tool 执行路由(由 reactLoop 调用)
1412
1475
  *
@@ -1482,9 +1545,15 @@ var Agent = class _Agent {
1482
1545
  * sub-agent 递归执行
1483
1546
  *
1484
1547
  * 1. `maxAgentDepth` 防护——超限抛 {@link AgentRecursionError}
1485
- * 2. sub-agent handler 导出 `run` 时调自定义 `mod.run(args)`(无 trace、无结构化
1548
+ * 2. **派发入参校验(富 schema 模式)**——sub 元数据声明 `inputTypeName` 且
1549
+ * `resolveAgentInputSchema` 已接线时执行前 `validate(args)`:失败返回
1550
+ * `{ error }` 回灌主控 LLM 重试(与常规 tool 校验失败同语义,不进入子循环——
1551
+ * 省掉一次注定失败的子 agent 轮次);通过后以 coerce 后的 value 继续传导。
1552
+ * 声明了但解析为 `undefined`(zod.js 缺失/损坏)抛 `AgentError` 显式失败;
1553
+ * resolver 未接线或未声明 `Input` 时跳过校验,行为与历史版本一致
1554
+ * 3. sub-agent handler 导出 `run` 时调自定义 `mod.run(args)`(无 trace、无结构化
1486
1555
  * usage 可卷——直接返回业务结果,其 token 不进入父 run 台账)
1487
- * 3. 无 `run` 时调 `subAgent.run(stringify(args), { agent, provider, model, enableTracing })`
1556
+ * 4. 无 `run` 时调 `subAgent.run(stringify(args), { agent, provider, model, enableTracing })`
1488
1557
  * 走默认 reactLoop——继承父调用的 provider,sub 元数据声明 `model` 时优先用自身的,
1489
1558
  * 未声明时沿用父 model
1490
1559
  *
@@ -1499,12 +1568,27 @@ var Agent = class _Agent {
1499
1568
  * user 消息:args 恰为单字段 `{ input: <string> }`(与显式入参 schema 形状一致)
1500
1569
  * 时直传字符串,其余形状 stringify 兜底(见 `extractSubAgentUserInput`)。
1501
1570
  */
1502
- async executeSubAgent(subName, args, callCtx, deltaEmitter) {
1571
+ async executeSubAgent(subName, rawArgs, callCtx, deltaEmitter) {
1503
1572
  const newDepth = this.depth + 1;
1504
1573
  const maxDepth = this.deps.config?.maxAgentDepth ?? DEFAULT_MAX_AGENT_DEPTH;
1505
1574
  if (newDepth > maxDepth) {
1506
1575
  throw new AgentRecursionError(maxDepth, newDepth);
1507
1576
  }
1577
+ let args = rawArgs;
1578
+ const entry = this.deps.getAgentEntry(subName);
1579
+ if (entry?.inputTypeName && this.deps.resolveAgentInputSchema) {
1580
+ const schemaRes = await this.deps.resolveAgentInputSchema(entry);
1581
+ if (!schemaRes) {
1582
+ throw new AgentError(
1583
+ `Sub-agent "${subName}" declares input schema "${entry.inputTypeName}" but its zod.js artifact is missing or broken (run \`faapi build\` / restart \`faapi dev\` to regenerate)`
1584
+ );
1585
+ }
1586
+ const result2 = schemaRes.validate(args);
1587
+ if (!result2.ok) {
1588
+ return { error: result2.error };
1589
+ }
1590
+ args = result2.value ?? args;
1591
+ }
1508
1592
  const toolName = subAgentToolName(subName);
1509
1593
  const subAgent = new _Agent(this.deps, newDepth);
1510
1594
  const subMeta = this.deps.getAgent(subName);
@@ -1572,12 +1656,78 @@ var Agent = class _Agent {
1572
1656
  }
1573
1657
  };
1574
1658
 
1659
+ // src/lightComplete.ts
1660
+ var DEFAULT_LIGHT_TIMEOUT_MS = 6e4;
1661
+ function createLightComplete(deps) {
1662
+ const llms = deps.llms;
1663
+ const providers = deps.providers ?? buildProviders(llms);
1664
+ return {
1665
+ async complete(input, options) {
1666
+ const defaultKey = Object.keys(llms)[0];
1667
+ const { provider, providerName, model } = resolveModelKey(
1668
+ options?.model,
1669
+ { providers, llms },
1670
+ defaultKey
1671
+ );
1672
+ const messages = options?.system ? [
1673
+ { role: "system", content: options.system },
1674
+ { role: "user", content: input }
1675
+ ] : [{ role: "user", content: input }];
1676
+ const llmConfig = llms[providerName];
1677
+ const configTimeout = typeof llmConfig?.timeoutMs === "number" ? llmConfig.timeoutMs : void 0;
1678
+ const timeoutMs = options?.timeoutMs ?? configTimeout ?? DEFAULT_LIGHT_TIMEOUT_MS;
1679
+ try {
1680
+ const response = await provider.complete({
1681
+ messages,
1682
+ model,
1683
+ ...options?.temperature !== void 0 ? { temperature: options.temperature } : {},
1684
+ ...options?.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
1685
+ ...options?.signal !== void 0 ? { signal: options.signal } : {},
1686
+ timeoutMs,
1687
+ ...options?.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
1688
+ });
1689
+ return response.message.content ?? "";
1690
+ } catch (err) {
1691
+ if (err instanceof AgentAbortError) throw err;
1692
+ if (!(err instanceof LLMProviderError)) throw err;
1693
+ return handleFailure(err, options);
1694
+ }
1695
+ }
1696
+ };
1697
+ }
1698
+ function buildProviders(llms) {
1699
+ const providers = /* @__PURE__ */ new Map();
1700
+ for (const [name, llmConfig] of Object.entries(llms)) {
1701
+ providers.set(name, createProvider(llmConfig));
1702
+ }
1703
+ return providers;
1704
+ }
1705
+ function handleFailure(err, options) {
1706
+ const { onFailure, fallback } = options ?? {};
1707
+ const attempts = err.attempts ?? 1;
1708
+ if (onFailure) {
1709
+ try {
1710
+ onFailure(err, { attempts });
1711
+ } catch {
1712
+ }
1713
+ }
1714
+ if (fallback !== void 0) {
1715
+ if (!onFailure) {
1716
+ console.warn(
1717
+ `[faapi/agent] llm.complete fallback engaged after ${attempts} attempt(s): ${err.message}`
1718
+ );
1719
+ }
1720
+ return Promise.resolve(fallback);
1721
+ }
1722
+ return Promise.reject(err);
1723
+ }
1724
+
1575
1725
  // src/toolSchemaResolver.ts
1576
1726
  import { statSync } from "fs";
1577
1727
  import { z } from "zod";
1578
1728
  import { loadToolSchema, getToolSchemaPath } from "@faapi/faapi";
1579
- async function resolveToolSchemaImpl(tool, rootDir) {
1580
- const schemaMod = await loadToolSchema(tool, rootDir);
1729
+ async function resolveToolSchemaImpl(ref, rootDir) {
1730
+ const schemaMod = await loadToolSchema(ref, rootDir);
1581
1731
  if (!schemaMod) return void 0;
1582
1732
  const schema = schemaMod.schema;
1583
1733
  return {
@@ -1594,9 +1744,9 @@ async function resolveToolSchemaImpl(tool, rootDir) {
1594
1744
  function createToolSchemaResolver(options) {
1595
1745
  const rootDir = options?.rootDir ?? process.cwd();
1596
1746
  const schemaCache = /* @__PURE__ */ new Map();
1597
- return (tool) => {
1598
- const zodPath = getToolSchemaPath(tool, rootDir);
1599
- const key = `${zodPath}#${tool.inputTypeName ?? ""}`;
1747
+ return (ref) => {
1748
+ const zodPath = getToolSchemaPath(ref, rootDir);
1749
+ const key = `${zodPath}#${ref.inputTypeName ?? ""}`;
1600
1750
  let mtimeMs = -1;
1601
1751
  try {
1602
1752
  mtimeMs = statSync(zodPath).mtimeMs;
@@ -1606,7 +1756,7 @@ function createToolSchemaResolver(options) {
1606
1756
  if (hit && hit.mtimeMs === mtimeMs) {
1607
1757
  return hit.resolution;
1608
1758
  }
1609
- const resolution = resolveToolSchemaImpl(tool, rootDir);
1759
+ const resolution = resolveToolSchemaImpl(ref, rootDir);
1610
1760
  schemaCache.set(key, { mtimeMs, resolution });
1611
1761
  return resolution;
1612
1762
  };
@@ -1641,6 +1791,7 @@ var agentPlugin = {
1641
1791
  }
1642
1792
  providers.set(name, createProvider(llmConfig));
1643
1793
  }
1794
+ ctx.registries.llm.register(createLightComplete({ llms, providers }));
1644
1795
  const runtimeConfig = {
1645
1796
  maxTurns: agentConfig?.maxTurns,
1646
1797
  maxAgentDepth: agentConfig?.maxAgentDepth,
@@ -1651,7 +1802,7 @@ var agentPlugin = {
1651
1802
  filterTools: agentConfig?.filterTools
1652
1803
  };
1653
1804
  const rootDir = ctx.rootDir;
1654
- const resolveToolSchema = createToolSchemaResolver({ rootDir });
1805
+ const resolveSchema = createToolSchemaResolver({ rootDir });
1655
1806
  ctx.registries.agentHandle.register((ctx2) => {
1656
1807
  return new Agent({
1657
1808
  providers,
@@ -1670,8 +1821,9 @@ var agentPlugin = {
1670
1821
  resolveSubAgents: registries.agent.resolveSubAgents,
1671
1822
  // 加载器包装:注入 rootDir 用于 dev 按需编译模式
1672
1823
  loadToolModule: (filePath, functionName) => loadToolModule(filePath, functionName, rootDir),
1673
- // tool schema 解析(zod.js → JSON Schema + safeParse 校验)
1674
- resolveToolSchema
1824
+ // schema 解析(zod.js → JSON Schema + safeParse 校验)——tool 与 agent 入参共用
1825
+ resolveToolSchema: resolveSchema,
1826
+ resolveAgentInputSchema: resolveSchema
1675
1827
  });
1676
1828
  });
1677
1829
  console.log(
@@ -1687,7 +1839,9 @@ export {
1687
1839
  AgentRecursionError,
1688
1840
  AgentToolTimeoutError,
1689
1841
  LLMProviderError,
1842
+ LLMTimeoutError,
1690
1843
  ReactLoopError,
1844
+ createLightComplete,
1691
1845
  createOpenAIProvider,
1692
1846
  createProvider,
1693
1847
  createToolSchemaResolver,