@caupulican/pi-ai 0.81.13 → 0.81.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC;AAI1C,OAAO,KAAK,EACX,GAAG,EACH,gBAAgB,EAChB,OAAO,EACP,KAAK,EACL,qBAAqB,EACrB,mBAAmB,EAGnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAOtE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAsGjD,wBAAgB,MAAM,CAAC,IAAI,SAAS,GAAG,EACtC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,qBAAqB,GAC7B,2BAA2B,CAU7B;AAED,wBAAsB,QAAQ,CAAC,IAAI,SAAS,GAAG,EAC9C,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAG3B;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAS,GAAG,EAC5C,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,2BAA2B,CAU7B;AAED,wBAAsB,cAAc,CAAC,IAAI,SAAS,GAAG,EACpD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAG3B","sourcesContent":["import \"./providers/register-builtins.ts\";\n\nimport { getApiProvider } from \"./api-registry.ts\";\nimport { getEnvApiKey } from \"./env-api-keys.ts\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tContext,\n\tModel,\n\tProviderStreamOptions,\n\tSimpleStreamOptions,\n\tStreamOptions,\n\tTextToolProtocolParseEvent,\n} from \"./types.ts\";\nimport { AssistantMessageEventStream } from \"./utils/event-stream.ts\";\nimport {\n\tgenerateTextToolProtocolPrimer,\n\tnormalizeTextToolProtocolOptions,\n\tparseTextToolCalls,\n} from \"./utils/tool-repair/text-protocol.ts\";\n\nexport { getEnvApiKey } from \"./env-api-keys.ts\";\n\nfunction hasExplicitApiKey(apiKey: string | undefined): apiKey is string {\n\treturn typeof apiKey === \"string\" && apiKey.trim().length > 0;\n}\n\nfunction withEnvApiKey<TOptions extends StreamOptions>(\n\tmodel: Model<Api>,\n\toptions: TOptions | undefined,\n): TOptions | undefined {\n\tif (hasExplicitApiKey(options?.apiKey)) return options;\n\tconst apiKey = getEnvApiKey(model.provider);\n\tif (!apiKey) return options;\n\treturn { ...options, apiKey } as TOptions;\n}\n\nfunction withTextToolProtocolContext(context: Context, options: StreamOptions | undefined): Context {\n\tconst protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);\n\tif (!protocolOptions || !context.tools?.length) return context;\n\tconst primer = generateTextToolProtocolPrimer(context.tools, protocolOptions);\n\tif (!primer) return context;\n\treturn {\n\t\t...context,\n\t\tsystemPrompt: context.systemPrompt ? `${context.systemPrompt}\\n\\n${primer}` : primer,\n\t};\n}\n\nasync function notifyTextToolProtocolParse(\n\toptions: StreamOptions | undefined,\n\tevent: TextToolProtocolParseEvent,\n): Promise<void> {\n\ttry {\n\t\tawait options?.onTextToolProtocolParse?.(event);\n\t} catch {\n\t\t// Parse telemetry must not change provider stream semantics.\n\t}\n}\n\nfunction withTextToolProtocolResult(\n\tstream: AssistantMessageEventStream,\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions: StreamOptions | undefined,\n): AssistantMessageEventStream {\n\tconst protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);\n\tif (!protocolOptions || !context.tools?.length) return stream;\n\tconst wrapped = new AssistantMessageEventStream();\n\tvoid (async () => {\n\t\tfor await (const event of stream) {\n\t\t\tif (event.type !== \"done\") {\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst message = event.message;\n\t\t\tif (message.content.length !== 1 || message.content[0]?.type !== \"text\") {\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst text = message.content[0].text;\n\t\t\tconst parsed = parseTextToolCalls(text, context.tools ?? []);\n\t\t\tif (parsed.calls.length === 0) {\n\t\t\t\tif (parsed.failure) {\n\t\t\t\t\tawait notifyTextToolProtocolParse(options, {\n\t\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\t\tmodel: model.id,\n\t\t\t\t\t\tvariant: protocolOptions.variant ?? \"tool-tag\",\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tcallCount: 0,\n\t\t\t\t\t\ttextLength: text.length,\n\t\t\t\t\t\treason: parsed.failure,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait notifyTextToolProtocolParse(options, {\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tvariant: protocolOptions.variant ?? \"tool-tag\",\n\t\t\t\tstatus: \"parsed\",\n\t\t\t\tcallCount: parsed.calls.length,\n\t\t\t\ttextLength: text.length,\n\t\t\t});\n\t\t\tconst content = parsed.text ? [{ type: \"text\" as const, text: parsed.text }, ...parsed.calls] : parsed.calls;\n\t\t\twrapped.push({\n\t\t\t\ttype: \"done\",\n\t\t\t\treason: \"toolUse\",\n\t\t\t\tmessage: { ...message, content, stopReason: \"toolUse\" },\n\t\t\t});\n\t\t}\n\t})();\n\treturn wrapped;\n}\n\nfunction resolveApiProvider(api: Api) {\n\tconst provider = getApiProvider(api);\n\tif (!provider) {\n\t\tthrow new Error(`No API provider registered for api: ${api}`);\n\t}\n\treturn provider;\n}\n\nexport function stream<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: ProviderStreamOptions,\n): AssistantMessageEventStream {\n\tconst provider = resolveApiProvider(model.api);\n\tconst resolvedOptions = withEnvApiKey(model, options) as StreamOptions;\n\tconst protocolContext = withTextToolProtocolContext(context, resolvedOptions);\n\treturn withTextToolProtocolResult(\n\t\tprovider.stream(model, protocolContext, resolvedOptions),\n\t\tmodel,\n\t\tprotocolContext,\n\t\tresolvedOptions,\n\t);\n}\n\nexport async function complete<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: ProviderStreamOptions,\n): Promise<AssistantMessage> {\n\tconst s = stream(model, context, options);\n\treturn s.result();\n}\n\nexport function streamSimple<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream {\n\tconst provider = resolveApiProvider(model.api);\n\tconst resolvedOptions = withEnvApiKey(model, options);\n\tconst protocolContext = withTextToolProtocolContext(context, resolvedOptions);\n\treturn withTextToolProtocolResult(\n\t\tprovider.streamSimple(model, protocolContext, resolvedOptions),\n\t\tmodel,\n\t\tprotocolContext,\n\t\tresolvedOptions,\n\t);\n}\n\nexport async function completeSimple<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): Promise<AssistantMessage> {\n\tconst s = streamSimple(model, context, options);\n\treturn s.result();\n}\n"]}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC;AAI1C,OAAO,KAAK,EACX,GAAG,EACH,gBAAgB,EAChB,OAAO,EAEP,KAAK,EACL,qBAAqB,EACrB,mBAAmB,EAGnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAOtE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAwJjD,wBAAgB,MAAM,CAAC,IAAI,SAAS,GAAG,EACtC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,qBAAqB,GAC7B,2BAA2B,CAU7B;AAED,wBAAsB,QAAQ,CAAC,IAAI,SAAS,GAAG,EAC9C,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAG3B;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAS,GAAG,EAC5C,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,2BAA2B,CAU7B;AAED,wBAAsB,cAAc,CAAC,IAAI,SAAS,GAAG,EACpD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAG3B","sourcesContent":["import \"./providers/register-builtins.ts\";\n\nimport { getApiProvider } from \"./api-registry.ts\";\nimport { getEnvApiKey } from \"./env-api-keys.ts\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tContext,\n\tMessage,\n\tModel,\n\tProviderStreamOptions,\n\tSimpleStreamOptions,\n\tStreamOptions,\n\tTextToolProtocolParseEvent,\n} from \"./types.ts\";\nimport { AssistantMessageEventStream } from \"./utils/event-stream.ts\";\nimport {\n\tgenerateTextToolProtocolPrimer,\n\tnormalizeTextToolProtocolOptions,\n\tparseTextToolCalls,\n} from \"./utils/tool-repair/text-protocol.ts\";\n\nexport { getEnvApiKey } from \"./env-api-keys.ts\";\n\nfunction hasExplicitApiKey(apiKey: string | undefined): apiKey is string {\n\treturn typeof apiKey === \"string\" && apiKey.trim().length > 0;\n}\n\nfunction withEnvApiKey<TOptions extends StreamOptions>(\n\tmodel: Model<Api>,\n\toptions: TOptions | undefined,\n): TOptions | undefined {\n\tif (hasExplicitApiKey(options?.apiKey)) return options;\n\tconst apiKey = getEnvApiKey(model.provider);\n\tif (!apiKey) return options;\n\treturn { ...options, apiKey } as TOptions;\n}\n\nfunction withTextProtocolUserReminder(message: Message): Message {\n\tif (message.role !== \"user\") return message;\n\tconst reminder =\n\t\t\"Tool-use reminder: if this request asks to read a file, call exactly one tool named read before answering; do not guess file contents.\";\n\tif (typeof message.content === \"string\") return { ...message, content: `${message.content}\\n\\n${reminder}` };\n\treturn { ...message, content: [...message.content, { type: \"text\", text: reminder }] };\n}\n\nfunction withTextProtocolCurrentTurnReminder(messages: readonly Message[]): Message[] {\n\tlet lastUserIndex = -1;\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tif (messages[index]?.role === \"user\") {\n\t\t\tlastUserIndex = index;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn messages.map((message, index) => (index === lastUserIndex ? withTextProtocolUserReminder(message) : message));\n}\n\nfunction withTextToolProtocolContext(context: Context, options: StreamOptions | undefined): Context {\n\tconst protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);\n\tif (!protocolOptions || !context.tools?.length) return context;\n\tconst primer = generateTextToolProtocolPrimer(context.tools, protocolOptions);\n\tif (!primer) return context;\n\tconst { tools: _tools, ...providerContext } = context;\n\tconst messages = context.messages.length\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Tool-call instructions for this conversation:\\n${primer}\\n\\nDo not answer this instruction. Apply it to subsequent user requests. If the next user request asks to read a file, your first response must be a read tool call and nothing else.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\ttimestamp: 0,\n\t\t\t\t},\n\t\t\t\t...withTextProtocolCurrentTurnReminder(context.messages),\n\t\t\t]\n\t\t: context.messages;\n\treturn {\n\t\t...providerContext,\n\t\tsystemPrompt: context.systemPrompt ? `${context.systemPrompt}\\n\\n${primer}` : primer,\n\t\tmessages,\n\t};\n}\n\nasync function notifyTextToolProtocolParse(\n\toptions: StreamOptions | undefined,\n\tevent: TextToolProtocolParseEvent,\n): Promise<void> {\n\ttry {\n\t\tawait options?.onTextToolProtocolParse?.(event);\n\t} catch {\n\t\t// Parse telemetry must not change provider stream semantics.\n\t}\n}\n\nfunction withTextToolProtocolResult(\n\tstream: AssistantMessageEventStream,\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions: StreamOptions | undefined,\n): AssistantMessageEventStream {\n\tconst protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);\n\tconst tools = context.tools ?? [];\n\tif (!protocolOptions || tools.length === 0) return stream;\n\tconst wrapped = new AssistantMessageEventStream();\n\tvoid (async () => {\n\t\tfor await (const event of stream) {\n\t\t\tif (event.type !== \"done\") {\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst message = event.message;\n\t\t\tconst content: AssistantMessage[\"content\"] = [];\n\t\t\tlet callCount = 0;\n\t\t\tlet textLength = 0;\n\t\t\tlet failure: TextToolProtocolParseEvent[\"reason\"] | undefined;\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (block.type !== \"text\") {\n\t\t\t\t\tcontent.push(block);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttextLength += block.text.length;\n\t\t\t\tconst parsed = parseTextToolCalls(block.text, tools);\n\t\t\t\tif (parsed.calls.length === 0) {\n\t\t\t\t\tif (parsed.failure) failure ??= parsed.failure;\n\t\t\t\t\tcontent.push(block);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tcallCount += parsed.calls.length;\n\t\t\t\tif (parsed.text) content.push({ ...block, text: parsed.text });\n\t\t\t\tcontent.push(...parsed.calls);\n\t\t\t}\n\t\t\tif (callCount === 0) {\n\t\t\t\tif (failure) {\n\t\t\t\t\tawait notifyTextToolProtocolParse(options, {\n\t\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\t\tmodel: model.id,\n\t\t\t\t\t\tvariant: protocolOptions.variant ?? \"tool-tag\",\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tcallCount: 0,\n\t\t\t\t\t\ttextLength,\n\t\t\t\t\t\treason: failure,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait notifyTextToolProtocolParse(options, {\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tvariant: protocolOptions.variant ?? \"tool-tag\",\n\t\t\t\tstatus: \"parsed\",\n\t\t\t\tcallCount,\n\t\t\t\ttextLength,\n\t\t\t});\n\t\t\twrapped.push({\n\t\t\t\ttype: \"done\",\n\t\t\t\treason: \"toolUse\",\n\t\t\t\tmessage: { ...message, content, stopReason: \"toolUse\" },\n\t\t\t});\n\t\t}\n\t})();\n\treturn wrapped;\n}\n\nfunction resolveApiProvider(api: Api) {\n\tconst provider = getApiProvider(api);\n\tif (!provider) {\n\t\tthrow new Error(`No API provider registered for api: ${api}`);\n\t}\n\treturn provider;\n}\n\nexport function stream<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: ProviderStreamOptions,\n): AssistantMessageEventStream {\n\tconst provider = resolveApiProvider(model.api);\n\tconst resolvedOptions = withEnvApiKey(model, options) as StreamOptions;\n\tconst protocolContext = withTextToolProtocolContext(context, resolvedOptions);\n\treturn withTextToolProtocolResult(\n\t\tprovider.stream(model, protocolContext, resolvedOptions),\n\t\tmodel,\n\t\tcontext,\n\t\tresolvedOptions,\n\t);\n}\n\nexport async function complete<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: ProviderStreamOptions,\n): Promise<AssistantMessage> {\n\tconst s = stream(model, context, options);\n\treturn s.result();\n}\n\nexport function streamSimple<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream {\n\tconst provider = resolveApiProvider(model.api);\n\tconst resolvedOptions = withEnvApiKey(model, options);\n\tconst protocolContext = withTextToolProtocolContext(context, resolvedOptions);\n\treturn withTextToolProtocolResult(\n\t\tprovider.streamSimple(model, protocolContext, resolvedOptions),\n\t\tmodel,\n\t\tcontext,\n\t\tresolvedOptions,\n\t);\n}\n\nexport async function completeSimple<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): Promise<AssistantMessage> {\n\tconst s = streamSimple(model, context, options);\n\treturn s.result();\n}\n"]}
package/dist/stream.js CHANGED
@@ -15,6 +15,24 @@ function withEnvApiKey(model, options) {
15
15
  return options;
16
16
  return { ...options, apiKey };
17
17
  }
18
+ function withTextProtocolUserReminder(message) {
19
+ if (message.role !== "user")
20
+ return message;
21
+ const reminder = "Tool-use reminder: if this request asks to read a file, call exactly one tool named read before answering; do not guess file contents.";
22
+ if (typeof message.content === "string")
23
+ return { ...message, content: `${message.content}\n\n${reminder}` };
24
+ return { ...message, content: [...message.content, { type: "text", text: reminder }] };
25
+ }
26
+ function withTextProtocolCurrentTurnReminder(messages) {
27
+ let lastUserIndex = -1;
28
+ for (let index = messages.length - 1; index >= 0; index--) {
29
+ if (messages[index]?.role === "user") {
30
+ lastUserIndex = index;
31
+ break;
32
+ }
33
+ }
34
+ return messages.map((message, index) => (index === lastUserIndex ? withTextProtocolUserReminder(message) : message));
35
+ }
18
36
  function withTextToolProtocolContext(context, options) {
19
37
  const protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);
20
38
  if (!protocolOptions || !context.tools?.length)
@@ -22,9 +40,26 @@ function withTextToolProtocolContext(context, options) {
22
40
  const primer = generateTextToolProtocolPrimer(context.tools, protocolOptions);
23
41
  if (!primer)
24
42
  return context;
43
+ const { tools: _tools, ...providerContext } = context;
44
+ const messages = context.messages.length
45
+ ? [
46
+ {
47
+ role: "user",
48
+ content: [
49
+ {
50
+ type: "text",
51
+ text: `Tool-call instructions for this conversation:\n${primer}\n\nDo not answer this instruction. Apply it to subsequent user requests. If the next user request asks to read a file, your first response must be a read tool call and nothing else.`,
52
+ },
53
+ ],
54
+ timestamp: 0,
55
+ },
56
+ ...withTextProtocolCurrentTurnReminder(context.messages),
57
+ ]
58
+ : context.messages;
25
59
  return {
26
- ...context,
60
+ ...providerContext,
27
61
  systemPrompt: context.systemPrompt ? `${context.systemPrompt}\n\n${primer}` : primer,
62
+ messages,
28
63
  };
29
64
  }
