@compilr-dev/sdk 0.32.0 → 0.33.1

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.
@@ -161,60 +161,94 @@ export const MODEL_REGISTRY = [
161
161
  // ---------------------------------------------------------------------------
162
162
  // Gemini 2.x - Supported
163
163
  {
164
- id: 'gemini-2.5-flash-lite',
165
- displayName: 'Gemini 2.5 Flash Lite',
166
- description: 'Fast, low cost',
164
+ id: 'gemini-3.8-flash',
165
+ displayName: 'Gemini 3.8 Flash',
166
+ description: 'Most capable',
167
167
  provider: 'gemini',
168
168
  supportsImages: true,
169
- defaultTier: 'fast',
170
- thinkingFormat: 'gemini-v2',
169
+ supportsTools: true,
170
+ defaultTier: 'powerful',
171
+ thinkingFormat: 'gemini-v3',
171
172
  status: 'supported',
172
- contextWindow: 1000000,
173
+ contextWindow: 1048576,
174
+ notes: 'Newest Flash; max output 65,536',
173
175
  },
174
176
  {
175
- id: 'gemini-2.5-flash',
176
- displayName: 'Gemini 2.5 Flash',
177
+ id: 'gemini-3.5-flash',
178
+ displayName: 'Gemini 3.5 Flash',
177
179
  description: 'Balanced (recommended)',
178
180
  provider: 'gemini',
179
181
  supportsImages: true,
182
+ supportsTools: true,
180
183
  defaultTier: 'balanced',
181
- thinkingFormat: 'gemini-v2',
184
+ thinkingFormat: 'gemini-v3',
182
185
  status: 'supported',
183
- contextWindow: 1000000,
186
+ contextWindow: 1048576,
187
+ notes: 'GA; max output 65,536',
188
+ },
189
+ {
190
+ id: 'gemini-3.5-flash-lite',
191
+ displayName: 'Gemini 3.5 Flash Lite',
192
+ description: 'Fast, low cost',
193
+ provider: 'gemini',
194
+ supportsImages: true,
195
+ supportsTools: true,
196
+ defaultTier: 'fast',
197
+ thinkingFormat: 'gemini-v3',
198
+ status: 'supported',
199
+ contextWindow: 1048576,
200
+ notes: 'Max output 65,536',
184
201
  },
202
+ {
203
+ id: 'gemini-3.1-pro-preview',
204
+ displayName: 'Gemini 3.1 Pro (Preview)',
205
+ description: 'Pro tier, preview',
206
+ provider: 'gemini',
207
+ supportsImages: true,
208
+ supportsTools: true,
209
+ thinkingFormat: 'gemini-v3',
210
+ status: 'experimental',
211
+ contextWindow: 1048576,
212
+ notes: 'The only Pro on the Gemini API; PREVIEW — measured 2026-09-30, ~1 turn in 5 returns ' +
213
+ 'MALFORMED_RESPONSE (the provider now reports it instead of showing its internal text)',
214
+ },
215
+ // Legacy Gemini models. ⚠️ Google restricts the 2.5 line to accounts that already used it,
216
+ // so these can fail for a new key even though they work for an established one.
185
217
  {
186
218
  id: 'gemini-2.5-pro',
187
219
  displayName: 'Gemini 2.5 Pro',
188
- description: 'Most capable',
220
+ description: 'Previous generation',
189
221
  provider: 'gemini',
190
222
  supportsImages: true,
191
- defaultTier: 'powerful',
223
+ supportsTools: true,
192
224
  thinkingFormat: 'gemini-v2',
193
225
  status: 'supported',
194
- contextWindow: 1000000,
226
+ contextWindow: 1048576,
227
+ notes: 'Legacy — restricted to accounts that used it before; consider Gemini 3.8 Flash',
195
228
  },
196
- // Gemini 3.x - Preview, has stability issues with function calling
197
229
  {
198
- id: 'gemini-3-flash-preview',
199
- displayName: 'Gemini 3 Flash (Preview)',
200
- description: 'Preview - may have stability issues',
230
+ id: 'gemini-2.5-flash',
231
+ displayName: 'Gemini 2.5 Flash',
232
+ description: 'Previous generation',
201
233
  provider: 'gemini',
202
234
  supportsImages: true,
203
- thinkingFormat: 'gemini-v3',
235
+ supportsTools: true,
236
+ thinkingFormat: 'gemini-v2',
204
237
  status: 'supported',
205
- contextWindow: 1000000,
206
- notes: 'Preview: 500 errors during function calling, high token usage',
238
+ contextWindow: 1048576,
239
+ notes: 'Legacy — restricted to accounts that used it before; consider Gemini 3.5 Flash',
207
240
  },
208
241
  {
209
- id: 'gemini-3-pro-preview',
210
- displayName: 'Gemini 3 Pro (Preview)',
211
- description: 'Preview - may have stability issues',
242
+ id: 'gemini-2.5-flash-lite',
243
+ displayName: 'Gemini 2.5 Flash Lite',
244
+ description: 'Previous generation',
212
245
  provider: 'gemini',
213
246
  supportsImages: true,
214
- thinkingFormat: 'gemini-v3',
247
+ supportsTools: true,
248
+ thinkingFormat: 'gemini-v2',
215
249
  status: 'supported',
216
- contextWindow: 1000000,
217
- notes: 'Preview: Works but very high token usage during function calling',
250
+ contextWindow: 1048576,
251
+ notes: 'Legacy — restricted to accounts that used it before; consider Gemini 3.5 Flash Lite',
218
252
  },
219
253
  // ---------------------------------------------------------------------------
220
254
  // OpenAI Models
@@ -18,6 +18,37 @@ import { type SceneFile } from '../../canvas/scene.js';
18
18
  export declare const SCENE_TOOL_NAMES: readonly ["scene_create", "scene_get", "scene_add_object", "scene_update_object", "scene_remove_object", "scene_set_camera", "scene_screenshot"];
19
19
  /** "sofa · box · Sofa" rows, indented under their group parent. */
20
20
  export declare function sceneOutline(scene: SceneFile): string;
21
+ /**
22
+ * The most JSON one `scene_get` may return.
23
+ *
24
+ * ⚠️ Sized to stay UNDER the context manager's 8,000-token delegation threshold
25
+ * (`delegationThreshold`, agents lib). A read that crosses it gets auto-summarised — and a
26
+ * summary of a scene loses the exact object ids, which is the one thing the agent reads a scene
27
+ * for. So an oversized read must be refused with instructions, never silently shortened.
28
+ */
29
+ export declare const SCENE_READ_MAX_CHARS = 20000;
30
+ /**
31
+ * What `scene_get` returns: the outline, named objects in full, or the whole scene.
32
+ *
33
+ * ⚠️ THE OUTLINE IS THE DEFAULT ON PURPOSE. This tool used to return
34
+ * `serializeScene(scene)` every time, and its own description told the agent to call it before
35
+ * every edit — about 4,500 tokens for a 51-object house, 11x the outline, on every read of a
36
+ * build loop. It also taught the model to answer in the same register: having just read a page
37
+ * of fully-specified pretty-printed JSON, it would emit one enormous `scene_add_object` and get
38
+ * cut off at the output limit mid-arguments.
39
+ *
40
+ * The outline carries ids, types, names and hierarchy — enough to address anything. Numbers come
41
+ * from `ids`, which is the common case for an edit (the roof, not the house).
42
+ *
43
+ * See 3d-canvas-spec §6.4 for the measurements. Exported for its tests.
44
+ */
45
+ export declare function sceneReadBody(scene: SceneFile, head: string, ids: unknown, full: unknown): {
46
+ ok: true;
47
+ text: string;
48
+ } | {
49
+ ok: false;
50
+ error: string;
51
+ };
21
52
  /**
22
53
  * Render a scene through the host's `renderSceneToImage` — shared by scene_screenshot and
23
54
  * canvas_screenshot (either tool works on a scene).
@@ -47,6 +78,10 @@ export declare function createSceneTools(config: PlatformToolsConfig, options?:
47
78
  title: string;
48
79
  scene?: unknown;
49
80
  project_id?: number;
81
+ }> | import("@compilr-dev/agents").Tool<{
82
+ canvas_id: number;
83
+ ids?: unknown;
84
+ full?: unknown;
50
85
  }> | import("@compilr-dev/agents").Tool<Record<string, unknown> & {
51
86
  canvas_id: number;
52
87
  }> | import("@compilr-dev/agents").Tool<{
@@ -72,6 +72,75 @@ export function sceneOutline(scene) {
72
72
  walk(undefined, 0);
73
73
  return rows.join('\n');
74
74
  }
75
+ /**
76
+ * The most JSON one `scene_get` may return.
77
+ *
78
+ * ⚠️ Sized to stay UNDER the context manager's 8,000-token delegation threshold
79
+ * (`delegationThreshold`, agents lib). A read that crosses it gets auto-summarised — and a
80
+ * summary of a scene loses the exact object ids, which is the one thing the agent reads a scene
81
+ * for. So an oversized read must be refused with instructions, never silently shortened.
82
+ */
83
+ export const SCENE_READ_MAX_CHARS = 20_000;
84
+ /** "roof, walls" — the first few of a list, for an error that has to name what is available. */
85
+ function someIds(ids, limit = 12) {
86
+ return ids.length <= limit
87
+ ? ids.join(', ')
88
+ : `${ids.slice(0, limit).join(', ')}, … (${String(ids.length)} in all)`;
89
+ }
90
+ /**
91
+ * What `scene_get` returns: the outline, named objects in full, or the whole scene.
92
+ *
93
+ * ⚠️ THE OUTLINE IS THE DEFAULT ON PURPOSE. This tool used to return
94
+ * `serializeScene(scene)` every time, and its own description told the agent to call it before
95
+ * every edit — about 4,500 tokens for a 51-object house, 11x the outline, on every read of a
96
+ * build loop. It also taught the model to answer in the same register: having just read a page
97
+ * of fully-specified pretty-printed JSON, it would emit one enormous `scene_add_object` and get
98
+ * cut off at the output limit mid-arguments.
99
+ *
100
+ * The outline carries ids, types, names and hierarchy — enough to address anything. Numbers come
101
+ * from `ids`, which is the common case for an edit (the roof, not the house).
102
+ *
103
+ * See 3d-canvas-spec §6.4 for the measurements. Exported for its tests.
104
+ */
105
+ export function sceneReadBody(scene, head, ids, full) {
106
+ const known = scene.objects.map((o) => o.id);
107
+ const guard = (json, over) => json.length > SCENE_READ_MAX_CHARS
108
+ ? { ok: false, error: over }
109
+ : { ok: true, text: `${head}\n\n${json}` };
110
+ if (ids !== undefined) {
111
+ if (!Array.isArray(ids) || ids.some((i) => typeof i !== 'string')) {
112
+ return { ok: false, error: 'ids must be an array of object id strings.' };
113
+ }
114
+ const wanted = ids;
115
+ if (wanted.length === 0)
116
+ return {
117
+ ok: false,
118
+ error: 'ids was empty. Omit it for the outline, or name the objects you need.',
119
+ };
120
+ const found = scene.objects.filter((o) => wanted.includes(o.id));
121
+ const missing = wanted.filter((i) => !known.includes(i));
122
+ if (found.length === 0) {
123
+ return {
124
+ ok: false,
125
+ error: `No object in this scene has ${missing.length === 1 ? 'that id' : 'those ids'} (${someIds(missing)}). The scene has: ${someIds(known)}. Call scene_get without ids for the outline.`,
126
+ };
127
+ }
128
+ const body = guard(JSON.stringify(found, null, 2), `Those ${String(wanted.length)} objects come to more than ${String(SCENE_READ_MAX_CHARS)} characters. Ask for fewer ids at a time.`);
129
+ // A partial hit still answers, but says what was not there — a silently short list reads as
130
+ // "those objects do not exist".
131
+ if (body.ok && missing.length > 0) {
132
+ return { ok: true, text: `${body.text}\n\nNot in this scene: ${someIds(missing)}.` };
133
+ }
134
+ return body;
135
+ }
136
+ if (full === true) {
137
+ return guard(serializeScene(scene), `This scene's JSON is over ${String(SCENE_READ_MAX_CHARS)} characters, which would be summarised before you saw it and you would lose the ids. Call scene_get without full for the outline, then with ids: [...] for the objects you need.`);
138
+ }
139
+ return {
140
+ ok: true,
141
+ text: `${head}\n\n--- objects (id · type · name) ---\n${sceneOutline(scene)}`,
142
+ };
143
+ }
75
144
  function clampShot(v, fallback) {
76
145
  if (typeof v !== 'number' || !Number.isFinite(v))
77
146
  return fallback;
@@ -231,7 +300,8 @@ export function createSceneTools(config, options) {
231
300
  name: 'scene_create',
232
301
  description: 'Create a 3D SCENE canvas — objects in metres the user can orbit, select, edit and export. ' +
233
302
  'Scenes are JSON data, not HTML/code (never use canvas_write for 3D). Pass only a title for an ' +
234
- 'empty scene, then build it with scene_add_object (up to 50 objects per call), or pass a whole ' +
303
+ 'empty scene, then build it with scene_add_object (up to 50 per call, but see its note on ' +
304
+ 'batch size), or pass a whole ' +
235
305
  'scene { objects: [...], camera?, lights? }. Returns the canvas ID used by every scene_* tool.',
236
306
  inputSchema: {
237
307
  type: 'object',
@@ -284,12 +354,26 @@ export function createSceneTools(config, options) {
284
354
  // ---------------------------------------------------------------------------
285
355
  const sceneGetTool = defineTool({
286
356
  name: 'scene_get',
287
- description: 'Read a 3D scene: the full JSON (pretty-printed) plus a one-line summary. Always read before ' +
288
- 'editing — the user edits scenes too, and object ids come from here.',
357
+ description: 'Read a 3D scene. By default returns a one-line summary and the OUTLINE — every object as ' +
358
+ '"id · type · name", indented by group — which is what you need to address objects. ' +
359
+ 'Always read before editing: the user edits scenes too, and object ids come from here. ' +
360
+ 'For an object\'s numbers (position, size, rotation, colour) pass ids: ["roof", "walls"] ' +
361
+ 'and you get those objects in full. full: true dumps the entire scene JSON and is rarely ' +
362
+ 'worth it — it costs about 11x the outline and is refused on a large scene.',
289
363
  inputSchema: {
290
364
  type: 'object',
291
365
  properties: {
292
366
  canvas_id: { type: 'number', description: 'The scene canvas id.' },
367
+ ids: {
368
+ type: 'array',
369
+ items: { type: 'string' },
370
+ description: 'Return these objects IN FULL (all their fields) instead of the outline. The ids come ' +
371
+ 'from a previous outline. This is the right way to read detail before an edit.',
372
+ },
373
+ full: {
374
+ type: 'boolean',
375
+ description: 'Return the whole scene as JSON. Only for a wholesale rewrite or an export; prefer ids.',
376
+ },
293
377
  },
294
378
  required: ['canvas_id'],
295
379
  },
@@ -298,9 +382,11 @@ export function createSceneTools(config, options) {
298
382
  const r = await writer.read(input.canvas_id);
299
383
  if (!r.ok)
300
384
  return createErrorResult(r.error);
301
- const body = `Scene "${r.title}" (canvas ${String(r.canvasId)}): ${describeScene(r.scene)} · rev ${String(r.rev)}\n\n` +
302
- serializeScene(r.scene);
303
- return { success: true, result: withNotice(r.canvasId, body) };
385
+ const head = `Scene "${r.title}" (canvas ${String(r.canvasId)}): ${describeScene(r.scene)} · rev ${String(r.rev)}`;
386
+ const body = sceneReadBody(r.scene, head, input.ids, input.full);
387
+ if (!body.ok)
388
+ return createErrorResult(body.error);
389
+ return { success: true, result: withNotice(r.canvasId, body.text) };
304
390
  }
305
391
  catch (error) {
306
392
  return createErrorResult(errorMessage('read scene', error));
@@ -314,7 +400,11 @@ export function createSceneTools(config, options) {
314
400
  "Units are metres; position.y is the object's BOTTOM (0 = on the floor); rotation in degrees about Y. " +
315
401
  'Build hierarchically: a type "group" with your own id (e.g. "house", then "walls", "roof" inside it), ' +
316
402
  'and its parts with group: "<id>" — a batch may name a group it creates itself. ' +
317
- 'Returns the new id(s) — yours, or generated from name when id is omitted.',
403
+ 'Returns the new id(s) — yours, or generated from name when id is omitted. ' +
404
+ 'BATCH SIZE: about 15-20 per call once objects carry names, colours, groups and rotations. ' +
405
+ 'Fifty of those is roughly 3,000 tokens of arguments, your reasoning is drawn from the same ' +
406
+ 'output budget, and a call cut off at that limit adds NOTHING and has to be redone — so two ' +
407
+ 'moderate calls finish sooner than one oversized one.',
318
408
  inputSchema: {
319
409
  type: 'object',
320
410
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -81,7 +81,7 @@
81
81
  "node": ">=20.0.0"
82
82
  },
83
83
  "dependencies": {
84
- "@compilr-dev/agents": "^0.7.0",
84
+ "@compilr-dev/agents": "^0.7.1",
85
85
  "@compilr-dev/logger": "^0.1.0",
86
86
  "ajv": "^6.14.0",
87
87
  "yaml": "^2.8.4"