@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 (prop.type === 'array' && name in coerced && !Array.isArray(coerced[name])) {
501
- const value = coerced[name];
502
- if (value !== null && value !== undefined) {
503
- if (typeof value === 'object') {
504
- // Single object → wrap in array
505
- coerced[name] = [value];
506
- changed = true;
507
- }
508
- else if (typeof value === 'string') {
509
- // JSON string → try parsing
510
- try {
511
- const parsed = JSON.parse(value);
512
- if (Array.isArray(parsed)) {
513
- coerced[name] = parsed;
514
- changed = true;
515
- }
516
- }
517
- catch {
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
- if (input.questions.length === 0) {
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 (input.questions.length > 5) {
110
+ if (questions.length > 5) {
93
111
  return { success: false, error: 'Maximum 5 questions allowed' };
94
112
  }
95
- const result = await handler(input);
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
- if (input.alternatives.length < 2) {
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 (input.alternatives.length > 3) {
112
+ if (alternatives.length > 3) {
95
113
  return { success: false, error: 'Maximum 3 alternatives allowed' };
96
114
  }
97
- const result = await handler(input);
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.10.33",
3
+ "version": "0.10.35",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",