30
65
  async function notifyTextToolProtocolParse(options, event) {
@@ -37,7 +72,8 @@ async function notifyTextToolProtocolParse(options, event) {
37
72
  }
38
73
  function withTextToolProtocolResult(stream, model, context, options) {
39
74
  const protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);
40
- if (!protocolOptions || !context.tools?.length)
75
+ const tools = context.tools ?? [];
76
+ if (!protocolOptions || tools.length === 0)
41
77
  return stream;
42
78
  const wrapped = new AssistantMessageEventStream();
43
79
  void (async () => {
@@ -47,22 +83,38 @@ function withTextToolProtocolResult(stream, model, context, options) {
47
83
  continue;
48
84
  }
49
85
  const message = event.message;
50
- if (message.content.length !== 1 || message.content[0]?.type !== "text") {
51
- wrapped.push(event);
52
- continue;
86
+ const content = [];
87
+ let callCount = 0;
88
+ let textLength = 0;
89
+ let failure;
90
+ for (const block of message.content) {
91
+ if (block.type !== "text") {
92
+ content.push(block);
93
+ continue;
94
+ }
95
+ textLength += block.text.length;
96
+ const parsed = parseTextToolCalls(block.text, tools);
97
+ if (parsed.calls.length === 0) {
98
+ if (parsed.failure)
99
+ failure ??= parsed.failure;
100
+ content.push(block);
101
+ continue;
102
+ }
103
+ callCount += parsed.calls.length;
104
+ if (parsed.text)
105
+ content.push({ ...block, text: parsed.text });
106
+ content.push(...parsed.calls);
53
107
  }
54
- const text = message.content[0].text;
55
- const parsed = parseTextToolCalls(text, context.tools ?? []);
56
- if (parsed.calls.length === 0) {
57
- if (parsed.failure) {
108
+ if (callCount === 0) {
109
+ if (failure) {
58
110
  await notifyTextToolProtocolParse(options, {
59
111
  provider: model.provider,
60
112
  model: model.id,
61
113
  variant: protocolOptions.variant ?? "tool-tag",
62
114
  status: "failed",
63
115
  callCount: 0,
64
- textLength: text.length,
65
- reason: parsed.failure,
116
+ textLength,
117
+ reason: failure,
66
118
  });
67
119
  }
68
120
  wrapped.push(event);
@@ -73,10 +125,9 @@ function withTextToolProtocolResult(stream, model, context, options) {
73
125
  model: model.id,
74
126
  variant: protocolOptions.variant ?? "tool-tag",
75
127
  status: "parsed",
76
- callCount: parsed.calls.length,
77
- textLength: text.length,
128
+ callCount,
129
+ textLength,
78
130
  });
79
- const content = parsed.text ? [{ type: "text", text: parsed.text }, ...parsed.calls] : parsed.calls;
80
131
  wrapped.push({
81
132
  type: "done",
82
133
  reason: "toolUse",
@@ -97,7 +148,7 @@ export function stream(model, context, options) {
97
148
  const provider = resolveApiProvider(model.api);
98
149
  const resolvedOptions = withEnvApiKey(model, options);
99
150
  const protocolContext = withTextToolProtocolContext(context, resolvedOptions);
100
- return withTextToolProtocolResult(provider.stream(model, protocolContext, resolvedOptions), model, protocolContext, resolvedOptions);
151
+ return withTextToolProtocolResult(provider.stream(model, protocolContext, resolvedOptions), model, context, resolvedOptions);
101
152
  }
102
153
  export async function complete(model, context, options) {
103
154
  const s = stream(model, context, options);
@@ -107,7 +158,7 @@ export function streamSimple(model, context, options) {
107
158
  const provider = resolveApiProvider(model.api);
108
159
  const resolvedOptions = withEnvApiKey(model, options);
109
160
  const protocolContext = withTextToolProtocolContext(context, resolvedOptions);
110
- return withTextToolProtocolResult(provider.streamSimple(model, protocolContext, resolvedOptions), model, protocolContext, resolvedOptions);
161
+ return withTextToolProtocolResult(provider.streamSimple(model, protocolContext, resolvedOptions), model, context, resolvedOptions);
111
162
  }
112
163
  export async function completeSimple(model, context, options) {
113
164
  const s = streamSimple(model, context, options);
@@ -1 +1 @@
1
- {"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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","sourcesContent":["import \"./providers/register-builtins.ts\";\n\nimport { getApiProvider } from \"./api-registry.ts\";\nimport { getEnvApiKey } from \"./env-api-keys.ts\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tContext,\n\tModel,\n\tProviderStreamOptions,\n\tSimpleStreamOptions,\n\tStreamOptions,\n\tTextToolProtocolParseEvent,\n} from \"./types.ts\";\nimport { AssistantMessageEventStream } from \"./utils/event-stream.ts\";\nimport {\n\tgenerateTextToolProtocolPrimer,\n\tnormalizeTextToolProtocolOptions,\n\tparseTextToolCalls,\n} from \"./utils/tool-repair/text-protocol.ts\";\n\nexport { getEnvApiKey } from \"./env-api-keys.ts\";\n\nfunction hasExplicitApiKey(apiKey: string | undefined): apiKey is string {\n\treturn typeof apiKey === \"string\" && apiKey.trim().length > 0;\n}\n\nfunction withEnvApiKey<TOptions extends StreamOptions>(\n\tmodel: Model<Api>,\n\toptions: TOptions | undefined,\n): TOptions | undefined {\n\tif (hasExplicitApiKey(options?.apiKey)) return options;\n\tconst apiKey = getEnvApiKey(model.provider);\n\tif (!apiKey) return options;\n\treturn { ...options, apiKey } as TOptions;\n}\n\nfunction withTextToolProtocolContext(context: Context, options: StreamOptions | undefined): Context {\n\tconst protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);\n\tif (!protocolOptions || !context.tools?.length) return context;\n\tconst primer = generateTextToolProtocolPrimer(context.tools, protocolOptions);\n\tif (!primer) return context;\n\treturn {\n\t\t...context,\n\t\tsystemPrompt: context.systemPrompt ? `${context.systemPrompt}\\n\\n${primer}` : primer,\n\t};\n}\n\nasync function notifyTextToolProtocolParse(\n\toptions: StreamOptions | undefined,\n\tevent: TextToolProtocolParseEvent,\n): Promise<void> {\n\ttry {\n\t\tawait options?.onTextToolProtocolParse?.(event);\n\t} catch {\n\t\t// Parse telemetry must not change provider stream semantics.\n\t}\n}\n\nfunction withTextToolProtocolResult(\n\tstream: AssistantMessageEventStream,\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions: StreamOptions | undefined,\n): AssistantMessageEventStream {\n\tconst protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);\n\tif (!protocolOptions || !context.tools?.length) return stream;\n\tconst wrapped = new AssistantMessageEventStream();\n\tvoid (async () => {\n\t\tfor await (const event of stream) {\n\t\t\tif (event.type !== \"done\") {\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst message = event.message;\n\t\t\tif (message.content.length !== 1 || message.content[0]?.type !== \"text\") {\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst text = message.content[0].text;\n\t\t\tconst parsed = parseTextToolCalls(text, context.tools ?? []);\n\t\t\tif (parsed.calls.length === 0) {\n\t\t\t\tif (parsed.failure) {\n\t\t\t\t\tawait notifyTextToolProtocolParse(options, {\n\t\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\t\tmodel: model.id,\n\t\t\t\t\t\tvariant: protocolOptions.variant ?? \"tool-tag\",\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tcallCount: 0,\n\t\t\t\t\t\ttextLength: text.length,\n\t\t\t\t\t\treason: parsed.failure,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait notifyTextToolProtocolParse(options, {\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tvariant: protocolOptions.variant ?? \"tool-tag\",\n\t\t\t\tstatus: \"parsed\",\n\t\t\t\tcallCount: parsed.calls.length,\n\t\t\t\ttextLength: text.length,\n\t\t\t});\n\t\t\tconst content = parsed.text ? [{ type: \"text\" as const, text: parsed.text }, ...parsed.calls] : parsed.calls;\n\t\t\twrapped.push({\n\t\t\t\ttype: \"done\",\n\t\t\t\treason: \"toolUse\",\n\t\t\t\tmessage: { ...message, content, stopReason: \"toolUse\" },\n\t\t\t});\n\t\t}\n\t})();\n\treturn wrapped;\n}\n\nfunction resolveApiProvider(api: Api) {\n\tconst provider = getApiProvider(api);\n\tif (!provider) {\n\t\tthrow new Error(`No API provider registered for api: ${api}`);\n\t}\n\treturn provider;\n}\n\nexport function stream<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: ProviderStreamOptions,\n): AssistantMessageEventStream {\n\tconst provider = resolveApiProvider(model.api);\n\tconst resolvedOptions = withEnvApiKey(model, options) as StreamOptions;\n\tconst protocolContext = withTextToolProtocolContext(context, resolvedOptions);\n\treturn withTextToolProtocolResult(\n\t\tprovider.stream(model, protocolContext, resolvedOptions),\n\t\tmodel,\n\t\tprotocolContext,\n\t\tresolvedOptions,\n\t);\n}\n\nexport async function complete<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: ProviderStreamOptions,\n): Promise<AssistantMessage> {\n\tconst s = stream(model, context, options);\n\treturn s.result();\n}\n\nexport function streamSimple<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream {\n\tconst provider = resolveApiProvider(model.api);\n\tconst resolvedOptions = withEnvApiKey(model, options);\n\tconst protocolContext = withTextToolProtocolContext(context, resolvedOptions);\n\treturn withTextToolProtocolResult(\n\t\tprovider.streamSimple(model, protocolContext, resolvedOptions),\n\t\tmodel,\n\t\tprotocolContext,\n\t\tresolvedOptions,\n\t);\n}\n\nexport async function completeSimple<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): Promise<AssistantMessage> {\n\tconst s = streamSimple(model, context, options);\n\treturn s.result();\n}\n"]}
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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","sourcesContent":["import \"./providers/register-builtins.ts\";\n\nimport { getApiProvider } from \"./api-registry.ts\";\nimport { getEnvApiKey } from \"./env-api-keys.ts\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tContext,\n\tMessage,\n\tModel,\n\tProviderStreamOptions,\n\tSimpleStreamOptions,\n\tStreamOptions,\n\tTextToolProtocolParseEvent,\n} from \"./types.ts\";\nimport { AssistantMessageEventStream } from \"./utils/event-stream.ts\";\nimport {\n\tgenerateTextToolProtocolPrimer,\n\tnormalizeTextToolProtocolOptions,\n\tparseTextToolCalls,\n} from \"./utils/tool-repair/text-protocol.ts\";\n\nexport { getEnvApiKey } from \"./env-api-keys.ts\";\n\nfunction hasExplicitApiKey(apiKey: string | undefined): apiKey is string {\n\treturn typeof apiKey === \"string\" && apiKey.trim().length > 0;\n}\n\nfunction withEnvApiKey<TOptions extends StreamOptions>(\n\tmodel: Model<Api>,\n\toptions: TOptions | undefined,\n): TOptions | undefined {\n\tif (hasExplicitApiKey(options?.apiKey)) return options;\n\tconst apiKey = getEnvApiKey(model.provider);\n\tif (!apiKey) return options;\n\treturn { ...options, apiKey } as TOptions;\n}\n\nfunction withTextProtocolUserReminder(message: Message): Message {\n\tif (message.role !== \"user\") return message;\n\tconst reminder =\n\t\t\"Tool-use reminder: if this request asks to read a file, call exactly one tool named read before answering; do not guess file contents.\";\n\tif (typeof message.content === \"string\") return { ...message, content: `${message.content}\\n\\n${reminder}` };\n\treturn { ...message, content: [...message.content, { type: \"text\", text: reminder }] };\n}\n\nfunction withTextProtocolCurrentTurnReminder(messages: readonly Message[]): Message[] {\n\tlet lastUserIndex = -1;\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tif (messages[index]?.role === \"user\") {\n\t\t\tlastUserIndex = index;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn messages.map((message, index) => (index === lastUserIndex ? withTextProtocolUserReminder(message) : message));\n}\n\nfunction withTextToolProtocolContext(context: Context, options: StreamOptions | undefined): Context {\n\tconst protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);\n\tif (!protocolOptions || !context.tools?.length) return context;\n\tconst primer = generateTextToolProtocolPrimer(context.tools, protocolOptions);\n\tif (!primer) return context;\n\tconst { tools: _tools, ...providerContext } = context;\n\tconst messages = context.messages.length\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Tool-call instructions for this conversation:\\n${primer}\\n\\nDo not answer this instruction. Apply it to subsequent user requests. If the next user request asks to read a file, your first response must be a read tool call and nothing else.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\ttimestamp: 0,\n\t\t\t\t},\n\t\t\t\t...withTextProtocolCurrentTurnReminder(context.messages),\n\t\t\t]\n\t\t: context.messages;\n\treturn {\n\t\t...providerContext,\n\t\tsystemPrompt: context.systemPrompt ? `${context.systemPrompt}\\n\\n${primer}` : primer,\n\t\tmessages,\n\t};\n}\n\nasync function notifyTextToolProtocolParse(\n\toptions: StreamOptions | undefined,\n\tevent: TextToolProtocolParseEvent,\n): Promise<void> {\n\ttry {\n\t\tawait options?.onTextToolProtocolParse?.(event);\n\t} catch {\n\t\t// Parse telemetry must not change provider stream semantics.\n\t}\n}\n\nfunction withTextToolProtocolResult(\n\tstream: AssistantMessageEventStream,\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions: StreamOptions | undefined,\n): AssistantMessageEventStream {\n\tconst protocolOptions = normalizeTextToolProtocolOptions(options?.textToolCallProtocol);\n\tconst tools = context.tools ?? [];\n\tif (!protocolOptions || tools.length === 0) return stream;\n\tconst wrapped = new AssistantMessageEventStream();\n\tvoid (async () => {\n\t\tfor await (const event of stream) {\n\t\t\tif (event.type !== \"done\") {\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst message = event.message;\n\t\t\tconst content: AssistantMessage[\"content\"] = [];\n\t\t\tlet callCount = 0;\n\t\t\tlet textLength = 0;\n\t\t\tlet failure: TextToolProtocolParseEvent[\"reason\"] | undefined;\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (block.type !== \"text\") {\n\t\t\t\t\tcontent.push(block);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttextLength += block.text.length;\n\t\t\t\tconst parsed = parseTextToolCalls(block.text, tools);\n\t\t\t\tif (parsed.calls.length === 0) {\n\t\t\t\t\tif (parsed.failure) failure ??= parsed.failure;\n\t\t\t\t\tcontent.push(block);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tcallCount += parsed.calls.length;\n\t\t\t\tif (parsed.text) content.push({ ...block, text: parsed.text });\n\t\t\t\tcontent.push(...parsed.calls);\n\t\t\t}\n\t\t\tif (callCount === 0) {\n\t\t\t\tif (failure) {\n\t\t\t\t\tawait notifyTextToolProtocolParse(options, {\n\t\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\t\tmodel: model.id,\n\t\t\t\t\t\tvariant: protocolOptions.variant ?? \"tool-tag\",\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tcallCount: 0,\n\t\t\t\t\t\ttextLength,\n\t\t\t\t\t\treason: failure,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\twrapped.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait notifyTextToolProtocolParse(options, {\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tvariant: protocolOptions.variant ?? \"tool-tag\",\n\t\t\t\tstatus: \"parsed\",\n\t\t\t\tcallCount,\n\t\t\t\ttextLength,\n\t\t\t});\n\t\t\twrapped.push({\n\t\t\t\ttype: \"done\",\n\t\t\t\treason: \"toolUse\",\n\t\t\t\tmessage: { ...message, content, stopReason: \"toolUse\" },\n\t\t\t});\n\t\t}\n\t})();\n\treturn wrapped;\n}\n\nfunction resolveApiProvider(api: Api) {\n\tconst provider = getApiProvider(api);\n\tif (!provider) {\n\t\tthrow new Error(`No API provider registered for api: ${api}`);\n\t}\n\treturn provider;\n}\n\nexport function stream<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: ProviderStreamOptions,\n): AssistantMessageEventStream {\n\tconst provider = resolveApiProvider(model.api);\n\tconst resolvedOptions = withEnvApiKey(model, options) as StreamOptions;\n\tconst protocolContext = withTextToolProtocolContext(context, resolvedOptions);\n\treturn withTextToolProtocolResult(\n\t\tprovider.stream(model, protocolContext, resolvedOptions),\n\t\tmodel,\n\t\tcontext,\n\t\tresolvedOptions,\n\t);\n}\n\nexport async function complete<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: ProviderStreamOptions,\n): Promise<AssistantMessage> {\n\tconst s = stream(model, context, options);\n\treturn s.result();\n}\n\nexport function streamSimple<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream {\n\tconst provider = resolveApiProvider(model.api);\n\tconst resolvedOptions = withEnvApiKey(model, options);\n\tconst protocolContext = withTextToolProtocolContext(context, resolvedOptions);\n\treturn withTextToolProtocolResult(\n\t\tprovider.streamSimple(model, protocolContext, resolvedOptions),\n\t\tmodel,\n\t\tcontext,\n\t\tresolvedOptions,\n\t);\n}\n\nexport async function completeSimple<TApi extends Api>(\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): Promise<AssistantMessage> {\n\tconst s = streamSimple(model, context, options);\n\treturn s.result();\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../../src/utils/tool-repair/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,KAAK,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IAC9C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IAClD,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;IACzB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,kBAAkB,EAAE,CAAC;CAC5B;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAE5E;AAED,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAOhE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEhE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAc/E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAuBjE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAkBhE;AAED,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,GACrB;IACF,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB,CA8BA;AA8HD,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,SAAS,mBAAmB,EAAE,GACpC,eAAe,EAAE,CAiCnB","sourcesContent":["import type { Tool } from \"../../types.ts\";\nimport type { ToolRepairModeName } from \"./registry.ts\";\n\nexport interface JsonSchemaObject {\n\ttype?: string | string[];\n\tproperties?: Record<string, JsonSchemaObject>;\n\titems?: JsonSchemaObject | JsonSchemaObject[];\n\trequired?: string[];\n\tadditionalProperties?: boolean | JsonSchemaObject;\n\tallOf?: JsonSchemaObject[];\n\tanyOf?: JsonSchemaObject[];\n\toneOf?: JsonSchemaObject[];\n\tenum?: unknown[];\n\tconst?: unknown;\n\tdefault?: unknown;\n}\n\nexport interface ValidationErrorLike {\n\tinstancePath: string;\n\tkeyword: string;\n\tmessage: string;\n}\n\nexport interface ToolRepairIssue {\n\tpath: string[];\n\tpathText: string;\n\tvalue: unknown;\n\tschema: JsonSchemaObject;\n\tparentSchema?: JsonSchemaObject;\n\tparentValue?: unknown;\n\tpropertyKey?: string;\n\trequired: boolean;\n\tmodes: ToolRepairModeName[];\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isJsonSchemaObject(value: unknown): value is JsonSchemaObject {\n\treturn isRecord(value);\n}\n\nexport function parseInstancePath(instancePath: string): string[] {\n\tif (!instancePath) return [];\n\treturn instancePath\n\t\t.replace(/^\\//, \"\")\n\t\t.split(\"/\")\n\t\t.filter((segment) => segment.length > 0)\n\t\t.map((segment) => segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n}\n\nexport function formatRepairPath(path: readonly string[]): string {\n\treturn path.length === 0 ? \"root\" : path.join(\".\");\n}\n\nexport function getValueAtPath(value: unknown, path: readonly string[]): unknown {\n\tlet cursor = value;\n\tfor (const segment of path) {\n\t\tif (Array.isArray(cursor)) {\n\t\t\tconst index = Number(segment);\n\t\t\tif (!Number.isInteger(index)) return undefined;\n\t\t\tcursor = cursor[index];\n\t\t} else if (isRecord(cursor)) {\n\t\t\tcursor = cursor[segment];\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn cursor;\n}\n\nexport function getSchemaTypes(schema: JsonSchemaObject): string[] {\n\tconst types = new Set<string>();\n\tconst addTypes = (candidate: JsonSchemaObject): void => {\n\t\tif (typeof candidate.type === \"string\") {\n\t\t\ttypes.add(candidate.type);\n\t\t} else if (Array.isArray(candidate.type)) {\n\t\t\tfor (const type of candidate.type) {\n\t\t\t\tif (typeof type === \"string\") types.add(type);\n\t\t\t}\n\t\t}\n\t\tif (candidate.enum?.every((value) => typeof value === \"string\")) {\n\t\t\ttypes.add(\"string\");\n\t\t}\n\t\tif (typeof candidate.const === \"string\") {\n\t\t\ttypes.add(\"string\");\n\t\t}\n\t};\n\n\taddTypes(schema);\n\tfor (const nested of [...(schema.anyOf ?? []), ...(schema.oneOf ?? []), ...(schema.allOf ?? [])]) {\n\t\taddTypes(nested);\n\t}\n\treturn [...types];\n}\n\nexport function getEnumValues(schema: JsonSchemaObject): string[] {\n\tconst values: string[] = [];\n\tconst addValues = (candidate: JsonSchemaObject): void => {\n\t\tif (Array.isArray(candidate.enum)) {\n\t\t\tfor (const value of candidate.enum) {\n\t\t\t\tif (typeof value === \"string\") values.push(value);\n\t\t\t}\n\t\t}\n\t\tif (typeof candidate.const === \"string\") {\n\t\t\tvalues.push(candidate.const);\n\t\t}\n\t};\n\n\taddValues(schema);\n\tfor (const nested of [...(schema.anyOf ?? []), ...(schema.oneOf ?? [])]) {\n\t\taddValues(nested);\n\t}\n\treturn values;\n}\n\nexport function resolveSchemaAtPath(\n\tschema: JsonSchemaObject,\n\tpath: readonly string[],\n): {\n\tschema?: JsonSchemaObject;\n\tparentSchema?: JsonSchemaObject;\n\tpropertyKey?: string;\n} {\n\tlet cursor: JsonSchemaObject | undefined = schema;\n\tlet parentSchema: JsonSchemaObject | undefined;\n\tlet propertyKey: string | undefined;\n\n\tfor (const segment of path) {\n\t\tif (!cursor) return { schema: undefined, parentSchema, propertyKey };\n\t\tparentSchema = cursor;\n\t\tpropertyKey = segment;\n\t\tif (cursor.properties?.[segment]) {\n\t\t\tcursor = cursor.properties[segment];\n\t\t\tcontinue;\n\t\t}\n\t\tif (Array.isArray(cursor.items)) {\n\t\t\tconst index = Number(segment);\n\t\t\tcursor = Number.isInteger(index) ? cursor.items[index] : undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isJsonSchemaObject(cursor.items)) {\n\t\t\tcursor = cursor.items;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isJsonSchemaObject(cursor.additionalProperties)) {\n\t\t\tcursor = cursor.additionalProperties;\n\t\t\tcontinue;\n\t\t}\n\t\treturn { schema: undefined, parentSchema, propertyKey };\n\t}\n\n\treturn { schema: cursor, parentSchema, propertyKey };\n}\n\nfunction isRequired(parentSchema: JsonSchemaObject | undefined, propertyKey: string | undefined): boolean {\n\treturn Boolean(propertyKey && parentSchema?.required?.includes(propertyKey));\n}\n\nfunction isScalarSchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).some((type) => [\"string\", \"number\", \"integer\", \"boolean\"].includes(type));\n}\n\nfunction isArraySchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).includes(\"array\");\n}\n\nfunction isObjectSchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).includes(\"object\");\n}\n\nfunction normalizeEnumValue(value: string): string {\n\treturn value.trim().replace(/\\s+/g, \" \").toLowerCase();\n}\n\nfunction enumNormalizeHasMatch(schema: JsonSchemaObject, value: string): boolean {\n\tconst normalized = normalizeEnumValue(value);\n\treturn getEnumValues(schema).filter((candidate) => normalizeEnumValue(candidate) === normalized).length === 1;\n}\n\nfunction isEmptyObject(value: unknown): boolean {\n\treturn isRecord(value) && Object.keys(value).length === 0;\n}\n\nfunction isScalarValue(value: unknown): boolean {\n\treturn [\"string\", \"number\", \"boolean\"].includes(typeof value);\n}\n\nfunction getParentPath(path: readonly string[]): string[] {\n\treturn path.slice(0, -1);\n}\n\nfunction isNumericArrayParentIssue(\n\targs: Record<string, unknown>,\n\trootSchema: JsonSchemaObject,\n\tpath: readonly string[],\n): ToolRepairIssue | undefined {\n\tif (path.length === 0) return undefined;\n\tconst parentPath = getParentPath(path);\n\tconst parentLookup = resolveSchemaAtPath(rootSchema, parentPath);\n\tconst parentSchema = parentLookup.schema;\n\tif (!parentSchema || !isArraySchema(parentSchema) || Array.isArray(parentSchema.items)) return undefined;\n\tif (!isJsonSchemaObject(parentSchema.items)) return undefined;\n\tconst itemTypes = getSchemaTypes(parentSchema.items);\n\tif (!itemTypes.some((type) => type === \"number\" || type === \"integer\")) return undefined;\n\tconst parentValue = getValueAtPath(args, parentPath);\n\tif (!Array.isArray(parentValue) || !parentValue.every((item) => typeof item === \"string\")) return undefined;\n\treturn {\n\t\tpath: parentPath,\n\t\tpathText: formatRepairPath(parentPath),\n\t\tvalue: parentValue,\n\t\tschema: parentSchema,\n\t\tparentSchema: parentLookup.parentSchema,\n\t\tparentValue: getValueAtPath(args, getParentPath(parentPath)),\n\t\tpropertyKey: parentLookup.propertyKey,\n\t\trequired: isRequired(parentLookup.parentSchema, parentLookup.propertyKey),\n\t\tmodes: [\"stringifiedNumberInArray\"],\n\t};\n}\n\nfunction classifyModes(toolName: string, issue: Omit<ToolRepairIssue, \"modes\">): ToolRepairModeName[] {\n\tconst modes: ToolRepairModeName[] = [];\n\tconst expectedTypes = getSchemaTypes(issue.schema);\n\n\tif (toolName === \"bash\" && issue.pathText === \"command\") {\n\t\tif (Array.isArray(issue.value) && issue.value.every((item) => typeof item === \"string\")) {\n\t\t\tmodes.push(\"bashCommandArgvJoin\");\n\t\t}\n\t\tif (isRecord(issue.value)) {\n\t\t\tmodes.push(\"bashCommandUnwrap\");\n\t\t}\n\t}\n\n\tif (typeof issue.value === \"string\" && (isArraySchema(issue.schema) || isObjectSchema(issue.schema))) {\n\t\tmodes.push(\"jsonStringParse\");\n\t}\n\tif (isArraySchema(issue.schema) && isRecord(issue.value)) {\n\t\tmodes.push(\"singleObjectWrap\", \"emptyObjectPlaceholder\");\n\t}\n\tif (isArraySchema(issue.schema) && isScalarValue(issue.value)) {\n\t\tmodes.push(\"bareScalarWrap\");\n\t}\n\tif (typeof issue.value === \"string\" && expectedTypes.some((type) => type === \"number\" || type === \"integer\")) {\n\t\tmodes.push(\"numberFromString\");\n\t}\n\tif (typeof issue.value === \"string\" && expectedTypes.includes(\"boolean\")) {\n\t\tmodes.push(\"boolFromString\");\n\t}\n\tif (typeof issue.value === \"string\" && enumNormalizeHasMatch(issue.schema, issue.value)) {\n\t\tmodes.push(\"enumCaseNormalize\");\n\t}\n\tif (Array.isArray(issue.value) && issue.value.length === 1 && isScalarSchema(issue.schema)) {\n\t\tmodes.push(\"singleElementUnwrap\");\n\t}\n\tif (!issue.required && isEmptyObject(issue.value) && isScalarSchema(issue.schema)) {\n\t\tmodes.push(\"emptyObjectPlaceholder\");\n\t}\n\tif (issue.value === null) {\n\t\tmodes.push(issue.required ? \"nullRequiredBounce\" : \"nullOptionalDrop\");\n\t}\n\n\treturn modes;\n}\n\nfunction comparePaths(a: readonly string[], b: readonly string[]): number {\n\tconst length = Math.min(a.length, b.length);\n\tfor (let index = 0; index < length; index++) {\n\t\tconst left = a[index];\n\t\tconst right = b[index];\n\t\tconst leftNumber = Number(left);\n\t\tconst rightNumber = Number(right);\n\t\tif (Number.isInteger(leftNumber) && Number.isInteger(rightNumber) && leftNumber !== rightNumber) {\n\t\t\treturn leftNumber - rightNumber;\n\t\t}\n\t\tif (left !== right) return left < right ? -1 : 1;\n\t}\n\treturn a.length - b.length;\n}\n\nexport function analyzeToolArgumentErrors(\n\ttoolName: string,\n\tschema: Tool[\"parameters\"],\n\targs: Record<string, unknown>,\n\terrors: readonly ValidationErrorLike[],\n): ToolRepairIssue[] {\n\tconst rootSchema = schema as JsonSchemaObject;\n\tconst issues = new Map<string, ToolRepairIssue>();\n\n\tfor (const error of errors) {\n\t\tif (error.keyword === \"required\") continue;\n\t\tconst path = parseInstancePath(error.instancePath);\n\t\tconst numericArrayIssue = isNumericArrayParentIssue(args, rootSchema, path);\n\t\tif (numericArrayIssue) {\n\t\t\tissues.set(numericArrayIssue.pathText, numericArrayIssue);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst lookup = resolveSchemaAtPath(rootSchema, path);\n\t\tif (!lookup.schema) continue;\n\t\tconst value = getValueAtPath(args, path);\n\t\tconst issueBase = {\n\t\t\tpath,\n\t\t\tpathText: formatRepairPath(path),\n\t\t\tvalue,\n\t\t\tschema: lookup.schema,\n\t\t\tparentSchema: lookup.parentSchema,\n\t\t\tparentValue: getValueAtPath(args, getParentPath(path)),\n\t\t\tpropertyKey: lookup.propertyKey,\n\t\t\trequired: isRequired(lookup.parentSchema, lookup.propertyKey),\n\t\t};\n\t\tconst modes = classifyModes(toolName, issueBase);\n\t\tif (modes.length > 0) {\n\t\t\tissues.set(issueBase.pathText, { ...issueBase, modes });\n\t\t}\n\t}\n\n\treturn [...issues.values()].sort((left, right) => comparePaths(left.path, right.path));\n}\n"]}
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../../src/utils/tool-repair/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,KAAK,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IAC9C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IAClD,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;IACzB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,kBAAkB,EAAE,CAAC;CAC5B;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAE5E;AAED,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAOhE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEhE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAc/E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAuBjE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAkBhE;AAED,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,GACrB;IACF,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB,CA8BA;AA+HD,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,SAAS,mBAAmB,EAAE,GACpC,eAAe,EAAE,CAiCnB","sourcesContent":["import type { Tool } from \"../../types.ts\";\nimport type { ToolRepairModeName } from \"./registry.ts\";\n\nexport interface JsonSchemaObject {\n\ttype?: string | string[];\n\tproperties?: Record<string, JsonSchemaObject>;\n\titems?: JsonSchemaObject | JsonSchemaObject[];\n\trequired?: string[];\n\tadditionalProperties?: boolean | JsonSchemaObject;\n\tallOf?: JsonSchemaObject[];\n\tanyOf?: JsonSchemaObject[];\n\toneOf?: JsonSchemaObject[];\n\tenum?: unknown[];\n\tconst?: unknown;\n\tdefault?: unknown;\n}\n\nexport interface ValidationErrorLike {\n\tinstancePath: string;\n\tkeyword: string;\n\tmessage: string;\n}\n\nexport interface ToolRepairIssue {\n\tpath: string[];\n\tpathText: string;\n\tvalue: unknown;\n\tschema: JsonSchemaObject;\n\tparentSchema?: JsonSchemaObject;\n\tparentValue?: unknown;\n\tpropertyKey?: string;\n\trequired: boolean;\n\tmodes: ToolRepairModeName[];\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isJsonSchemaObject(value: unknown): value is JsonSchemaObject {\n\treturn isRecord(value);\n}\n\nexport function parseInstancePath(instancePath: string): string[] {\n\tif (!instancePath) return [];\n\treturn instancePath\n\t\t.replace(/^\\//, \"\")\n\t\t.split(\"/\")\n\t\t.filter((segment) => segment.length > 0)\n\t\t.map((segment) => segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n}\n\nexport function formatRepairPath(path: readonly string[]): string {\n\treturn path.length === 0 ? \"root\" : path.join(\".\");\n}\n\nexport function getValueAtPath(value: unknown, path: readonly string[]): unknown {\n\tlet cursor = value;\n\tfor (const segment of path) {\n\t\tif (Array.isArray(cursor)) {\n\t\t\tconst index = Number(segment);\n\t\t\tif (!Number.isInteger(index)) return undefined;\n\t\t\tcursor = cursor[index];\n\t\t} else if (isRecord(cursor)) {\n\t\t\tcursor = cursor[segment];\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn cursor;\n}\n\nexport function getSchemaTypes(schema: JsonSchemaObject): string[] {\n\tconst types = new Set<string>();\n\tconst addTypes = (candidate: JsonSchemaObject): void => {\n\t\tif (typeof candidate.type === \"string\") {\n\t\t\ttypes.add(candidate.type);\n\t\t} else if (Array.isArray(candidate.type)) {\n\t\t\tfor (const type of candidate.type) {\n\t\t\t\tif (typeof type === \"string\") types.add(type);\n\t\t\t}\n\t\t}\n\t\tif (candidate.enum?.every((value) => typeof value === \"string\")) {\n\t\t\ttypes.add(\"string\");\n\t\t}\n\t\tif (typeof candidate.const === \"string\") {\n\t\t\ttypes.add(\"string\");\n\t\t}\n\t};\n\n\taddTypes(schema);\n\tfor (const nested of [...(schema.anyOf ?? []), ...(schema.oneOf ?? []), ...(schema.allOf ?? [])]) {\n\t\taddTypes(nested);\n\t}\n\treturn [...types];\n}\n\nexport function getEnumValues(schema: JsonSchemaObject): string[] {\n\tconst values: string[] = [];\n\tconst addValues = (candidate: JsonSchemaObject): void => {\n\t\tif (Array.isArray(candidate.enum)) {\n\t\t\tfor (const value of candidate.enum) {\n\t\t\t\tif (typeof value === \"string\") values.push(value);\n\t\t\t}\n\t\t}\n\t\tif (typeof candidate.const === \"string\") {\n\t\t\tvalues.push(candidate.const);\n\t\t}\n\t};\n\n\taddValues(schema);\n\tfor (const nested of [...(schema.anyOf ?? []), ...(schema.oneOf ?? [])]) {\n\t\taddValues(nested);\n\t}\n\treturn values;\n}\n\nexport function resolveSchemaAtPath(\n\tschema: JsonSchemaObject,\n\tpath: readonly string[],\n): {\n\tschema?: JsonSchemaObject;\n\tparentSchema?: JsonSchemaObject;\n\tpropertyKey?: string;\n} {\n\tlet cursor: JsonSchemaObject | undefined = schema;\n\tlet parentSchema: JsonSchemaObject | undefined;\n\tlet propertyKey: string | undefined;\n\n\tfor (const segment of path) {\n\t\tif (!cursor) return { schema: undefined, parentSchema, propertyKey };\n\t\tparentSchema = cursor;\n\t\tpropertyKey = segment;\n\t\tif (cursor.properties?.[segment]) {\n\t\t\tcursor = cursor.properties[segment];\n\t\t\tcontinue;\n\t\t}\n\t\tif (Array.isArray(cursor.items)) {\n\t\t\tconst index = Number(segment);\n\t\t\tcursor = Number.isInteger(index) ? cursor.items[index] : undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isJsonSchemaObject(cursor.items)) {\n\t\t\tcursor = cursor.items;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isJsonSchemaObject(cursor.additionalProperties)) {\n\t\t\tcursor = cursor.additionalProperties;\n\t\t\tcontinue;\n\t\t}\n\t\treturn { schema: undefined, parentSchema, propertyKey };\n\t}\n\n\treturn { schema: cursor, parentSchema, propertyKey };\n}\n\nfunction isRequired(parentSchema: JsonSchemaObject | undefined, propertyKey: string | undefined): boolean {\n\treturn Boolean(propertyKey && parentSchema?.required?.includes(propertyKey));\n}\n\nfunction isScalarSchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).some((type) => [\"string\", \"number\", \"integer\", \"boolean\"].includes(type));\n}\n\nfunction isArraySchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).includes(\"array\");\n}\n\nfunction isObjectSchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).includes(\"object\");\n}\n\nfunction normalizeEnumValue(value: string): string {\n\treturn value.trim().replace(/\\s+/g, \" \").toLowerCase();\n}\n\nfunction enumNormalizeHasMatch(schema: JsonSchemaObject, value: string): boolean {\n\tconst normalized = normalizeEnumValue(value);\n\treturn getEnumValues(schema).filter((candidate) => normalizeEnumValue(candidate) === normalized).length === 1;\n}\n\nfunction isEmptyObject(value: unknown): boolean {\n\treturn isRecord(value) && Object.keys(value).length === 0;\n}\n\nfunction isScalarValue(value: unknown): boolean {\n\treturn [\"string\", \"number\", \"boolean\"].includes(typeof value);\n}\n\nfunction getParentPath(path: readonly string[]): string[] {\n\treturn path.slice(0, -1);\n}\n\nfunction isNumericArrayParentIssue(\n\targs: Record<string, unknown>,\n\trootSchema: JsonSchemaObject,\n\tpath: readonly string[],\n): ToolRepairIssue | undefined {\n\tif (path.length === 0) return undefined;\n\tconst parentPath = getParentPath(path);\n\tconst parentLookup = resolveSchemaAtPath(rootSchema, parentPath);\n\tconst parentSchema = parentLookup.schema;\n\tif (!parentSchema || !isArraySchema(parentSchema) || Array.isArray(parentSchema.items)) return undefined;\n\tif (!isJsonSchemaObject(parentSchema.items)) return undefined;\n\tconst itemTypes = getSchemaTypes(parentSchema.items);\n\tif (!itemTypes.some((type) => type === \"number\" || type === \"integer\")) return undefined;\n\tconst parentValue = getValueAtPath(args, parentPath);\n\tif (!Array.isArray(parentValue) || !parentValue.every((item) => typeof item === \"string\")) return undefined;\n\treturn {\n\t\tpath: parentPath,\n\t\tpathText: formatRepairPath(parentPath),\n\t\tvalue: parentValue,\n\t\tschema: parentSchema,\n\t\tparentSchema: parentLookup.parentSchema,\n\t\tparentValue: getValueAtPath(args, getParentPath(parentPath)),\n\t\tpropertyKey: parentLookup.propertyKey,\n\t\trequired: isRequired(parentLookup.parentSchema, parentLookup.propertyKey),\n\t\tmodes: [\"stringifiedNumberInArray\"],\n\t};\n}\n\nfunction classifyModes(toolName: string, issue: Omit<ToolRepairIssue, \"modes\">): ToolRepairModeName[] {\n\tconst modes: ToolRepairModeName[] = [];\n\tconst expectedTypes = getSchemaTypes(issue.schema);\n\n\tif (toolName === \"bash\" && issue.pathText === \"command\") {\n\t\tif (Array.isArray(issue.value) && issue.value.every((item) => typeof item === \"string\")) {\n\t\t\tmodes.push(\"bashCommandArgvJoin\");\n\t\t}\n\t\tif (isRecord(issue.value)) {\n\t\t\tmodes.push(\"bashCommandUnwrap\");\n\t\t}\n\t}\n\n\tif (typeof issue.value === \"string\" && (isArraySchema(issue.schema) || isObjectSchema(issue.schema))) {\n\t\tmodes.push(\"jsonStringParse\");\n\t\tif (isObjectSchema(issue.schema)) modes.push(\"jsonObjectPropertySalvage\");\n\t}\n\tif (isArraySchema(issue.schema) && isRecord(issue.value)) {\n\t\tmodes.push(\"singleObjectWrap\", \"emptyObjectPlaceholder\");\n\t}\n\tif (isArraySchema(issue.schema) && isScalarValue(issue.value)) {\n\t\tmodes.push(\"bareScalarWrap\");\n\t}\n\tif (typeof issue.value === \"string\" && expectedTypes.some((type) => type === \"number\" || type === \"integer\")) {\n\t\tmodes.push(\"numberFromString\");\n\t}\n\tif (typeof issue.value === \"string\" && expectedTypes.includes(\"boolean\")) {\n\t\tmodes.push(\"boolFromString\");\n\t}\n\tif (typeof issue.value === \"string\" && enumNormalizeHasMatch(issue.schema, issue.value)) {\n\t\tmodes.push(\"enumCaseNormalize\");\n\t}\n\tif (Array.isArray(issue.value) && issue.value.length === 1 && isScalarSchema(issue.schema)) {\n\t\tmodes.push(\"singleElementUnwrap\");\n\t}\n\tif (!issue.required && isEmptyObject(issue.value) && isScalarSchema(issue.schema)) {\n\t\tmodes.push(\"emptyObjectPlaceholder\");\n\t}\n\tif (issue.value === null) {\n\t\tmodes.push(issue.required ? \"nullRequiredBounce\" : \"nullOptionalDrop\");\n\t}\n\n\treturn modes;\n}\n\nfunction comparePaths(a: readonly string[], b: readonly string[]): number {\n\tconst length = Math.min(a.length, b.length);\n\tfor (let index = 0; index < length; index++) {\n\t\tconst left = a[index];\n\t\tconst right = b[index];\n\t\tconst leftNumber = Number(left);\n\t\tconst rightNumber = Number(right);\n\t\tif (Number.isInteger(leftNumber) && Number.isInteger(rightNumber) && leftNumber !== rightNumber) {\n\t\t\treturn leftNumber - rightNumber;\n\t\t}\n\t\tif (left !== right) return left < right ? -1 : 1;\n\t}\n\treturn a.length - b.length;\n}\n\nexport function analyzeToolArgumentErrors(\n\ttoolName: string,\n\tschema: Tool[\"parameters\"],\n\targs: Record<string, unknown>,\n\terrors: readonly ValidationErrorLike[],\n): ToolRepairIssue[] {\n\tconst rootSchema = schema as JsonSchemaObject;\n\tconst issues = new Map<string, ToolRepairIssue>();\n\n\tfor (const error of errors) {\n\t\tif (error.keyword === \"required\") continue;\n\t\tconst path = parseInstancePath(error.instancePath);\n\t\tconst numericArrayIssue = isNumericArrayParentIssue(args, rootSchema, path);\n\t\tif (numericArrayIssue) {\n\t\t\tissues.set(numericArrayIssue.pathText, numericArrayIssue);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst lookup = resolveSchemaAtPath(rootSchema, path);\n\t\tif (!lookup.schema) continue;\n\t\tconst value = getValueAtPath(args, path);\n\t\tconst issueBase = {\n\t\t\tpath,\n\t\t\tpathText: formatRepairPath(path),\n\t\t\tvalue,\n\t\t\tschema: lookup.schema,\n\t\t\tparentSchema: lookup.parentSchema,\n\t\t\tparentValue: getValueAtPath(args, getParentPath(path)),\n\t\t\tpropertyKey: lookup.propertyKey,\n\t\t\trequired: isRequired(lookup.parentSchema, lookup.propertyKey),\n\t\t};\n\t\tconst modes = classifyModes(toolName, issueBase);\n\t\tif (modes.length > 0) {\n\t\t\tissues.set(issueBase.pathText, { ...issueBase, modes });\n\t\t}\n\t}\n\n\treturn [...issues.values()].sort((left, right) => comparePaths(left.path, right.path));\n}\n"]}
@@ -177,6 +177,8 @@ function classifyModes(toolName, issue) {
177
177
  }
178
178
  if (typeof issue.value === "string" && (isArraySchema(issue.schema) || isObjectSchema(issue.schema))) {
179
179
  modes.push("jsonStringParse");
180
+ if (isObjectSchema(issue.schema))
181
+ modes.push("jsonObjectPropertySalvage");
180
182
  }
181
183
  if (isArraySchema(issue.schema) && isRecord(issue.value)) {
182
184
  modes.push("singleObjectWrap", "emptyObjectPlaceholder");
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../../src/utils/tool-repair/analyzer.ts"],"names":[],"mappings":"AAmCA,MAAM,UAAU,QAAQ,CAAC,KAAc,EAAoC;IAC1E,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAAA,CAC5E;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc,EAA6B;IAC7E,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;AAAA,CACvB;AAED,MAAM,UAAU,iBAAiB,CAAC,YAAoB,EAAY;IACjE,IAAI,CAAC,YAAY;QAAE,OAAO,EAAE,CAAC;IAC7B,OAAO,YAAY;SACjB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;SACvC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AAAA,CACpE;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAuB,EAAU;IACjE,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAAA,CACnD;AAED,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,IAAuB,EAAW;IAChF,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,MAAM,OAAO,IAAI,IAAI,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAC;YAC/C,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACP,OAAO,SAAS,CAAC;QAClB,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED,MAAM,UAAU,cAAc,CAAC,MAAwB,EAAY;IAClE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,QAAQ,GAAG,CAAC,SAA2B,EAAQ,EAAE,CAAC;QACvD,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACxC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;gBACnC,IAAI,OAAO,IAAI,KAAK,QAAQ;oBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC;QACF,CAAC;QACD,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,EAAE,CAAC;YACjE,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;QACD,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACzC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;IAAA,CACD,CAAC;IAEF,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjB,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAClG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;AAAA,CAClB;AAED,MAAM,UAAU,aAAa,CAAC,MAAwB,EAAY;IACjE,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,CAAC,SAA2B,EAAQ,EAAE,CAAC;QACxD,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;gBACpC,IAAI,OAAO,KAAK,KAAK,QAAQ;oBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnD,CAAC;QACF,CAAC;QACD,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IAAA,CACD,CAAC;IAEF,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACzE,SAAS,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED,MAAM,UAAU,mBAAmB,CAClC,MAAwB,EACxB,IAAuB,EAKtB;IACD,IAAI,MAAM,GAAiC,MAAM,CAAC;IAClD,IAAI,YAA0C,CAAC;IAC/C,IAAI,WAA+B,CAAC;IAEpC,KAAK,MAAM,OAAO,IAAI,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;QACrE,YAAY,GAAG,MAAM,CAAC;QACtB,WAAW,GAAG,OAAO,CAAC;QACtB,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACpC,SAAS;QACV,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACnE,SAAS;QACV,CAAC;QACD,IAAI,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC;YACtB,SAAS;QACV,CAAC;QACD,IAAI,kBAAkB,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACrD,MAAM,GAAG,MAAM,CAAC,oBAAoB,CAAC;YACrC,SAAS;QACV,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;IACzD,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AAAA,CACrD;AAED,SAAS,UAAU,CAAC,YAA0C,EAAE,WAA+B,EAAW;IACzG,OAAO,OAAO,CAAC,WAAW,IAAI,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;AAAA,CAC7E;AAED,SAAS,cAAc,CAAC,MAAwB,EAAW;IAC1D,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAAA,CACxG;AAED,SAAS,aAAa,CAAC,MAAwB,EAAW;IACzD,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAAA,CAChD;AAED,SAAS,cAAc,CAAC,MAAwB,EAAW;IAC1D,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAAA,CACjD;AAED,SAAS,kBAAkB,CAAC,KAAa,EAAU;IAClD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;AAAA,CACvD;AAED,SAAS,qBAAqB,CAAC,MAAwB,EAAE,KAAa,EAAW;IAChF,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,kBAAkB,CAAC,SAAS,CAAC,KAAK,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAAA,CAC9G;AAED,SAAS,aAAa,CAAC,KAAc,EAAW;IAC/C,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAAA,CAC1D;AAED,SAAS,aAAa,CAAC,KAAc,EAAW;IAC/C,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,KAAK,CAAC,CAAC;AAAA,CAC9D;AAED,SAAS,aAAa,CAAC,IAAuB,EAAY;IACzD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAAA,CACzB;AAED,SAAS,yBAAyB,CACjC,IAA6B,EAC7B,UAA4B,EAC5B,IAAuB,EACO;IAC9B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,YAAY,GAAG,mBAAmB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACjE,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC;IACzC,IAAI,CAAC,YAAY,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACzG,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9D,MAAM,SAAS,GAAG,cAAc,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACzF,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5G,OAAO;QACN,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC;QACtC,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,YAAY;QACpB,YAAY,EAAE,YAAY,CAAC,YAAY;QACvC,WAAW,EAAE,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;QAC5D,WAAW,EAAE,YAAY,CAAC,WAAW;QACrC,QAAQ,EAAE,UAAU,CAAC,YAAY,CAAC,YAAY,EAAE,YAAY,CAAC,WAAW,CAAC;QACzE,KAAK,EAAE,CAAC,0BAA0B,CAAC;KACnC,CAAC;AAAA,CACF;AAED,SAAS,aAAa,CAAC,QAAgB,EAAE,KAAqC,EAAwB;IACrG,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,MAAM,aAAa,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEnD,IAAI,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACzD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;YACzF,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;IAED,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACtG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,wBAAwB,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;QAC9G,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1E,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,qBAAqB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5F,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACnF,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,YAAY,CAAC,CAAoB,EAAE,CAAoB,EAAU;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACtB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACvB,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;YACjG,OAAO,UAAU,GAAG,WAAW,CAAC;QACjC,CAAC;QACD,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;AAAA,CAC3B;AAED,MAAM,UAAU,yBAAyB,CACxC,QAAgB,EAChB,MAA0B,EAC1B,IAA6B,EAC7B,MAAsC,EAClB;IACpB,MAAM,UAAU,GAAG,MAA0B,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAC;IAElD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,OAAO,KAAK,UAAU;YAAE,SAAS;QAC3C,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAC5E,IAAI,iBAAiB,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;YAC1D,SAAS;QACV,CAAC;QAED,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,SAAS;QAC7B,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG;YACjB,IAAI;YACJ,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC;YAChC,KAAK;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,WAAW,EAAE,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;YACtD,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC;SAC7D,CAAC;QACF,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACjD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACzD,CAAC;IACF,CAAC;IAED,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;AAAA,CACvF","sourcesContent":["import type { Tool } from \"../../types.ts\";\nimport type { ToolRepairModeName } from \"./registry.ts\";\n\nexport interface JsonSchemaObject {\n\ttype?: string | string[];\n\tproperties?: Record<string, JsonSchemaObject>;\n\titems?: JsonSchemaObject | JsonSchemaObject[];\n\trequired?: string[];\n\tadditionalProperties?: boolean | JsonSchemaObject;\n\tallOf?: JsonSchemaObject[];\n\tanyOf?: JsonSchemaObject[];\n\toneOf?: JsonSchemaObject[];\n\tenum?: unknown[];\n\tconst?: unknown;\n\tdefault?: unknown;\n}\n\nexport interface ValidationErrorLike {\n\tinstancePath: string;\n\tkeyword: string;\n\tmessage: string;\n}\n\nexport interface ToolRepairIssue {\n\tpath: string[];\n\tpathText: string;\n\tvalue: unknown;\n\tschema: JsonSchemaObject;\n\tparentSchema?: JsonSchemaObject;\n\tparentValue?: unknown;\n\tpropertyKey?: string;\n\trequired: boolean;\n\tmodes: ToolRepairModeName[];\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isJsonSchemaObject(value: unknown): value is JsonSchemaObject {\n\treturn isRecord(value);\n}\n\nexport function parseInstancePath(instancePath: string): string[] {\n\tif (!instancePath) return [];\n\treturn instancePath\n\t\t.replace(/^\\//, \"\")\n\t\t.split(\"/\")\n\t\t.filter((segment) => segment.length > 0)\n\t\t.map((segment) => segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n}\n\nexport function formatRepairPath(path: readonly string[]): string {\n\treturn path.length === 0 ? \"root\" : path.join(\".\");\n}\n\nexport function getValueAtPath(value: unknown, path: readonly string[]): unknown {\n\tlet cursor = value;\n\tfor (const segment of path) {\n\t\tif (Array.isArray(cursor)) {\n\t\t\tconst index = Number(segment);\n\t\t\tif (!Number.isInteger(index)) return undefined;\n\t\t\tcursor = cursor[index];\n\t\t} else if (isRecord(cursor)) {\n\t\t\tcursor = cursor[segment];\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn cursor;\n}\n\nexport function getSchemaTypes(schema: JsonSchemaObject): string[] {\n\tconst types = new Set<string>();\n\tconst addTypes = (candidate: JsonSchemaObject): void => {\n\t\tif (typeof candidate.type === \"string\") {\n\t\t\ttypes.add(candidate.type);\n\t\t} else if (Array.isArray(candidate.type)) {\n\t\t\tfor (const type of candidate.type) {\n\t\t\t\tif (typeof type === \"string\") types.add(type);\n\t\t\t}\n\t\t}\n\t\tif (candidate.enum?.every((value) => typeof value === \"string\")) {\n\t\t\ttypes.add(\"string\");\n\t\t}\n\t\tif (typeof candidate.const === \"string\") {\n\t\t\ttypes.add(\"string\");\n\t\t}\n\t};\n\n\taddTypes(schema);\n\tfor (const nested of [...(schema.anyOf ?? []), ...(schema.oneOf ?? []), ...(schema.allOf ?? [])]) {\n\t\taddTypes(nested);\n\t}\n\treturn [...types];\n}\n\nexport function getEnumValues(schema: JsonSchemaObject): string[] {\n\tconst values: string[] = [];\n\tconst addValues = (candidate: JsonSchemaObject): void => {\n\t\tif (Array.isArray(candidate.enum)) {\n\t\t\tfor (const value of candidate.enum) {\n\t\t\t\tif (typeof value === \"string\") values.push(value);\n\t\t\t}\n\t\t}\n\t\tif (typeof candidate.const === \"string\") {\n\t\t\tvalues.push(candidate.const);\n\t\t}\n\t};\n\n\taddValues(schema);\n\tfor (const nested of [...(schema.anyOf ?? []), ...(schema.oneOf ?? [])]) {\n\t\taddValues(nested);\n\t}\n\treturn values;\n}\n\nexport function resolveSchemaAtPath(\n\tschema: JsonSchemaObject,\n\tpath: readonly string[],\n): {\n\tschema?: JsonSchemaObject;\n\tparentSchema?: JsonSchemaObject;\n\tpropertyKey?: string;\n} {\n\tlet cursor: JsonSchemaObject | undefined = schema;\n\tlet parentSchema: JsonSchemaObject | undefined;\n\tlet propertyKey: string | undefined;\n\n\tfor (const segment of path) {\n\t\tif (!cursor) return { schema: undefined, parentSchema, propertyKey };\n\t\tparentSchema = cursor;\n\t\tpropertyKey = segment;\n\t\tif (cursor.properties?.[segment]) {\n\t\t\tcursor = cursor.properties[segment];\n\t\t\tcontinue;\n\t\t}\n\t\tif (Array.isArray(cursor.items)) {\n\t\t\tconst index = Number(segment);\n\t\t\tcursor = Number.isInteger(index) ? cursor.items[index] : undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isJsonSchemaObject(cursor.items)) {\n\t\t\tcursor = cursor.items;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isJsonSchemaObject(cursor.additionalProperties)) {\n\t\t\tcursor = cursor.additionalProperties;\n\t\t\tcontinue;\n\t\t}\n\t\treturn { schema: undefined, parentSchema, propertyKey };\n\t}\n\n\treturn { schema: cursor, parentSchema, propertyKey };\n}\n\nfunction isRequired(parentSchema: JsonSchemaObject | undefined, propertyKey: string | undefined): boolean {\n\treturn Boolean(propertyKey && parentSchema?.required?.includes(propertyKey));\n}\n\nfunction isScalarSchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).some((type) => [\"string\", \"number\", \"integer\", \"boolean\"].includes(type));\n}\n\nfunction isArraySchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).includes(\"array\");\n}\n\nfunction isObjectSchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).includes(\"object\");\n}\n\nfunction normalizeEnumValue(value: string): string {\n\treturn value.trim().replace(/\\s+/g, \" \").toLowerCase();\n}\n\nfunction enumNormalizeHasMatch(schema: JsonSchemaObject, value: string): boolean {\n\tconst normalized = normalizeEnumValue(value);\n\treturn getEnumValues(schema).filter((candidate) => normalizeEnumValue(candidate) === normalized).length === 1;\n}\n\nfunction isEmptyObject(value: unknown): boolean {\n\treturn isRecord(value) && Object.keys(value).length === 0;\n}\n\nfunction isScalarValue(value: unknown): boolean {\n\treturn [\"string\", \"number\", \"boolean\"].includes(typeof value);\n}\n\nfunction getParentPath(path: readonly string[]): string[] {\n\treturn path.slice(0, -1);\n}\n\nfunction isNumericArrayParentIssue(\n\targs: Record<string, unknown>,\n\trootSchema: JsonSchemaObject,\n\tpath: readonly string[],\n): ToolRepairIssue | undefined {\n\tif (path.length === 0) return undefined;\n\tconst parentPath = getParentPath(path);\n\tconst parentLookup = resolveSchemaAtPath(rootSchema, parentPath);\n\tconst parentSchema = parentLookup.schema;\n\tif (!parentSchema || !isArraySchema(parentSchema) || Array.isArray(parentSchema.items)) return undefined;\n\tif (!isJsonSchemaObject(parentSchema.items)) return undefined;\n\tconst itemTypes = getSchemaTypes(parentSchema.items);\n\tif (!itemTypes.some((type) => type === \"number\" || type === \"integer\")) return undefined;\n\tconst parentValue = getValueAtPath(args, parentPath);\n\tif (!Array.isArray(parentValue) || !parentValue.every((item) => typeof item === \"string\")) return undefined;\n\treturn {\n\t\tpath: parentPath,\n\t\tpathText: formatRepairPath(parentPath),\n\t\tvalue: parentValue,\n\t\tschema: parentSchema,\n\t\tparentSchema: parentLookup.parentSchema,\n\t\tparentValue: getValueAtPath(args, getParentPath(parentPath)),\n\t\tpropertyKey: parentLookup.propertyKey,\n\t\trequired: isRequired(parentLookup.parentSchema, parentLookup.propertyKey),\n\t\tmodes: [\"stringifiedNumberInArray\"],\n\t};\n}\n\nfunction classifyModes(toolName: string, issue: Omit<ToolRepairIssue, \"modes\">): ToolRepairModeName[] {\n\tconst modes: ToolRepairModeName[] = [];\n\tconst expectedTypes = getSchemaTypes(issue.schema);\n\n\tif (toolName === \"bash\" && issue.pathText === \"command\") {\n\t\tif (Array.isArray(issue.value) && issue.value.every((item) => typeof item === \"string\")) {\n\t\t\tmodes.push(\"bashCommandArgvJoin\");\n\t\t}\n\t\tif (isRecord(issue.value)) {\n\t\t\tmodes.push(\"bashCommandUnwrap\");\n\t\t}\n\t}\n\n\tif (typeof issue.value === \"string\" && (isArraySchema(issue.schema) || isObjectSchema(issue.schema))) {\n\t\tmodes.push(\"jsonStringParse\");\n\t}\n\tif (isArraySchema(issue.schema) && isRecord(issue.value)) {\n\t\tmodes.push(\"singleObjectWrap\", \"emptyObjectPlaceholder\");\n\t}\n\tif (isArraySchema(issue.schema) && isScalarValue(issue.value)) {\n\t\tmodes.push(\"bareScalarWrap\");\n\t}\n\tif (typeof issue.value === \"string\" && expectedTypes.some((type) => type === \"number\" || type === \"integer\")) {\n\t\tmodes.push(\"numberFromString\");\n\t}\n\tif (typeof issue.value === \"string\" && expectedTypes.includes(\"boolean\")) {\n\t\tmodes.push(\"boolFromString\");\n\t}\n\tif (typeof issue.value === \"string\" && enumNormalizeHasMatch(issue.schema, issue.value)) {\n\t\tmodes.push(\"enumCaseNormalize\");\n\t}\n\tif (Array.isArray(issue.value) && issue.value.length === 1 && isScalarSchema(issue.schema)) {\n\t\tmodes.push(\"singleElementUnwrap\");\n\t}\n\tif (!issue.required && isEmptyObject(issue.value) && isScalarSchema(issue.schema)) {\n\t\tmodes.push(\"emptyObjectPlaceholder\");\n\t}\n\tif (issue.value === null) {\n\t\tmodes.push(issue.required ? \"nullRequiredBounce\" : \"nullOptionalDrop\");\n\t}\n\n\treturn modes;\n}\n\nfunction comparePaths(a: readonly string[], b: readonly string[]): number {\n\tconst length = Math.min(a.length, b.length);\n\tfor (let index = 0; index < length; index++) {\n\t\tconst left = a[index];\n\t\tconst right = b[index];\n\t\tconst leftNumber = Number(left);\n\t\tconst rightNumber = Number(right);\n\t\tif (Number.isInteger(leftNumber) && Number.isInteger(rightNumber) && leftNumber !== rightNumber) {\n\t\t\treturn leftNumber - rightNumber;\n\t\t}\n\t\tif (left !== right) return left < right ? -1 : 1;\n\t}\n\treturn a.length - b.length;\n}\n\nexport function analyzeToolArgumentErrors(\n\ttoolName: string,\n\tschema: Tool[\"parameters\"],\n\targs: Record<string, unknown>,\n\terrors: readonly ValidationErrorLike[],\n): ToolRepairIssue[] {\n\tconst rootSchema = schema as JsonSchemaObject;\n\tconst issues = new Map<string, ToolRepairIssue>();\n\n\tfor (const error of errors) {\n\t\tif (error.keyword === \"required\") continue;\n\t\tconst path = parseInstancePath(error.instancePath);\n\t\tconst numericArrayIssue = isNumericArrayParentIssue(args, rootSchema, path);\n\t\tif (numericArrayIssue) {\n\t\t\tissues.set(numericArrayIssue.pathText, numericArrayIssue);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst lookup = resolveSchemaAtPath(rootSchema, path);\n\t\tif (!lookup.schema) continue;\n\t\tconst value = getValueAtPath(args, path);\n\t\tconst issueBase = {\n\t\t\tpath,\n\t\t\tpathText: formatRepairPath(path),\n\t\t\tvalue,\n\t\t\tschema: lookup.schema,\n\t\t\tparentSchema: lookup.parentSchema,\n\t\t\tparentValue: getValueAtPath(args, getParentPath(path)),\n\t\t\tpropertyKey: lookup.propertyKey,\n\t\t\trequired: isRequired(lookup.parentSchema, lookup.propertyKey),\n\t\t};\n\t\tconst modes = classifyModes(toolName, issueBase);\n\t\tif (modes.length > 0) {\n\t\t\tissues.set(issueBase.pathText, { ...issueBase, modes });\n\t\t}\n\t}\n\n\treturn [...issues.values()].sort((left, right) => comparePaths(left.path, right.path));\n}\n"]}
1
+ {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../../src/utils/tool-repair/analyzer.ts"],"names":[],"mappings":"AAmCA,MAAM,UAAU,QAAQ,CAAC,KAAc,EAAoC;IAC1E,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAAA,CAC5E;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc,EAA6B;IAC7E,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;AAAA,CACvB;AAED,MAAM,UAAU,iBAAiB,CAAC,YAAoB,EAAY;IACjE,IAAI,CAAC,YAAY;QAAE,OAAO,EAAE,CAAC;IAC7B,OAAO,YAAY;SACjB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;SACvC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AAAA,CACpE;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAuB,EAAU;IACjE,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAAA,CACnD;AAED,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,IAAuB,EAAW;IAChF,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,MAAM,OAAO,IAAI,IAAI,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAC;YAC/C,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACP,OAAO,SAAS,CAAC;QAClB,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED,MAAM,UAAU,cAAc,CAAC,MAAwB,EAAY;IAClE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,QAAQ,GAAG,CAAC,SAA2B,EAAQ,EAAE,CAAC;QACvD,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACxC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;gBACnC,IAAI,OAAO,IAAI,KAAK,QAAQ;oBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC;QACF,CAAC;QACD,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,EAAE,CAAC;YACjE,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;QACD,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACzC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;IAAA,CACD,CAAC;IAEF,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjB,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAClG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;AAAA,CAClB;AAED,MAAM,UAAU,aAAa,CAAC,MAAwB,EAAY;IACjE,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,CAAC,SAA2B,EAAQ,EAAE,CAAC;QACxD,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;gBACpC,IAAI,OAAO,KAAK,KAAK,QAAQ;oBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnD,CAAC;QACF,CAAC;QACD,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IAAA,CACD,CAAC;IAEF,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACzE,SAAS,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED,MAAM,UAAU,mBAAmB,CAClC,MAAwB,EACxB,IAAuB,EAKtB;IACD,IAAI,MAAM,GAAiC,MAAM,CAAC;IAClD,IAAI,YAA0C,CAAC;IAC/C,IAAI,WAA+B,CAAC;IAEpC,KAAK,MAAM,OAAO,IAAI,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;QACrE,YAAY,GAAG,MAAM,CAAC;QACtB,WAAW,GAAG,OAAO,CAAC;QACtB,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACpC,SAAS;QACV,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACnE,SAAS;QACV,CAAC;QACD,IAAI,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC;YACtB,SAAS;QACV,CAAC;QACD,IAAI,kBAAkB,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACrD,MAAM,GAAG,MAAM,CAAC,oBAAoB,CAAC;YACrC,SAAS;QACV,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;IACzD,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AAAA,CACrD;AAED,SAAS,UAAU,CAAC,YAA0C,EAAE,WAA+B,EAAW;IACzG,OAAO,OAAO,CAAC,WAAW,IAAI,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;AAAA,CAC7E;AAED,SAAS,cAAc,CAAC,MAAwB,EAAW;IAC1D,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAAA,CACxG;AAED,SAAS,aAAa,CAAC,MAAwB,EAAW;IACzD,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAAA,CAChD;AAED,SAAS,cAAc,CAAC,MAAwB,EAAW;IAC1D,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAAA,CACjD;AAED,SAAS,kBAAkB,CAAC,KAAa,EAAU;IAClD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;AAAA,CACvD;AAED,SAAS,qBAAqB,CAAC,MAAwB,EAAE,KAAa,EAAW;IAChF,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,kBAAkB,CAAC,SAAS,CAAC,KAAK,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAAA,CAC9G;AAED,SAAS,aAAa,CAAC,KAAc,EAAW;IAC/C,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAAA,CAC1D;AAED,SAAS,aAAa,CAAC,KAAc,EAAW;IAC/C,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,KAAK,CAAC,CAAC;AAAA,CAC9D;AAED,SAAS,aAAa,CAAC,IAAuB,EAAY;IACzD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAAA,CACzB;AAED,SAAS,yBAAyB,CACjC,IAA6B,EAC7B,UAA4B,EAC5B,IAAuB,EACO;IAC9B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,YAAY,GAAG,mBAAmB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACjE,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC;IACzC,IAAI,CAAC,YAAY,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACzG,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9D,MAAM,SAAS,GAAG,cAAc,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACzF,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5G,OAAO;QACN,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC;QACtC,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,YAAY;QACpB,YAAY,EAAE,YAAY,CAAC,YAAY;QACvC,WAAW,EAAE,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;QAC5D,WAAW,EAAE,YAAY,CAAC,WAAW;QACrC,QAAQ,EAAE,UAAU,CAAC,YAAY,CAAC,YAAY,EAAE,YAAY,CAAC,WAAW,CAAC;QACzE,KAAK,EAAE,CAAC,0BAA0B,CAAC;KACnC,CAAC;AAAA,CACF;AAED,SAAS,aAAa,CAAC,QAAgB,EAAE,KAAqC,EAAwB;IACrG,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,MAAM,aAAa,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEnD,IAAI,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACzD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;YACzF,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;IAED,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACtG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC9B,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,wBAAwB,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;QAC9G,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1E,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,qBAAqB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5F,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACnF,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,YAAY,CAAC,CAAoB,EAAE,CAAoB,EAAU;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACtB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACvB,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;YACjG,OAAO,UAAU,GAAG,WAAW,CAAC;QACjC,CAAC;QACD,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;AAAA,CAC3B;AAED,MAAM,UAAU,yBAAyB,CACxC,QAAgB,EAChB,MAA0B,EAC1B,IAA6B,EAC7B,MAAsC,EAClB;IACpB,MAAM,UAAU,GAAG,MAA0B,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAC;IAElD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,OAAO,KAAK,UAAU;YAAE,SAAS;QAC3C,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAC5E,IAAI,iBAAiB,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;YAC1D,SAAS;QACV,CAAC;QAED,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,SAAS;QAC7B,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG;YACjB,IAAI;YACJ,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC;YAChC,KAAK;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,WAAW,EAAE,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;YACtD,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC;SAC7D,CAAC;QACF,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACjD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACzD,CAAC;IACF,CAAC;IAED,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;AAAA,CACvF","sourcesContent":["import type { Tool } from \"../../types.ts\";\nimport type { ToolRepairModeName } from \"./registry.ts\";\n\nexport interface JsonSchemaObject {\n\ttype?: string | string[];\n\tproperties?: Record<string, JsonSchemaObject>;\n\titems?: JsonSchemaObject | JsonSchemaObject[];\n\trequired?: string[];\n\tadditionalProperties?: boolean | JsonSchemaObject;\n\tallOf?: JsonSchemaObject[];\n\tanyOf?: JsonSchemaObject[];\n\toneOf?: JsonSchemaObject[];\n\tenum?: unknown[];\n\tconst?: unknown;\n\tdefault?: unknown;\n}\n\nexport interface ValidationErrorLike {\n\tinstancePath: string;\n\tkeyword: string;\n\tmessage: string;\n}\n\nexport interface ToolRepairIssue {\n\tpath: string[];\n\tpathText: string;\n\tvalue: unknown;\n\tschema: JsonSchemaObject;\n\tparentSchema?: JsonSchemaObject;\n\tparentValue?: unknown;\n\tpropertyKey?: string;\n\trequired: boolean;\n\tmodes: ToolRepairModeName[];\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isJsonSchemaObject(value: unknown): value is JsonSchemaObject {\n\treturn isRecord(value);\n}\n\nexport function parseInstancePath(instancePath: string): string[] {\n\tif (!instancePath) return [];\n\treturn instancePath\n\t\t.replace(/^\\//, \"\")\n\t\t.split(\"/\")\n\t\t.filter((segment) => segment.length > 0)\n\t\t.map((segment) => segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n}\n\nexport function formatRepairPath(path: readonly string[]): string {\n\treturn path.length === 0 ? \"root\" : path.join(\".\");\n}\n\nexport function getValueAtPath(value: unknown, path: readonly string[]): unknown {\n\tlet cursor = value;\n\tfor (const segment of path) {\n\t\tif (Array.isArray(cursor)) {\n\t\t\tconst index = Number(segment);\n\t\t\tif (!Number.isInteger(index)) return undefined;\n\t\t\tcursor = cursor[index];\n\t\t} else if (isRecord(cursor)) {\n\t\t\tcursor = cursor[segment];\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn cursor;\n}\n\nexport function getSchemaTypes(schema: JsonSchemaObject): string[] {\n\tconst types = new Set<string>();\n\tconst addTypes = (candidate: JsonSchemaObject): void => {\n\t\tif (typeof candidate.type === \"string\") {\n\t\t\ttypes.add(candidate.type);\n\t\t} else if (Array.isArray(candidate.type)) {\n\t\t\tfor (const type of candidate.type) {\n\t\t\t\tif (typeof type === \"string\") types.add(type);\n\t\t\t}\n\t\t}\n\t\tif (candidate.enum?.every((value) => typeof value === \"string\")) {\n\t\t\ttypes.add(\"string\");\n\t\t}\n\t\tif (typeof candidate.const === \"string\") {\n\t\t\ttypes.add(\"string\");\n\t\t}\n\t};\n\n\taddTypes(schema);\n\tfor (const nested of [...(schema.anyOf ?? []), ...(schema.oneOf ?? []), ...(schema.allOf ?? [])]) {\n\t\taddTypes(nested);\n\t}\n\treturn [...types];\n}\n\nexport function getEnumValues(schema: JsonSchemaObject): string[] {\n\tconst values: string[] = [];\n\tconst addValues = (candidate: JsonSchemaObject): void => {\n\t\tif (Array.isArray(candidate.enum)) {\n\t\t\tfor (const value of candidate.enum) {\n\t\t\t\tif (typeof value === \"string\") values.push(value);\n\t\t\t}\n\t\t}\n\t\tif (typeof candidate.const === \"string\") {\n\t\t\tvalues.push(candidate.const);\n\t\t}\n\t};\n\n\taddValues(schema);\n\tfor (const nested of [...(schema.anyOf ?? []), ...(schema.oneOf ?? [])]) {\n\t\taddValues(nested);\n\t}\n\treturn values;\n}\n\nexport function resolveSchemaAtPath(\n\tschema: JsonSchemaObject,\n\tpath: readonly string[],\n): {\n\tschema?: JsonSchemaObject;\n\tparentSchema?: JsonSchemaObject;\n\tpropertyKey?: string;\n} {\n\tlet cursor: JsonSchemaObject | undefined = schema;\n\tlet parentSchema: JsonSchemaObject | undefined;\n\tlet propertyKey: string | undefined;\n\n\tfor (const segment of path) {\n\t\tif (!cursor) return { schema: undefined, parentSchema, propertyKey };\n\t\tparentSchema = cursor;\n\t\tpropertyKey = segment;\n\t\tif (cursor.properties?.[segment]) {\n\t\t\tcursor = cursor.properties[segment];\n\t\t\tcontinue;\n\t\t}\n\t\tif (Array.isArray(cursor.items)) {\n\t\t\tconst index = Number(segment);\n\t\t\tcursor = Number.isInteger(index) ? cursor.items[index] : undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isJsonSchemaObject(cursor.items)) {\n\t\t\tcursor = cursor.items;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isJsonSchemaObject(cursor.additionalProperties)) {\n\t\t\tcursor = cursor.additionalProperties;\n\t\t\tcontinue;\n\t\t}\n\t\treturn { schema: undefined, parentSchema, propertyKey };\n\t}\n\n\treturn { schema: cursor, parentSchema, propertyKey };\n}\n\nfunction isRequired(parentSchema: JsonSchemaObject | undefined, propertyKey: string | undefined): boolean {\n\treturn Boolean(propertyKey && parentSchema?.required?.includes(propertyKey));\n}\n\nfunction isScalarSchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).some((type) => [\"string\", \"number\", \"integer\", \"boolean\"].includes(type));\n}\n\nfunction isArraySchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).includes(\"array\");\n}\n\nfunction isObjectSchema(schema: JsonSchemaObject): boolean {\n\treturn getSchemaTypes(schema).includes(\"object\");\n}\n\nfunction normalizeEnumValue(value: string): string {\n\treturn value.trim().replace(/\\s+/g, \" \").toLowerCase();\n}\n\nfunction enumNormalizeHasMatch(schema: JsonSchemaObject, value: string): boolean {\n\tconst normalized = normalizeEnumValue(value);\n\treturn getEnumValues(schema).filter((candidate) => normalizeEnumValue(candidate) === normalized).length === 1;\n}\n\nfunction isEmptyObject(value: unknown): boolean {\n\treturn isRecord(value) && Object.keys(value).length === 0;\n}\n\nfunction isScalarValue(value: unknown): boolean {\n\treturn [\"string\", \"number\", \"boolean\"].includes(typeof value);\n}\n\nfunction getParentPath(path: readonly string[]): string[] {\n\treturn path.slice(0, -1);\n}\n\nfunction isNumericArrayParentIssue(\n\targs: Record<string, unknown>,\n\trootSchema: JsonSchemaObject,\n\tpath: readonly string[],\n): ToolRepairIssue | undefined {\n\tif (path.length === 0) return undefined;\n\tconst parentPath = getParentPath(path);\n\tconst parentLookup = resolveSchemaAtPath(rootSchema, parentPath);\n\tconst parentSchema = parentLookup.schema;\n\tif (!parentSchema || !isArraySchema(parentSchema) || Array.isArray(parentSchema.items)) return undefined;\n\tif (!isJsonSchemaObject(parentSchema.items)) return undefined;\n\tconst itemTypes = getSchemaTypes(parentSchema.items);\n\tif (!itemTypes.some((type) => type === \"number\" || type === \"integer\")) return undefined;\n\tconst parentValue = getValueAtPath(args, parentPath);\n\tif (!Array.isArray(parentValue) || !parentValue.every((item) => typeof item === \"string\")) return undefined;\n\treturn {\n\t\tpath: parentPath,\n\t\tpathText: formatRepairPath(parentPath),\n\t\tvalue: parentValue,\n\t\tschema: parentSchema,\n\t\tparentSchema: parentLookup.parentSchema,\n\t\tparentValue: getValueAtPath(args, getParentPath(parentPath)),\n\t\tpropertyKey: parentLookup.propertyKey,\n\t\trequired: isRequired(parentLookup.parentSchema, parentLookup.propertyKey),\n\t\tmodes: [\"stringifiedNumberInArray\"],\n\t};\n}\n\nfunction classifyModes(toolName: string, issue: Omit<ToolRepairIssue, \"modes\">): ToolRepairModeName[] {\n\tconst modes: ToolRepairModeName[] = [];\n\tconst expectedTypes = getSchemaTypes(issue.schema);\n\n\tif (toolName === \"bash\" && issue.pathText === \"command\") {\n\t\tif (Array.isArray(issue.value) && issue.value.every((item) => typeof item === \"string\")) {\n\t\t\tmodes.push(\"bashCommandArgvJoin\");\n\t\t}\n\t\tif (isRecord(issue.value)) {\n\t\t\tmodes.push(\"bashCommandUnwrap\");\n\t\t}\n\t}\n\n\tif (typeof issue.value === \"string\" && (isArraySchema(issue.schema) || isObjectSchema(issue.schema))) {\n\t\tmodes.push(\"jsonStringParse\");\n\t\tif (isObjectSchema(issue.schema)) modes.push(\"jsonObjectPropertySalvage\");\n\t}\n\tif (isArraySchema(issue.schema) && isRecord(issue.value)) {\n\t\tmodes.push(\"singleObjectWrap\", \"emptyObjectPlaceholder\");\n\t}\n\tif (isArraySchema(issue.schema) && isScalarValue(issue.value)) {\n\t\tmodes.push(\"bareScalarWrap\");\n\t}\n\tif (typeof issue.value === \"string\" && expectedTypes.some((type) => type === \"number\" || type === \"integer\")) {\n\t\tmodes.push(\"numberFromString\");\n\t}\n\tif (typeof issue.value === \"string\" && expectedTypes.includes(\"boolean\")) {\n\t\tmodes.push(\"boolFromString\");\n\t}\n\tif (typeof issue.value === \"string\" && enumNormalizeHasMatch(issue.schema, issue.value)) {\n\t\tmodes.push(\"enumCaseNormalize\");\n\t}\n\tif (Array.isArray(issue.value) && issue.value.length === 1 && isScalarSchema(issue.schema)) {\n\t\tmodes.push(\"singleElementUnwrap\");\n\t}\n\tif (!issue.required && isEmptyObject(issue.value) && isScalarSchema(issue.schema)) {\n\t\tmodes.push(\"emptyObjectPlaceholder\");\n\t}\n\tif (issue.value === null) {\n\t\tmodes.push(issue.required ? \"nullRequiredBounce\" : \"nullOptionalDrop\");\n\t}\n\n\treturn modes;\n}\n\nfunction comparePaths(a: readonly string[], b: readonly string[]): number {\n\tconst length = Math.min(a.length, b.length);\n\tfor (let index = 0; index < length; index++) {\n\t\tconst left = a[index];\n\t\tconst right = b[index];\n\t\tconst leftNumber = Number(left);\n\t\tconst rightNumber = Number(right);\n\t\tif (Number.isInteger(leftNumber) && Number.isInteger(rightNumber) && leftNumber !== rightNumber) {\n\t\t\treturn leftNumber - rightNumber;\n\t\t}\n\t\tif (left !== right) return left < right ? -1 : 1;\n\t}\n\treturn a.length - b.length;\n}\n\nexport function analyzeToolArgumentErrors(\n\ttoolName: string,\n\tschema: Tool[\"parameters\"],\n\targs: Record<string, unknown>,\n\terrors: readonly ValidationErrorLike[],\n): ToolRepairIssue[] {\n\tconst rootSchema = schema as JsonSchemaObject;\n\tconst issues = new Map<string, ToolRepairIssue>();\n\n\tfor (const error of errors) {\n\t\tif (error.keyword === \"required\") continue;\n\t\tconst path = parseInstancePath(error.instancePath);\n\t\tconst numericArrayIssue = isNumericArrayParentIssue(args, rootSchema, path);\n\t\tif (numericArrayIssue) {\n\t\t\tissues.set(numericArrayIssue.pathText, numericArrayIssue);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst lookup = resolveSchemaAtPath(rootSchema, path);\n\t\tif (!lookup.schema) continue;\n\t\tconst value = getValueAtPath(args, path);\n\t\tconst issueBase = {\n\t\t\tpath,\n\t\t\tpathText: formatRepairPath(path),\n\t\t\tvalue,\n\t\t\tschema: lookup.schema,\n\t\t\tparentSchema: lookup.parentSchema,\n\t\t\tparentValue: getValueAtPath(args, getParentPath(path)),\n\t\t\tpropertyKey: lookup.propertyKey,\n\t\t\trequired: isRequired(lookup.parentSchema, lookup.propertyKey),\n\t\t};\n\t\tconst modes = classifyModes(toolName, issueBase);\n\t\tif (modes.length > 0) {\n\t\t\tissues.set(issueBase.pathText, { ...issueBase, modes });\n\t\t}\n\t}\n\n\treturn [...issues.values()].sort((left, right) => comparePaths(left.path, right.path));\n}\n"]}
@@ -1,4 +1,4 @@
1
- export declare const TOOL_REPAIR_MODE_NAMES: readonly ["nullOptionalDrop", "nullRequiredBounce", "jsonStringParse", "singleObjectWrap", "bareScalarWrap", "emptyObjectPlaceholder", "numberFromString", "boolFromString", "enumCaseNormalize", "singleElementUnwrap", "stringifiedNumberInArray", "bashCommandArgvJoin", "bashCommandUnwrap"];
1
+ export declare const TOOL_REPAIR_MODE_NAMES: readonly ["nullOptionalDrop", "nullRequiredBounce", "jsonStringParse", "jsonObjectPropertySalvage", "singleObjectWrap", "bareScalarWrap", "emptyObjectPlaceholder", "numberFromString", "boolFromString", "enumCaseNormalize", "propertyCaseNormalize", "singleElementUnwrap", "stringifiedNumberInArray", "bashCommandArgvJoin", "bashCommandUnwrap"];
2
2
  export type ToolRepairModeName = (typeof TOOL_REPAIR_MODE_NAMES)[number];
3
3
  export type ToolRepairFailureModeName = ToolRepairModeName | "other";
4
4
  export interface ToolRepairRegistryEntry {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/utils/tool-repair/registry.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,kSAczB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AACzE,MAAM,MAAM,yBAAyB,GAAG,kBAAkB,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,uBAAuB;IACvC,IAAI,EAAE,kBAAkB,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,oBAAoB,EAAE,SAAS,uBAAuB,EAoEzD,CAAC;AAIX,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,kBAAkB,GAAG,uBAAuB,CAI5F;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAE7E;AAED,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;EA6BjC,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9F,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEtF","sourcesContent":["export const TOOL_REPAIR_MODE_NAMES = [\n\t\"nullOptionalDrop\",\n\t\"nullRequiredBounce\",\n\t\"jsonStringParse\",\n\t\"singleObjectWrap\",\n\t\"bareScalarWrap\",\n\t\"emptyObjectPlaceholder\",\n\t\"numberFromString\",\n\t\"boolFromString\",\n\t\"enumCaseNormalize\",\n\t\"singleElementUnwrap\",\n\t\"stringifiedNumberInArray\",\n\t\"bashCommandArgvJoin\",\n\t\"bashCommandUnwrap\",\n] as const;\n\nexport type ToolRepairModeName = (typeof TOOL_REPAIR_MODE_NAMES)[number];\nexport type ToolRepairFailureModeName = ToolRepairModeName | \"other\";\n\nexport interface ToolRepairRegistryEntry {\n\tname: ToolRepairModeName;\n\tnoteTemplate: string;\n\tstandingRule: string;\n}\n\nexport const TOOL_REPAIR_REGISTRY: readonly ToolRepairRegistryEntry[] = [\n\t{\n\t\tname: \"nullOptionalDrop\",\n\t\tnoteTemplate: \"sent null for optional `{path}` -> omit the field instead\",\n\t\tstandingRule: \"Omit optional fields instead of sending null.\",\n\t},\n\t{\n\t\tname: \"nullRequiredBounce\",\n\t\tnoteTemplate: \"`{path}` is required and cannot be null -> send a real value\",\n\t\tstandingRule: \"Send real values for required fields; never send null for a required field.\",\n\t},\n\t{\n\t\tname: \"jsonStringParse\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted JSON string -> send a raw JSON array/object\",\n\t\tstandingRule:\n\t\t\t\"Send raw JSON arrays/objects where the tool schema expects arrays/objects; do not quote them as JSON strings.\",\n\t},\n\t{\n\t\tname: \"singleObjectWrap\",\n\t\tnoteTemplate: \"sent one object where `{path}` takes a list -> wrap it in [ ]\",\n\t\tstandingRule: \"Wrap a single object in [ ] when the tool schema expects a list.\",\n\t},\n\t{\n\t\tname: \"bareScalarWrap\",\n\t\tnoteTemplate: \"sent a single value where `{path}` takes a list -> wrap it in [ ]\",\n\t\tstandingRule: \"Wrap a single scalar in [ ] when the tool schema expects a list.\",\n\t},\n\t{\n\t\tname: \"emptyObjectPlaceholder\",\n\t\tnoteTemplate: \"sent `{}` as a placeholder -> omit `{path}`; its default applies\",\n\t\tstandingRule: \"Omit defaulted object fields instead of sending `{}` placeholders.\",\n\t},\n\t{\n\t\tname: \"numberFromString\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted number -> send a bare number\",\n\t\tstandingRule: \"Send bare numbers where the tool schema expects numbers; do not quote them.\",\n\t},\n\t{\n\t\tname: \"boolFromString\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted boolean -> send bare true/false\",\n\t\tstandingRule: \"Send bare true/false where the tool schema expects booleans; do not quote them.\",\n\t},\n\t{\n\t\tname: \"enumCaseNormalize\",\n\t\tnoteTemplate: \"`{path}` matched a declared enum value after case/space normalization\",\n\t\tstandingRule: \"Use enum values exactly as declared, preserving case and spacing.\",\n\t},\n\t{\n\t\tname: \"singleElementUnwrap\",\n\t\tnoteTemplate: \"sent `{path}` as a 1-item list where a single value was expected -> send the value\",\n\t\tstandingRule:\n\t\t\t\"Send a single value directly when the tool schema expects a single value; do not wrap it in a one-item list.\",\n\t},\n\t{\n\t\tname: \"stringifiedNumberInArray\",\n\t\tnoteTemplate: \"list `{path}` holds quoted numbers -> send bare numbers\",\n\t\tstandingRule: \"Use bare numbers inside number arrays; do not quote them.\",\n\t},\n\t{\n\t\tname: \"bashCommandArgvJoin\",\n\t\tnoteTemplate: \"bash takes one command string, not an argv list -> joined the argv values\",\n\t\tstandingRule: \"For bash, send one command string rather than an argv array.\",\n\t},\n\t{\n\t\tname: \"bashCommandUnwrap\",\n\t\tnoteTemplate: \"bash `command` is a string -> unwrapped the command object\",\n\t\tstandingRule: \"For bash, send `command` as a string rather than an object wrapper.\",\n\t},\n] as const;\n\nconst registryByName = new Map(TOOL_REPAIR_REGISTRY.map((entry) => [entry.name, entry]));\n\nexport function getToolRepairRegistryEntry(name: ToolRepairModeName): ToolRepairRegistryEntry {\n\tconst entry = registryByName.get(name);\n\tif (!entry) throw new Error(`Unknown tool repair mode: ${name}`);\n\treturn entry;\n}\n\nexport function formatToolRepairNote(name: ToolRepairModeName, path: string): string {\n\treturn getToolRepairRegistryEntry(name).noteTemplate.replaceAll(\"{path}\", path);\n}\n\nexport function formatToolRepairStandingRule(name: ToolRepairModeName): string {\n\treturn getToolRepairRegistryEntry(name).standingRule;\n}\n\nexport const TOOL_EXECUTION_ERROR_CATALOGUE = [\n\t{\n\t\tname: \"commandNotFound\",\n\t\tguidance: \"Command was not found; check the command name or available tools before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /^spawn \\S+ ENOENT\\b/i.test(message) || /(?:^|\\n|:)\\s*command not found\\b/i.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"fileNotFound\",\n\t\tguidance: \"Path was not found; list the parent directory or re-read the path before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /\\bENOENT\\b/i.test(message) || /no such file or directory/i.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"editOldTextNotFound\",\n\t\tguidance: \"Re-read the target file and use the exact current text before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /(?:oldText|old text|exact text).*(?:not found|no match|failed to match|must match)/is.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"pathOutsideCwd\",\n\t\tguidance: \"Choose a path inside the current working directory, or ask before changing scope.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /outside (?:the )?(?:current working directory|cwd|workspace|root)/i.test(message);\n\t\t},\n\t},\n] as const;\n\nexport type ToolExecutionErrorClass = (typeof TOOL_EXECUTION_ERROR_CATALOGUE)[number][\"name\"];\n\nexport function getToolExecutionErrorGuidance(errorMessage: string): string | undefined {\n\treturn TOOL_EXECUTION_ERROR_CATALOGUE.find((entry) => entry.matches(errorMessage))?.guidance;\n}\n"]}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/utils/tool-repair/registry.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,wVAgBzB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AACzE,MAAM,MAAM,yBAAyB,GAAG,kBAAkB,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,uBAAuB;IACvC,IAAI,EAAE,kBAAkB,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,oBAAoB,EAAE,SAAS,uBAAuB,EAgFzD,CAAC;AAIX,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,kBAAkB,GAAG,uBAAuB,CAI5F;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAE7E;AAED,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;EA6BjC,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9F,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEtF","sourcesContent":["export const TOOL_REPAIR_MODE_NAMES = [\n\t\"nullOptionalDrop\",\n\t\"nullRequiredBounce\",\n\t\"jsonStringParse\",\n\t\"jsonObjectPropertySalvage\",\n\t\"singleObjectWrap\",\n\t\"bareScalarWrap\",\n\t\"emptyObjectPlaceholder\",\n\t\"numberFromString\",\n\t\"boolFromString\",\n\t\"enumCaseNormalize\",\n\t\"propertyCaseNormalize\",\n\t\"singleElementUnwrap\",\n\t\"stringifiedNumberInArray\",\n\t\"bashCommandArgvJoin\",\n\t\"bashCommandUnwrap\",\n] as const;\n\nexport type ToolRepairModeName = (typeof TOOL_REPAIR_MODE_NAMES)[number];\nexport type ToolRepairFailureModeName = ToolRepairModeName | \"other\";\n\nexport interface ToolRepairRegistryEntry {\n\tname: ToolRepairModeName;\n\tnoteTemplate: string;\n\tstandingRule: string;\n}\n\nexport const TOOL_REPAIR_REGISTRY: readonly ToolRepairRegistryEntry[] = [\n\t{\n\t\tname: \"nullOptionalDrop\",\n\t\tnoteTemplate: \"sent null for optional `{path}` -> omit the field instead\",\n\t\tstandingRule: \"Omit optional fields instead of sending null.\",\n\t},\n\t{\n\t\tname: \"nullRequiredBounce\",\n\t\tnoteTemplate: \"`{path}` is required and cannot be null -> send a real value\",\n\t\tstandingRule: \"Send real values for required fields; never send null for a required field.\",\n\t},\n\t{\n\t\tname: \"jsonStringParse\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted JSON string -> send a raw JSON array/object\",\n\t\tstandingRule:\n\t\t\t\"Send raw JSON arrays/objects where the tool schema expects arrays/objects; do not quote them as JSON strings.\",\n\t},\n\t{\n\t\tname: \"jsonObjectPropertySalvage\",\n\t\tnoteTemplate:\n\t\t\t\"sent `{path}` as malformed JSON with recoverable declared properties -> keep the schema-declared properties\",\n\t\tstandingRule:\n\t\t\t\"When sending JSON objects, use strict JSON syntax with commas between properties and no extra text inside the object.\",\n\t},\n\t{\n\t\tname: \"singleObjectWrap\",\n\t\tnoteTemplate: \"sent one object where `{path}` takes a list -> wrap it in [ ]\",\n\t\tstandingRule: \"Wrap a single object in [ ] when the tool schema expects a list.\",\n\t},\n\t{\n\t\tname: \"bareScalarWrap\",\n\t\tnoteTemplate: \"sent a single value where `{path}` takes a list -> wrap it in [ ]\",\n\t\tstandingRule: \"Wrap a single scalar in [ ] when the tool schema expects a list.\",\n\t},\n\t{\n\t\tname: \"emptyObjectPlaceholder\",\n\t\tnoteTemplate: \"sent `{}` as a placeholder -> omit `{path}`; its default applies\",\n\t\tstandingRule: \"Omit defaulted object fields instead of sending `{}` placeholders.\",\n\t},\n\t{\n\t\tname: \"numberFromString\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted number -> send a bare number\",\n\t\tstandingRule: \"Send bare numbers where the tool schema expects numbers; do not quote them.\",\n\t},\n\t{\n\t\tname: \"boolFromString\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted boolean -> send bare true/false\",\n\t\tstandingRule: \"Send bare true/false where the tool schema expects booleans; do not quote them.\",\n\t},\n\t{\n\t\tname: \"enumCaseNormalize\",\n\t\tnoteTemplate: \"`{path}` matched a declared enum value after case/space normalization\",\n\t\tstandingRule: \"Use enum values exactly as declared, preserving case and spacing.\",\n\t},\n\t{\n\t\tname: \"propertyCaseNormalize\",\n\t\tnoteTemplate: \"sent `{path}` with different property-key casing -> use the schema key casing\",\n\t\tstandingRule: \"Use tool argument property names exactly as declared in the schema, preserving case.\",\n\t},\n\t{\n\t\tname: \"singleElementUnwrap\",\n\t\tnoteTemplate: \"sent `{path}` as a 1-item list where a single value was expected -> send the value\",\n\t\tstandingRule:\n\t\t\t\"Send a single value directly when the tool schema expects a single value; do not wrap it in a one-item list.\",\n\t},\n\t{\n\t\tname: \"stringifiedNumberInArray\",\n\t\tnoteTemplate: \"list `{path}` holds quoted numbers -> send bare numbers\",\n\t\tstandingRule: \"Use bare numbers inside number arrays; do not quote them.\",\n\t},\n\t{\n\t\tname: \"bashCommandArgvJoin\",\n\t\tnoteTemplate: \"bash takes one command string, not an argv list -> joined the argv values\",\n\t\tstandingRule: \"For bash, send one command string rather than an argv array.\",\n\t},\n\t{\n\t\tname: \"bashCommandUnwrap\",\n\t\tnoteTemplate: \"bash `command` is a string -> unwrapped the command object\",\n\t\tstandingRule: \"For bash, send `command` as a string rather than an object wrapper.\",\n\t},\n] as const;\n\nconst registryByName = new Map(TOOL_REPAIR_REGISTRY.map((entry) => [entry.name, entry]));\n\nexport function getToolRepairRegistryEntry(name: ToolRepairModeName): ToolRepairRegistryEntry {\n\tconst entry = registryByName.get(name);\n\tif (!entry) throw new Error(`Unknown tool repair mode: ${name}`);\n\treturn entry;\n}\n\nexport function formatToolRepairNote(name: ToolRepairModeName, path: string): string {\n\treturn getToolRepairRegistryEntry(name).noteTemplate.replaceAll(\"{path}\", path);\n}\n\nexport function formatToolRepairStandingRule(name: ToolRepairModeName): string {\n\treturn getToolRepairRegistryEntry(name).standingRule;\n}\n\nexport const TOOL_EXECUTION_ERROR_CATALOGUE = [\n\t{\n\t\tname: \"commandNotFound\",\n\t\tguidance: \"Command was not found; check the command name or available tools before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /^spawn \\S+ ENOENT\\b/i.test(message) || /(?:^|\\n|:)\\s*command not found\\b/i.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"fileNotFound\",\n\t\tguidance: \"Path was not found; list the parent directory or re-read the path before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /\\bENOENT\\b/i.test(message) || /no such file or directory/i.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"editOldTextNotFound\",\n\t\tguidance: \"Re-read the target file and use the exact current text before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /(?:oldText|old text|exact text).*(?:not found|no match|failed to match|must match)/is.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"pathOutsideCwd\",\n\t\tguidance: \"Choose a path inside the current working directory, or ask before changing scope.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /outside (?:the )?(?:current working directory|cwd|workspace|root)/i.test(message);\n\t\t},\n\t},\n] as const;\n\nexport type ToolExecutionErrorClass = (typeof TOOL_EXECUTION_ERROR_CATALOGUE)[number][\"name\"];\n\nexport function getToolExecutionErrorGuidance(errorMessage: string): string | undefined {\n\treturn TOOL_EXECUTION_ERROR_CATALOGUE.find((entry) => entry.matches(errorMessage))?.guidance;\n}\n"]}
@@ -2,12 +2,14 @@ export const TOOL_REPAIR_MODE_NAMES = [
2
2
  "nullOptionalDrop",
3
3
  "nullRequiredBounce",
4
4
  "jsonStringParse",
5
+ "jsonObjectPropertySalvage",
5
6
  "singleObjectWrap",
6
7
  "bareScalarWrap",
7
8
  "emptyObjectPlaceholder",
8
9
  "numberFromString",
9
10
  "boolFromString",
10
11
  "enumCaseNormalize",
12
+ "propertyCaseNormalize",
11
13
  "singleElementUnwrap",
12
14
  "stringifiedNumberInArray",
13
15
  "bashCommandArgvJoin",
@@ -29,6 +31,11 @@ export const TOOL_REPAIR_REGISTRY = [
29
31
  noteTemplate: "sent `{path}` as a quoted JSON string -> send a raw JSON array/object",
30
32
  standingRule: "Send raw JSON arrays/objects where the tool schema expects arrays/objects; do not quote them as JSON strings.",
31
33
  },
34
+ {
35
+ name: "jsonObjectPropertySalvage",
36
+ noteTemplate: "sent `{path}` as malformed JSON with recoverable declared properties -> keep the schema-declared properties",
37
+ standingRule: "When sending JSON objects, use strict JSON syntax with commas between properties and no extra text inside the object.",
38
+ },
32
39
  {
33
40
  name: "singleObjectWrap",
34
41
  noteTemplate: "sent one object where `{path}` takes a list -> wrap it in [ ]",
@@ -59,6 +66,11 @@ export const TOOL_REPAIR_REGISTRY = [
59
66
  noteTemplate: "`{path}` matched a declared enum value after case/space normalization",
60
67
  standingRule: "Use enum values exactly as declared, preserving case and spacing.",
61
68
  },
69
+ {
70
+ name: "propertyCaseNormalize",
71
+ noteTemplate: "sent `{path}` with different property-key casing -> use the schema key casing",
72
+ standingRule: "Use tool argument property names exactly as declared in the schema, preserving case.",
73
+ },
62
74
  {
63
75
  name: "singleElementUnwrap",
64
76
  noteTemplate: "sent `{path}` as a 1-item list where a single value was expected -> send the value",
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/utils/tool-repair/registry.ts"],"names":[],"mappings":"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","sourcesContent":["export const TOOL_REPAIR_MODE_NAMES = [\n\t\"nullOptionalDrop\",\n\t\"nullRequiredBounce\",\n\t\"jsonStringParse\",\n\t\"singleObjectWrap\",\n\t\"bareScalarWrap\",\n\t\"emptyObjectPlaceholder\",\n\t\"numberFromString\",\n\t\"boolFromString\",\n\t\"enumCaseNormalize\",\n\t\"singleElementUnwrap\",\n\t\"stringifiedNumberInArray\",\n\t\"bashCommandArgvJoin\",\n\t\"bashCommandUnwrap\",\n] as const;\n\nexport type ToolRepairModeName = (typeof TOOL_REPAIR_MODE_NAMES)[number];\nexport type ToolRepairFailureModeName = ToolRepairModeName | \"other\";\n\nexport interface ToolRepairRegistryEntry {\n\tname: ToolRepairModeName;\n\tnoteTemplate: string;\n\tstandingRule: string;\n}\n\nexport const TOOL_REPAIR_REGISTRY: readonly ToolRepairRegistryEntry[] = [\n\t{\n\t\tname: \"nullOptionalDrop\",\n\t\tnoteTemplate: \"sent null for optional `{path}` -> omit the field instead\",\n\t\tstandingRule: \"Omit optional fields instead of sending null.\",\n\t},\n\t{\n\t\tname: \"nullRequiredBounce\",\n\t\tnoteTemplate: \"`{path}` is required and cannot be null -> send a real value\",\n\t\tstandingRule: \"Send real values for required fields; never send null for a required field.\",\n\t},\n\t{\n\t\tname: \"jsonStringParse\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted JSON string -> send a raw JSON array/object\",\n\t\tstandingRule:\n\t\t\t\"Send raw JSON arrays/objects where the tool schema expects arrays/objects; do not quote them as JSON strings.\",\n\t},\n\t{\n\t\tname: \"singleObjectWrap\",\n\t\tnoteTemplate: \"sent one object where `{path}` takes a list -> wrap it in [ ]\",\n\t\tstandingRule: \"Wrap a single object in [ ] when the tool schema expects a list.\",\n\t},\n\t{\n\t\tname: \"bareScalarWrap\",\n\t\tnoteTemplate: \"sent a single value where `{path}` takes a list -> wrap it in [ ]\",\n\t\tstandingRule: \"Wrap a single scalar in [ ] when the tool schema expects a list.\",\n\t},\n\t{\n\t\tname: \"emptyObjectPlaceholder\",\n\t\tnoteTemplate: \"sent `{}` as a placeholder -> omit `{path}`; its default applies\",\n\t\tstandingRule: \"Omit defaulted object fields instead of sending `{}` placeholders.\",\n\t},\n\t{\n\t\tname: \"numberFromString\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted number -> send a bare number\",\n\t\tstandingRule: \"Send bare numbers where the tool schema expects numbers; do not quote them.\",\n\t},\n\t{\n\t\tname: \"boolFromString\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted boolean -> send bare true/false\",\n\t\tstandingRule: \"Send bare true/false where the tool schema expects booleans; do not quote them.\",\n\t},\n\t{\n\t\tname: \"enumCaseNormalize\",\n\t\tnoteTemplate: \"`{path}` matched a declared enum value after case/space normalization\",\n\t\tstandingRule: \"Use enum values exactly as declared, preserving case and spacing.\",\n\t},\n\t{\n\t\tname: \"singleElementUnwrap\",\n\t\tnoteTemplate: \"sent `{path}` as a 1-item list where a single value was expected -> send the value\",\n\t\tstandingRule:\n\t\t\t\"Send a single value directly when the tool schema expects a single value; do not wrap it in a one-item list.\",\n\t},\n\t{\n\t\tname: \"stringifiedNumberInArray\",\n\t\tnoteTemplate: \"list `{path}` holds quoted numbers -> send bare numbers\",\n\t\tstandingRule: \"Use bare numbers inside number arrays; do not quote them.\",\n\t},\n\t{\n\t\tname: \"bashCommandArgvJoin\",\n\t\tnoteTemplate: \"bash takes one command string, not an argv list -> joined the argv values\",\n\t\tstandingRule: \"For bash, send one command string rather than an argv array.\",\n\t},\n\t{\n\t\tname: \"bashCommandUnwrap\",\n\t\tnoteTemplate: \"bash `command` is a string -> unwrapped the command object\",\n\t\tstandingRule: \"For bash, send `command` as a string rather than an object wrapper.\",\n\t},\n] as const;\n\nconst registryByName = new Map(TOOL_REPAIR_REGISTRY.map((entry) => [entry.name, entry]));\n\nexport function getToolRepairRegistryEntry(name: ToolRepairModeName): ToolRepairRegistryEntry {\n\tconst entry = registryByName.get(name);\n\tif (!entry) throw new Error(`Unknown tool repair mode: ${name}`);\n\treturn entry;\n}\n\nexport function formatToolRepairNote(name: ToolRepairModeName, path: string): string {\n\treturn getToolRepairRegistryEntry(name).noteTemplate.replaceAll(\"{path}\", path);\n}\n\nexport function formatToolRepairStandingRule(name: ToolRepairModeName): string {\n\treturn getToolRepairRegistryEntry(name).standingRule;\n}\n\nexport const TOOL_EXECUTION_ERROR_CATALOGUE = [\n\t{\n\t\tname: \"commandNotFound\",\n\t\tguidance: \"Command was not found; check the command name or available tools before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /^spawn \\S+ ENOENT\\b/i.test(message) || /(?:^|\\n|:)\\s*command not found\\b/i.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"fileNotFound\",\n\t\tguidance: \"Path was not found; list the parent directory or re-read the path before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /\\bENOENT\\b/i.test(message) || /no such file or directory/i.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"editOldTextNotFound\",\n\t\tguidance: \"Re-read the target file and use the exact current text before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /(?:oldText|old text|exact text).*(?:not found|no match|failed to match|must match)/is.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"pathOutsideCwd\",\n\t\tguidance: \"Choose a path inside the current working directory, or ask before changing scope.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /outside (?:the )?(?:current working directory|cwd|workspace|root)/i.test(message);\n\t\t},\n\t},\n] as const;\n\nexport type ToolExecutionErrorClass = (typeof TOOL_EXECUTION_ERROR_CATALOGUE)[number][\"name\"];\n\nexport function getToolExecutionErrorGuidance(errorMessage: string): string | undefined {\n\treturn TOOL_EXECUTION_ERROR_CATALOGUE.find((entry) => entry.matches(errorMessage))?.guidance;\n}\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/utils/tool-repair/registry.ts"],"names":[],"mappings":"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","sourcesContent":["export const TOOL_REPAIR_MODE_NAMES = [\n\t\"nullOptionalDrop\",\n\t\"nullRequiredBounce\",\n\t\"jsonStringParse\",\n\t\"jsonObjectPropertySalvage\",\n\t\"singleObjectWrap\",\n\t\"bareScalarWrap\",\n\t\"emptyObjectPlaceholder\",\n\t\"numberFromString\",\n\t\"boolFromString\",\n\t\"enumCaseNormalize\",\n\t\"propertyCaseNormalize\",\n\t\"singleElementUnwrap\",\n\t\"stringifiedNumberInArray\",\n\t\"bashCommandArgvJoin\",\n\t\"bashCommandUnwrap\",\n] as const;\n\nexport type ToolRepairModeName = (typeof TOOL_REPAIR_MODE_NAMES)[number];\nexport type ToolRepairFailureModeName = ToolRepairModeName | \"other\";\n\nexport interface ToolRepairRegistryEntry {\n\tname: ToolRepairModeName;\n\tnoteTemplate: string;\n\tstandingRule: string;\n}\n\nexport const TOOL_REPAIR_REGISTRY: readonly ToolRepairRegistryEntry[] = [\n\t{\n\t\tname: \"nullOptionalDrop\",\n\t\tnoteTemplate: \"sent null for optional `{path}` -> omit the field instead\",\n\t\tstandingRule: \"Omit optional fields instead of sending null.\",\n\t},\n\t{\n\t\tname: \"nullRequiredBounce\",\n\t\tnoteTemplate: \"`{path}` is required and cannot be null -> send a real value\",\n\t\tstandingRule: \"Send real values for required fields; never send null for a required field.\",\n\t},\n\t{\n\t\tname: \"jsonStringParse\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted JSON string -> send a raw JSON array/object\",\n\t\tstandingRule:\n\t\t\t\"Send raw JSON arrays/objects where the tool schema expects arrays/objects; do not quote them as JSON strings.\",\n\t},\n\t{\n\t\tname: \"jsonObjectPropertySalvage\",\n\t\tnoteTemplate:\n\t\t\t\"sent `{path}` as malformed JSON with recoverable declared properties -> keep the schema-declared properties\",\n\t\tstandingRule:\n\t\t\t\"When sending JSON objects, use strict JSON syntax with commas between properties and no extra text inside the object.\",\n\t},\n\t{\n\t\tname: \"singleObjectWrap\",\n\t\tnoteTemplate: \"sent one object where `{path}` takes a list -> wrap it in [ ]\",\n\t\tstandingRule: \"Wrap a single object in [ ] when the tool schema expects a list.\",\n\t},\n\t{\n\t\tname: \"bareScalarWrap\",\n\t\tnoteTemplate: \"sent a single value where `{path}` takes a list -> wrap it in [ ]\",\n\t\tstandingRule: \"Wrap a single scalar in [ ] when the tool schema expects a list.\",\n\t},\n\t{\n\t\tname: \"emptyObjectPlaceholder\",\n\t\tnoteTemplate: \"sent `{}` as a placeholder -> omit `{path}`; its default applies\",\n\t\tstandingRule: \"Omit defaulted object fields instead of sending `{}` placeholders.\",\n\t},\n\t{\n\t\tname: \"numberFromString\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted number -> send a bare number\",\n\t\tstandingRule: \"Send bare numbers where the tool schema expects numbers; do not quote them.\",\n\t},\n\t{\n\t\tname: \"boolFromString\",\n\t\tnoteTemplate: \"sent `{path}` as a quoted boolean -> send bare true/false\",\n\t\tstandingRule: \"Send bare true/false where the tool schema expects booleans; do not quote them.\",\n\t},\n\t{\n\t\tname: \"enumCaseNormalize\",\n\t\tnoteTemplate: \"`{path}` matched a declared enum value after case/space normalization\",\n\t\tstandingRule: \"Use enum values exactly as declared, preserving case and spacing.\",\n\t},\n\t{\n\t\tname: \"propertyCaseNormalize\",\n\t\tnoteTemplate: \"sent `{path}` with different property-key casing -> use the schema key casing\",\n\t\tstandingRule: \"Use tool argument property names exactly as declared in the schema, preserving case.\",\n\t},\n\t{\n\t\tname: \"singleElementUnwrap\",\n\t\tnoteTemplate: \"sent `{path}` as a 1-item list where a single value was expected -> send the value\",\n\t\tstandingRule:\n\t\t\t\"Send a single value directly when the tool schema expects a single value; do not wrap it in a one-item list.\",\n\t},\n\t{\n\t\tname: \"stringifiedNumberInArray\",\n\t\tnoteTemplate: \"list `{path}` holds quoted numbers -> send bare numbers\",\n\t\tstandingRule: \"Use bare numbers inside number arrays; do not quote them.\",\n\t},\n\t{\n\t\tname: \"bashCommandArgvJoin\",\n\t\tnoteTemplate: \"bash takes one command string, not an argv list -> joined the argv values\",\n\t\tstandingRule: \"For bash, send one command string rather than an argv array.\",\n\t},\n\t{\n\t\tname: \"bashCommandUnwrap\",\n\t\tnoteTemplate: \"bash `command` is a string -> unwrapped the command object\",\n\t\tstandingRule: \"For bash, send `command` as a string rather than an object wrapper.\",\n\t},\n] as const;\n\nconst registryByName = new Map(TOOL_REPAIR_REGISTRY.map((entry) => [entry.name, entry]));\n\nexport function getToolRepairRegistryEntry(name: ToolRepairModeName): ToolRepairRegistryEntry {\n\tconst entry = registryByName.get(name);\n\tif (!entry) throw new Error(`Unknown tool repair mode: ${name}`);\n\treturn entry;\n}\n\nexport function formatToolRepairNote(name: ToolRepairModeName, path: string): string {\n\treturn getToolRepairRegistryEntry(name).noteTemplate.replaceAll(\"{path}\", path);\n}\n\nexport function formatToolRepairStandingRule(name: ToolRepairModeName): string {\n\treturn getToolRepairRegistryEntry(name).standingRule;\n}\n\nexport const TOOL_EXECUTION_ERROR_CATALOGUE = [\n\t{\n\t\tname: \"commandNotFound\",\n\t\tguidance: \"Command was not found; check the command name or available tools before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /^spawn \\S+ ENOENT\\b/i.test(message) || /(?:^|\\n|:)\\s*command not found\\b/i.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"fileNotFound\",\n\t\tguidance: \"Path was not found; list the parent directory or re-read the path before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /\\bENOENT\\b/i.test(message) || /no such file or directory/i.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"editOldTextNotFound\",\n\t\tguidance: \"Re-read the target file and use the exact current text before retrying.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /(?:oldText|old text|exact text).*(?:not found|no match|failed to match|must match)/is.test(message);\n\t\t},\n\t},\n\t{\n\t\tname: \"pathOutsideCwd\",\n\t\tguidance: \"Choose a path inside the current working directory, or ask before changing scope.\",\n\t\tmatches(message: string): boolean {\n\t\t\treturn /outside (?:the )?(?:current working directory|cwd|workspace|root)/i.test(message);\n\t\t},\n\t},\n] as const;\n\nexport type ToolExecutionErrorClass = (typeof TOOL_EXECUTION_ERROR_CATALOGUE)[number][\"name\"];\n\nexport function getToolExecutionErrorGuidance(errorMessage: string): string | undefined {\n\treturn TOOL_EXECUTION_ERROR_CATALOGUE.find((entry) => entry.matches(errorMessage))?.guidance;\n}\n"]}