@faapi/faapi 6.27.0 → 6.29.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.
@@ -51,7 +51,7 @@ interface ToolMetadata extends ToolCore {
51
51
  * 透传到 [ToolMetadata](./extractToolMetadata.ts) 输出,与 AST 提取字段合并。
52
52
  */
53
53
  interface ToolPathMeta {
54
- /** 路径推导的 tool 名(如 `weather.getWeather`) */
54
+ /** 路径推导的 tool 名(如 `weather_getWeather`) */
55
55
  name: string;
56
56
  /** 源码相对路径(如 `src/tools/weather/handler.ts`) */
57
57
  filePath: string;
@@ -432,7 +432,7 @@ declare function runTaskInWorker(options: TaskWorkerOptions): Promise<unknown>;
432
432
  interface ToolRegistry {
433
433
  /** 全量替换(tool 清单来自编译期产物,reload 时整体重新生成) */
434
434
  hydrate(tools: ToolMetadata[]): void;
435
- /** 按全名查找(如 `weather.getWeather`) */
435
+ /** 按全名查找(如 `weather_getWeather`) */
436
436
  get(name: string): ToolMetadata | undefined;
437
437
  /** 所有已注册 tool(副本) */
438
438
  list(): ToolMetadata[];
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { A as AppRegistries, F as FaapiContext, T as TaskClient, L as LogConfig, a as TaskFailedHandler, b as TaskQueueDeps, c as TaskQueue, d as TaskDriver, e as TaskRegistry, f as TaskManifest, g as ToolMetadata, h as AgentCore, i as AgentMetadata, C as CreateLoggerOptions, j as Logger } from './contextTypes-CBE86AO2.js';
2
- export { k as AgentHandleFactory, l as AgentHandleStore, m as AgentPathMeta, n as AgentRegistry, o as AgentToolDescriptor, p as FaapiContextConfig, q as FaapiTaskMeta, r as FailOptions, s as LogEntry, t as LogLevel, u as LogSink, S as SkillRegistry, v as SseEvent, w as SseOptions, x as SseWriter, y as TaskContext, z as TaskDriverJob, B as TaskDriverProcess, D as TaskDriverRecord, E as TaskFailedInfo, G as TaskJob, H as TaskJobStatus, I as TaskMetadata, J as TaskModule, K as TaskRegistriesSnapshot, M as TaskRegistriesView, N as ToolCore, O as ToolPathMeta, P as ToolRegistry, Q as createAppRegistries, R as createTaskRegistriesView, U as createTaskRegistry } from './contextTypes-CBE86AO2.js';
3
- import { F as FaapiMiddleware, R as RouteManifest, C as CorsOptions, H as HelmetOptions, I as InjectorMap, W as WsRouteManifest } from './routeTypes-DVSnH0jk.js';
4
- export { a as Injector, b as RouteInfo, c as RouteInputSchema, d as RouteOutputSchema, e as RouteParamSchema, f as cors, h as helmet } from './routeTypes-DVSnH0jk.js';
1
+ import { A as AppRegistries, F as FaapiContext, T as TaskClient, L as LogConfig, a as TaskFailedHandler, b as TaskQueueDeps, c as TaskQueue, d as TaskDriver, e as TaskRegistry, f as TaskManifest, g as ToolMetadata, h as AgentCore, i as AgentMetadata, C as CreateLoggerOptions, j as Logger } from './contextTypes-DhagvRH_.js';
2
+ export { k as AgentHandleFactory, l as AgentHandleStore, m as AgentPathMeta, n as AgentRegistry, o as AgentToolDescriptor, p as FaapiContextConfig, q as FaapiTaskMeta, r as FailOptions, s as LogEntry, t as LogLevel, u as LogSink, S as SkillRegistry, v as SseEvent, w as SseOptions, x as SseWriter, y as TaskContext, z as TaskDriverJob, B as TaskDriverProcess, D as TaskDriverRecord, E as TaskFailedInfo, G as TaskJob, H as TaskJobStatus, I as TaskMetadata, J as TaskModule, K as TaskRegistriesSnapshot, M as TaskRegistriesView, N as ToolCore, O as ToolPathMeta, P as ToolRegistry, Q as createAppRegistries, R as createTaskRegistriesView, U as createTaskRegistry } from './contextTypes-DhagvRH_.js';
3
+ import { F as FaapiMiddleware, R as RouteManifest, C as CorsOptions, H as HelmetOptions, I as InjectorMap, W as WsRouteManifest } from './routeTypes-0rwuIws-.js';
4
+ export { a as Injector, b as RouteInfo, c as RouteInputSchema, d as RouteOutputSchema, e as RouteParamSchema, f as cors, h as helmet } from './routeTypes-0rwuIws-.js';
5
5
  import * as node_http from 'node:http';
6
6
  import { Server, IncomingMessage, ServerResponse } from 'node:http';
7
7
  import { Socket } from 'node:net';
@@ -1348,11 +1348,40 @@ declare function resolveSubAgents(name: string): AgentCore[];
1348
1348
  *
1349
1349
  * 框架路径(`@faapi/agent` 插件 / 请求注入)从 app 实例查找。
1350
1350
  *
1351
- * @param tool 全名(如 `weather.getWeather`)
1351
+ * @param tool 全名(如 `weather_getWeather`)
1352
1352
  * @returns `ToolMetadata` 或 `undefined`(未注册)
1353
1353
  */
1354
1354
  declare function getTool(name: string): ToolMetadata | undefined;
1355
1355
 
1356
+ /**
1357
+ * sub-agent 派发工具名生成(唯一入口)
1358
+ *
1359
+ * agent-as-tool 把 sub-agent 包装成 tool 发给 LLM,`function.name` 受 OpenAI 兼容协议
1360
+ * 硬约束 `^[a-zA-Z0-9_-]+$`——DeepSeek / OpenAI 等强校验上游对非法字符整单 400。
1361
+ * 历史上派发名是 `agent.<agentName>`(点号前缀 + 嵌套 agent 名的点号分隔),在强校验
1362
+ * 上游完全不可用(业务方反馈 TODO-faapi-gaps #1)。
1363
+ *
1364
+ * 命名规则:`agent-` 前缀 + agent 名直拼。agent 名的合法性由上游保证——文件型 agent
1365
+ * 在 scanAgents 目录段校验(段内 `[a-zA-Z0-9-]`,`_` 为 `/` 规范化后的嵌套分隔符)、
1366
+ * `@agent` 覆盖名在 extractAgentMetadata 校验(整体 `^[a-zA-Z0-9_-]+$`);本模块对组合
1367
+ * 结果做最后一道运行时闸门(程序化 hydrate 的 agent 名不经构建期扫描),违例显式抛错
1368
+ * ——不静默净化(掩盖配置错误),不降级放行(强校验上游 400)。
1369
+ *
1370
+ * 详见 [subAgentToolName.md](./subAgentToolName.md)。
1371
+ */
1372
+ /** OpenAI 兼容协议对 LLM 工具名(function.name)的字符集约束 */
1373
+ declare const LLM_TOOL_NAME_PATTERN: RegExp;
1374
+ /** sub-agent 派发工具名前缀(旧版 `agent.` 点号前缀违反上述字符集,已废弃) */
1375
+ declare const SUB_AGENT_TOOL_PREFIX = "agent-";
1376
+ /**
1377
+ * 生成 sub-agent 派发工具名:`agent-<agentName>`
1378
+ *
1379
+ * @param agentName agent 注册名(scanAgents 目录推导或 `@agent` 覆盖名)
1380
+ * @returns 满足 {@link LLM_TOOL_NAME_PATTERN} 的派发工具名
1381
+ * @throws agent 名含工具名字符集之外的字符时抛错(含原名与改名指引)
1382
+ */
1383
+ declare function subAgentToolName(agentName: string): string;
1384
+
1356
1385
  /**
1357
1386
  * skill 注册表全局访问器(默认实例便捷入口)
1358
1387
  *
@@ -1655,14 +1684,6 @@ interface AppBase {
1655
1684
  * 业务方定位任意产物文件时与 rootDir 配合使用。
1656
1685
  */
1657
1686
  dist: string;
1658
- /**
1659
- * 运行时资源根目录绝对路径(<rootDir>/<dist>/resources)
1660
- *
1661
- * `src/resources/` 经 dev/build 复制进产物后的位置,业务方
1662
- * `path.join(app.resourcesDir, 'prompts/foo.md')` 后自行读取。
1663
- * 源码无 `src/resources/` 目录时路径仍指向约定位置(读文件由业务方容错)。
1664
- */
1665
- resourcesDir: string;
1666
1687
  /** 任务队列客户端(入队/查询;app 实例级,close 时随队列停机) */
1667
1688
  tasks: TaskClient;
1668
1689
  /** 启动 HTTP server,打印路由表,执行 onReady 钩子 */
@@ -1743,4 +1764,4 @@ type ProdApp = AppBase;
1743
1764
  */
1744
1765
  declare function createProdApp(options?: CreateAppOptions): Promise<ProdApp>;
1745
1766
 
1746
- export { type AgentConfig, AgentCore, AgentMetadata, type ProdApp as App, AppRegistries, CorsOptions, type CreateAppOptions, CreateLoggerOptions, type CronScheduler, type DevApp, type FaapiConfig, FaapiContext, FaapiError, FaapiMiddleware, type FaapiPlugin, type HandlerTypeInfo, HelmetOptions, type InjectOptions, type InjectResponse, InjectorMap, InternalError, type LifecycleContext, type LifecycleHooks, type LlmConfig, type LlmModelConfig, LogConfig, Logger, type LoggerOptions, MethodNotAllowedError, ModuleLoadError, type PluginContext, type PluginDeclaration, type ProdApp, type PropertyType, type RequestHandler, type ResponseConfig, RouteManifest, RouteNotFoundError, type RouteSchemaSource, type RuntimeType, SchemaExtractionError, TASK_PATTERNS, type TaskBullMqOptions, TaskClient, type TaskConfig, TaskDriver, TaskFailedHandler, TaskManifest, type TaskPgBossOptions, TaskQueue, TaskRegistry, ToolMetadata, type ToolModule, type ToolSchemaModule, type TypeConstraint, type UpgradeHandler, ValidationError, type ValidationErrorCode, type ValidationIssue, type WsContext, type WsEventHandlers, type WsHandler, type WsSocket, clearAgentHandleFactory, collectRouteSchemaSources, configureLogging, createProdApp as createApp, createCronScheduler, createDevApp, createLogger, createProdApp, createProgram, createPrograms, createTaskQueue, extractTypeInfo, flushLogging, getAgent, getAgentEntry, getApp, getInputTypeForMethod, getSkill, getTool, getToolSchemaPath, hydrateSkillRegistry, invalidateProgramCache, listSkills, loadConfig, loadEnv, loadTaskDriver, loadToolModule, loadToolSchema, logger, readResource, registerAgentHandleFactory, removeSkill, resolveAgentTools, resolveSubAgents, resolveTypeNode, scanTasks, upsertSkill };
1767
+ export { type AgentConfig, AgentCore, AgentMetadata, type ProdApp as App, AppRegistries, CorsOptions, type CreateAppOptions, CreateLoggerOptions, type CronScheduler, type DevApp, type FaapiConfig, FaapiContext, FaapiError, FaapiMiddleware, type FaapiPlugin, type HandlerTypeInfo, HelmetOptions, type InjectOptions, type InjectResponse, InjectorMap, InternalError, LLM_TOOL_NAME_PATTERN, type LifecycleContext, type LifecycleHooks, type LlmConfig, type LlmModelConfig, LogConfig, Logger, type LoggerOptions, MethodNotAllowedError, ModuleLoadError, type PluginContext, type PluginDeclaration, type ProdApp, type PropertyType, type RequestHandler, type ResponseConfig, RouteManifest, RouteNotFoundError, type RouteSchemaSource, type RuntimeType, SUB_AGENT_TOOL_PREFIX, SchemaExtractionError, TASK_PATTERNS, type TaskBullMqOptions, TaskClient, type TaskConfig, TaskDriver, TaskFailedHandler, TaskManifest, type TaskPgBossOptions, TaskQueue, TaskRegistry, ToolMetadata, type ToolModule, type ToolSchemaModule, type TypeConstraint, type UpgradeHandler, ValidationError, type ValidationErrorCode, type ValidationIssue, type WsContext, type WsEventHandlers, type WsHandler, type WsSocket, clearAgentHandleFactory, collectRouteSchemaSources, configureLogging, createProdApp as createApp, createCronScheduler, createDevApp, createLogger, createProdApp, createProgram, createPrograms, createTaskQueue, extractTypeInfo, flushLogging, getAgent, getAgentEntry, getApp, getInputTypeForMethod, getSkill, getTool, getToolSchemaPath, hydrateSkillRegistry, invalidateProgramCache, listSkills, loadConfig, loadEnv, loadTaskDriver, loadToolModule, loadToolSchema, logger, readResource, registerAgentHandleFactory, removeSkill, resolveAgentTools, resolveSubAgents, resolveTypeNode, scanTasks, subAgentToolName, upsertSkill };
package/dist/index.js CHANGED
@@ -1835,6 +1835,19 @@ function createTaskRegistry() {
1835
1835
  };
1836
1836
  }
1837
1837
 
1838
+ // src/injection/subAgentToolName.ts
1839
+ var LLM_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
1840
+ var SUB_AGENT_TOOL_PREFIX = "agent-";
1841
+ function subAgentToolName(agentName) {
1842
+ const toolName = SUB_AGENT_TOOL_PREFIX + agentName;
1843
+ if (!LLM_TOOL_NAME_PATTERN.test(toolName)) {
1844
+ throw new Error(
1845
+ `Sub-agent tool name "${toolName}" (from agent name "${agentName}") violates the LLM tool name pattern ${LLM_TOOL_NAME_PATTERN} \u2014 rename the agent (allowed: a-z A-Z 0-9 '-' '_'; file-based agent directory segments allow a-z A-Z 0-9 '-' only, '_' is the nesting separator)`
1846
+ );
1847
+ }
1848
+ return toolName;
1849
+ }
1850
+
1838
1851
  // src/injection/registries.ts
1839
1852
  function createToolRegistry() {
1840
1853
  let registry = /* @__PURE__ */ new Map();
@@ -1882,7 +1895,7 @@ function createAgentRegistry(tool) {
1882
1895
  if (!agent) return void 0;
1883
1896
  return {
1884
1897
  kind: "agent",
1885
- name: `agent.${agent.name}`,
1898
+ name: subAgentToolName(agent.name),
1886
1899
  agentName: agent.name,
1887
1900
  description: agent.description,
1888
1901
  metadata: agent
@@ -3685,6 +3698,7 @@ function extractJSDocTagValue(jsDoc, tagName) {
3685
3698
  }
3686
3699
 
3687
3700
  // src/ast/extractToolMetadata.ts
3701
+ init_resolveTypeNode();
3688
3702
  function extractToolMetadata(program, filePath, functionName, pathMeta) {
3689
3703
  const sourceFile = program.getSourceFile(filePath);
3690
3704
  if (!sourceFile) return null;
@@ -3694,6 +3708,14 @@ function extractToolMetadata(program, filePath, functionName, pathMeta) {
3694
3708
  const jsDoc = getJSDocFromNode(jsDocOwner);
3695
3709
  const description = extractDescription(jsDoc);
3696
3710
  const toolNameOverride = extractJSDocTagValue(jsDoc, "tool");
3711
+ if (toolNameOverride !== void 0 && !LLM_TOOL_NAME_PATTERN.test(toolNameOverride)) {
3712
+ throw SchemaExtractionError.at(
3713
+ jsDocOwner,
3714
+ "@tool",
3715
+ `\u8986\u76D6\u540D "${toolNameOverride}" \u542B\u975E\u6CD5\u5B57\u7B26\u2014\u2014\u987B\u6EE1\u8DB3 ${LLM_TOOL_NAME_PATTERN}\uFF08tool \u540D\u76F4\u63A5\u4F5C\u4E3A LLM function.name\uFF0C\u5F3A\u6821\u9A8C\u4E0A\u6E38\u5BF9\u975E\u6CD5\u5B57\u7B26\u6574\u5355 400\uFF09`,
3716
+ sourceFile
3717
+ );
3718
+ }
3697
3719
  const inputTypeName = getFirstParamTypeName(fn, sourceFile);
3698
3720
  return {
3699
3721
  name: toolNameOverride ?? pathMeta.name,
@@ -6825,6 +6847,14 @@ function extractAgentMetadata(program, filePath, pathMeta) {
6825
6847
  const jsDoc = getJSDocFromNode(configFound.jsDocOwner);
6826
6848
  const description = extractDescription(jsDoc);
6827
6849
  const agentNameOverride = extractJSDocTagValue(jsDoc, "agent");
6850
+ if (agentNameOverride !== void 0 && !LLM_TOOL_NAME_PATTERN.test(agentNameOverride)) {
6851
+ throw SchemaExtractionError.at(
6852
+ configFound.jsDocOwner,
6853
+ "@agent",
6854
+ `\u8986\u76D6\u540D "${agentNameOverride}" \u542B\u975E\u6CD5\u5B57\u7B26\u2014\u2014\u987B\u6EE1\u8DB3 ${LLM_TOOL_NAME_PATTERN}\uFF08\u8986\u76D6\u540D\u6210\u4E3A LLM \u5DE5\u5177\u540D "agent-<\u8986\u76D6\u540D>"\uFF0C\u5F3A\u6821\u9A8C\u4E0A\u6E38\u5BF9\u975E\u6CD5\u5B57\u7B26\u6574\u5355 400\uFF09`,
6855
+ sourceFile
6856
+ );
6857
+ }
6828
6858
  const { systemPrompt, systemPromptFile, tools, agents, model, maxTurns, inputDescription } = extractConfigFields(objectLiteral, sourceFile);
6829
6859
  return {
6830
6860
  name: agentNameOverride ?? pathMeta.name,
@@ -7573,7 +7603,6 @@ async function createAppBase(options) {
7573
7603
  wsRoutes,
7574
7604
  rootDir,
7575
7605
  dist,
7576
- resourcesDir,
7577
7606
  tasks: taskQueue,
7578
7607
  async listen(listenPort) {
7579
7608
  const envPort = process.env.PORT ? Number(process.env.PORT) : void 0;
@@ -7897,11 +7926,20 @@ function extractToolExportsFromSource(source) {
7897
7926
  }
7898
7927
  return names;
7899
7928
  }
7929
+ var TOOL_DIR_SEGMENT_PATTERN = /^[a-zA-Z0-9-]+$/;
7900
7930
  function extractNamespaceFromRelPath(relPath) {
7901
7931
  const lastSlash = relPath.lastIndexOf("/");
7902
7932
  const dirPath = lastSlash === -1 ? "" : relPath.slice(0, lastSlash);
7903
7933
  if (!dirPath) return "";
7904
- return dirPath.split("/").join(".");
7934
+ const segments = dirPath.split("/");
7935
+ for (const segment of segments) {
7936
+ if (!TOOL_DIR_SEGMENT_PATTERN.test(segment)) {
7937
+ throw new Error(
7938
+ `Invalid tool directory segment "${segment}" in "tools/${relPath}": tool directory names allow a-z A-Z 0-9 '-' only ('_' is reserved as the nesting separator, '.' is not allowed \u2014 tool names are sent to LLMs as function.name) \u2014 rename the directory`
7939
+ );
7940
+ }
7941
+ }
7942
+ return segments.join("_");
7905
7943
  }
7906
7944
  function filePathToToolNamespace(filePath) {
7907
7945
  const normalized = filePath.replace(/\\/g, "/");
@@ -7912,7 +7950,13 @@ function filePathToToolNamespace(filePath) {
7912
7950
  return "";
7913
7951
  }
7914
7952
  function buildToolName(namespace, functionName) {
7915
- return namespace ? `${namespace}.${functionName}` : functionName;
7953
+ const name = namespace ? `${namespace}_${functionName}` : functionName;
7954
+ if (!LLM_TOOL_NAME_PATTERN.test(name)) {
7955
+ throw new Error(
7956
+ `Tool name "${name}" (function "${functionName}"${namespace ? ` in namespace "${namespace}"` : ""}) violates the LLM tool name pattern ${LLM_TOOL_NAME_PATTERN} \u2014 rename the function or use a @tool JSDoc override (allowed: a-z A-Z 0-9 '-' '_')`
7957
+ );
7958
+ }
7959
+ return name;
7916
7960
  }
7917
7961
  async function scanTools(rootDir, patterns) {
7918
7962
  const files = await fg4(patterns, {
@@ -7954,6 +7998,7 @@ async function scanTools(rootDir, patterns) {
7954
7998
  // src/agents/scanAgents.ts
7955
7999
  import fg5 from "fast-glob";
7956
8000
  var DEFAULT_AGENT_PATTERNS = ["src/agents/**/handler.ts"];
8001
+ var AGENT_DIR_SEGMENT_PATTERN = /^[a-zA-Z0-9-]+$/;
7957
8002
  function extractAgentNameFromPath(filePath) {
7958
8003
  const normalized = filePath.replace(/\\/g, "/");
7959
8004
  const match = normalized.match(/(?:^|\/)agents\/(.+)\/handler\.ts$/);
@@ -7962,7 +8007,15 @@ function extractAgentNameFromPath(filePath) {
7962
8007
  `Not an agent path: "${filePath}". Expected pattern: src/agents/<name>/handler.ts`
7963
8008
  );
7964
8009
  }
7965
- return match[1].replace(/\//g, ".");
8010
+ const segments = match[1].split("/");
8011
+ for (const segment of segments) {
8012
+ if (!AGENT_DIR_SEGMENT_PATTERN.test(segment)) {
8013
+ throw new Error(
8014
+ `Invalid agent directory segment "${segment}" in "${filePath}": agent directory names allow a-z A-Z 0-9 '-' only ('_' is reserved as the nesting separator, '.' is not allowed \u2014 agent names become LLM tool names "agent-<name>") \u2014 rename the directory`
8015
+ );
8016
+ }
8017
+ }
8018
+ return segments.join("_");
7966
8019
  }
7967
8020
  async function scanAgents(rootDir, patterns) {
7968
8021
  const files = await fg5(patterns, {
@@ -8078,9 +8131,11 @@ async function createProdApp(options) {
8078
8131
  export {
8079
8132
  FaapiError,
8080
8133
  InternalError,
8134
+ LLM_TOOL_NAME_PATTERN,
8081
8135
  MethodNotAllowedError,
8082
8136
  ModuleLoadError,
8083
8137
  RouteNotFoundError,
8138
+ SUB_AGENT_TOOL_PREFIX,
8084
8139
  SchemaExtractionError,
8085
8140
  TASK_PATTERNS,
8086
8141
  ValidationError,
@@ -8125,6 +8180,7 @@ export {
8125
8180
  resolveSubAgents,
8126
8181
  resolveTypeNode,
8127
8182
  scanTasks,
8183
+ subAgentToolName,
8128
8184
  upsertSkill
8129
8185
  };
8130
8186
  //# sourceMappingURL=index.js.map