@faapi/faapi 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.
@@ -1,3 +1,7 @@
1
+ import * as node_http from 'node:http';
2
+ import { Server, IncomingMessage, ServerResponse } from 'node:http';
3
+ import { Socket } from 'node:net';
4
+
1
5
  /**
2
6
  * Tool 的 LLM 可见核心字段
3
7
  *
@@ -130,6 +134,15 @@ interface AgentCore {
130
134
  interface AgentMetadata extends AgentCore {
131
135
  /** 源码相对路径(从 `pathMeta` 透传)——声明文件定位与清单可观测性用 */
132
136
  filePath: string;
137
+ /**
138
+ * 派发入参 schema 声明(handler.ts 顶层 `interface Input` / `type Input` 检测)
139
+ *
140
+ * 声明时为 `'Input'`——[generateAgentArtifacts](../cli/generateAgentArtifacts.md)
141
+ * 据此生成 agent 的 zod.js,`@faapi/agent` 派发该 agent 时用富 JSON Schema 替代
142
+ * 默认单字段 `input` 交接单并在执行前校验;未声明为 `undefined`,派发行为不变
143
+ * (完全向后兼容)。DB-driven skill 无源文件,恒为 `undefined`。
144
+ */
145
+ inputTypeName?: string;
133
146
  }
134
147
  /**
135
148
  * 路径推导的 agent 元数据(由 [scanAgents](../agents/scanAgents.ts) 计算)
@@ -353,67 +366,162 @@ interface LogConfig {
353
366
  }
354
367
 
355
368
  /**
356
- * 隔离执行器签名(taskQueue 按任务 meta.timeoutMs 调用;测试可注入 spy)
369
+ * faapi 中间件(洋葱模型)
370
+ *
371
+ * 单一 async 函数,通过 `await next()` 衔接前置/后置逻辑:
372
+ * - `await next()` 之前的代码:前置处理(鉴权、日志开始计时等)
373
+ * - `await next()` 之后的代码:后置处理(日志输出、响应修改等)
374
+ * - 不调用 `next()` 即拦截请求(如鉴权失败直接返回 Response)
375
+ * - `next()` 返回内层 Response,中间件可选择使用或替换
376
+ * - 返回 `Response`:作为响应返回(可用于拦截或错误处理)
377
+ * - 返回 `void`:使用 `await next()` 返回的内层响应
378
+ *
379
+ * 错误处理用 try/catch 包裹 `await next()`,而非独立的 error 钩子。
380
+ *
381
+ * 执行顺序(洋葱模型):
382
+ * ```
383
+ * mw1.before → mw2.before → handler → mw2.after → mw1.after
384
+ * ```
385
+ *
386
+ * 示例 middlewares.ts:
387
+ * ```ts
388
+ * import type { FaapiMiddleware } from '@faapi/faapi';
389
+ *
390
+ * export default [
391
+ * // 鉴权:不调 next() 即拦截
392
+ * async (ctx, next) => {
393
+ * const token = ctx.headers.get('authorization');
394
+ * if (!token) return new Response('Unauthorized', { status: 401 });
395
+ * ctx.user = await verifyToken(token);
396
+ * await next();
397
+ * },
398
+ * // 日志:before/after 一体,闭包共享状态
399
+ * async (ctx, next) => {
400
+ * const start = Date.now();
401
+ * await next();
402
+ * console.log(`${ctx.method} ${ctx.path} ${Date.now() - start}ms`);
403
+ * },
404
+ * // 错误处理:try/catch 语义
405
+ * async (ctx, next) => {
406
+ * try {
407
+ * await next();
408
+ * } catch (err) {
409
+ * return new Response(JSON.stringify({ error: String(err) }), { status: 500 });
410
+ * }
411
+ * },
412
+ * ] satisfies FaapiMiddleware[];
413
+ * ```
357
414
  */
