@jsonstudio/llms 0.6.567 → 0.6.568

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 (55) hide show
  1. package/dist/conversion/codecs/gemini-openai-codec.js +33 -4
  2. package/dist/conversion/codecs/openai-openai-codec.js +2 -1
  3. package/dist/conversion/codecs/responses-openai-codec.js +3 -2
  4. package/dist/conversion/compat/actions/glm-history-image-trim.d.ts +2 -0
  5. package/dist/conversion/compat/actions/glm-history-image-trim.js +88 -0
  6. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +6 -1
  7. package/dist/conversion/hub/pipeline/hub-pipeline.js +25 -13
  8. package/dist/conversion/hub/process/chat-process.js +65 -11
  9. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +16 -3
  10. package/dist/conversion/hub/semantic-mappers/responses-mapper.js +51 -2
  11. package/dist/conversion/hub/types/chat-envelope.d.ts +1 -0
  12. package/dist/conversion/shared/anthropic-message-utils.js +54 -0
  13. package/dist/conversion/shared/args-mapping.js +11 -3
  14. package/dist/conversion/shared/responses-output-builder.js +42 -21
  15. package/dist/conversion/shared/streaming-text-extractor.d.ts +25 -0
  16. package/dist/conversion/shared/streaming-text-extractor.js +31 -38
  17. package/dist/conversion/shared/text-markup-normalizer.js +42 -27
  18. package/dist/conversion/shared/tool-filter-pipeline.js +2 -1
  19. package/dist/conversion/shared/tool-harvester.js +43 -12
  20. package/dist/conversion/shared/tool-mapping.d.ts +1 -0
  21. package/dist/conversion/shared/tool-mapping.js +33 -19
  22. package/dist/filters/index.d.ts +1 -0
  23. package/dist/filters/index.js +1 -0
  24. package/dist/filters/special/request-tools-normalize.js +14 -4
  25. package/dist/filters/special/response-apply-patch-toon-decode.d.ts +23 -0
  26. package/dist/filters/special/response-apply-patch-toon-decode.js +109 -0
  27. package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +10 -0
  28. package/dist/filters/special/response-tool-arguments-toon-decode.js +55 -13
  29. package/dist/guidance/index.js +69 -42
  30. package/dist/router/virtual-router/bootstrap.js +10 -5
  31. package/dist/router/virtual-router/classifier.js +9 -4
  32. package/dist/router/virtual-router/engine-health.d.ts +11 -0
  33. package/dist/router/virtual-router/engine-health.js +217 -4
  34. package/dist/router/virtual-router/engine-logging.d.ts +2 -1
  35. package/dist/router/virtual-router/engine-logging.js +35 -3
  36. package/dist/router/virtual-router/engine.d.ts +17 -1
  37. package/dist/router/virtual-router/engine.js +154 -6
  38. package/dist/router/virtual-router/routing-instructions.d.ts +2 -0
  39. package/dist/router/virtual-router/routing-instructions.js +19 -1
  40. package/dist/router/virtual-router/tool-signals.js +57 -11
  41. package/dist/router/virtual-router/types.d.ts +30 -0
  42. package/dist/router/virtual-router/types.js +1 -1
  43. package/dist/servertool/engine.js +3 -0
  44. package/dist/servertool/handlers/iflow-model-error-retry.d.ts +1 -0
  45. package/dist/servertool/handlers/iflow-model-error-retry.js +93 -0
  46. package/dist/servertool/handlers/stop-message-auto.js +61 -4
  47. package/dist/servertool/server-side-tools.d.ts +1 -0
  48. package/dist/servertool/server-side-tools.js +27 -0
  49. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +16 -0
  50. package/dist/tools/apply-patch-structured.d.ts +20 -0
  51. package/dist/tools/apply-patch-structured.js +239 -0
  52. package/dist/tools/tool-description-utils.d.ts +5 -0
  53. package/dist/tools/tool-description-utils.js +50 -0
  54. package/dist/tools/tool-registry.js +11 -193
  55. package/package.json +2 -2
