@compilr-dev/sdk 0.10.33 → 0.10.35
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.
|
@@ -489,6 +489,8 @@ function buildCompactSchema(tool) {
|
|
|
489
489
|
* Handles common LLM mistakes when calling tools without guided decoding:
|
|
490
490
|
* - Single object where array expected → wraps in array
|
|
491
491
|
* - JSON string where array expected → parses it
|
|
492
|
+
* - JSON string where object expected → parses it (covers e.g. Gemini Flash
|
|
493
|
+
* stringifying complex nested objects like build_interactive_flow.flow)
|
|
492
494
|
*/
|
|
493
495
|
function coerceArgs(schema, args) {
|
|
494
496
|
const properties = schema.properties;
|
|
@@ -497,27 +499,43 @@ function coerceArgs(schema, args) {
|
|
|
497
499
|
let changed = false;
|
|
498
500
|
const coerced = { ...args };
|
|
499
501
|
for (const [name, prop] of Object.entries(properties)) {
|
|
500
|
-
if (
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
// Leave as-is, will fail validation
|
|
502
|
+
if (!(name in coerced))
|
|
503
|
+
continue;
|
|
504
|
+
const value = coerced[name];
|
|
505
|
+
if (value === null || value === undefined)
|
|
506
|
+
continue;
|
|
507
|
+
if (prop.type === 'array' && !Array.isArray(value)) {
|
|
508
|
+
if (typeof value === 'object') {
|
|
509
|
+
// Single object → wrap in array
|
|
510
|
+
coerced[name] = [value];
|
|
511
|
+
changed = true;
|
|
512
|
+
}
|
|
513
|
+
else if (typeof value === 'string') {
|
|
514
|
+
// JSON string → try parsing
|
|
515
|
+
try {
|
|
516
|
+
const parsed = JSON.parse(value);
|
|
517
|
+
if (Array.isArray(parsed)) {
|
|
518
|
+
coerced[name] = parsed;
|
|
519
|
+
changed = true;
|
|
519
520
|
}
|
|
520
521
|
}
|
|
522
|
+
catch {
|
|
523
|
+
// Leave as-is, will fail validation
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
else if (prop.type === 'object' && typeof value === 'string') {
|
|
528
|
+
// JSON-stringified object → try parsing. Some providers (Gemini Flash
|
|
529
|
+
// via OpenAI compat, etc.) ship deeply-nested object args as strings.
|
|
530
|
+
try {
|
|
531
|
+
const parsed = JSON.parse(value);
|
|
532
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
533
|
+
coerced[name] = parsed;
|
|
534
|
+
changed = true;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
// Leave as-is, will fail validation
|
|
521
539
|
}
|
|
522
540
|
}
|
|
523
541
|
}
|
|
@@ -86,13 +86,35 @@ export function createAskUserTool(handler) {
|
|
|
86
86
|
},
|
|
87
87
|
execute: async (input) => {
|
|
88
88
|
try {
|
|
89
|
-
|
|
89
|
+
// Defensive — some providers (Gemini Flash via OpenAI compat, etc.)
|
|
90
|
+
// ship nested object arguments as JSON-stringified strings. Parse
|
|
91
|
+
// before the length check so hosts always see a real array.
|
|
92
|
+
let questions = input.questions;
|
|
93
|
+
if (typeof questions === 'string') {
|
|
94
|
+
try {
|
|
95
|
+
questions = JSON.parse(questions);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
error: 'questions must be an array (received a non-JSON string)',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!Array.isArray(questions)) {
|
|
105
|
+
return { success: false, error: 'questions must be an array' };
|
|
106
|
+
}
|
|
107
|
+
if (questions.length === 0) {
|
|
90
108
|
return { success: false, error: 'At least one question is required' };
|
|
91
109
|
}
|
|
92
|
-
if (
|
|
110
|
+
if (questions.length > 5) {
|
|
93
111
|
return { success: false, error: 'Maximum 5 questions allowed' };
|
|
94
112
|
}
|
|
95
|
-
const
|
|
113
|
+
const normalised = {
|
|
114
|
+
...input,
|
|
115
|
+
questions: questions,
|
|
116
|
+
};
|
|
117
|
+
const result = await handler(normalised);
|
|
96
118
|
return { success: true, result };
|
|
97
119
|
}
|
|
98
120
|
catch (err) {
|
|
@@ -88,13 +88,35 @@ export function createProposeAlternativesTool(handler) {
|
|
|
88
88
|
},
|
|
89
89
|
execute: async (input) => {
|
|
90
90
|
try {
|
|
91
|
-
|
|
91
|
+
// Defensive — some providers (Gemini Flash via OpenAI compat, etc.)
|
|
92
|
+
// ship nested object arguments as JSON-stringified strings. Parse
|
|
93
|
+
// before validation so hosts always see a real array.
|
|
94
|
+
let alternatives = input.alternatives;
|
|
95
|
+
if (typeof alternatives === 'string') {
|
|
96
|
+
try {
|
|
97
|
+
alternatives = JSON.parse(alternatives);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
error: 'alternatives must be an array (received a non-JSON string)',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!Array.isArray(alternatives)) {
|
|
107
|
+
return { success: false, error: 'alternatives must be an array' };
|
|
108
|
+
}
|
|
109
|
+
if (alternatives.length < 2) {
|
|
92
110
|
return { success: false, error: 'At least 2 alternatives are required' };
|
|
93
111
|
}
|
|
94
|
-
if (
|
|
112
|
+
if (alternatives.length > 3) {
|
|
95
113
|
return { success: false, error: 'Maximum 3 alternatives allowed' };
|
|
96
114
|
}
|
|
97
|
-
const
|
|
115
|
+
const normalised = {
|
|
116
|
+
...input,
|
|
117
|
+
alternatives: alternatives,
|
|
118
|
+
};
|
|
119
|
+
const result = await handler(normalised);
|
|
98
120
|
if (result.rejected) {
|
|
99
121
|
return {
|
|
100
122
|
success: true,
|