@jsonstudio/llms 0.6.802 → 0.6.954

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 (188) hide show
  1. package/dist/bridge/routecodex-adapter.d.ts +74 -0
  2. package/dist/config-unified/enhanced-path-resolver.d.ts +5 -0
  3. package/dist/config-unified/unified-config.d.ts +26 -0
  4. package/dist/conversion/codec-registry.d.ts +10 -0
  5. package/dist/conversion/codecs/gemini-openai-codec.d.ts +16 -0
  6. package/dist/conversion/codecs/openai-openai-codec.d.ts +12 -0
  7. package/dist/conversion/codecs/responses-openai-codec.d.ts +12 -0
  8. package/dist/conversion/compat/profiles/chat-gemini.json +12 -0
  9. package/dist/conversion/config/config-manager.d.ts +212 -0
  10. package/dist/conversion/hub/config/types.d.ts +26 -0
  11. package/dist/conversion/hub/core/detour-registry.d.ts +9 -0
  12. package/dist/conversion/hub/core/hub-context.d.ts +21 -0
  13. package/dist/conversion/hub/core/index.d.ts +3 -0
  14. package/dist/conversion/hub/core/stage-driver.d.ts +30 -0
  15. package/dist/conversion/hub/format-adapters/anthropic-format-adapter.d.ts +16 -0
  16. package/dist/conversion/hub/format-adapters/chat-format-adapter.d.ts +17 -0
  17. package/dist/conversion/hub/format-adapters/gemini-format-adapter.d.ts +16 -0
  18. package/dist/conversion/hub/format-adapters/index.d.ts +21 -0
  19. package/dist/conversion/hub/hub-feature.d.ts +1 -0
  20. package/dist/conversion/hub/node-support.d.ts +19 -0
  21. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +11 -0
  22. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
  23. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +7 -0
  24. package/dist/conversion/hub/pipeline/hub-pipeline.js +113 -17
  25. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +6 -3
  26. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -0
  27. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +23 -1
  28. package/dist/conversion/hub/pipelines/inbound.d.ts +22 -0
  29. package/dist/conversion/hub/pipelines/outbound.d.ts +22 -0
  30. package/dist/conversion/hub/policy/policy-engine.d.ts +46 -0
  31. package/dist/conversion/hub/policy/policy-engine.js +176 -0
  32. package/dist/conversion/hub/policy/protocol-spec.d.ts +50 -0
  33. package/dist/conversion/hub/policy/protocol-spec.js +105 -0
  34. package/dist/conversion/hub/process/chat-process.d.ts +32 -0
  35. package/dist/conversion/hub/registry.d.ts +28 -0
  36. package/dist/conversion/hub/response/chat-response-utils.d.ts +6 -0
  37. package/dist/conversion/hub/response/provider-response.js +31 -0
  38. package/dist/conversion/hub/semantic-mappers/chat-mapper.js +32 -1
  39. package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +7 -0
  40. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +96 -1
  41. package/dist/conversion/hub/semantic-mappers/index.d.ts +4 -0
  42. package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +21 -0
  43. package/dist/conversion/hub/standardized-bridge.d.ts +12 -0
  44. package/dist/conversion/hub/types/chat-schema.d.ts +112 -0
  45. package/dist/conversion/hub/types/errors.d.ts +5 -0
  46. package/dist/conversion/hub/types/format-envelope.d.ts +7 -0
  47. package/dist/conversion/hub/types/index.d.ts +6 -0
  48. package/dist/conversion/hub/types/json.d.ts +9 -0
  49. package/dist/conversion/hub/types/node.d.ts +31 -0
  50. package/dist/conversion/responses/responses-openai-bridge.js +263 -10
  51. package/dist/conversion/schema-validator.d.ts +7 -0
  52. package/dist/conversion/shared/args-mapping.d.ts +18 -0
  53. package/dist/conversion/shared/chat-request-filters.d.ts +9 -0
  54. package/dist/conversion/shared/errors.d.ts +1 -1
  55. package/dist/conversion/shared/gemini-tool-utils.js +105 -1
  56. package/dist/conversion/shared/jsonish.d.ts +3 -0
  57. package/dist/conversion/shared/mcp-injection.d.ts +2 -0
  58. package/dist/conversion/shared/media.d.ts +1 -0
  59. package/dist/conversion/shared/openai-message-normalize.d.ts +1 -0
  60. package/dist/conversion/shared/payload-budget.d.ts +13 -0
  61. package/dist/conversion/shared/reasoning-mapping.d.ts +5 -0
  62. package/dist/conversion/shared/responses-request-adapter.d.ts +1 -28
  63. package/dist/conversion/shared/responses-request-adapter.js +1 -430
  64. package/dist/conversion/shared/snapshot-hooks.js +58 -3
  65. package/dist/conversion/shared/tool-governor.js +8 -2
  66. package/dist/conversion/shared/tool-harvester.d.ts +31 -0
  67. package/dist/conversion/shared/tool-mapping.js +10 -29
  68. package/dist/conversion/types.d.ts +33 -0
  69. package/dist/filters/builtin/add-fields-filter.d.ts +8 -0
  70. package/dist/filters/builtin/blacklist-filter.d.ts +8 -0
  71. package/dist/filters/builtin/whitelist-filter.d.ts +8 -0
  72. package/dist/filters/engine.d.ts +16 -0
  73. package/dist/filters/special/request-tool-choice-policy.d.ts +11 -0
  74. package/dist/filters/special/response-finish-invariants.d.ts +11 -0
  75. package/dist/filters/special/response-openai-to-responses-bridge.d.ts +13 -0
  76. package/dist/filters/special/response-tool-arguments-blacklist.d.ts +12 -0
  77. package/dist/filters/special/response-tool-arguments-schema-converge.d.ts +13 -0
  78. package/dist/filters/special/response-tool-arguments-stringify.d.ts +9 -0
  79. package/dist/filters/special/response-tool-arguments-whitelist.d.ts +11 -0
  80. package/dist/filters/special/tool-filter-hooks.d.ts +19 -0
  81. package/dist/filters/special/tool-post-constraints.d.ts +31 -0
  82. package/dist/filters/types.d.ts +68 -0
  83. package/dist/filters/utils/fieldmap-loader.d.ts +2 -0
  84. package/dist/filters/utils/snapshot-writer.d.ts +10 -0
  85. package/dist/guidance/index.d.ts +3 -0
  86. package/dist/guidance/index.js +78 -83
  87. package/dist/http/sse-response.d.ts +22 -0
  88. package/dist/router/virtual-router/bootstrap.d.ts +6 -0
  89. package/dist/router/virtual-router/bootstrap.js +49 -5
  90. package/dist/router/virtual-router/classifier.d.ts +10 -0
  91. package/dist/router/virtual-router/engine-selection.js +98 -11
  92. package/dist/router/virtual-router/engine.js +177 -31
  93. package/dist/router/virtual-router/error-center.d.ts +10 -0
  94. package/dist/router/virtual-router/features.d.ts +3 -0
  95. package/dist/router/virtual-router/routing-instructions.d.ts +23 -1
  96. package/dist/router/virtual-router/routing-instructions.js +120 -30
  97. package/dist/router/virtual-router/types.d.ts +11 -0
  98. package/dist/servertool/engine.js +192 -17
  99. package/dist/servertool/handlers/apply-patch-guard.js +269 -0
  100. package/dist/servertool/handlers/exec-command-guard.js +558 -0
  101. package/dist/servertool/handlers/followup-message-trimmer.d.ts +16 -0
  102. package/dist/servertool/handlers/followup-message-trimmer.js +198 -0
  103. package/dist/servertool/handlers/followup-request-builder.d.ts +17 -0
  104. package/dist/servertool/handlers/followup-request-builder.js +122 -0
  105. package/dist/servertool/handlers/gemini-empty-reply-continue.js +252 -51
  106. package/dist/servertool/handlers/iflow-model-error-retry.js +12 -22
  107. package/dist/servertool/handlers/stop-message-auto.js +237 -75
  108. package/dist/servertool/handlers/vision.js +15 -27
  109. package/dist/servertool/handlers/web-search.js +17 -43
  110. package/dist/servertool/server-side-tools.d.ts +3 -0
  111. package/dist/servertool/server-side-tools.js +3 -0
  112. package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +2 -1
  113. package/dist/sse/json-to-sse/chat-json-to-sse-converter.d.ts +80 -0
  114. package/dist/sse/json-to-sse/event-generators/chat.d.ts +55 -0
  115. package/dist/sse/json-to-sse/event-generators/responses.d.ts +99 -0
  116. package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +2 -1
  117. package/dist/sse/json-to-sse/responses-json-to-sse-converter.d.ts +80 -0
  118. package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +1 -1
  119. package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +2 -2
  120. package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +1 -1
  121. package/dist/sse/json-to-sse/sequencers/responses-sequencer.d.ts +40 -0
  122. package/dist/sse/shared/chat-serializer.d.ts +4 -0
  123. package/dist/sse/shared/constants.d.ts +272 -0
  124. package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +1 -1
  125. package/dist/sse/shared/serializers/base-serializer.d.ts +158 -0
  126. package/dist/sse/shared/serializers/chat-event-serializer.d.ts +82 -0
  127. package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +1 -1
  128. package/dist/sse/shared/serializers/index.d.ts +2 -1
  129. package/dist/sse/shared/serializers/responses-event-serializer.d.ts +123 -0
  130. package/dist/sse/shared/serializers/types.d.ts +51 -0
  131. package/dist/sse/shared/utils.d.ts +254 -0
  132. package/dist/sse/shared/writer.d.ts +2 -2
  133. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -1
  134. package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +1 -1
  135. package/dist/sse/sse-to-json/builders/response-builder.d.ts +1 -1
  136. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +2 -1
  137. package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +2 -1
  138. package/dist/sse/sse-to-json/parsers/sse-parser.d.ts +73 -0
  139. package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
  140. package/dist/sse/types/chat-types.d.ts +1 -1
  141. package/dist/sse/types/responses-types.d.ts +1 -1
  142. package/dist/tools/apply-patch/execution-capturer.d.ts +13 -0
  143. package/dist/tools/apply-patch/execution-capturer.js +158 -0
  144. package/dist/tools/apply-patch/regression-capturer.d.ts +1 -0
  145. package/dist/tools/apply-patch/regression-capturer.js +5 -4
  146. package/dist/tools/apply-patch/structured.js +109 -13
  147. package/dist/tools/apply-patch/validator.js +112 -18
  148. package/dist/tools/tool-registry.d.ts +8 -0
  149. package/dist/tools/tool-registry.js +2 -1
  150. package/package.json +4 -4
  151. package/dist/conversion/compat/actions/apply-patch-format-fixer.js +0 -233
  152. package/dist/conversion/config/compat-profiles.json +0 -38
  153. package/dist/conversion/hub/pipeline/context-limit.d.ts +0 -13
  154. package/dist/conversion/hub/pipeline/context-limit.js +0 -55
  155. package/dist/conversion/hub/response/server-side-tools.d.ts +0 -26
  156. package/dist/conversion/hub/response/server-side-tools.js +0 -383
  157. package/dist/conversion/shared/bridge-conversation-store.d.ts +0 -41
  158. package/dist/conversion/shared/bridge-conversation-store.js +0 -279
  159. package/dist/conversion/shared/bridge-request-adapter.d.ts +0 -28
  160. package/dist/conversion/shared/bridge-request-adapter.js +0 -430
  161. package/dist/conversion/shared/responses-id-utils.js +0 -42
  162. package/dist/conversion/shared/responses-instructions.js +0 -113
  163. package/dist/conversion/shared/responses-message-utils.d.ts +0 -15
  164. package/dist/conversion/shared/responses-message-utils.js +0 -206
  165. package/dist/conversion/shared/responses-metadata.js +0 -1
  166. package/dist/conversion/shared/responses-output-utils.d.ts +0 -7
  167. package/dist/conversion/shared/responses-output-utils.js +0 -108
  168. package/dist/conversion/shared/responses-types.d.ts +0 -33
  169. package/dist/conversion/shared/tool-normalizers.d.ts +0 -4
  170. package/dist/conversion/shared/tool-normalizers.js +0 -84
  171. package/dist/filters/special/request-streaming-to-nonstreaming.d.ts +0 -13
  172. package/dist/filters/special/request-streaming-to-nonstreaming.js +0 -39
  173. package/dist/filters/special/response-apply-patch-toon-decode.d.ts +0 -23
  174. package/dist/filters/special/response-apply-patch-toon-decode.js +0 -460
  175. package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +0 -10
  176. package/dist/filters/special/response-tool-arguments-toon-decode.js +0 -154
  177. package/dist/servertool/flow-types.d.ts +0 -40
  178. package/dist/servertool/flow-types.js +0 -1
  179. package/dist/servertool/orchestration-types.d.ts +0 -33
  180. package/dist/servertool/orchestration-types.js +0 -1
  181. package/dist/servertool/vision-tool.d.ts +0 -2
  182. package/dist/servertool/vision-tool.js +0 -185
  183. package/dist/tools/patch-args-normalizer.d.ts +0 -15
  184. package/dist/tools/patch-args-normalizer.js +0 -472
  185. package/dist/utils/toon.d.ts +0 -4
  186. package/dist/utils/toon.js +0 -75
  187. /package/dist/{conversion/compat/actions/apply-patch-format-fixer.d.ts → servertool/handlers/apply-patch-guard.d.ts} +0 -0
  188. /package/dist/{conversion/shared/responses-types.js → servertool/handlers/exec-command-guard.d.ts} +0 -0