@@ -101,7 +101,25 @@ const SHELL_READ_PATTERNS = [
101
101
  'python - <<',
102
102
  'python -c',
103
103
  'node - <<',
104
- 'node -e'
104
+ 'node -e',
105
+ 'sed -n',
106
+ 'sed --quiet',
107
+ 'sed ',
108
+ 'rg ',
109
+ ' ripgrep',
110
+ 'grep ',
111
+ 'egrep ',
112
+ 'fgrep ',
113
+ 'ag ',
114
+ 'ack ',
115
+ 'find ',
116
+ 'nl ',
117
+ 'less',
118
+ 'more',
119
+ 'awk ',
120
+ 'perl -ne',
121
+ 'perl -pe',
122
+ 'strings '
105
123
  ];
106
124
  export function detectVisionTool(request) {
107
125
  if (!Array.isArray(request.tools)) {
@@ -197,21 +215,49 @@ function classifyToolCall(call) {
197
215
  }
198
216
  const argsObject = parseToolArguments(call?.function?.arguments);
199
217
  const commandText = extractCommandText(argsObject);
200
- const nameCategory = categorizeToolName(functionName);
201
218
  const snippet = buildCommandSnippet(commandText);
202
- if (nameCategory === 'write' || nameCategory === 'read' || nameCategory === 'search') {
203
- return { category: nameCategory, name: functionName, commandSnippet: snippet };
204
- }
205
- if (SHELL_TOOL_NAMES.has(functionName)) {
206
- const shellCategory = classifyShellCommand(commandText);
207
- return { category: shellCategory, name: functionName, commandSnippet: snippet };
208
- }
209
- if (commandText) {
219
+ const normalizedName = functionName.toLowerCase();
220
+ const normalizedCmd = commandText.toLowerCase();
221
+ // 1) Web search 优先:函数名或命令文本中命中 web 搜索关键字时,一律归类为 search,优先级最高。
222
+ const isWebSearch = WEB_TOOL_KEYWORDS.some((keyword) => normalizedName.includes(keyword)) ||
223
+ WEB_TOOL_KEYWORDS.some((keyword) => normalizedCmd.includes(keyword));
224
+ // 2) 基于工具名的初步分类(read / write / search / other)
225
+ const nameCategory = categorizeToolName(functionName);
226
+ // 3) shell_command / exec_command 根据内部命令判断读写性质
227
+ let shellCategory = 'other';
228
+ if (SHELL_TOOL_NAMES.has(functionName) || functionName === 'exec_command') {
229
+ shellCategory = classifyShellCommand(commandText);
230
+ }
231
+ // 按优先级合并分类结果:
232
+ // 1. web search
233
+ // 2. 写文件(任一维度命中写)
234
+ // 3. 读文件(任一维度命中读)
235
+ // 4. 其他搜索(非 web search)
236
+ // 5. 其它工具
237
+ // Priority 1: Web search
238
+ if (isWebSearch) {
239
+ return { category: 'search', name: functionName, commandSnippet: snippet };
240
+ }
241
+ // Priority 2: Write (写文件) — 名称或内部命令任一判断为写,都按写处理
242
+ if (nameCategory === 'write' || shellCategory === 'write') {
243
+ return { category: 'write', name: functionName, commandSnippet: snippet };
244
+ }
245
+ // Priority 3: Read (读文件) — 仅在没有写的情况下,再看读
246
+ if (nameCategory === 'read' || shellCategory === 'read') {
247
+ return { category: 'read', name: functionName, commandSnippet: snippet };
248
+ }
249
+ // Priority 4: 其他 search 类工具(非 web search)
250
+ if (nameCategory === 'search') {
251
+ return { category: 'search', name: functionName, commandSnippet: snippet };
252
+ }
253
+ // Priority 5: 兜底用命令文本再判断一次 shell 风格读写(非 shell/exec_command 的工具)
254
+ if (!SHELL_TOOL_NAMES.has(functionName) && functionName !== 'exec_command' && commandText) {
210
255
  const derivedCategory = classifyShellCommand(commandText);
211
- if (derivedCategory !== 'other') {
256
+ if (derivedCategory === 'write' || derivedCategory === 'read') {
212
257
  return { category: derivedCategory, name: functionName, commandSnippet: snippet };
213
258
  }
214
259
  }
260
+ // 最终兜底:other
215
261
  return { category: 'other', name: functionName, commandSnippet: snippet };
216
262
  }
217
263
  function extractToolName(tool) {
@@ -172,6 +172,11 @@ export interface RouterMetadataInput {
172
172
  * 强制路由模式,从消息中的 <**...**> 指令解析得出
173
173
  */
174
174
  routingMode?: RoutingInstructionMode;
175
+ /**
176
+ * 当 disableStickyRoutes=true 时,本次请求仍使用 sticky session 状态,
177
+ * 但不继承 sticky target,允许后续路由重新选择 provider。
178
+ */
179
+ disableStickyRoutes?: boolean;
175
180
  /**
176
181
  * 允许的 provider 白名单
177
182
  */
@@ -343,3 +348,28 @@ export interface ProviderErrorEvent {
343
348
  export interface FeatureBuilder {
344
349
  build(request: StandardizedRequest, metadata: RouterMetadataInput): RoutingFeatures;
345
350
  }
351
+ export interface ProviderCooldownState {
352
+ providerKey: string;
353
+ cooldownExpiresAt: number;
354
+ reason?: string;
355
+ }
356
+ export interface VirtualRouterHealthSnapshot {
357
+ providers: ProviderHealthState[];
358
+ cooldowns: ProviderCooldownState[];
359
+ }
360
+ export interface VirtualRouterHealthStore {
361
+ /**
362
+ * 在 VirtualRouterEngine 初始化时提供上一次持久化的健康快照。
363
+ * 调用方应仅返回仍在有效期内的 cooldown/熔断信息,或返回 null 表示无可恢复状态。
364
+ */
365
+ loadInitialSnapshot(): VirtualRouterHealthSnapshot | null;
366
+ /**
367
+ * 当 VirtualRouterEngine 更新 provider 健康状态或 cooldown 时,可选地持久化最新快照。
368
+ * 实现应保证内部吞掉 I/O 错误,不影响路由主流程。
369
+ */
370
+ persistSnapshot?(snapshot: VirtualRouterHealthSnapshot): void;
371
+ /**
372
+ * 可选:记录原始 ProviderErrorEvent,便于后续离线统计与诊断。
373
+ */
374
+ recordProviderError?(event: ProviderErrorEvent): void;
375
+ }
@@ -8,8 +8,8 @@ export const ROUTE_PRIORITY = [
8
8
  'longcontext',
9
9
  'web_search',
10
10
  'search',
11
- 'coding',
12
11
  'thinking',
12
+ 'coding',
13
13
  'tools',
14
14
  'background',
15
15
  DEFAULT_ROUTE
@@ -94,6 +94,9 @@ function resolveRouteHint(adapterContext, flowId) {
94
94
  if (!routeId) {
95
95
  return undefined;
96
96
  }
97
+ if (routeId.toLowerCase() === 'default') {
98
+ return undefined;
99
+ }
97
100
  if (flowId && routeId.toLowerCase() === flowId.toLowerCase()) {
98
101
  return undefined;
99
102
  }
@@ -0,0 +1,93 @@
1
+ import { registerServerToolHandler } from '../registry.js';
2
+ import { cloneJson } from '../server-side-tools.js';
3
+ const FLOW_ID = 'iflow_model_error_retry';
4
+ const handler = async (ctx) => {
5
+ if (!ctx.options.reenterPipeline) {
6
+ return null;
7
+ }
8
+ const adapterRecord = ctx.adapterContext;
9
+ // 避免在 followup 请求里再次触发,防止循环。
10
+ const followupRaw = adapterRecord.serverToolFollowup;
11
+ if (followupRaw === true || (typeof followupRaw === 'string' && followupRaw.trim().toLowerCase() === 'true')) {
12
+ return null;
13
+ }
14
+ // 仅针对 openai-chat 协议 + iflow.* providerKey 的 /v1/responses 路径启用。
15
+ if (ctx.options.providerProtocol !== 'openai-chat') {
16
+ return null;
17
+ }
18
+ const entryEndpoint = (ctx.options.entryEndpoint || '').toLowerCase();
19
+ if (!entryEndpoint.includes('/v1/responses')) {
20
+ return null;
21
+ }
22
+ const providerKey = typeof adapterRecord.providerKey === 'string' && adapterRecord.providerKey.trim()
23
+ ? adapterRecord.providerKey.trim().toLowerCase()
24
+ : '';
25
+ if (!providerKey.startsWith('iflow.')) {
26
+ return null;
27
+ }
28
+ // 仅在上游返回 error_code(HTTP 200 + 业务错误)时触发一次自动重试。
29
+ const base = ctx.base;
30
+ const errorCode = base.error_code;
31
+ const msg = base.msg;
32
+ if (typeof errorCode !== 'number' || errorCode === 0) {
33
+ return null;
34
+ }
35
+ if (typeof msg !== 'string' || !msg.trim().length) {
36
+ return null;
37
+ }
38
+ const captured = getCapturedRequest(ctx.adapterContext);
39
+ if (!captured) {
40
+ return null;
41
+ }
42
+ const followupPayload = buildRetryFollowupPayload(captured);
43
+ if (!followupPayload) {
44
+ return null;
45
+ }
46
+ return {
47
+ chatResponse: ctx.base,
48
+ execution: {
49
+ flowId: FLOW_ID,
50
+ followup: {
51
+ requestIdSuffix: ':retry',
52
+ payload: followupPayload,
53
+ metadata: {
54
+ serverToolFollowup: true
55
+ }
56
+ }
57
+ }
58
+ };
59
+ };
60
+ registerServerToolHandler('iflow_model_error_retry', handler, { trigger: 'auto' });
61
+ function getCapturedRequest(adapterContext) {
62
+ if (!adapterContext || typeof adapterContext !== 'object') {
63
+ return null;
64
+ }
65
+ const captured = adapterContext.capturedChatRequest;
66
+ if (!captured || typeof captured !== 'object' || Array.isArray(captured)) {
67
+ return null;
68
+ }
69
+ return captured;
70
+ }
71
+ function buildRetryFollowupPayload(source) {
72
+ if (!source || typeof source !== 'object') {
73
+ return null;
74
+ }
75
+ const payload = {};
76
+ if (typeof source.model === 'string' && source.model.trim()) {
77
+ payload.model = source.model.trim();
78
+ }
79
+ const rawMessages = source.messages;
80
+ if (Array.isArray(rawMessages)) {
81
+ payload.messages = cloneJson(rawMessages);
82
+ }
83
+ const rawTools = source.tools;
84
+ if (Array.isArray(rawTools) && rawTools.length) {
85
+ payload.tools = cloneJson(rawTools);
86
+ }
87
+ const parameters = source.parameters;
88
+ if (parameters && typeof parameters === 'object' && !Array.isArray(parameters)) {
89
+ const params = cloneJson(parameters);
90
+ Object.assign(payload, params);
91
+ }
92
+ return payload;
93
+ }
@@ -1,24 +1,52 @@
1
1
  import { registerServerToolHandler } from '../registry.js';
2
2
  import { cloneJson } from '../server-side-tools.js';
3
3
  import { loadRoutingInstructionStateSync, saveRoutingInstructionStateAsync } from '../../router/virtual-router/sticky-session-store.js';
4
+ const STOPMESSAGE_DEBUG = (process.env.ROUTECODEX_STOPMESSAGE_DEBUG || '').trim() === '1';
5
+ function debugLog(message, extra) {
6
+ if (!STOPMESSAGE_DEBUG) {
7
+ return;
8
+ }
9
+ try {
10
+ // eslint-disable-next-line no-console
11
+ console.log(`\x1b[38;5;33m[stopMessage][debug] ${message}` +
12
+ (extra ? ` ${JSON.stringify(extra)}` : '') +
13
+ '\x1b[0m');
14
+ }
15
+ catch {
16
+ /* ignore logging failures */
17
+ }
18
+ }
4
19
  const FLOW_ID = 'stop_message_flow';
5
20
  const handler = async (ctx) => {
6
21
  const record = ctx.adapterContext;
22
+ debugLog('handler_start', {
23
+ requestId: record.requestId,
24
+ providerProtocol: record.providerProtocol
25
+ });
7
26
  const followupRaw = record.serverToolFollowup;
8
27
  if (followupRaw === true || (typeof followupRaw === 'string' && followupRaw.trim().toLowerCase() === 'true')) {
28
+ debugLog('skip_servertool_followup_flag');
29
+ return null;
30
+ }
31
+ const connectionState = resolveClientConnectionState(record.clientConnectionState);
32
+ if (connectionState?.disconnected === true) {
33
+ debugLog('skip_client_disconnected');
9
34
  return null;
10
35
  }
11
36
  const clientDisconnectedRaw = record.clientDisconnected;
12
37
  if (clientDisconnectedRaw === true ||
13
38
  (typeof clientDisconnectedRaw === 'string' && clientDisconnectedRaw.trim().toLowerCase() === 'true')) {
39
+ debugLog('skip_client_disconnected_flag');
14
40
  return null;
15
41
  }
16
42
  const stickyKey = resolveStickyKey(record);
17
43
  if (!stickyKey) {
44
+ debugLog('skip_no_sticky_key');
18
45
  return null;
19
46
  }
20
47
  const state = loadRoutingInstructionStateSync(stickyKey);
21
48
  if (!state || !state.stopMessageText || !state.stopMessageMaxRepeats) {
49
+ debugLog('skip_no_state', { stickyKey });
22
50
  return null;
23
51
  }
24
52
  const text = typeof state.stopMessageText === 'string' ? state.stopMessageText.trim() : '';
@@ -26,25 +54,45 @@ const handler = async (ctx) => {
26
54
  ? Math.max(1, Math.floor(state.stopMessageMaxRepeats))
27
55
  : 0;
28
56
  if (!text || maxRepeats <= 0) {
57
+ debugLog('skip_invalid_text_or_maxRepeats', {
58
+ stickyKey,
59
+ textLength: text.length,
60
+ maxRepeats
61
+ });
29
62
  return null;
30
63
  }
31
64
  const used = typeof state.stopMessageUsed === 'number' && Number.isFinite(state.stopMessageUsed)
32
65
  ? Math.max(0, Math.floor(state.stopMessageUsed))
33
66
  : 0;
34
67
  if (used >= maxRepeats) {
68
+ debugLog('skip_reached_max_repeats', {
69
+ stickyKey,
70
+ used,
71
+ maxRepeats
72
+ });
35
73
  return null;
36
74
  }
37
75
  if (!isStopFinishReason(ctx.base)) {
76
+ debugLog('skip_not_stop_finish_reason', {
77
+ stickyKey
78
+ });
38
79
  return null;
39
80
  }
40
81
  const captured = getCapturedRequest(ctx.adapterContext);
41
82
  if (!captured) {
83
+ debugLog('skip_no_captured_request', {
84
+ stickyKey
85
+ });
42
86
  return null;
43
87
  }
44
88
  state.stopMessageUsed = used + 1;
89
+ state.stopMessageLastUsedAt = Date.now();
45
90
  saveRoutingInstructionStateAsync(stickyKey, state);
46
91
  const followupPayload = buildStopMessageFollowupPayload(captured, text);
47
92
  if (!followupPayload) {
93
+ debugLog('skip_failed_build_followup', {
94
+ stickyKey
95
+ });
48
96
  return null;
49
97
  }
50
98
  return {
@@ -56,7 +104,10 @@ const handler = async (ctx) => {
56
104
  payload: followupPayload,
57
105
  metadata: {
58
106
  serverToolFollowup: true,
59
- stream: false
107
+ stream: false,
108
+ preserveRouteHint: false,
109
+ disableStickyRoutes: true,
110
+ ...(connectionState ? { clientConnectionState: connectionState } : {})
60
111
  }
61
112
  }
62
113
  }
@@ -65,12 +116,12 @@ const handler = async (ctx) => {
65
116
  registerServerToolHandler('stop_message_auto', handler, { trigger: 'auto' });
66
117
  function resolveStickyKey(record) {
67
118
  const sessionId = typeof record.sessionId === 'string' && record.sessionId.trim() ? record.sessionId.trim() : '';
68
- if (sessionId) {
69
- return `session:${sessionId}`;
70
- }
71
119
  const conversationId = typeof record.conversationId === 'string' && record.conversationId.trim()
72
120
  ? record.conversationId.trim()
73
121
  : '';
122
+ if (sessionId) {
123
+ return `session:${sessionId}`;
124
+ }
74
125
  if (conversationId) {
75
126
  return `conversation:${conversationId}`;
76
127
  }
@@ -145,3 +196,9 @@ function buildStopMessageFollowupPayload(source, text) {
145
196
  }
146
197
  return payload;
147
198
  }
199
+ function resolveClientConnectionState(value) {
200
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
201
+ return null;
202
+ }
203
+ return value;
204
+ }
@@ -3,6 +3,7 @@ import type { ServerSideToolEngineOptions, ServerSideToolEngineResult, ToolCall
3
3
  import './handlers/web-search.js';
4
4
  import './handlers/vision.js';
5
5
  import './handlers/gemini-empty-reply-continue.js';
6
+ import './handlers/iflow-model-error-retry.js';
6
7
  import './handlers/stop-message-auto.js';
7
8
  export declare function runServerSideToolEngine(options: ServerSideToolEngineOptions): Promise<ServerSideToolEngineResult>;
8
9
  export declare function extractToolCalls(chatResponse: JsonObject): ToolCall[];
@@ -2,12 +2,16 @@ import { getServerToolHandler, listAutoServerToolHandlers } from './registry.js'
2
2
  import './handlers/web-search.js';
3
3
  import './handlers/vision.js';
4
4
  import './handlers/gemini-empty-reply-continue.js';
5
+ import './handlers/iflow-model-error-retry.js';
5
6
  import './handlers/stop-message-auto.js';
6
7
  export async function runServerSideToolEngine(options) {
7
8
  const base = asObject(options.chatResponse);
8
9
  if (!base) {
9
10
  return { mode: 'passthrough', finalChatResponse: options.chatResponse };
10
11
  }
12
+ if (isClientDisconnected(options.adapterContext)) {
13
+ return { mode: 'passthrough', finalChatResponse: base };
14
+ }
11
15
  const toolCalls = extractToolCalls(base);
12
16
  const contextBase = {
13
17
  base,
@@ -91,6 +95,29 @@ function getArray(value) {
91
95
  export function cloneJson(value) {
92
96
  return JSON.parse(JSON.stringify(value));
93
97
  }
98
+ function isClientDisconnected(adapterContext) {
99
+ if (!adapterContext || typeof adapterContext !== 'object') {
100
+ return false;
101
+ }
102
+ const state = adapterContext.clientConnectionState;
103
+ if (state && typeof state === 'object' && !Array.isArray(state)) {
104
+ const disconnected = state.disconnected;
105
+ if (disconnected === true) {
106
+ return true;
107
+ }
108
+ if (typeof disconnected === 'string' && disconnected.trim().toLowerCase() === 'true') {
109
+ return true;
110
+ }
111
+ }
112
+ const raw = adapterContext.clientDisconnected;
113
+ if (raw === true) {
114
+ return true;
115
+ }
116
+ if (typeof raw === 'string' && raw.trim().toLowerCase() === 'true') {
117
+ return true;
118
+ }
119
+ return false;
120
+ }
94
121
  export function extractTextFromChatLike(payload) {
95
122
  let current = payload;
96
123
  const visited = new Set();
@@ -132,6 +132,22 @@ export function createAnthropicResponseBuilder(options) {
132
132
  },
133
133
  getResult() {
134
134
  if (!state.completed) {
135
+ // 对部分实现(或网络提前关闭)导致缺失 message_stop 的 SSE 流,
136
+ // 只要已经累计到可用内容,就以最佳努力方式返回结果,而不是直接抛错。
137
+ if (state.content.length > 0) {
138
+ return {
139
+ success: true,
140
+ response: {
141
+ id: state.id || `msg_${Date.now()}`,
142
+ type: 'message',
143
+ role: state.role || 'assistant',
144
+ model: state.model || 'unknown',
145
+ content: state.content,
146
+ usage: state.usage,
147
+ stop_reason: state.stopReason ?? 'end_turn'
148
+ }
149
+ };
150
+ }
135
151
  return { success: false, error: new Error('Anthropic SSE stream incomplete') };
136
152
  }
137
153
  return {
@@ -0,0 +1,20 @@
1
+ export type StructuredApplyPatchKind = 'insert_after' | 'insert_before' | 'replace' | 'delete' | 'create_file' | 'delete_file';
2
+ export interface StructuredApplyPatchChange {
3
+ file?: string;
4
+ kind: StructuredApplyPatchKind | string;
5
+ anchor?: string;
6
+ target?: string;
7
+ lines?: string[] | string;
8
+ use_anchor_indent?: boolean;
9
+ }
10
+ export interface StructuredApplyPatchPayload extends Record<string, unknown> {
11
+ instructions?: string;
12
+ file?: string;
13
+ changes: StructuredApplyPatchChange[];
14
+ }
15
+ export declare class StructuredApplyPatchError extends Error {
16
+ reason: string;
17
+ constructor(reason: string, message: string);
18
+ }
19
+ export declare function buildStructuredPatch(payload: StructuredApplyPatchPayload): string;
20
+ export declare function isStructuredApplyPatchPayload(candidate: unknown): candidate is StructuredApplyPatchPayload;