@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
@@ -1,4 +1,4 @@
1
- import { AnthropicMessageResponse, AnthropicSseEvent } from '../../types/index.js';
1
+ import type { AnthropicMessageResponse, AnthropicSseEvent } from '../../types/index.js';
2
2
  import type { ChatReasoningMode } from '../../types/chat-types.js';
3
3
  interface BuilderOptions {
4
4
  reasoningMode?: ChatReasoningMode;
@@ -2,7 +2,7 @@
2
2
  * Responses响应构建器
3
3
  * 负责状态机和事件聚合,从SSE事件构建完整的Responses响应对象
4
4
  */
5
- import { ResponsesSseEvent, ResponsesResponse } from '../../types/index.js';
5
+ import type { ResponsesSseEvent, ResponsesResponse } from '../../types/index.js';
6
6
  export type ResponseBuilderState = 'initial' | 'building' | 'completed' | 'error';
7
7
  export interface ResponseBuilderConfig {
8
8
  enableStrictValidation: boolean;
@@ -2,7 +2,8 @@
2
2
  * Chat SSE → JSON转换器
3
3
  * 将SSE事件流聚合为ChatCompletion响应
4
4
  */
5
- import { ChatCompletionResponse, ChatSseEvent, SseToChatJsonOptions, ChatEventStats, DEFAULT_CHAT_CONVERSION_CONFIG } from '../types/index.js';
5
+ import { DEFAULT_CHAT_CONVERSION_CONFIG } from '../types/index.js';
6
+ import type { ChatCompletionResponse, ChatSseEvent, SseToChatJsonOptions, ChatEventStats } from '../types/index.js';
6
7
  /**
7
8
  * Chat SSE到JSON转换器
8
9
  */
@@ -1,4 +1,5 @@
1
- import { GeminiResponse, SseToGeminiJsonOptions, DEFAULT_GEMINI_CONVERSION_CONFIG } from '../types/index.js';
1
+ import { DEFAULT_GEMINI_CONVERSION_CONFIG } from '../types/index.js';
2
+ import type { GeminiResponse, SseToGeminiJsonOptions } from '../types/index.js';
2
3
  export declare class GeminiSseToJsonConverter {
3
4
  private config;
4
5
  private contexts;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * SSE事件解析器
3
+ * 负责将SSE文本帧解析为标准化的SSE事件对象
4
+ */
5
+ import type { BaseSseEvent } from '../../types/index.js';
6
+ export interface SseParserConfig {
7
+ enableStrictValidation: boolean;
8
+ enableEventRecovery: boolean;
9
+ maxEventSize: number;
10
+ allowedEventTypes: Set<string>;
11
+ }
12
+ export interface SseParseResult {
13
+ success: boolean;
14
+ event?: BaseSseEvent;
15
+ error?: string;
16
+ rawData: string;
17
+ }
18
+ export declare const DEFAULT_SSE_PARSER_CONFIG: SseParserConfig;
19
+ /**
20
+ * SSE原始事件数据
21
+ */
22
+ export interface RawSseEvent {
23
+ id?: string;
24
+ event: string;
25
+ data: string;
26
+ retry?: string;
27
+ timestamp?: number;
28
+ }
29
+ /**
30
+ * 将原始SSE行组装为事件
31
+ */
32
+ export declare function assembleSseEvent(lines: string[]): RawSseEvent | null;
33
+ /**
34
+ * 解析SSE事件文本为标准化事件对象
35
+ */
36
+ export declare function parseSseEvent(sseText: string, config?: SseParserConfig): SseParseResult;
37
+ /**
38
+ * 解析SSE流(处理多事件数据)
39
+ */
40
+ export declare function parseSseStream(sseData: string, config?: SseParserConfig): Generator<SseParseResult>;
41
+ /**
42
+ * 流式解析SSE数据
43
+ */
44
+ export declare function parseSseStreamAsync(asyncSseData: AsyncIterable<string>, config?: SseParserConfig): AsyncGenerator<SseParseResult>;
45
+ /**
46
+ * 创建SSE解析器工厂
47
+ */
48
+ export declare function createSseParser(config?: Partial<SseParserConfig>): {
49
+ /**
50
+ * 解析单个事件
51
+ */
52
+ parseEvent(sseText: string): SseParseResult;
53
+ /**
54
+ * 解析多事件流
55
+ */
56
+ parseStream(sseData: string): Generator<SseParseResult>;
57
+ /**
58
+ * 异步解析流
59
+ */
60
+ parseStreamAsync(asyncSseData: AsyncIterable<string>): AsyncGenerator<SseParseResult>;
61
+ /**
62
+ * 获取当前配置
63
+ */
64
+ getConfig(): SseParserConfig;
65
+ /**
66
+ * 添加允许的事件类型
67
+ */
68
+ addEventType(eventType: string): void;
69
+ /**
70
+ * 移除允许的事件类型
71
+ */
72
+ removeEventType(eventType: string): void;
73
+ };
@@ -2,7 +2,7 @@
2
2
  * Responses SSE → JSON转换器(重构版本)
3
3
  * 使用函数化架构:解析 + 构建 + 验证分离
4
4
  */
5
- import { ResponsesResponse, SseToResponsesJsonContext, SseToResponsesJsonOptions, ResponsesSseEventStream } from '../types/index.js';
5
+ import type { ResponsesResponse, SseToResponsesJsonContext, SseToResponsesJsonOptions, ResponsesSseEventStream } from '../types/index.js';
6
6
  /**
7
7
  * 重构后的Responses SSE到JSON转换器
8
8
  * 采用函数化架构,专注于编排而非具体业务逻辑
@@ -2,7 +2,7 @@
2
2
  * Chat协议相关类型定义
3
3
  * 支持OpenAI Chat Completions的JSON↔SSE双向转换
4
4
  */
5
- import { BaseSseEvent, StreamDirection } from './core-interfaces.js';
5
+ import type { BaseSseEvent, StreamDirection } from './core-interfaces.js';
6
6
  type JsonObject = Record<string, unknown>;
7
7
  export type ChatSseEventType = 'chat_chunk' | 'chat.done' | 'error' | 'ping';
8
8
  export interface ChatSseEvent extends BaseSseEvent {
@@ -2,7 +2,7 @@
2
2
  * Responses协议相关类型定义
3
3
  * 支持OpenAI Responses API的JSON↔SSE双向转换
4
4
  */
5
- import { BaseSseEvent, StreamDirection, type JsonObject, type JsonValue } from './core-interfaces.js';
5
+ import type { BaseSseEvent, StreamDirection, JsonObject, JsonValue } from './core-interfaces.js';
6
6
  import type { RequiredAction } from './sse-events.js';
7
7
  export * from './sse-events.js';
8
8
  export type ResponsesSseEventType = 'response.created' | 'response.in_progress' | 'response.completed' | 'response.required_action' | 'response.done' | 'response.output_item.added' | 'response.output_item.done' | 'response.content_part.added' | 'response.content_part.done' | 'response.output_text.delta' | 'response.output_text.done' | 'response.reasoning_text.delta' | 'response.reasoning_text.done' | 'response.reasoning_signature.delta' | 'response.reasoning_image.delta' | 'response.reasoning_summary_part.added' | 'response.reasoning_summary_part.done' | 'response.reasoning_summary_text.delta' | 'response.reasoning_summary_text.done' | 'response.function_call_arguments.delta' | 'response.function_call_arguments.done' | 'response.error' | 'response.cancelled' | 'response.start' | 'content_part.delta' | 'reasoning.delta' | 'function_call.start' | 'function_call.delta' | 'function_call.done' | 'output_item.start' | 'content_part.start' | 'content_part.done' | 'output_item.done' | 'reasoning.start' | 'reasoning.done' | 'required_action' | 'error';
@@ -0,0 +1,13 @@
1
+ export interface ApplyPatchExecutionFailureCaptureContext {
2
+ requestId?: string;
3
+ entryEndpoint?: string;
4
+ providerKey?: string;
5
+ model?: string;
6
+ source?: string;
7
+ meta?: Record<string, unknown>;
8
+ }
9
+ export declare function captureApplyPatchExecutionFailure(content: string, context: ApplyPatchExecutionFailureCaptureContext & {
10
+ toolCallId?: string;
11
+ toolCallArgs?: string;
12
+ }): void;
13
+ export declare function captureApplyPatchExecutionFailuresFromProcessedRequest(processedRequest: unknown): void;
@@ -0,0 +1,158 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import { createHash } from 'crypto';
5
+ /**
6
+ * Captures apply_patch execution failures (tool role message contents) into errorsamples.
7
+ *
8
+ * These failures are not schema/shape failures; they happen when the tool is executed
9
+ * (e.g. file missing, context mismatch). We capture for debugging and compatibility
10
+ * tracking, but do NOT attempt semantic repair.
11
+ *
12
+ * Default destination:
13
+ * ~/.routecodex/errorsamples/apply_patch_exec/<error-type>/
14
+ *
15
+ * Explicit override:
16
+ * ROUTECODEX_APPLY_PATCH_EXEC_DIR=/abs/path
17
+ *
18
+ * Note: if ROUTECODEX_ERRORSAMPLES_DIR is set, it is used as the base root.
19
+ */
20
+ const MAX_SAMPLES_PER_TYPE = 250;
21
+ function resolveSamplesRoot() {
22
+ const explicit = String(process?.env?.ROUTECODEX_APPLY_PATCH_EXEC_DIR || '').trim();
23
+ if (explicit)
24
+ return path.resolve(explicit);
25
+ const base = String(process?.env?.ROUTECODEX_ERRORSAMPLES_DIR || '').trim();
26
+ if (base)
27
+ return path.join(path.resolve(base), 'apply_patch_exec');
28
+ return path.join(os.homedir(), '.routecodex', 'errorsamples', 'apply_patch_exec');
29
+ }
30
+ function classifyExecutionFailure(content) {
31
+ const raw = String(content || '');
32
+ const trimmed = raw.trim();
33
+ const prefix = 'apply_patch verification failed:';
34
+ const msg = trimmed.toLowerCase().startsWith(prefix) ? trimmed.slice(prefix.length).trim() : trimmed;
35
+ const lower = msg.toLowerCase();
36
+ if (lower.includes('failed to read file'))
37
+ return { errorType: 'read_file_failed', message: msg };
38
+ if (lower.includes('no such file') || lower.includes('file not found'))
39
+ return { errorType: 'file_not_found', message: msg };
40
+ if (lower.includes('failed to find context'))
41
+ return { errorType: 'context_not_found', message: msg };
42
+ if (lower.includes('failed to find expected lines'))
43
+ return { errorType: 'expected_lines_not_found', message: msg };
44
+ if (lower.includes('invalid patch'))
45
+ return { errorType: 'invalid_patch', message: msg };
46
+ if (lower.includes('failed to parse'))
47
+ return { errorType: 'parse_failed', message: msg };
48
+ return { errorType: 'unknown', message: msg };
49
+ }
50
+ function stableSampleId(fields) {
51
+ const key = `${String(fields.errorType || 'unknown')}:${String(fields.errorMessage || '')}:` +
52
+ `${String(fields.toolCallId || '')}:${String(fields.toolCallArgs || '')}:${String(fields.requestId || '')}:${String(fields.mode || '')}`;
53
+ return createHash('sha1').update(key).digest('hex').slice(0, 16);
54
+ }
55
+ export function captureApplyPatchExecutionFailure(content, context) {
56
+ try {
57
+ const { errorType, message } = classifyExecutionFailure(content);
58
+ const root = resolveSamplesRoot();
59
+ const safeType = String(errorType || 'unknown').replace(/[^a-z0-9-]/gi, '_');
60
+ const typeDir = path.join(root, safeType);
61
+ if (!fs.existsSync(typeDir))
62
+ fs.mkdirSync(typeDir, { recursive: true });
63
+ // Check limit (best-effort; keep runtime safe)
64
+ try {
65
+ const existing = fs.readdirSync(typeDir).filter((f) => f.endsWith('.json'));
66
+ if (existing.length >= MAX_SAMPLES_PER_TYPE)
67
+ return;
68
+ }
69
+ catch {
70
+ // ignore
71
+ }
72
+ const mode = String(context.meta?.applyPatchToolMode || '').trim();
73
+ const id = `sample_${stableSampleId({
74
+ errorType,
75
+ errorMessage: message,
76
+ toolCallId: context.toolCallId,
77
+ toolCallArgs: context.toolCallArgs,
78
+ requestId: context.requestId,
79
+ mode
80
+ })}`;
81
+ const file = path.join(typeDir, `${id}.json`);
82
+ if (fs.existsSync(file))
83
+ return;
84
+ const sample = {
85
+ id,
86
+ timestamp: new Date().toISOString(),
87
+ errorType,
88
+ errorMessage: message,
89
+ toolCallId: context.toolCallId,
90
+ toolCallArgs: context.toolCallArgs,
91
+ requestId: context.requestId,
92
+ entryEndpoint: context.entryEndpoint,
93
+ providerKey: context.providerKey,
94
+ model: context.model,
95
+ source: context.source,
96
+ meta: context.meta
97
+ };
98
+ fs.writeFileSync(file, JSON.stringify(sample, null, 2), 'utf-8');
99
+ }
100
+ catch {
101
+ // Silently fail to avoid disrupting runtime
102
+ }
103
+ }
104
+ export function captureApplyPatchExecutionFailuresFromProcessedRequest(processedRequest) {
105
+ try {
106
+ const anyReq = processedRequest;
107
+ const messages = Array.isArray(anyReq?.messages) ? anyReq.messages : [];
108
+ if (!messages.length)
109
+ return;
110
+ const metadata = anyReq?.metadata || {};
111
+ const requestId = typeof metadata?.requestId === 'string' ? metadata.requestId : undefined;
112
+ const entryEndpoint = typeof metadata?.originalEndpoint === 'string' ? metadata.originalEndpoint : undefined;
113
+ const providerKey = typeof metadata?.providerKey === 'string' ? metadata.providerKey : undefined;
114
+ const model = typeof anyReq?.model === 'string' ? anyReq.model : undefined;
115
+ const toolCallArgsById = new Map();
116
+ for (const msg of messages) {
117
+ if (!msg || typeof msg !== 'object')
118
+ continue;
119
+ const toolCalls = Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
120
+ for (const tc of toolCalls) {
121
+ const id = typeof tc?.id === 'string' ? String(tc.id) : '';
122
+ const fn = tc?.function;
123
+ const name = typeof fn?.name === 'string' ? String(fn.name).trim().toLowerCase() : '';
124
+ if (!id || name !== 'apply_patch')
125
+ continue;
126
+ const args = typeof fn?.arguments === 'string' ? String(fn.arguments) : '';
127
+ if (args)
128
+ toolCallArgsById.set(id, args);
129
+ }
130
+ }
131
+ for (const msg of messages) {
132
+ if (!msg || typeof msg !== 'object')
133
+ continue;
134
+ const role = msg.role;
135
+ const name = typeof msg.name === 'string' ? String(msg.name).trim().toLowerCase() : '';
136
+ const content = typeof msg.content === 'string' ? String(msg.content) : '';
137
+ if (role !== 'tool' || name !== 'apply_patch')
138
+ continue;
139
+ if (!content.toLowerCase().includes('apply_patch verification failed'))
140
+ continue;
141
+ const toolCallId = typeof msg.tool_call_id === 'string' ? String(msg.tool_call_id) : undefined;
142
+ const toolCallArgs = toolCallId ? toolCallArgsById.get(toolCallId) : undefined;
143
+ captureApplyPatchExecutionFailure(content, {
144
+ requestId,
145
+ entryEndpoint,
146
+ providerKey,
147
+ model,
148
+ source: 'req_process_stage1_tool_governance',
149
+ meta: { applyPatchToolMode: 'freeform' },
150
+ toolCallId,
151
+ toolCallArgs
152
+ });
153
+ }
154
+ }
155
+ catch {
156
+ // ignore
157
+ }
158
+ }
@@ -7,6 +7,7 @@ interface RegressionSample {
7
7
  fixerResult?: string;
8
8
  validationError?: string;
9
9
  source?: string;
10
+ meta?: Record<string, unknown>;
10
11
  }
11
12
  export declare function captureApplyPatchRegression(sample: Omit<RegressionSample, 'id' | 'timestamp'>): void;
12
13
  export {};
@@ -9,7 +9,7 @@ import { createHash } from 'crypto';
9
9
  * Condition: non-context errors only (format/json/prefix/etc.)
10
10
  *
11
11
  * Default destination:
12
- * ~/.routecodex/golden_samples/ci-regression/apply_patch/<error-type>/
12
+ * ~/.routecodex/errorsamples/apply_patch/<error-type>/
13
13
  *
14
14
  * Optional repo destination (explicit opt-in):
15
15
  * ROUTECODEX_APPLY_PATCH_REGRESSION_TO_REPO=1
@@ -18,7 +18,7 @@ import { createHash } from 'crypto';
18
18
  * Explicit override:
19
19
  * ROUTECODEX_APPLY_PATCH_REGRESSION_DIR=/abs/path
20
20
  */
21
- const MAX_SAMPLES_PER_TYPE = 50;
21
+ const MAX_SAMPLES_PER_TYPE = 250;
22
22
  function detectRepoRootFromCwd() {
23
23
  try {
24
24
  let dir = process.cwd();
@@ -60,7 +60,7 @@ function resolveSamplesRoot() {
60
60
  return path.join(repoRoot, 'samples', 'ci-goldens', '_regressions', 'apply_patch');
61
61
  }
62
62
  }
63
- return path.join(os.homedir(), '.routecodex', 'golden_samples', 'ci-regression', 'apply_patch');
63
+ return path.join(os.homedir(), '.routecodex', 'errorsamples', 'apply_patch');
64
64
  }
65
65
  function isContextError(error) {
66
66
  if (!error)
@@ -72,8 +72,9 @@ function isContextError(error) {
72
72
  lower.includes('no such file'));
73
73
  }
74
74
  function stableSampleId(sample) {
75
+ const mode = String(sample.meta?.applyPatchToolMode || '').trim();
75
76
  const key = `${String(sample.errorType || 'unknown')}:${String(sample.originalArgs ?? '')}:` +
76
- `${String(sample.normalizedArgs ?? '')}:${String(sample.fixerResult ?? '')}:${String(sample.validationError ?? '')}`;
77
+ `${String(sample.normalizedArgs ?? '')}:${String(sample.fixerResult ?? '')}:${String(sample.validationError ?? '')}:${mode}`;
77
78
  return createHash('sha1').update(key).digest('hex').slice(0, 16);
78
79
  }
79
80
  export function captureApplyPatchRegression(sample) {
@@ -134,13 +134,25 @@ const tryApplyToFileLines = (section, kindRaw, change, changeRec, index) => {
134
134
  change.target ??
135
135
  change.context ??
136
136
  change.from ??
137
- change.old;
137
+ change.old ??
138
+ change.oldText ??
139
+ change.old_text ??
140
+ change.beforeText ??
141
+ change.before_text;
138
142
  const anchor = toSafeString(anchorSource, `changes[${index}].anchor`);
139
143
  const anchorLines = splitTextIntoLines(anchor);
140
144
  const anchorIndex = findSubsequence(lines, anchorLines);
141
145
  if (anchorIndex < 0)
142
146
  return false;
143
- const linesSource = changeRec.lines ?? changeRec.text ?? changeRec.content ?? changeRec.body ?? changeRec.replacement;
147
+ const linesSource = changeRec.lines ??
148
+ changeRec.text ??
149
+ changeRec.content ??
150
+ changeRec.body ??
151
+ changeRec.replacement ??
152
+ changeRec.newText ??
153
+ changeRec.new_text ??
154
+ changeRec.afterText ??
155
+ changeRec.after_text;
144
156
  const additions = normalizeLines(linesSource, `changes[${index}].lines`);
145
157
  if (!additions.length) {
146
158
  throw new StructuredApplyPatchError('invalid_lines', `changes[${index}].lines must include at least one line`);
@@ -157,7 +169,11 @@ const tryApplyToFileLines = (section, kindRaw, change, changeRec, index) => {
157
169
  change.anchor ??
158
170
  change.context ??
159
171
  change.from ??
160
- change.old;
172
+ change.old ??
173
+ change.oldText ??
174
+ change.old_text ??
175
+ change.beforeText ??
176
+ change.before_text;
161
177
  const target = toSafeString(targetSource, `changes[${index}].target`);
162
178
  const targetLines = splitTextIntoLines(target);
163
179
  const targetIndex = findSubsequence(lines, targetLines);
@@ -167,7 +183,15 @@ const tryApplyToFileLines = (section, kindRaw, change, changeRec, index) => {
167
183
  lines.splice(targetIndex, targetLines.length);
168
184
  return true;
169
185
  }
170
- const linesSource = changeRec.lines ?? changeRec.text ?? changeRec.content ?? changeRec.body ?? changeRec.replacement;
186
+ const linesSource = changeRec.lines ??
187
+ changeRec.text ??
188
+ changeRec.content ??
189
+ changeRec.body ??
190
+ changeRec.replacement ??
191
+ changeRec.newText ??
192
+ changeRec.new_text ??
193
+ changeRec.afterText ??
194
+ changeRec.after_text;
171
195
  let replacements;
172
196
  if ((linesSource === null || linesSource === undefined) && typeof change.anchor === 'string' && typeof change.target === 'string') {
173
197
  replacements = splitTextIntoLines(String(change.anchor));
@@ -301,9 +325,25 @@ export function buildStructuredPatch(payload) {
301
325
  }
302
326
  // Common shape: replace with lines but no target → treat as full-file replacement.
303
327
  if (kindRaw === 'replace') {
304
- const targetSource = change.target ?? change.anchor ?? change.context ?? change.from ?? change.old;
328
+ const targetSource = change.target ??
329
+ change.anchor ??
330
+ change.context ??
331
+ change.from ??
332
+ change.old ??
333
+ change.oldText ??
334
+ change.old_text ??
335
+ change.beforeText ??
336
+ change.before_text;
305
337
  const hasTarget = typeof targetSource === 'string' && targetSource.trim().length > 0;
306
- const linesSource = changeRec.lines ?? changeRec.text ?? changeRec.content ?? changeRec.body ?? changeRec.replacement;
338
+ const linesSource = changeRec.lines ??
339
+ changeRec.text ??
340
+ changeRec.content ??
341
+ changeRec.body ??
342
+ changeRec.replacement ??
343
+ changeRec.newText ??
344
+ changeRec.new_text ??
345
+ changeRec.afterText ??
346
+ changeRec.after_text;
307
347
  const hasReplacementBody = !(linesSource === null || linesSource === undefined);
308
348
  if (!hasTarget && hasReplacementBody) {
309
349
  const lines = normalizeLines(linesSource, `changes[${index}].lines`);
@@ -328,10 +368,26 @@ export function buildStructuredPatch(payload) {
328
368
  const section = ensureUpdateSection(file);
329
369
  switch (kindRaw) {
330
370
  case 'insert_after': {
331
- const anchorSource = change.anchor ?? change.target ?? change.context ?? change.from ?? change.old;
371
+ const anchorSource = change.anchor ??
372
+ change.target ??
373
+ change.context ??
374
+ change.from ??
375
+ change.old ??
376
+ change.oldText ??
377
+ change.old_text ??
378
+ change.beforeText ??
379
+ change.before_text;
332
380
  const anchor = toSafeString(anchorSource, `changes[${index}].anchor`);
333
381
  const anchorLines = splitTextIntoLines(anchor);
334
- const linesSource = changeRec.lines ?? changeRec.text ?? changeRec.content ?? changeRec.body ?? changeRec.replacement;
382
+ const linesSource = changeRec.lines ??
383
+ changeRec.text ??
384
+ changeRec.content ??
385
+ changeRec.body ??
386
+ changeRec.replacement ??
387
+ changeRec.newText ??
388
+ changeRec.new_text ??
389
+ changeRec.afterText ??
390
+ changeRec.after_text;
335
391
  const additions = normalizeLines(linesSource, `changes[${index}].lines`);
336
392
  if (!additions.length) {
337
393
  throw new StructuredApplyPatchError('invalid_lines', `changes[${index}].lines must include at least one line`);
@@ -342,10 +398,26 @@ export function buildStructuredPatch(payload) {
342
398
  break;
343
399
  }
344
400
  case 'insert_before': {
345
- const anchorSource = change.anchor ?? change.target ?? change.context ?? change.from ?? change.old;
401
+ const anchorSource = change.anchor ??
402
+ change.target ??
403
+ change.context ??
404
+ change.from ??
405
+ change.old ??
406
+ change.oldText ??
407
+ change.old_text ??
408
+ change.beforeText ??
409
+ change.before_text;
346
410
  const anchor = toSafeString(anchorSource, `changes[${index}].anchor`);
347
411
  const anchorLines = splitTextIntoLines(anchor);
348
- const linesSource = changeRec.lines ?? changeRec.text ?? changeRec.content ?? changeRec.body ?? changeRec.replacement;
412
+ const linesSource = changeRec.lines ??
413
+ changeRec.text ??
414
+ changeRec.content ??
415
+ changeRec.body ??
416
+ changeRec.replacement ??
417
+ changeRec.newText ??
418
+ changeRec.new_text ??
419
+ changeRec.afterText ??
420
+ changeRec.after_text;
349
421
  const additions = normalizeLines(linesSource, `changes[${index}].lines`);
350
422
  if (!additions.length) {
351
423
  throw new StructuredApplyPatchError('invalid_lines', `changes[${index}].lines must include at least one line`);
@@ -357,9 +429,25 @@ export function buildStructuredPatch(payload) {
357
429
  }
358
430
  case 'replace': {
359
431
  // 兼容仅提供 anchor 的 replace 形态:将 anchor 视为 target 以尽可能保留用户意图。
360
- const targetSource = change.target ?? change.anchor ?? change.context ?? change.from ?? change.old;
432
+ const targetSource = change.target ??
433
+ change.anchor ??
434
+ change.context ??
435
+ change.from ??
436
+ change.old ??
437
+ change.oldText ??
438
+ change.old_text ??
439
+ change.beforeText ??
440
+ change.before_text;
361
441
  const target = toSafeString(targetSource, `changes[${index}].target`);
362
- const linesSource = changeRec.lines ?? changeRec.text ?? changeRec.content ?? changeRec.body ?? changeRec.replacement;
442
+ const linesSource = changeRec.lines ??
443
+ changeRec.text ??
444
+ changeRec.content ??
445
+ changeRec.body ??
446
+ changeRec.replacement ??
447
+ changeRec.newText ??
448
+ changeRec.new_text ??
449
+ changeRec.afterText ??
450
+ changeRec.after_text;
363
451
  let replacements;
364
452
  if ((linesSource === null || linesSource === undefined) && typeof change.anchor === 'string' && typeof change.target === 'string') {
365
453
  // Common model mistake: provide { anchor: <new>, target: <old> } but omit lines.
@@ -377,7 +465,15 @@ export function buildStructuredPatch(payload) {
377
465
  break;
378
466
  }
379
467
  case 'delete': {
380
- const targetSource = change.target ?? change.anchor ?? change.context ?? change.from ?? change.old;
468
+ const targetSource = change.target ??
469
+ change.anchor ??
470
+ change.context ??
471
+ change.from ??
472
+ change.old ??
473
+ change.oldText ??
474
+ change.old_text ??
475
+ change.beforeText ??
476
+ change.before_text;
381
477
  const target = toSafeString(targetSource, `changes[${index}].target`);
382
478
  const hunkBody = buildPrefixedLines(splitTextIntoLines(target), '-');
383
479
  section.hunks.push(hunkBody);