@@ -0,0 +1,8 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ export declare class AddFieldsFilter implements Filter<JsonObject> {
3
+ readonly name = "add_fields";
4
+ readonly stage: any;
5
+ private readonly fields;
6
+ constructor(stage: FilterContext['stage'], fields: Record<string, unknown>);
7
+ apply(input: JsonObject): FilterResult<JsonObject>;
8
+ }
@@ -0,0 +1,8 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ export declare class BlacklistFilter implements Filter<JsonObject> {
3
+ readonly name = "blacklist";
4
+ readonly stage: any;
5
+ private readonly deny;
6
+ constructor(stage: FilterContext['stage'], deny: string[]);
7
+ apply(input: JsonObject): FilterResult<JsonObject>;
8
+ }
@@ -0,0 +1,8 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ export declare class WhitelistFilter implements Filter<JsonObject> {
3
+ readonly name = "whitelist";
4
+ readonly stage: any;
5
+ private readonly allow;
6
+ constructor(stage: FilterContext['stage'], allow: string[]);
7
+ apply(input: JsonObject): FilterResult<JsonObject>;
8
+ }
@@ -0,0 +1,16 @@
1
+ import type { Filter, FilterContext, FieldMapConfig, JsonObject } from './types.js';
2
+ export declare class FilterEngine {
3
+ private readonly filters;
4
+ private fieldMap?;
5
+ private readonly transforms;
6
+ registerFilter(filter: Filter): void;
7
+ setFieldMap(config: FieldMapConfig): void;
8
+ registerTransform(name: string, fn: (v: unknown) => unknown): void;
9
+ run(stage: FilterContext['stage'], payload: JsonObject, ctxBase: Omit<FilterContext, 'stage'>): Promise<JsonObject>;
10
+ private applyFieldMap;
11
+ private coerce;
12
+ private deepClone;
13
+ private mutateWildcardPath;
14
+ private getByPath;
15
+ private setByPath;
16
+ }
@@ -0,0 +1,11 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ /**
3
+ * Tool choice policy:
4
+ * - If tools exist and no tool_choice, set tool_choice='auto'
5
+ * - If no tools, remove tool_choice
6
+ */
7
+ export declare class RequestToolChoicePolicyFilter implements Filter<JsonObject> {
8
+ readonly name = "request_tool_choice_policy";
9
+ readonly stage: FilterContext['stage'];
10
+ apply(input: JsonObject): FilterResult<JsonObject>;
11
+ }
@@ -0,0 +1,11 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ /**
3
+ * Ensure finish_reason and content invariants on response side:
4
+ * - If tool_calls present, finish_reason defaults to 'tool_calls'
5
+ * - If tool_calls present, message.content=null (Chat path)
6
+ */
7
+ export declare class ResponseFinishInvariantsFilter implements Filter<JsonObject> {
8
+ readonly name = "response_finish_invariants";
9
+ readonly stage: FilterContext['stage'];
10
+ apply(input: JsonObject): FilterResult<JsonObject>;
11
+ }
@@ -0,0 +1,13 @@
1
+ import type { Filter, FilterContext, JsonObject, FilterResult } from '../types.js';
2
+ /**
3
+ * ResponseOpenAIToResponsesBridgeFilter
4
+ * - 将 OpenAI Chat 形状的响应桥接为 OpenAI Responses 形状
5
+ * - 与 Anthropic 并行:使用 filter 进行形状/协议转换(而非 endpoint 分支)
6
+ * - 阶段:response_post(在基础规范化之后执行)
7
+ */
8
+ export declare class ResponseOpenAIToResponsesBridgeFilter implements Filter<JsonObject> {
9
+ readonly name = "response_openai_to_responses_bridge";
10
+ readonly stage: FilterContext['stage'];
11
+ apply(input: JsonObject, ctx: FilterContext): Promise<FilterResult<JsonObject>>;
12
+ }
13
+ export default ResponseOpenAIToResponsesBridgeFilter;
@@ -0,0 +1,12 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ /**
3
+ * ResponseToolArgumentsBlacklistFilter
4
+ * - Remove keys listed in ROUTECODEX_TOOLARGS_BLACKLIST from tool_calls[].function.arguments (after decode).
5
+ * - Default: empty blacklist (no-op). Enables incremental hardening without risking valid params.
6
+ * Stage: response_pre
7
+ */
8
+ export declare class ResponseToolArgumentsBlacklistFilter implements Filter<JsonObject> {
9
+ readonly name = "response_tool_arguments_blacklist";
10
+ readonly stage: FilterContext['stage'];
11
+ apply(input: JsonObject): FilterResult<JsonObject>;
12
+ }
@@ -0,0 +1,13 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ /**
3
+ * ResponseToolArgumentsSchemaConvergeFilter
4
+ * - If tool schemas are available on context (context.toolSchemas[name] = JSONSchema),
5
+ * keep only keys defined in schema.properties for each tool call arguments.
6
+ * - Does not validate or coerce types; only converges keys to schema surface.
7
+ * - Stage: response_pre
8
+ */
9
+ export declare class ResponseToolArgumentsSchemaConvergeFilter implements Filter<JsonObject> {
10
+ readonly name = "response_tool_arguments_schema_converge";
11
+ readonly stage: FilterContext['stage'];
12
+ apply(input: JsonObject, context?: FilterContext): FilterResult<JsonObject>;
13
+ }
@@ -0,0 +1,9 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ /**
3
+ * Ensure choices[*].message.tool_calls[].function.arguments is a JSON string.
4
+ */
5
+ export declare class ResponseToolArgumentsStringifyFilter implements Filter<JsonObject> {
6
+ readonly name = "response_tool_arguments_stringify";
7
+ readonly stage: FilterContext['stage'];
8
+ apply(input: JsonObject): FilterResult<JsonObject>;
9
+ }
@@ -0,0 +1,11 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ /**
3
+ * Whitelist tool function arguments keys after decode to match schema (e.g., keep only 'command'/'workdir').
4
+ * Stage: response_pre. Runs after TOON decode and before stringify.
5
+ */
6
+ export declare class ResponseToolArgumentsWhitelistFilter implements Filter<JsonObject> {
7
+ readonly name = "response_tool_arguments_whitelist";
8
+ readonly stage: FilterContext['stage'];
9
+ private getWhitelist;
10
+ apply(input: JsonObject, context?: FilterContext): FilterResult<JsonObject>;
11
+ }
@@ -0,0 +1,19 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ interface ToolFilterConfigCategoryPolicy {
3
+ mode?: 'allow' | 'block' | 'require_content';
4
+ requireContentTypes?: string[];
5
+ }
6
+ interface ToolFilterConfig {
7
+ categories?: {
8
+ vision?: ToolFilterConfigCategoryPolicy;
9
+ mcp?: ToolFilterConfigCategoryPolicy;
10
+ };
11
+ }
12
+ export declare function setGlobalToolFilterConfig(cfg: ToolFilterConfig | undefined): void;
13
+ export declare function getGlobalToolFilterConfig(): ToolFilterConfig;
14
+ export declare class ToolFilterHookFilter implements Filter<JsonObject> {
15
+ readonly name = "tool_filter_hook";
16
+ readonly stage: FilterContext['stage'];
17
+ apply(input: JsonObject, ctx: FilterContext): FilterResult<JsonObject>;
18
+ }
19
+ export {};
@@ -0,0 +1,31 @@
1
+ import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
2
+ type ConstraintMatchCondition = 'missing' | 'empty' | 'missingOrEmpty';
3
+ interface ToolConstraintRule {
4
+ id: string;
5
+ direction: 'request' | 'response' | 'both';
6
+ providerProtocol?: string;
7
+ providerId?: string;
8
+ modelId?: string;
9
+ toolNamePattern?: string;
10
+ path: string;
11
+ when: ConstraintMatchCondition;
12
+ action: 'warn' | 'patch' | 'drop';
13
+ patchText?: string;
14
+ }
15
+ interface ToolGovernanceConstraintsConfig {
16
+ rules: ToolConstraintRule[];
17
+ }
18
+ /**
19
+ * Tool post-constraints filter(请求/响应阶段的最后一小步约束):
20
+ * - 默认只记录(warn),不修改 payload;
21
+ * - 修剪/修复行为完全由配置驱动(ToolGovernanceConstraintsConfig);
22
+ * - 当前仅实现缺失/空 description 的简单场景,后续可按配置扩展。
23
+ */
24
+ export declare class ToolPostConstraintsFilter implements Filter<JsonObject> {
25
+ readonly name = "tool_post_constraints";
26
+ readonly stage: FilterContext['stage'];
27
+ private readonly config;
28
+ constructor(stage: FilterContext['stage'], config?: ToolGovernanceConstraintsConfig);
29
+ apply(input: JsonObject, ctx: FilterContext): FilterResult<JsonObject>;
30
+ }
31
+ export {};
@@ -0,0 +1,68 @@
1
+ export type JsonObject = Record<string, unknown>;
2
+ export type FilterStage = 'request_pre' | 'request_map' | 'request_post' | 'request_finalize' | 'response_pre' | 'response_map' | 'response_post' | 'response_finalize';
3
+ export interface FilterContext {
4
+ requestId?: string;
5
+ model?: string;
6
+ endpoint?: string;
7
+ provider?: string;
8
+ profile?: string;
9
+ stage: FilterStage;
10
+ stream?: boolean;
11
+ toolFilterHints?: ToolFilterHints;
12
+ debug?: {
13
+ emit?: (event: string, data: unknown) => void;
14
+ };
15
+ }
16
+ export interface FilterResult<T = JsonObject> {
17
+ ok: boolean;
18
+ data: T;
19
+ warnings?: string[];
20
+ metrics?: Record<string, unknown>;
21
+ }
22
+ export interface Filter<T = JsonObject> {
23
+ readonly name: string;
24
+ readonly stage: FilterStage;
25
+ apply(input: T, ctx: FilterContext): Promise<FilterResult<T>> | FilterResult<T>;
26
+ }
27
+ export type ToolFilterAction = 'allow' | 'block' | 'rewrite';
28
+ export interface ToolFilterDecision {
29
+ name: string;
30
+ action: ToolFilterAction;
31
+ reason?: string;
32
+ category?: string;
33
+ }
34
+ export interface ToolFilterHints {
35
+ requestedDecisions?: ToolFilterDecision[];
36
+ categories?: {
37
+ vision?: boolean;
38
+ mcp?: boolean;
39
+ webSearch?: boolean;
40
+ codeExecution?: boolean;
41
+ fileSearch?: boolean;
42
+ dataAnalysis?: boolean;
43
+ };
44
+ categoryOverrides?: {
45
+ vision?: 'allow' | 'block' | 'require_content';
46
+ mcp?: 'allow' | 'block';
47
+ };
48
+ decided?: ToolFilterDecision[];
49
+ }
50
+ export interface FieldMapRule {
51
+ sourcePath: string;
52
+ targetPath: string;
53
+ type?: 'string' | 'number' | 'boolean' | 'object' | 'array';
54
+ transform?: string;
55
+ }
56
+ export interface FieldMapConfig {
57
+ request?: FieldMapRule[];
58
+ response?: FieldMapRule[];
59
+ }
60
+ export interface WhitelistConfig {
61
+ allow: string[];
62
+ }
63
+ export interface BlacklistConfig {
64
+ deny: string[];
65
+ }
66
+ export interface AddFieldsConfig {
67
+ fields: Record<string, unknown>;
68
+ }
@@ -0,0 +1,2 @@
1
+ import type { FieldMapConfig } from '../types.js';
2
+ export declare function loadFieldMapConfig(relativeJsonPath: string): Promise<FieldMapConfig | null>;
@@ -0,0 +1,10 @@
1
+ import type { FilterStage } from '../types.js';
2
+ export declare function writeFilterSnapshot(options: {
3
+ requestId?: string;
4
+ endpoint?: string;
5
+ profile?: string;
6
+ stage: FilterStage;
7
+ name?: string;
8
+ tag?: string;
9
+ data: unknown;
10
+ }): Promise<void>;
@@ -0,0 +1,3 @@
1
+ export declare function augmentOpenAITools(tools: unknown[]): unknown[];
2
+ export declare function augmentAnthropicTools(tools: unknown[]): unknown[];
3
+ export declare function buildSystemToolGuidance(): string;
@@ -19,6 +19,58 @@ function appendOnce(desc, guidance, marker) {
19
19
  return base;
20
20
  return base ? `${base}\n\n${guidance}` : guidance;
21
21
  }