358
- type TaskWorkerRunner = typeof runTaskInWorker;
359
- interface TaskWorkerOptions {
360
- /** 任务产物模块绝对路径(`<dist>/tasks/<dir>/task.js`) */
361
- taskModulePath: string;
362
- payload: unknown;
363
- /** signal 由执行器构造(abort/terminate 时触发),宿主只传 config 与 job 信息 */
364
- taskCtx: {
365
- config: unknown;
366
- /**
367
- * 产物 resources 目录绝对路径(纯字符串可结构化克隆)——经 workerData
368
- * 传给入口在任务模块求值前播种全局读取根(内部字段,不进业务 taskCtx)
369
- */
370
- resourcesDir?: string;
371
- job: {
372
- id: string;
373
- name: string;
374
- attempt: number;
375
- };
376
- };
377
- /** 单次执行超时(毫秒) */
378
- timeoutMs: number;
415
+ type FaapiMiddleware = (ctx: FaapiContext, next: () => Promise<Response>) => Promise<void | Response>;
416
+
417
+ interface CorsOptions {
418
+ origin?: string | string[] | true;
419
+ methods?: string[];
420
+ allowedHeaders?: string[];
421
+ exposeHeaders?: string[];
422
+ credentials?: boolean;
423
+ maxAge?: number;
424
+ }
425
+ /**
426
+ * 创建 CORS 中间件(洋葱模型)
427
+ *
428
+ * - origin=true: 允许所有来源(反射请求的 Origin)
429
+ * - origin=string: 允许指定来源
430
+ * - origin=string[]: 允许多个来源
431
+ *
432
+ * OPTIONS 预检请求直接返回 204,不调用 next()。
433
+ */
434
+ declare function cors(options?: CorsOptions): FaapiMiddleware;
435
+
436
+ /**
437
+ * 轻量 LLM 补全通道规范类型
438
+ *
439
+ * `LlmComplete` / `LlmCompleteOptions` 是轻量补全通道(`@faapi/agent` 的
440
+ * `createLightComplete` 实现)的接口契约,规范类型由主包持有——`TaskContext.llm` /
441
+ * `TaskQueueDeps` / `LlmChannelStore` 等主包类型需引用,而主包不能反向依赖
442
+ * `@faapi/agent`(依赖方向 agent → 主包 peer)。`@faapi/agent` 实现并 re-export,
443
+ * 业务方统一从 `@faapi/agent` 导入标注。
444
+ *
445
+ * `onFailure` 的 error 参数在此层是 `Error`——具体错误类(`LLMProviderError` /
446
+ * `LLMTimeoutError`)由实现层抛出,业务方 `instanceof` 细分时从 `@faapi/agent`
447
+ * 导入类做窄化。
448
+ *
449
+ * 详见 [llmTypes.md](./llmTypes.md) 与 `@faapi/agent` 的 lightComplete.md。
450
+ */
451
+ /** 轻量补全的调用级选项——传输策略(重试/超时/降级/留痕)按场景声明 */
452
+ interface LlmCompleteOptions {
379
453
  /**
380
- * 取消宽限期(毫秒)——两段式取消第一段发出 abort 信号后等待任务自行退出的
381
- * 最长时间,超时未退出 `terminate()` 硬杀。来自 task meta `graceMs`,
382
- * 未声明用 `KILL_GRACE_MS`(5s);`0` 表示不留宽限期(判定取消即硬杀)
454
+ * 模型字符串 key(与 `agent.run` 的 `options.model` 同一解析规则):
455
+ * llms key(如 `'openai'`)/ `provider/model` 一体化(如 `'openai/gpt-4o'`)/
456
+ * 纯 model 名(在所有 provider 的 `models` 里查找,唯一时命中)
457
+ *
458
+ * 缺省回落 `agent.llms` 第一个 provider 的第一个 model。
383
459
  */
384
- graceMs?: number;
460
+ model?: string;
461
+ /** 可选 system 提示词(前置为 system 消息) */
462
+ system?: string;
463
+ /** 采样温度(0~2) */
464
+ temperature?: number;
465
+ /** 最大生成 token 数 */
466
+ maxTokens?: number;
385
467
  /**
386
- * 注册表快照(纯数据,postMessage 结构化克隆传入,worker 内重建只读视图注入
387
- * taskCtx.registries)——语义层从 `TaskRegistriesView` 生成,缺省为空视图
468
+ * 取消信号(透传到底层 HTTP 请求)
469
+ *
470
+ * abort 时抛 `AgentAbortError`——取消不是故障:不走 fallback、不触发 onFailure。
388
471
  */
389
- registries?: TaskRegistriesSnapshot;
472
+ signal?: AbortSignal;
390
473
  /**
391
- * 任务日志配置(纯数据,postMessage 传入,worker 内联重建日志器注入 taskCtx.log;
392
- * 缺省时 taskCtx.log 为 undefined):scope/fields 与进程内路径一致(`task:<name>`
393
- * + jobId/task/attempt),level 为宿主侧生效的全局级别(worker 侧预过滤,
394
- * 宿主 writeLogEntry 再次过滤;undefined = 宿主未配置阈值——文件管道默认全量,
395
- * worker 侧同样不过滤)
474
+ * 本次调用超时(毫秒)
475
+ *
476
+ * 优先级:本字段 > 目标 provider 的 `LlmConfig.timeoutMs` > 框架默认 60s。
477
+ * 超时抛 `LLMTimeoutError`(计入重试,与 429/5xx/网络错误同策略)。
396
478
  */
397
- log?: {
398
- level?: LogLevel;
399
- scope?: string;
400
- fields?: Record<string, unknown>;
401
- };
402
- /** 外部取消信号(驱动停机超时 abort)——abort 同样触发两段式取消 */
403
- externalSignal?: AbortSignal;
479
+ timeoutMs?: number;
404
480
  /**
405
- * 进度回调:worker 内 `taskCtx.progress(value)` 的值经 `{ type: 'progress' }`
406
- * 消息回传宿主(语义层记入 `TaskJob.progress`);不传则进度消息被忽略
481
+ * 本次调用重试上限(429/5xx/网络错误/超时计入重试)
482
+ *
483
+ * 缺省回落 `LlmConfig.maxRetries`(默认 2,0 关闭)。
407
484
  */
408
- onProgress?: (value: unknown) => void;
485
+ maxRetries?: number;
409
486
  /**
410
- * 日志回调:worker 内 taskCtx.log 的条目经 `{ type: 'log' }` 消息回传宿主,
411
- * 由语义层接 writeLogEntry 走统一管道(自定义 sink 同样覆盖隔离任务);
412
- * 不传则日志条目被忽略。宽限期(取消判定后)到达的条目不采纳(超时判定即终局)
487
+ * 降级值:传输失败(重试耗尽)时返回本值而不抛
488
+ *
489
+ * 「LLM 失败可降级不可静默」——fallback 命中且未声明 `onFailure` 时,
490
+ * 框架 `console.warn` 兜底留痕(错误消息含尝试次数)。
491
+ * 未声明时失败原样抛 `LLMProviderError`。
413
492
  */
414
- onLog?: (entry: LogEntry) => void;
493
+ fallback?: string;
494
+ /**
495
+ * 失败钩子:重试耗尽后触发(留痕/告警/台账等副作用;自身抛错被忽略)
496
+ *
497
+ * - `error` 为 `LLMProviderError`(`instanceof LLMTimeoutError` 细分超时,
498
+ * `error.status` 区分 HTTP 状态——502/504 分型等)
499
+ * - `info.attempts` 为实际发起的 HTTP 尝试次数(含失败尝试,≥1)
500
+ *
501
+ * 声明本钩子后框架不再重复 `console.warn`(钩子即留痕点)。
502
+ * 用户取消(`AgentAbortError`)不触发本钩子。
503
+ */
504
+ onFailure?: (error: Error, info: {
505
+ attempts: number;
506
+ }) => void;
507
+ }
508
+ /**
509
+ * 轻量补全通道(handler `llm` 注入参数 / `taskCtx.llm` 的类型)
510
+ *
511
+ * 由 `@faapi/agent` 插件注册到 `registries.llm`(插件未加载时注入 `undefined`)。
512
+ * 与 agent 循环共享 `agent.llms` 同源 providers,项目零新增配置。
513
+ */
514
+ interface LlmComplete {
515
+ /**
516
+ * 一次性补全:字符串进字符串出
517
+ *
518
+ * @returns assistant 消息 content(恒字符串;轻量通道不发 tools,LLM 不会请求 tool_call)
519
+ * @throws {AgentError} model key 解析失败 / llms 未配置(编程/配置错误,不重试不降级)
520
+ * @throws {LLMProviderError} 传输失败(重试耗尽)且未声明 `fallback`
521
+ * @throws {AgentAbortError} 用户取消(`options.signal` 触发)
522
+ */
523
+ complete(input: string, options?: LlmCompleteOptions): Promise<string>;
415
524
  }
416
- declare function runTaskInWorker(options: TaskWorkerOptions): Promise<unknown>;
417
525
 
418
526
  /**
419
527
  * app 级注册表(方案 A:注册表实例化)
@@ -486,6 +594,20 @@ interface AgentHandleStore {
486
594
  get(ctx: FaapiContext): unknown;
487
595
  clear(): void;
488
596
  }
597
+ /**
598
+ * 轻量 LLM 补全通道 store(由 `@faapi/agent` 插件注册)
599
+ *
600
+ * 与 AgentHandleStore 同形态(app 实例级、随 app 生命周期),差异:channel 与
601
+ * 请求上下文无关——存实例本身而非工厂。handler 的 `llm` 注入参数与任务执行
602
+ * 上下文(进程内路径)读取;插件未加载时为空(注入 `undefined`)。
603
+ */
604
+ interface LlmChannelStore {
605
+ /** 注册 channel(null 清理);二次注册覆盖 */
606
+ register(channel: LlmComplete | null): void;
607
+ /** channel 已注册时返回实例,未注册返回 undefined */
608
+ get(): LlmComplete | undefined;
609
+ clear(): void;
610
+ }
489
611
  /** task 客户端工厂函数(由 createAppBase 注册,返回 TaskClient 门面) */
490
612
  type TaskHandleFactory = (ctx: FaapiContext) => unknown;
491
613
  interface TaskHandleStore {
@@ -503,6 +625,8 @@ interface AppRegistries {
503
625
  task: TaskRegistry;
504
626
  agentHandle: AgentHandleStore;
505
627
  taskHandle: TaskHandleStore;
628
+ /** 轻量 LLM 补全通道(`@faapi/agent` 插件注册;未加载插件时为空) */
629
+ llm: LlmChannelStore;
506
630
  }
507
631
  /**
508
632
  * 任务侧注册表只读视图(AppRegistries 的查询投影)
@@ -521,6 +645,960 @@ declare function createTaskRegistriesView(registries: AppRegistries): TaskRegist
521
645
  /** 创建一套 app 级注册表(`createAppBase` 每次调用创建独立实例) */
522
646
  declare function createAppRegistries(): AppRegistries;
523
647
 
648
+ declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
649
+ type HttpMethod = (typeof HTTP_METHODS)[number];
650
+
651
+ /**
652
+ * 注入器:按参数名匹配,提供 handler 所需的依赖
653
+ *
654
+ * 注入器是 faapi 的依赖注入扩展点,与中间件解耦:
655
+ * - 中间件只管请求流程(鉴权、日志、错误处理)
656
+ * - 注入器只管提供依赖(数据库连接、用户对象等)
657
+ *
658
+ * 注入器可以读取中间件塞进 ctx 的值(如鉴权中间件塞的 ctx.user),
659
+ * 也可以独立提供依赖(如数据库连接池)。
660
+ *
661
+ * 注入器按需执行:只对 handler 声明的参数执行对应的注入器,避免无谓计算。
662
+ *
663
+ * 在 middlewares.ts 中通过命名导出 `injectors` 注册:
664
+ * ```ts
665
+ * import type { InjectorMap } from '@faapi/faapi';
666
+ *
667
+ * export const injectors: InjectorMap = {
668
+ * db: () => getDbConnection(),
669
+ * user: (ctx) => ctx.user, // 取中间件塞的值
670
+ * };
671
+ * ```
672
+ */
673
+ type Injector = (ctx: FaapiContext) => unknown | Promise<unknown>;
674
+ /**
675
+ * 注入器映射表:参数名 → 注入器函数
676
+ *
677
+ * key 必须与 handler 参数名一致,运行时按参数名匹配执行。
678
+ */
679
+ type InjectorMap = Record<string, Injector>;
680
+
681
+ interface RouteRecord {
682
+ method: HttpMethod;
683
+ urlPath: string;
684
+ filePath: string;
685
+ paramNames: string[];
686
+ isDynamic: boolean;
687
+ /** 是否为 catch-all 路由([...slug]) */
688
+ isCatchAll?: boolean;
689
+ /** 中间件文件绝对路径列表(根在前,路由目录在后;按需加载用) */
690
+ middlewarePaths?: string[];
691
+ /** 路由对应的中间件集合(从根到路由目录合并,按需加载后缓存) */
692
+ middlewares?: FaapiMiddleware[];
693
+ /** 路由对应的注入器映射表(从根到路由目录合并,按需加载后缓存) */
694
+ injectors?: InjectorMap;
695
+ }
696
+ /**
697
+ * WebSocket 路由记录
698
+ *
699
+ * 与 HTTP RouteRecord 类似,但不绑定 HTTP 方法(WS 是协议升级,不区分 GET/POST)。
700
+ * 一个 handler.ts 中导出 WS 即生成一条 WS 路由记录。
701
+ */
702
+ interface WsRouteRecord {
703
+ urlPath: string;
704
+ filePath: string;
705
+ paramNames: string[];
706
+ isDynamic: boolean;
707
+ /** 是否为 catch-all 路由([...slug]) */
708
+ isCatchAll?: boolean;
709
+ /** 中间件文件绝对路径列表(根在前,路由目录在后;按需加载用) */
710
+ middlewarePaths?: string[];
711
+ /** 路由对应的中间件集合(握手阶段执行,复用鉴权/CORS/日志;按需加载后缓存) */
712
+ middlewares?: FaapiMiddleware[];
713
+ /** 路由对应的注入器映射表 */
714
+ injectors?: InjectorMap;
715
+ }
716
+ type RouteManifest = RouteRecord[];
717
+ type WsRouteManifest = WsRouteRecord[];
718
+ /**
719
+ * 路由单个参数的 schema 描述
720
+ *
721
+ * 供 @faapi/schema 扩展包消费,通过 MCP 暴露给 LLM。
722
+ */
723
+ interface RouteParamSchema {
724
+ name: string;
725
+ type: string;
726
+ required: boolean;
727
+ }
728
+ /**
729
+ * 路由单个输入源的 schema 描述
730
+ */
731
+ interface RouteInputSchema {
732
+ source: 'query' | 'body' | 'params';
733
+ schemaName: string | null;
734
+ properties: RouteParamSchema[];
735
+ }
736
+ /**
737
+ * 路由响应类型的 schema 描述
738
+ *
739
+ * 由 @faapi/schema 扩展包的 buildRouteSchemas 生成。
740
+ * output 为 null 表示无显式返回类型注解、void/Promise<void>、或解析失败降级。
741
+ */
742
+ interface RouteOutputSchema {
743
+ /** 命名类型名(如 'UserResponse'),内联类型为 null */
744
+ schemaName: string | null;
745
+ /** 顶层属性列表 */
746
+ properties: RouteParamSchema[];
747
+ }
748
+ /**
749
+ * 路由的完整 schema 描述
750
+ *
751
+ * 由 @faapi/schema 扩展包的 buildRouteSchemas 生成。
752
+ * 主包只定义类型契约,逻辑实现在扩展包。
753
+ */
754
+ interface RouteInfo {
755
+ method: string;
756
+ path: string;
757
+ filePath: string;
758
+ isDynamic: boolean;
759
+ inputs: RouteInputSchema[];
760
+ /** 响应类型描述(null 表示无返回类型注解/void/解析失败) */
761
+ output: RouteOutputSchema | null;
762
+ }
763
+
764
+ /** HTTP 请求 handler 类型 */
765
+ type RequestHandler = (req: IncomingMessage, res: ServerResponse) => void;
766
+ /** WebSocket 升级 handler 类型 */
767
+ type UpgradeHandler = (req: IncomingMessage, socket: Socket, head: Buffer) => void;
768
+ /**
769
+ * 插件上下文:插件 setup 函数接收的框架能力
770
+ *
771
+ * 插件通过 ctx 访问路由、服务器实例等,不需要直接依赖框架内部模块。
772
+ *
773
+ * 插件可通过 `wrapHandler` / `wrapUpgradeHandler` 在 server.listen 之前包装请求处理逻辑,
774
+ * 用于集成其他框架(如 Next.js):`/api/*` 走 faapi,其余走被集成框架。
775
+ */
776
+ interface PluginContext {
777
+ /** 项目根目录 */
778
+ rootDir: string;
779
+ /** app 级注册表(tool/agent/skill/agentHandle 实例,随 app 生命周期) */
780
+ registries: AppRegistries;
781
+ /** 当前路由清单(setup 时的快照,reloadRoutes 后不会更新;需最新路由用 getRoutes()) */
782
+ routes: RouteManifest;
783
+ /** 获取最新路由清单(reloadRoutes 后返回更新后的数组) */
784
+ getRoutes: () => RouteManifest;
785
+ /** HTTP 服务器实例(未 listen) */
786
+ server: Server;
787
+ /** 自定义业务配置(faapi.config.ts 中的自定义 key) */
788
+ config: Record<string, unknown>;
789
+ /** 插件选项(来自声明中的 options 字段或元组第二个元素) */
790
+ options?: unknown;
791
+ /**
792
+ * 注册 HTTP handler 包装函数(在 server.listen 之前应用,按注册顺序嵌套)
793
+ *
794
+ * 包装函数接收原始 handler,返回新的 handler。多个包装器按注册顺序嵌套:
795
+ * finalHandler = wrap1(wrap2(originalHandler))
796
+ *
797
+ * 典型场景:集成 Next.js,`/api/*` 走 faapi,其余走 Next.js getRequestHandler。
798
+ */
799
+ wrapHandler?: (fn: (original: RequestHandler) => RequestHandler) => void;
800
+ /**
801
+ * 注册 WS upgrade handler 包装函数(在 server.listen 之前应用,按注册顺序嵌套)
802
+ *
803
+ * 包装函数接收原始 upgrade handler(可能为 undefined,表示 faapi 无 WS 路由),
804
+ * 返回新的 upgrade handler。多个包装器按注册顺序嵌套。
805
+ *
806
+ * 典型场景:faapi WS 路由走 original,其余走 Next.js HMR。
807
+ */
808
+ wrapUpgradeHandler?: (fn: (original: UpgradeHandler | undefined) => UpgradeHandler) => void;
809
+ }
810
+ /**
811
+ * faapi 插件接口
812
+ *
813
+ * 插件是一个对象,包含 name 和 setup 函数。
814
+ * 框架在 server 创建后、listen 之前按声明顺序加载插件,调用 setup(ctx)。
815
+ *
816
+ * 插件可通过 ctx.wrapHandler / ctx.wrapUpgradeHandler 包装请求处理逻辑,
817
+ * 用于集成其他框架(如 Next.js)。
818
+ *
819
+ * ```ts
820
+ * import type { FaapiPlugin, PluginContext } from '@faapi/faapi';
821
+ *
822
+ * export default {
823
+ * name: 'my-plugin',
824
+ * setup(ctx: PluginContext) {
825
+ * console.log(`Plugin loaded, ${ctx.routes.length} routes found`);
826
+ * },
827
+ * } satisfies FaapiPlugin;
828
+ * ```
829
+ */
830
+ interface FaapiPlugin {
831
+ /** 插件名称(用于去重和日志) */
832
+ name: string;
833
+ /** 插件初始化函数,在 server 创建后、listen 之前调用 */
834
+ setup: (ctx: PluginContext) => Promise<void> | void;
835
+ }
836
+ /**
837
+ * 插件声明:用户在 faapi.config.ts 的 plugins 字段中使用
838
+ *
839
+ * 支持三种形式:
840
+ * - 包名字符串:`'@faapi/schema'`
841
+ * - 带选项的元组:`['@faapi/schema', { stdio: true }]`
842
+ * - 完整声明对象:`{ package: '@faapi/schema', enable: true }`
843
+ * - 本地路径:`{ path: './my-plugin' }`
844
+ */
845
+ type PluginDeclaration = string | [string, unknown] | {
846
+ package: string;
847
+ enable?: boolean;
848
+ options?: unknown;
849
+ } | {
850
+ path: string;
851
+ enable?: boolean;
852
+ options?: unknown;
853
+ };
854
+
855
+ interface HelmetOptions {
856
+ contentSecurityPolicy?: string | false;
857
+ xFrameOptions?: 'DENY' | 'SAMEORIGIN' | false;
858
+ xContentTypeOptions?: boolean;
859
+ referrerPolicy?: string | false;
860
+ strictTransportSecurity?: string | false;
861
+ xDnsPrefetchControl?: boolean;
862
+ xDownloadOptions?: boolean;
863
+ xPermittedCrossDomainPolicies?: string | false;
864
+ crossOriginOpenerPolicy?: string | false;
865
+ crossOriginResourcePolicy?: string | false;
866
+ crossOriginEmbedderPolicy?: string | false;
867
+ originAgentCluster?: boolean;
868
+ xPoweredBy?: boolean;
869
+ }
870
+ declare function helmet(options?: HelmetOptions): FaapiMiddleware;
871
+
872
+ interface CompressionOptions {
873
+ /**
874
+ * 最小压缩字节数,body 低于该值不压缩(小 payload 压缩后反而变大)
875
+ * @default 1024
876
+ */
877
+ threshold?: number;
878
+ }
879
+
880
+ interface EtagOptions {
881
+ /**
882
+ * 生成弱 ETag(`W/"<hash>"`)。弱校验器允许压缩等表示差异下的 304 协商,
883
+ * 与 compression 中间件配合正确;强 ETag 需对每个表示(编码)单独生成
884
+ * @default true
885
+ */
886
+ weak?: boolean;
887
+ }
888
+
889
+ interface Http2Options {
890
+ key?: string;
891
+ cert?: string;
892
+ }
893
+
894
+ /**
895
+ * 生命周期钩子
896
+ *
897
+ * 执行时序:onBoot(.listen 调用前)→ server.listen → listen 回调内 onReady → 运行期 onError → 关闭时 onClose
898
+ */
899
+ interface LifecycleHooks {
900
+ /**
901
+ * 服务器 listen 之前调用(启动校验钩子)
902
+ *
903
+ * 时机:`app.listen()` 内、`server.listen` 调用之前——server 已创建但未监听
904
+ * (`server.listening === false`),路由/tool/agent 清单已水合、插件已加载。
905
+ *
906
+ * 适合启动校验(环境变量、下游依赖可达性)、DB 迁移等**失败即不该暴露端口**的逻辑:
907
+ * 钩子抛错 → `listen()` 以原始错误 reject,`server.listen` 不会被调用,端口不暴露。
908
+ *
909
+ * 与 onReady 的差异:onReady 在 listen 回调内执行,失败时端口已开,
910
+ * 存在"接受连接但不服务"的窗口——启动校验请用 onBoot,资源初始化用 onReady。
911
+ */
912
+ onBoot?: (ctx: LifecycleContext) => Promise<void> | void;
913
+ /** 服务器启动后调用(适合初始化数据库连接等) */
914
+ onReady?: (ctx: LifecycleContext) => Promise<void> | void;
915
+ /** 服务器关闭时调用(适合清理资源、优雅关闭) */
916
+ onClose?: (ctx: LifecycleContext) => Promise<void> | void;
917
+ /**
918
+ * 请求错误已被处理为响应后调用(参考 Fastify onError 语义)
919
+ *
920
+ * 时机:handler 抛错 → 全局中间件 try/catch(若有) → 框架内置 formatErrorResponse 兜底
921
+ * → 响应发出后 → onError 触发副作用
922
+ *
923
+ * 职责:日志上报、告警、链路追踪等副作用。**不修改、不替换已生成的响应**。
924
+ * 自身抛错会被捕获并忽略,不影响响应已发送的事实。
925
+ *
926
+ * 与全局错误中间件的区别:
927
+ * - 全局错误中间件:把 error 翻译成 Response(主入口,决定响应内容)
928
+ * - onError:响应发出后的副作用(不能改响应)
929
+ */
930
+ onError?: (error: unknown, ctx: FaapiContext) => Promise<void> | void;
931
+ }
932
+ /**
933
+ * pg-boss 驱动选项(透传给 @faapi/task-pgboss)
934
+ *
935
+ * 主包不依赖 pg-boss,这里只声明常用连接字段的透传形状;完整项见 pg-boss 文档
936
+ * (ConstructorOptions)。
937
+ */
938
+ interface TaskPgBossOptions {
939
+ /** PostgreSQL 连接串(如 `postgres://localhost:5432/app`) */
940
+ connectionString?: string;
941
+ /** 其余 pg-boss ConstructorOptions 字段原样透传 */
942
+ [key: string]: unknown;
943
+ }
944
+ /**
945
+ * BullMQ 驱动选项(透传给 @faapi/task-bullmq)
946
+ */
947
+ interface TaskBullMqOptions {
948
+ /** Redis 连接配置(透传给 Queue / Worker 的 connection) */
949
+ connection?: {
950
+ host?: string;
951
+ port?: number;
952
+ password?: string;
953
+ db?: number;
954
+ [key: string]: unknown;
955
+ };
956
+ /** 队列名前缀(默认 `faapi`——同一 Redis 下多个 faapi 应用隔离用) */
957
+ prefix?: string;
958
+ }
959
+ /**
960
+ * 任务子系统配置
961
+ *
962
+ * 任务队列由持久化驱动承载(内置 memory 驱动已移除):存在任务清单
963
+ * (`src/tasks/` 下的 task.ts)时必须显式配置 `driver`,否则启动报错;
964
+ * 无任务清单的项目不加载驱动(零任务项目无需安装驱动子包)。
965
+ */
966
+ interface TaskConfig {
967
+ /**
968
+ * 队列驱动:'pgboss'(@faapi/task-pgboss,Postgres)| 'bullmq'(@faapi/task-bullmq,Redis)
969
+ *
970
+ * 有任务清单时必填;也可在编程式场景传入自定义 TaskDriver 实例
971
+ */
972
+ driver?: 'pgboss' | 'bullmq';
973
+ /** `driver: 'pgboss'` 时的选项(透传给 @faapi/task-pgboss) */
974
+ pgboss?: TaskPgBossOptions;
975
+ /** `driver: 'bullmq'` 时的选项(透传给 @faapi/task-bullmq) */
976
+ bullmq?: TaskBullMqOptions;
977
+ /**
978
+ * 是否启动任务队列(默认 true)
979
+ *
980
+ * 多实例部署时可在非 worker 节点设 `FAAPI_TASKS_DISABLED=1` 或 `task.enabled: false`,
981
+ * API 节点照常入队(入队能力保留),但不消费执行。
982
+ */
983
+ enabled?: boolean;
984
+ /** 优雅停机时等待在跑任务的最长时间(毫秒,默认 10000;超时 abort) */
985
+ shutdownTimeoutMs?: number;
986
+ /**
987
+ * 任务执行失败/取消钩子——每次 process 抛错后触发(含将重试的失败),
988
+ * `info.willRetry` 按任务 meta.retries 推算、`info.cancelled` 标记框架终止;
989
+ * 用于告警/死信上报等副作用,自身抛错被忽略
990
+ */
991
+ onFailed?: TaskFailedHandler;
992
+ }
993
+ /**
994
+ * 生命周期上下文
995
+ */
996
+ interface LifecycleContext {
997
+ /** 项目根目录 */
998
+ rootDir: string;
999
+ /** 当前路由清单 */
1000
+ routes: RouteManifest;
1001
+ /** 服务器实例(onBoot 钩子触发时已创建但未监听,`listening === false`) */
1002
+ server: node_http.Server;
1003
+ /** app 级注册表——skill 等运行时动态注册路径(`registries.skill.upsert(...)`) */
1004
+ registries: AppRegistries;
1005
+ /** 任务客户端——onBoot/onReady 中投递异步任务(`tasks.enqueue(name, payload)`) */
1006
+ tasks: TaskClient;
1007
+ }
1008
+ /**
1009
+ * 统一响应包装配置
1010
+ *
1011
+ * 配置后,框架自动:
1012
+ * - 成功响应:handler return 非 Response 的值时,用 ok 函数包裹
1013
+ * - 错误响应:ctx.fail() 用 fail 函数包装 body
1014
+ *
1015
+ * 未配置 response 时,使用框架默认实现:
1016
+ * - ok: (data) => ({ data })
1017
+ * - fail: ({ status, code, message }) => 省略的字段不放入 error 对象
1018
+ *
1019
+ * ```ts
1020
+ * import type { FaapiConfig } from '@faapi/faapi';
1021
+ * export default {
1022
+ * response: {
1023
+ * // 自定义成功包装(默认 { data })
1024
+ * ok: (data) => ({ code: 0, data }),
1025
+ * // 自定义错误包装(默认 { error: { message, ...code?, ...status? } })
1026
+ * fail: ({ status, code, message }) => ({ error: { code, message } }),
1027
+ * },
1028
+ * } satisfies FaapiConfig;
1029
+ * ```
1030
+ */
1031
+ interface ResponseConfig {
1032
+ /**
1033
+ * 成功响应包装函数
1034
+ *
1035
+ * handler return 非 Response 的值时调用。
1036
+ * 默认: (data) => ({ data })
1037
+ */
1038
+ ok?: (data: unknown) => unknown;
1039
+ /**
1040
+ * 错误响应包装函数
1041
+ *
1042
+ * ctx.fail() 调用时使用,接收 { status?, code?, message }。
1043
+ * status 和 code 均可能为 undefined(用户调用 ctx.fail 时省略则不传),
1044
+ * 默认实现只把非 undefined 的字段放入 error 对象。
1045
+ */
1046
+ fail?: (error: {
1047
+ status?: number;
1048
+ code?: string;
1049
+ message: string;
1050
+ }) => unknown;
1051
+ }
1052
+ /**
1053
+ * model 级配置(Phase 3.5)
1054
+ *
1055
+ * 挂在 provider 下的单个 model 配置,model 特定字段透传给 LLM API
1056
+ * (覆盖 provider 级同名字段)。空对象 `{}` 表示用 provider 级默认。
1057
+ *
1058
+ * ```ts
1059
+ * models: {
1060
+ * 'gpt-4o': {}, // 用 provider 级默认
1061
+ * 'gpt-4o-mini': { temperature: 0.5 }, // 覆盖 temperature
1062
+ * }
1063
+ * ```
1064
+ */
1065
+ interface LlmModelConfig {
1066
+ [key: string]: unknown;
1067
+ }
1068
+ /**
1069
+ * LLM provider 配置(Phase 2.4,Phase 3.5 改为嵌套级联结构)
1070
+ *
1071
+ * 嵌套级联:provider 在外层,model 在 `models` 下挂多个。
1072
+ * provider 级字段(`apiKey` / `baseURL`)共享给所有 model;
1073
+ * model 级字段在 `models[modelName]` 里覆盖 provider 级同名字段。
1074
+ *
1075
+ * `config.agent.llms` 的 key 是 provider 名(如 `'openai'` / `'anthropic'`)。
1076
+ * 无全局默认 provider——每次 `agent.run/stream` 调用通过 `options.model`(llms key /
1077
+ * `provider/model` / 纯 model 名)或 `options.provider`(外部 provider)显式指定。
1078
+ *
1079
+ * 由 Phase 3.2 的 `@faapi/agent` 插件读取,调 `createProvider` 创建实例存 Map。
1080
+ *
1081
+ * ```ts
1082
+ * llms: {
1083
+ * openai: {
1084
+ * provider: 'openai',
1085
+ * apiKey: process.env.OPENAI_API_KEY,
1086
+ * baseURL: 'https://api.openai.com/v1',
1087
+ * models: { 'gpt-4o': {}, 'gpt-4o-mini': { temperature: 0.5 } },
1088
+ * },
1089
+ * anthropic: {
1090
+ * provider: 'anthropic',
1091
+ * apiKey: process.env.ANTHROPIC_API_KEY,
1092
+ * models: { 'claude-3-5-sonnet': {} },
1093
+ * },
1094
+ * }
1095
+ * ```
1096
+ */
1097
+ interface LlmConfig {
1098
+ /**
1099
+ * LLM 提供方标识(如 'openai' / 'anthropic')
1100
+ *
1101
+ * Phase 3.2 的 provider 模块按此值选择对应的 LLM 适配器。
1102
+ */
1103
+ provider: string;
1104
+ /**
1105
+ * API key(从 `process.env` 读取,避免硬编码)
1106
+ *
1107
+ * 如 `process.env.OPENAI_API_KEY`。
1108
+ */
1109
+ apiKey?: string;
1110
+ /**
1111
+ * API 基础 URL(可选,用于 OpenAI 兼容 API 如 Azure OpenAI / 中转服务)
1112
+ *
1113
+ * 未设置时用 provider 对应的官方默认值(如 'https://api.openai.com/v1')。
1114
+ */
1115
+ baseURL?: string;
1116
+ /**
1117
+ * 该 provider 下挂的 model 列表(key 是 model 名)
1118
+ *
1119
+ * handler 通过 `agent.run(input, { model: 'gpt-4o' })` 切换 model,
1120
+ * 框架按 model 名在所有 provider 的 `models` 里查找定位 provider(详见
1121
+ * [agentHandle](../../agent/src/agentHandle.md) 的 Run-level 覆盖优先级表)。
1122
+ * model 级字段(如 `temperature`)覆盖 provider 级同名字段。
1123
+ */
1124
+ models: Record<string, LlmModelConfig>;
1125
+ /**
1126
+ * LLM 请求超时(毫秒,可选——未设置时无超时)
1127
+ *
1128
+ * provider 层用 `AbortSignal.timeout` 实现,与 run-level 的 `signal` 组合生效。
1129
+ * 超时触发抛 `LLMProviderError`(message 含 timed out),计入重试(429/5xx/网络错误同策略)。
1130
+ */
1131
+ timeoutMs?: number;
1132
+ /**
1133
+ * 429 / 5xx / 网络错误的最大重试次数(默认 2,设 0 关闭重试)
1134
+ *
1135
+ * 退避策略:优先尊重响应的 `Retry-After` 头(秒,封顶 30s),否则指数退避
1136
+ * 500ms * 2^attempt。4xx 其他状态(400/401 等)是确定性错误,不重试。
1137
+ * 流式请求仅在「连接建立前」重试,流开始输出后中断不重试。
1138
+ */
1139
+ maxRetries?: number;
1140
+ /**
1141
+ * 其他透传参数(provider 级,如 temperature / top_p / max_tokens)
1142
+ *
1143
+ * 这些字段原样传给 LLM API,由 provider 适配器处理。
1144
+ * model 级 `models[modelName]` 的同名字段优先。
1145
+ */
1146
+ [key: string]: unknown;
1147
+ }
1148
+ /**
1149
+ * agent 子系统全局配置(Phase 2.4,Phase 3.5 LLM 配置改为嵌套级联)
1150
+ *
1151
+ * 提供 agent 子系统的全局配置,所有字段均可选。
1152
+ * 无全局默认 agent / 默认 provider——`agent.run/stream` 每次调用必须显式传
1153
+ * `options.agent`(agent 名)和 `options.model` / `options.provider`(LLM 定位);
1154
+ * agent 自身 `config.maxTurns` / `config.model` 优先于全局配置,其中 `config.model`
1155
+ * 在调用未传 `options.model` 时作为缺省 key 参与 llms 解析。
1156
+ *
1157
+ * ```ts
1158
+ * import type { FaapiConfig } from '@faapi/faapi';
1159
+ * export default {
1160
+ * agent: {
1161
+ * llms: {
1162
+ * openai: {
1163
+ * provider: 'openai',
1164
+ * apiKey: process.env.OPENAI_API_KEY,
1165
+ * models: { 'gpt-4o': {}, 'gpt-4o-mini': { temperature: 0.5 } },
1166
+ * },
1167
+ * },
1168
+ * maxTurns: 10,
1169
+ * maxAgentDepth: 3,
1170
+ * },
1171
+ * } satisfies FaapiConfig;
1172
+ * ```
1173
+ *
1174
+ * 详见 `src/config/configTypes.md` agent 配置块章节。
1175
+ */
1176
+ interface AgentConfig {
1177
+ /**
1178
+ * LLM provider 配置映射(Phase 3.5 改为嵌套级联结构,key 是 provider 名)
1179
+ *
1180
+ * 值是 [LlmConfig](含 `models`)。plugin setup 时遍历每个 LlmConfig 调
1181
+ * `createProvider` 创建实例存 Map,handler 通过 `agent.run(input, { model })`
1182
+ * 切换 provider + model(详见 [agentHandle](../../agent/src/agentHandle.md))。
1183
+ *
1184
+ * 可选——未设置时插件仍注册 agent handle 工厂(外部 provider 模式),
1185
+ * `agent.run/stream` 需通过 `options.provider` 传入外部 provider 才能调用 LLM。
1186
+ */
1187
+ llms?: Record<string, LlmConfig>;
1188
+ /**
1189
+ * 默认最大对话轮数(覆盖 agent 自身 `config.maxTurns`,agent 自身配置优先)
1190
+ *
1191
+ * Phase 3.3 的 reactLoop 使用此值作为递归深度防护。
1192
+ */
1193
+ maxTurns?: number;
1194
+ /**
1195
+ * agent 调用 agent 的最大递归深度(防护无限递归,Phase 3.3 reactLoop 使用)
1196
+ *
1197
+ * 默认值由 Phase 3.x 的 @faapi/agent 插件定义(如 3)。
1198
+ */
1199
+ maxAgentDepth?: number;
1200
+ /**
1201
+ * 启用 tracing 的全局默认值(默认 false——opt-in,不开启零开销)
1202
+ *
1203
+ * 开启时 `agent.run()` / `agent.stream()` 返回的 `result.trace` /
1204
+ * `chunk.traceEvent` 填充结构化调用明细(按轮次组织的 LLM 调用、tool 调用、
1205
+ * sub-agent 嵌套调用事件,含 timing 与 token 用量)。
1206
+ *
1207
+ * 三层覆盖优先级:`AgentRunOptions.enableTracing` > agent 自身配置 >
1208
+ * 此全局配置 > 默认 `false`。
1209
+ *
1210
+ * tracing 采集每轮 LLM 消息快照与 tool 明细,有真实内存/CPU 开销——
1211
+ * 需要观测的端点显式开启:
1212
+ *
1213
+ * ```ts
1214
+ * import type { FaapiConfig } from '@faapi/faapi';
1215
+ * export default {
1216
+ * agent: {
1217
+ * enableTracing: true, // 全局开启;单次调用可用 run(input, { enableTracing: true }) 覆盖
1218
+ * llms: { openai: { provider: 'openai', apiKey: '...', models: { 'gpt-4o': {} } } },
1219
+ * },
1220
+ * } satisfies FaapiConfig;
1221
+ * ```
1222
+ *
1223
+ * 详见 `@faapi/agent` 的 [trace](../../agent/src/trace.md) 文档。
1224
+ */
1225
+ enableTracing?: boolean;
1226
+ /**
1227
+ * 发送给 LLM 的历史 token 预算(近似估算,未设置 = 不裁剪)
1228
+ *
1229
+ * 多轮 tool 循环中对话历史只增不减,大 tool 结果会撑爆模型上下文窗口导致
1230
+ * 下一轮 400。超预算时从最旧的轮组开始裁剪(system 与初始 user 保留、
1231
+ * tool 配对不拆散),只作用于发给 LLM 的消息副本。详见 @faapi/agent 的
1232
+ * reactLoop 文档历史裁剪章节。
1233
+ */
1234
+ maxHistoryTokens?: number;
1235
+ /**
1236
+ * 执行守卫(authHooks,见 @faapi/agent 的 authHooks 文档)
1237
+ *
1238
+ * 每次 tool / sub-agent 执行前调用(`agent-x` 派发名称为 sub-agent 递归)。
1239
+ * 三种返回:`void` 放行;`{ error }` 拒绝(不执行,error 回传 LLM 调整策略);
1240
+ * `{ args }` 改写后放行(多租户场景强制注入可信值,不信 LLM 传入的标识参数)。
1241
+ *
1242
+ * ctx 为请求上下文透传(`Partial<FaapiContext>`,框架零读取):HTTP 请求是完整
1243
+ * ctx,任务内组装 Agent 时业务方显式传窄对象(如鉴权硬闸需要的
1244
+ * `{ currentUserId }`),编程式直调为 undefined。
1245
+ *
1246
+ * 典型用法:中间件解析 `ctx.workspace` 后在此校验/强制改写 `args.workspaceId`。
1247
+ */
1248
+ beforeToolCall?: (name: string, args: Record<string, unknown>, ctx: Partial<FaapiContext> | undefined) => void | {
1249
+ error: string;
1250
+ } | {
1251
+ args: Record<string, unknown>;
1252
+ };
1253
+ /**
1254
+ * 审计钩子(authHooks):tool / sub-agent 成功返回后调用,返回值忽略。
1255
+ * 用于日志/审计/计量;异常路径不调用。
1256
+ */
1257
+ afterToolCall?: (name: string, args: Record<string, unknown>, result: unknown, ctx: Partial<FaapiContext> | undefined) => void;
1258
+ /**
1259
+ * 可见性过滤(authHooks):LLM 可见 tools 清单组装完成后调用,
1260
+ * 返回过滤后的数组(含 agent-as-tool 项)。每次 agent.run / stream 生效——
1261
+ * 无权 tool 不进 LLM 视野,比执行时拒绝省一轮 LLM 调用。
1262
+ *
1263
+ * tools 为 OpenAI chat completions 规范形(`type: 'function'` +
1264
+ * `function: { name, description?, parameters? }`),按 `tool.function.name` 过滤。
1265
+ */
1266
+ filterTools?: (tools: Array<{
1267
+ type: 'function';
1268
+ function: {
1269
+ name: string;
1270
+ description?: string;
1271
+ parameters?: Record<string, unknown>;
1272
+ };
1273
+ }>, ctx: Partial<FaapiContext> | undefined) => Array<{
1274
+ type: 'function';
1275
+ function: {
1276
+ name: string;
1277
+ description?: string;
1278
+ parameters?: Record<string, unknown>;
1279
+ };
1280
+ }>;
1281
+ }
1282
+ /**
1283
+ * faapi 配置文件类型
1284
+ *
1285
+ * 在项目根目录创建 faapi.config.ts:
1286
+ * ```ts
1287
+ * import type { FaapiConfig } from '@faapi/faapi';
1288
+ * export default {
1289
+ * cors: { origin: '*' },
1290
+ * } satisfies FaapiConfig;
1291
+ * ```
1292
+ *
1293
+ * 自定义业务配置(任意 key):
1294
+ * ```ts
1295
+ * import type { FaapiConfig } from '@faapi/faapi';
1296
+ * export default {
1297
+ * cors: { origin: '*' },
1298
+ * // 通过 process.env.XXX 读取 .env 文件加载的环境变量
1299
+ * db: { host: process.env.DB_HOST ?? 'localhost', port: 5432 },
1300
+ * } satisfies FaapiConfig;
1301
+ * ```
1302
+ *
1303
+ * 多环境差异通过 `.env` 系列文件实现(见 `loadEnv`):
1304
+ * - `.env` / `.env.local` / `.env.{env}` / `.env.{env}.local`
1305
+ * - 环境由 `NODE_ENV > 'development'` 决定
1306
+ *
1307
+ * 框架元信息通过环境变量配置(不放在 config 内):
1308
+ * - `PORT`:服务端口,默认 3000
1309
+ * - `FAAPI_DIST`:产物输出目录,dev 固定为 `.faapi`(不可修改),prod 默认为 `dist`(可通过 `--dist` 修改)
1310
+ */
1311
+ interface FaapiConfig {
1312
+ /** CORS 配置,false 禁用 */
1313
+ cors?: CorsOptions | boolean;
1314
+ /** 生命周期钩子 */
1315
+ lifecycle?: LifecycleHooks;
1316
+ /** 安全头配置,false 禁用 */
1317
+ helmet?: HelmetOptions | boolean;
1318
+ /**
1319
+ * 响应压缩(gzip/deflate/br 协商),默认关闭。
1320
+ * Vary: Accept-Encoding 自动附加;SSE/流式响应跳过,详见 middleware/compression.md
1321
+ */
1322
+ compression?: CompressionOptions | boolean;
1323
+ /**
1324
+ * ETag/304 条件请求协商(GET/HEAD 2xx 弱 ETag),默认关闭。
1325
+ * handler 显式 ctx.setETag() 时不覆盖,详见 middleware/etag.md
1326
+ */
1327
+ etag?: EtagOptions | boolean;
1328
+ /** 请求体大小限制(字节),默认 10MB(10 * 1024 * 1024) */
1329
+ bodyLimit?: number;
1330
+ /**
1331
+ * 日志全局配置——唯一日志配置入口,业务日志与请求日志同管道(egg 模型,
1332
+ * 详见 logger/logger.md;`config.logger` 独立配置已废除)
1333
+ *
1334
+ * - `false`:全部静默(含 error 与请求日志,测试降噪)
1335
+ * - `true` / 缺省:console 输出(consoleLevel 默认 'info'),管道不过滤
1336
+ * - `LogConfig`:`dir` egg 风格文件输出(app.log 全量 + error.log 仅 error,
1337
+ * splitByLevel 按级别四文件)或 `sink` 自定义管道接管(接 pino/winston),
1338
+ * 两者互斥;`level` 管道阈值(不配不过滤)、`consoleLevel` console 出口阈值
1339
+ * (不配 'info',false 关 console);请求日志无条件并入(`accessLog: false` 关闭)
1340
+ *
1341
+ * 生效范围:`ctx.log` / 参数注入 `log` / `createLogger` / `taskCtx.log` /
1342
+ * 请求日志中间件。日志全局配置是进程级资源,多 app 同进程时后启动覆盖先启动。
1343
+ *
1344
+ * ```ts
1345
+ * export default {
1346
+ * log: {
1347
+ * // 方式一:egg 风格文件输出(请求日志自动并入)
1348
+ * dir: 'logs',
1349
+ * // 方式二:自定义管道接管
1350
+ * // sink: (entry) => pinoLogger[entry.level]({ scope: entry.scope, ...entry.fields }, entry.message),
1351
+ * },
1352
+ * } satisfies FaapiConfig;
1353
+ * ```
1354
+ */
1355
+ log?: LogConfig | boolean;
1356
+ /** HTTP/2 配置,false 禁用(默认 http/1.1) */
1357
+ http2?: Http2Options | boolean;
1358
+ /**
1359
+ * 是否信任反向代理头(X-Forwarded-For),默认 false
1360
+ *
1361
+ * - `true`:`ctx.ip` 取 `x-forwarded-for` 第一个 IP——部署在 nginx/CDN 等受信任
1362
+ * 反向代理之后时开启
1363
+ * - `false`(默认):直取 socket 地址。客户端直连时 XFF 可被任意伪造,
1364
+ * 安全默认不信任(同时影响 HTTP 与 WS 握手的 `ctx.ip`)
1365
+ */
1366
+ trustedProxy?: boolean;
1367
+ /**
1368
+ * 统一响应包装配置
1369
+ *
1370
+ * 配置后,框架自动包裹 handler 返回值:
1371
+ * - 成功响应:handler return 非 Response → 用 ok 函数包裹(默认 `{ data }`)
1372
+ * - 错误响应:ctx.fail() 用 fail 函数包装(默认 `{ error: { message, ...code? } }`)
1373
+ *
1374
+ * 未配置 response 时,使用框架默认实现(见 ResponseConfig)。
1375
+ * 配置 response 后,ok/fail 各字段均可选,按需覆盖。
1376
+ *
1377
+ * ```ts
1378
+ * import type { FaapiConfig } from '@faapi/faapi';
1379
+ * export default {
1380
+ * response: {
1381
+ * ok: (data) => ({ code: 0, data }),
1382
+ * fail: ({ status, code, message }) => ({ error: { code, message } }),
1383
+ * },
1384
+ * } satisfies FaapiConfig;
1385
+ * ```
1386
+ *
1387
+ * 详见 `src/config/configTypes.md` 统一响应包装章节。
1388
+ */
1389
+ response?: ResponseConfig;
1390
+ /**
1391
+ * 全局中间件:对所有路由(HTTP + WebSocket 握手)生效
1392
+ *
1393
+ * 执行顺序:全局中间件在最外层,目录中间件在内层,handler 最内层。
1394
+ * 全局中间件拦截(返回 Response)则目录中间件和 handler 不执行。
1395
+ * 全局中间件塞入 ctx 的值,目录中间件和 handler 可读取。
1396
+ *
1397
+ * 与 CORS 的关系:CORS 由 `cors` 字段配置,全局中间件在 CORS 之后执行。
1398
+ *
1399
+ * ```ts
1400
+ * import type { FaapiConfig, FaapiMiddleware } from '@faapi/faapi';
1401
+ *
1402
+ * const requestId: FaapiMiddleware = async (ctx, next) => {
1403
+ * ctx.requestId = crypto.randomUUID();
1404
+ * await next();
1405
+ * };
1406
+ *
1407
+ * export default {
1408
+ * middlewares: [requestId],
1409
+ * } satisfies FaapiConfig;
1410
+ * ```
1411
+ *
1412
+ * 详见 `src/middleware/README.md` 全局中间件章节。
1413
+ */
1414
+ middlewares?: FaapiMiddleware[];
1415
+ /**
1416
+ * 全局注入器:对所有路由的 handler 参数注入生效
1417
+ *
1418
+ * 合并规则:`{ ...全局注入器, ...目录注入器 }`,目录注入器覆盖全局同名。
1419
+ * 全局注入器独立于中间件链,仅提供依赖(db、redis 等),不参与请求流程。
1420
+ *
1421
+ * ```ts
1422
+ * import type { FaapiConfig, InjectorMap } from '@faapi/faapi';
1423
+ *
1424
+ * export default {
1425
+ * injectors: {
1426
+ * db: () => getDbConnection(),
1427
+ * redis: () => getRedis(),
1428
+ * },
1429
+ * } satisfies FaapiConfig;
1430
+ * ```
1431
+ *
1432
+ * 详见 `src/middleware/README.md` 全局注入器章节。
1433
+ */
1434
+ injectors?: InjectorMap;
1435
+ /**
1436
+ * 插件:应用级扩展,在 server 启动后、onReady 之前按声明顺序加载
1437
+ *
1438
+ * 与中间件的区别:中间件拦截每个请求,插件在启动时初始化(如启动后台服务、注册协议等)
1439
+ *
1440
+ * ```ts
1441
+ * import type { FaapiConfig } from '@faapi/faapi';
1442
+ * export default {
1443
+ * plugins: [
1444
+ * '@faapi/schema', // 包名
1445
+ * ['@faapi/schema', { stdio: true }], // 带选项
1446
+ * { package: '@faapi/schema', enable: true }, // 完整声明
1447
+ * { path: './my-plugin' }, // 本地路径
1448
+ * ],
1449
+ * } satisfies FaapiConfig;
1450
+ * ```
1451
+ */
1452
+ plugins?: PluginDeclaration[];
1453
+ /**
1454
+ * agent 子系统全局配置(Phase 2.4)
1455
+ *
1456
+ * 提供 agent 子系统的全局配置:LLM 提供方、最大对话轮数、
1457
+ * agent 调用 agent 的最大递归深度。
1458
+ *
1459
+ * 无全局默认 agent / 默认 provider——`agent.run/stream` 每次调用显式传
1460
+ * `options.agent` + `options.model` / `options.provider`。
1461
+ * agent 自身 `config.maxTurns` / `config.model` 优先于全局配置。
1462
+ * tool 引用列表只在每个 agent 自身的 `config.tools` 里声明(无全局共享 defaultTools)。
1463
+ *
1464
+ * ```ts
1465
+ * import type { FaapiConfig } from '@faapi/faapi';
1466
+ * export default {
1467
+ * agent: {
1468
+ * llms: {
1469
+ * openai: {
1470
+ * provider: 'openai',
1471
+ * apiKey: process.env.OPENAI_API_KEY,
1472
+ * models: { 'gpt-4o': {} },
1473
+ * },
1474
+ * },
1475
+ * maxTurns: 10,
1476
+ * maxAgentDepth: 3,
1477
+ * },
1478
+ * } satisfies FaapiConfig;
1479
+ * ```
1480
+ *
1481
+ * 详见 `src/config/configTypes.md` agent 配置块章节。
1482
+ */
1483
+ agent?: AgentConfig;
1484
+ /**
1485
+ * 任务子系统全局配置(队列驱动、启停、停机超时)
1486
+ *
1487
+ * ```ts
1488
+ * import type { FaapiConfig } from '@faapi/faapi';
1489
+ * export default {
1490
+ * task: { enabled: true, shutdownTimeoutMs: 10_000 },
1491
+ * } satisfies FaapiConfig;
1492
+ * ```
1493
+ *
1494
+ * 详见 `src/task/README.md`。
1495
+ */
1496
+ task?: TaskConfig;
1497
+ /**
1498
+ * 扩展 ctx:在每次请求创建上下文后调用,可挂载自定义方法(如 ctx.xml、ctx.stream)
1499
+ *
1500
+ * 类型增强:用户通过 `declare module '@faapi/faapi'` 扩展 FaapiContext 接口获得类型提示
1501
+ *
1502
+ * ```ts
1503
+ * // faapi.config.ts
1504
+ * declare module '@faapi/faapi' {
1505
+ * interface FaapiContext {
1506
+ * xml(data: string): Response;
1507
+ * }
1508
+ * }
1509
+ * export default {
1510
+ * extendContext(ctx) {
1511
+ * ctx.xml = (data) => new Response(data, { headers: { 'Content-Type': 'application/xml' } });
1512
+ * },
1513
+ * } satisfies FaapiConfig;
1514
+ * ```
1515
+ */
1516
+ extendContext?: (ctx: FaapiContext) => void;
1517
+ /**
1518
+ * 自定义业务配置(任意 key)
1519
+ *
1520
+ * 用户可以在这里放数据库连接、Redis 配置等
1521
+ * 通过 ctx.config 访问
1522
+ *
1523
+ * ```ts
1524
+ * export default {
1525
+ * db: { host: 'localhost', port: 5432 },
1526
+ * redis: { host: '127.0.0.1', port: 6379 },
1527
+ * } satisfies FaapiConfig;
1528
+ * ```
1529
+ */
1530
+ [key: string]: unknown;
1531
+ }
1532
+
1533
+ /**
1534
+ * 隔离执行器签名(taskQueue 按任务 meta.timeoutMs 调用;测试可注入 spy)
1535
+ */
1536
+ type TaskWorkerRunner = typeof runTaskInWorker;
1537
+ interface TaskWorkerOptions {
1538
+ /** 任务产物模块绝对路径(`<dist>/tasks/<dir>/task.js`) */
1539
+ taskModulePath: string;
1540
+ payload: unknown;
1541
+ /** signal 由执行器构造(abort/terminate 时触发),宿主只传 config 与 job 信息 */
1542
+ taskCtx: {
1543
+ config: unknown;
1544
+ /**
1545
+ * 产物 resources 目录绝对路径(纯字符串可结构化克隆)——经 workerData
1546
+ * 传给入口在任务模块求值前播种全局读取根(内部字段,不进业务 taskCtx)
1547
+ */
1548
+ resourcesDir?: string;
1549
+ job: {
1550
+ id: string;
1551
+ name: string;
1552
+ attempt: number;
1553
+ };
1554
+ };
1555
+ /** 单次执行超时(毫秒) */
1556
+ timeoutMs: number;
1557
+ /**
1558
+ * 取消宽限期(毫秒)——两段式取消第一段发出 abort 信号后等待任务自行退出的
1559
+ * 最长时间,超时未退出 `terminate()` 硬杀。来自 task meta `graceMs`,
1560
+ * 未声明用 `KILL_GRACE_MS`(5s);`0` 表示不留宽限期(判定取消即硬杀)
1561
+ */
1562
+ graceMs?: number;
1563
+ /**
1564
+ * 注册表快照(纯数据,postMessage 结构化克隆传入,worker 内重建只读视图注入
1565
+ * taskCtx.registries)——语义层从 `TaskRegistriesView` 生成,缺省为空视图
1566
+ */
1567
+ registries?: TaskRegistriesSnapshot;
1568
+ /**
1569
+ * `agent.llms` 纯数据快照(postMessage 结构化克隆传入,worker 内动态加载
1570
+ * `@faapi/agent` 重建轻量补全通道注入 taskCtx.llm)——缺省不传,worker 内
1571
+ * taskCtx.llm 为 undefined
1572
+ */
1573
+ llms?: Record<string, LlmConfig>;
1574
+ /**
1575
+ * 任务日志配置(纯数据,postMessage 传入,worker 内联重建日志器注入 taskCtx.log;
1576
+ * 缺省时 taskCtx.log 为 undefined):scope/fields 与进程内路径一致(`task:<name>`
1577
+ * + jobId/task/attempt),level 为宿主侧生效的全局级别(worker 侧预过滤,
1578
+ * 宿主 writeLogEntry 再次过滤;undefined = 宿主未配置阈值——文件管道默认全量,
1579
+ * worker 侧同样不过滤)
1580
+ */
1581
+ log?: {
1582
+ level?: LogLevel;
1583
+ scope?: string;
1584
+ fields?: Record<string, unknown>;
1585
+ };
1586
+ /** 外部取消信号(驱动停机超时 abort)——abort 同样触发两段式取消 */
1587
+ externalSignal?: AbortSignal;
1588
+ /**
1589
+ * 进度回调:worker 内 `taskCtx.progress(value)` 的值经 `{ type: 'progress' }`
1590
+ * 消息回传宿主(语义层记入 `TaskJob.progress`);不传则进度消息被忽略
1591
+ */
1592
+ onProgress?: (value: unknown) => void;
1593
+ /**
1594
+ * 日志回调:worker 内 taskCtx.log 的条目经 `{ type: 'log' }` 消息回传宿主,
1595
+ * 由语义层接 writeLogEntry 走统一管道(自定义 sink 同样覆盖隔离任务);
1596
+ * 不传则日志条目被忽略。宽限期(取消判定后)到达的条目不采纳(超时判定即终局)
1597
+ */
1598
+ onLog?: (entry: LogEntry) => void;
1599
+ }
1600
+ declare function runTaskInWorker(options: TaskWorkerOptions): Promise<unknown>;
1601
+
524
1602
  /**
525
1603
  * 任务元信息(业务方在 task.ts 中 `export const task = {...}` 声明)
526
1604
  *
@@ -654,6 +1732,16 @@ interface TaskContext {
654
1732
  * 隔离执行时条目经 postMessage 回传宿主输出,fields 需可结构化克隆。
655
1733
  */
656
1734
  log?: Logger;
1735
+ /**
1736
+ * 轻量 LLM 补全通道(可选字段;`@faapi/agent` 插件加载且 `agent.llms` 可解析时注入)
1737
+ *
1738
+ * 进程内执行为 `registries.llm` 的活引用(与 agent 循环共享 providers 单例);
1739
+ * 隔离执行为 worker 内按 `agent.llms` 纯数据快照重建的实例(`@faapi/agent`
1740
+ * 不可解析时为 `undefined`,warn 留痕不中断执行)。
1741
+ * 一次性补全(分类/蒸馏/摘要等)用此通道,不必在任务内组装 agent;
1742
+ * 工具循环场景仍走 registries.agent 组装 Agent。详见 `@faapi/agent` 的 lightComplete.md。
1743
+ */
1744
+ llm?: LlmComplete;
657
1745
  }
658
1746
  /**
659
1747
  * 任务模块形态(task.ts 编译产物中与执行相关的导出)
@@ -738,6 +1826,17 @@ interface TaskQueueDeps {
738
1826
  * TaskContext.registries;缺省为空视图(直接构造队列的测试/嵌入场景)
739
1827
  */
740
1828
  registries?: TaskRegistriesView;
1829
+ /**
1830
+ * 轻量 LLM 补全通道 store(AppRegistries.llm)——进程内执行路径在任务执行时刻
1831
+ * 惰性读取(插件晚于队列构造注册,构造期快照会漏);缺省 taskCtx.llm 恒 undefined
1832
+ */
1833
+ llm?: LlmChannelStore;
1834
+ /**
1835
+ * `agent.llms` 纯数据快照——隔离执行路径经 postMessage 传入 worker,worker 内
1836
+ * 动态加载 `@faapi/agent` 重建补全通道(纯数据可结构化克隆)。缺省不传入
1837
+ * (worker 内 taskCtx.llm 为 undefined)
1838
+ */
1839
+ llms?: Record<string, LlmConfig>;
741
1840
  /**
742
1841
  * 队列驱动(必填):`loadTaskDriver` 解析结果(pgboss/bullmq 子包驱动)
743
1842
  * 或自定义 TaskDriver 实例;无任务清单时由 createAppBase 传入 idleTaskDriver
@@ -1159,4 +2258,4 @@ interface FaapiContext {
1159
2258
  deleteCookie(name: string): void;
1160
2259
  }
1161
2260
 
1162
- export { type AppRegistries as A, type TaskDriverProcess as B, type CreateLoggerOptions as C, type TaskDriverRecord as D, type TaskFailedInfo as E, type FaapiContext as F, type TaskJob as G, type TaskJobStatus as H, type TaskMetadata as I, type TaskModule as J, type TaskRegistriesSnapshot as K, type LogConfig as L, type TaskRegistriesView as M, type ToolCore as N, type ToolPathMeta as O, type ToolRegistry as P, createAppRegistries as Q, createTaskRegistriesView as R, type SkillRegistry as S, type TaskClient as T, createTaskRegistry as U, type TaskFailedHandler as a, type TaskQueueDeps as b, type TaskQueue as c, type TaskDriver as d, type TaskRegistry as e, type TaskManifest as f, type ToolMetadata as g, type AgentCore as h, type AgentMetadata as i, type Logger as j, type AgentHandleFactory as k, type AgentHandleStore as l, type AgentPathMeta as m, type AgentRegistry as n, type AgentToolDescriptor as o, type FaapiContextConfig as p, type FaapiTaskMeta as q, type FailOptions as r, type LogEntry as s, type LogLevel as t, type LogSink as u, type SseEvent as v, type SseOptions as w, type SseWriter as x, type TaskContext as y, type TaskDriverJob as z };
2261
+ export { type SseWriter as $, type AgentCore as A, type LlmComplete as B, type CreateLoggerOptions as C, type LlmCompleteOptions as D, type LlmConfig as E, type FaapiMiddleware as F, type LlmModelConfig as G, type HelmetOptions as H, type Injector as I, type LogEntry as J, type LogLevel as K, type LogConfig as L, type LogSink as M, type PluginDeclaration as N, type RequestHandler as O, type PluginContext as P, type ResponseConfig as Q, type RouteManifest as R, type RouteInfo as S, type TaskQueueDeps as T, type RouteInputSchema as U, type RouteOutputSchema as V, type WsRouteManifest as W, type RouteParamSchema as X, type SkillRegistry as Y, type SseEvent as Z, type SseOptions as _, type TaskQueue as a, type TaskBullMqOptions as a0, type TaskConfig as a1, type TaskContext as a2, type TaskDriverJob as a3, type TaskDriverProcess as a4, type TaskDriverRecord as a5, type TaskFailedHandler as a6, type TaskFailedInfo as a7, type TaskJob as a8, type TaskJobStatus as a9, type TaskMetadata as aa, type TaskModule as ab, type TaskPgBossOptions as ac, type TaskRegistriesSnapshot as ad, type TaskRegistriesView as ae, type ToolCore as af, type ToolPathMeta as ag, type ToolRegistry as ah, type UpgradeHandler as ai, cors as aj, createAppRegistries as ak, createTaskRegistriesView as al, createTaskRegistry as am, helmet as an, type TaskDriver as b, type TaskRegistry as c, type TaskManifest as d, type AgentMetadata as e, type ToolMetadata as f, type FaapiContext as g, type FaapiConfig as h, type Logger as i, type AppRegistries as j, type TaskClient as k, type AgentConfig as l, type AgentHandleFactory as m, type AgentHandleStore as n, type AgentPathMeta as o, type AgentRegistry as p, type AgentToolDescriptor as q, type CorsOptions as r, type FaapiContextConfig as s, type FaapiPlugin as t, type FaapiTaskMeta as u, type FailOptions as v, type InjectorMap as w, type LifecycleContext as x, type LifecycleHooks as y, type LlmChannelStore as z };