22
+ function buildApplyPatchGuidanceText() {
23
+ const marker = '[Codex ApplyPatch Guidance]';
24
+ const text = [
25
+ marker,
26
+ 'Send ONLY patch text (do not describe tools in prose).',
27
+ '',
28
+ '✅ Supported patch text formats:',
29
+ ' A) Internal patch format: "*** Begin Patch" / "*** End Patch" + "*** Update/Add/Delete File"',
30
+ ' B) GNU unified diff: "diff --git", "---/+++", "@@" hunks, /dev/null for new files',
31
+ '',
32
+ '✅ Create vs update:',
33
+ ' - "*** Update File" MUST target an existing file (no implicit create).',
34
+ ' - To create a file, use "*** Add File:" (or GNU diff with --- /dev/null).',
35
+ '',
36
+ '✅ Common pitfalls:',
37
+ ' - In "*** Add File", EVERY content line must start with "+".',
38
+ ' - In "*** Update File" hunks, EVERY line must start with one of: " " (context), "-" (remove), "+" (add), or "@@" (hunk header).',
39
+ '',
40
+ '✅ Minimal examples (patch text):',
41
+ '',
42
+ '1) Create a new file',
43
+ '```patch',
44
+ '*** Begin Patch',
45
+ '*** Add File: path/to/new.txt',
46
+ '+Line 1',
47
+ '+Line 2',
48
+ '*** End Patch',
49
+ '```',
50
+ '',
51
+ '2) Replace one line in an existing file (keep surrounding context)',
52
+ '```patch',
53
+ '*** Begin Patch',
54
+ '*** Update File: path/to/existing.txt',
55
+ '@@',
56
+ ' Line 1',
57
+ '-Line 2',
58
+ '+Modified Line 2',
59
+ ' Line 3',
60
+ '*** End Patch',
61
+ '```',
62
+ '',
63
+ '3) Delete a file',
64
+ '```patch',
65
+ '*** Begin Patch',
66
+ '*** Delete File: path/to/obsolete.txt',
67
+ '*** End Patch',
68
+ '```',
69
+ '',
70
+ 'Paths must stay relative to the workspace root (no leading "/" or drive letters).'
71
+ ].join('\n');
72
+ return { marker, text };
73
+ }
22
74
  function augmentShell(fn) {
23
75
  const marker = '[Codex Shell Guidance]';
24
76
  const guidance = [
@@ -59,53 +111,20 @@ function augmentShell(fn) {
59
111
  fn.description = appendOnce(fn.description, guidance, marker);
60
112
  }
61
113
  function augmentApplyPatch(fn) {
62
- const marker = '[Codex ApplyPatch Guidance]';
63
- const guidance = [
64
- marker,
65
- 'Provide structured edits instead of raw diff text. Always send JSON arguments with a `changes` array.',
66
- 'Each change describes one operation, e.g. `{ "file": "src/foo.ts", "kind": "insert_after", "anchor": "const foo = 1;", "lines": ["const bar = 2;"] }`.',
67
- 'Supported kinds: insert_after, insert_before, replace, delete, create_file, delete_file.',
68
- 'Paths must stay relative to the workspace root (no leading "/" or drive letters).',
69
- 'Insert operations require `anchor` text; replace/delete require exact `target` snippets; `lines` omit "+/-" prefixes.',
70
- 'If you must emit raw patch text, use "*** Begin Patch" / "*** End Patch" with "*** Update/Add/Delete File" headers (no "diff --git").',
71
- 'Do not output "*** Create File:"; use "*** Add File:".'
72
- ].join('\n');
114
+ const { marker, text: guidance } = buildApplyPatchGuidanceText();
73
115
  const params = ensureObjectSchema(fn.parameters);
74
116
  const props = params.properties;
75
- props.file = { type: 'string', description: 'Optional default file path (relative) for changes' };
76
- props.instructions = { type: 'string', description: 'Optional human-readable summary' };
77
- props.changes = {
78
- type: 'array',
79
- minItems: 1,
80
- items: {
81
- type: 'object',
82
- additionalProperties: false,
83
- required: ['kind'],
84
- properties: {
85
- file: { type: 'string', description: 'Relative path for this change (overrides top-level file)' },
86
- kind: {
87
- type: 'string',
88
- description: 'insert_after | insert_before | replace | delete | create_file | delete_file'
89
- },
90
- anchor: { type: 'string', description: 'Required for insert_before / insert_after' },
91
- target: { type: 'string', description: 'Snippet to replace/delete' },
92
- lines: {
93
- description: 'New content for insert/replace/create operations',
94
- oneOf: [
95
- { type: 'string' },
96
- { type: 'array', items: { type: 'string' } }
97
- ]
98
- },
99
- use_anchor_indent: {
100
- type: 'boolean',
101
- description: 'Reapply the indentation of the anchor snippet to inserted lines'
102
- }
103
- }
104
- }
117
+ props.patch = {
118
+ type: 'string',
119
+ description: 'Patch text. Supports "*** Begin Patch" format or GNU unified diff. Paths must be workspace-relative.'
120
+ };
121
+ props.input = {
122
+ type: 'string',
123
+ description: 'Alias of patch (patch text). Prefer using patch.'
105
124
  };
106
125
  params.required = Array.isArray(params.required) ? params.required : [];
107
- if (!params.required.includes('changes')) {
108
- params.required.push('changes');
126
+ if (!params.required.includes('patch')) {
127
+ params.required.push('patch');
109
128
  }
110
129
  params.additionalProperties = false;
111
130
  fn.parameters = params;
@@ -161,8 +180,11 @@ export function augmentOpenAITools(tools) {
161
180
  try {
162
181
  if (n === 'shell')
163
182
  augmentShell(fn);
164
- else if (n === 'apply_patch')
183
+ else if (n === 'apply_patch') {
184
+ const { marker, text } = buildApplyPatchGuidanceText();
185
+ fn.description = appendOnce(fn.description, text, marker);
165
186
  augmentApplyPatch(fn);
187
+ }
166
188
  else if (n === 'update_plan')
167
189
  augmentUpdatePlan(fn);
168
190
  else if (n === 'view_image')
@@ -194,49 +216,22 @@ export function augmentAnthropicTools(tools) {
194
216
  const n = name.trim();
195
217
  try {
196
218
  if (n === 'apply_patch') {
197
- if (!isObject(schema.properties?.changes)) {
198
- schema.properties.file = { type: 'string', description: 'Optional default file path' };
199
- schema.properties.instructions = { type: 'string', description: 'Optional summary' };
200
- schema.properties.changes = {
201
- type: 'array',
202
- minItems: 1,
203
- items: {
204
- type: 'object',
205
- additionalProperties: false,
206
- required: ['kind'],
207
- properties: {
208
- file: { type: 'string' },
209
- kind: { type: 'string' },
210
- anchor: { type: 'string' },
211
- target: { type: 'string' },
212
- lines: {
213
- oneOf: [
214
- { type: 'string' },
215
- { type: 'array', items: { type: 'string' } }
216
- ]
217
- },
218
- use_anchor_indent: { type: 'boolean' }
219
- }
220
- }
221
- };
222
- }
219
+ schema.properties.patch = {
220
+ type: 'string',
221
+ description: 'Patch text (*** Begin Patch / *** End Patch or GNU unified diff).'
222
+ };
223
+ schema.properties.input = {
224
+ type: 'string',
225
+ description: 'Alias of patch (patch text). Prefer patch.'
226
+ };
223
227
  if (!Array.isArray(schema.required))
224
228
  schema.required = [];
225
- if (!schema.required.includes('changes'))
226
- schema.required.push('changes');
229
+ if (!schema.required.includes('patch'))
230
+ schema.required.push('patch');
227
231
  schema.additionalProperties = false;
228
232
  copy.input_schema = schema;
229
233
  const marker = '[Codex ApplyPatch Guidance]';
230
- const guidance = [
231
- marker,
232
- 'Before using apply_patch, always read the latest content of the target file (via shell or another tool) and base your changes on that content.',
233
- 'Provide structured changes (insert_after / insert_before / replace / delete / create_file / delete_file) instead of raw patch text.',
234
- 'Each change must include the target file (relative path) plus anchor/target snippets and the replacement lines.',
235
- '所有路径必须相对工作区根目录,禁止输出以 / 或盘符开头的绝对路径。',
236
- 'Example: {\"changes\":[{\"file\":\"src/app.ts\",\"kind\":\"replace\",\"target\":\"const answer = 41;\",\"lines\":[\"const answer = 42;\"]}]}(修改同一文件时尽量只修改一段连续区域,多处不相邻修改请拆成多次 apply_patch 调用).',
237
- 'Raw patch text must use "*** Begin Patch" / "*** End Patch" + "*** Update/Add/Delete File" headers(不要输出 "diff --git")。',
238
- '不要输出 "*** Create File:";请使用 "*** Add File:".'
239
- ].join('\n');
234
+ const guidance = buildApplyPatchGuidanceText().text;
240
235
  copy.description = appendOnce(desc, guidance, marker);
241
236
  }
242
237
  if (n === 'shell') {
@@ -271,7 +266,7 @@ export function buildSystemToolGuidance() {
271
266
  lines.push(bullet('shell: Place ALL intent into the command argv array only; do not invent extra keys. / shell 所有意图写入 command 数组,不要添加额外键名。'));
272
267
  lines.push(bullet('File writes are FORBIDDEN via shell (no redirection, no here-doc, no sed -i, no ed -s, no tee). Use apply_patch ONLY. / 通过 shell 写文件一律禁止(不得使用重定向、heredoc、sed -i、ed -s、tee);必须使用 apply_patch。'));
273
268
  lines.push(bullet('apply_patch: Before writing, always read the target file first and compute changes against the latest content using appropriate tools. / apply_patch 在写入前必须先通过合适的工具读取目标文件最新内容,并基于该内容生成变更。'));
274
- lines.push(bullet('apply_patch: Provide structured JSON arguments with a `changes` array (insert_after / insert_before / replace / delete / create_file / delete_file); omit "+/-" prefixes in `lines`; file paths必须是相对路径。 / apply_patch 仅接受结构化 JSON。'));
269
+ lines.push(bullet('apply_patch: Send patch text only. Supported formats: "*** Begin Patch" OR GNU unified diff. "*** Update File" never implicitly creates files—use "*** Add File:" (or /dev/null diff). / apply_patch:仅发送补丁文本,支持 "*** Begin Patch" 或 GNU diff;"*** Update File" 不会隐式创建文件。'));
275
270
  lines.push(bullet('apply_patch: For a given file, prefer one contiguous change block per call; if you need to touch non-adjacent regions, split them into multiple apply_patch calls. / apply_patch 修改同一文件时尽量只提交一段连续补丁,多个不相邻位置请拆成多次调用。'));
276
271
  lines.push(bullet('update_plan: Keep exactly one step in_progress; others pending/completed. / 仅一个 in_progress 步骤。'));
277
272
  lines.push(bullet('view_image: Path must be an image file (.png .jpg .jpeg .gif .webp .bmp .svg). / 仅图片路径。'));
@@ -0,0 +1,22 @@
1
+ import type { ServerResponse } from 'node:http';
2
+ import type { Readable } from 'node:stream';
3
+ type AnyObj = Record<string, unknown>;
4
+ /**
5
+ * Pipe an SSE Readable to Node http.ServerResponse
6
+ */
7
+ export declare function sendNodeSSE(res: ServerResponse, stream: Readable): void;
8
+ /**
9
+ * Express response variant (duck-typed): res.set, res.write, res.end
10
+ */
11
+ export declare function sendExpressSSE(res: AnyObj, stream: Readable): void;
12
+ /**
13
+ * Fastify reply variant: reply.raw is ServerResponse
14
+ */
15
+ export declare function sendFastifySSE(reply: AnyObj, stream: Readable): void;
16
+ /**
17
+ * If the given payload contains __sse_responses or sseStream, stream it via the provided response object.
18
+ * Otherwise, send JSON.
19
+ * - Supported responders: Node ServerResponse, Express res, Fastify reply
20
+ */
21
+ export declare function sendSSEOrJSON(resOrReply: AnyObj, payload: AnyObj): any;
22
+ export {};
@@ -0,0 +1,6 @@
1
+ import { type VirtualRouterBootstrapInput, type VirtualRouterBootstrapResult } from './types.js';
2
+ /**
3
+ * 将用户提供的 Virtual Router 配置(或包含 virtualrouter 字段的整体配置)
4
+ * 规范化为 VirtualRouterConfig,供 HubPipeline / VirtualRouterEngine 直接使用。
5
+ */
6
+ export declare function bootstrapVirtualRouterConfig(input: VirtualRouterBootstrapInput): VirtualRouterBootstrapResult;
@@ -29,12 +29,32 @@ export function bootstrapVirtualRouterConfig(input) {
29
29
  }
30
30
  const webSearch = normalizeWebSearch(section.webSearch, routingSource);
31
31
  validateWebSearchRouting(webSearch, routingSource);
32
- const { runtimeEntries, aliasIndex } = buildProviderRuntimeEntries(providersSource);
32
+ const execCommandGuard = normalizeExecCommandGuard(section.execCommandGuard);
33
+ const { runtimeEntries, aliasIndex, modelIndex } = buildProviderRuntimeEntries(providersSource);
33
34
  const { routing, targetKeys } = expandRoutingTable(routingSource, aliasIndex);
34
35
  if (!routing.default || routing.default.length === 0) {
35
36
  throw new VirtualRouterError('Virtual Router default route must contain at least one provider target', VirtualRouterErrorCode.CONFIG_ERROR);
36
37
  }
37
- const { profiles: providerProfiles, targetRuntime } = buildProviderProfiles(targetKeys, runtimeEntries);
38
+ // Build provider profiles for:
39
+ // - routing targets (targetKeys), and
40
+ // - all declared provider models across all auth aliases (modelIndex × aliasIndex),
41
+ // so that "direct model" and "prefer model" routing can bypass route pools when needed.
42
+ const expandedTargetKeys = new Set(targetKeys);
43
+ for (const [providerId, aliases] of aliasIndex.entries()) {
44
+ const models = modelIndex.get(providerId) ?? [];
45
+ if (!models.length || !aliases.length) {
46
+ continue;
47
+ }
48
+ for (const alias of aliases) {
49
+ const runtimeKey = buildRuntimeKey(providerId, alias);
50
+ for (const modelId of models) {
51
+ if (modelId && typeof modelId === 'string') {
52
+ expandedTargetKeys.add(`${runtimeKey}.${modelId}`);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ const { profiles: providerProfiles, targetRuntime } = buildProviderProfiles(expandedTargetKeys, runtimeEntries);
38
58
  const classifier = normalizeClassifier(section.classifier);
39
59
  const loadBalancing = section.loadBalancing ?? DEFAULT_LOAD_BALANCING;
40
60
  const health = section.health ?? DEFAULT_HEALTH;
@@ -46,7 +66,8 @@ export function bootstrapVirtualRouterConfig(input) {
46
66
  loadBalancing,
47
67
  health,
48
68
  contextRouting,
49
- ...(webSearch ? { webSearch } : {})
69
+ ...(webSearch ? { webSearch } : {}),
70
+ ...(execCommandGuard ? { execCommandGuard } : {})
50
71
  };
51
72
  return {
52
73
  config,
@@ -68,13 +89,17 @@ function extractVirtualRouterSection(input) {
68
89
  const health = normalizeHealth(section.health ?? root.health);
69
90
  const contextRouting = normalizeContextRouting(section.contextRouting ?? root.contextRouting);
70
91
  const webSearch = section.webSearch ?? root.webSearch;
71
- return { providers, routing, classifier, loadBalancing, health, contextRouting, webSearch };
92
+ const execCommandGuard = section.execCommandGuard ?? root.execCommandGuard;
93
+ return { providers, routing, classifier, loadBalancing, health, contextRouting, webSearch, execCommandGuard };
72
94
  }
73
95
  function buildProviderRuntimeEntries(providers) {
74
96
  const runtimeEntries = {};
75
97
  const aliasIndex = new Map();
98
+ const modelIndex = new Map();
76
99
  for (const [providerId, providerRaw] of Object.entries(providers)) {
77
100
  const normalizedProvider = normalizeProvider(providerId, providerRaw);
101
+ const modelsNode = asRecord(providerRaw?.models);
102
+ modelIndex.set(providerId, modelsNode ? Object.keys(modelsNode).filter(Boolean) : []);
78
103
  const authEntries = extractProviderAuthEntries(providerId, providerRaw);
79
104
  if (!authEntries.length) {
80
105
  throw new VirtualRouterError(`Provider ${providerId} requires at least one auth entry`, VirtualRouterErrorCode.CONFIG_ERROR);
@@ -126,7 +151,7 @@ function buildProviderRuntimeEntries(providers) {
126
151
  };
127
152
  }
128
153
  }
129
- return { runtimeEntries, aliasIndex };
154
+ return { runtimeEntries, aliasIndex, modelIndex };
130
155
  }
131
156
  function expandRoutingTable(routingSource, aliasIndex) {
132
157
  const routing = {};
@@ -690,6 +715,25 @@ function normalizeWebSearch(input, routingSource) {
690
715
  ...(force ? { force } : {})
691
716
  };
692
717
  }
718
+ function normalizeExecCommandGuard(input) {
719
+ if (!input || typeof input !== 'object' || Array.isArray(input)) {
720
+ return undefined;
721
+ }
722
+ const record = input;
723
+ const enabledRaw = record.enabled;
724
+ const enabled = enabledRaw === true ||
725
+ (typeof enabledRaw === 'string' && enabledRaw.trim().toLowerCase() === 'true') ||
726
+ (typeof enabledRaw === 'number' && enabledRaw === 1);
727
+ if (!enabled) {
728
+ return undefined;
729
+ }
730
+ const policyFileRaw = record.policyFile ?? record?.policy_file;
731
+ const policyFile = typeof policyFileRaw === 'string' && policyFileRaw.trim().length ? policyFileRaw.trim() : undefined;
732
+ return {
733
+ enabled: true,
734
+ ...(policyFile ? { policyFile } : {})
735
+ };
736
+ }
693
737
  function extractProviderAuthEntries(providerId, raw) {
694
738
  const provider = asRecord(raw);
695
739
  const auth = asRecord(provider.auth);
@@ -0,0 +1,10 @@
1
+ import type { ClassificationResult, RoutingFeatures, VirtualRouterClassifierConfig } from './types.js';
2
+ export declare class RoutingClassifier {
3
+ private readonly config;
4
+ constructor(config: VirtualRouterClassifierConfig);
5
+ classify(features: RoutingFeatures): ClassificationResult;
6
+ private buildResult;
7
+ private ensureDefaultCandidate;
8
+ private orderRoutes;
9
+ private routeWeight;
10
+ }