@pinepaper.studio/mcp-server 1.6.32 → 1.6.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +53 -0
  2. package/dist/agent/types.d.ts +3 -3
  3. package/dist/build-info.json +2 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/ontology/upstream-vocabulary.d.ts +1 -1
  8. package/dist/ontology/upstream-vocabulary.js +1 -1
  9. package/dist/tools/definitions.d.ts.map +1 -1
  10. package/dist/tools/definitions.js +51 -38
  11. package/dist/tools/definitions.js.map +1 -1
  12. package/dist/tools/engine-lists.d.ts +5 -3
  13. package/dist/tools/engine-lists.d.ts.map +1 -1
  14. package/dist/tools/engine-lists.js +11 -3
  15. package/dist/tools/engine-lists.js.map +1 -1
  16. package/dist/tools/engine-surface.d.ts +1 -1
  17. package/dist/tools/engine-surface.d.ts.map +1 -1
  18. package/dist/tools/engine-surface.js +10 -4
  19. package/dist/tools/engine-surface.js.map +1 -1
  20. package/dist/tools/handlers.d.ts.map +1 -1
  21. package/dist/tools/handlers.js +59 -11
  22. package/dist/tools/handlers.js.map +1 -1
  23. package/dist/tools/lane.js +2 -2
  24. package/dist/tools/lane.js.map +1 -1
  25. package/dist/tools/reply-fields.d.ts +15 -0
  26. package/dist/tools/reply-fields.d.ts.map +1 -0
  27. package/dist/tools/reply-fields.js +117 -0
  28. package/dist/tools/reply-fields.js.map +1 -0
  29. package/dist/tools/scene-cache-export.d.ts.map +1 -1
  30. package/dist/tools/scene-cache-export.js +2 -1
  31. package/dist/tools/scene-cache-export.js.map +1 -1
  32. package/dist/tools/shape-params.d.ts +2 -2
  33. package/dist/tools/shape-params.d.ts.map +1 -1
  34. package/dist/tools/shape-params.js +4 -2
  35. package/dist/tools/shape-params.js.map +1 -1
  36. package/dist/tools/stick-vocabulary.d.ts +1 -1
  37. package/dist/tools/stick-vocabulary.js +1 -1
  38. package/dist/types/code-generator.d.ts.map +1 -1
  39. package/dist/types/code-generator.js +113 -34
  40. package/dist/types/code-generator.js.map +1 -1
  41. package/dist/types/schemas.d.ts +31 -6
  42. package/dist/types/schemas.d.ts.map +1 -1
  43. package/dist/types/schemas.js +11 -3
  44. package/dist/types/schemas.js.map +1 -1
  45. package/manifest.json +1 -1
  46. package/package.json +1 -1
@@ -9,6 +9,7 @@ import { dropPrivateRelationParams, normalizeBatchOperationNames, renamedArgumen
9
9
  import { nestedIgnoredPaths } from './nested-ignored.js';
10
10
  import { CRAFT_FRAME_JS, generateDrawWeave, generateDrawKnots, generateDrawSashiko, generateDrawSubdivision, generateUiKit, uiKitCallProblems, } from './craft-tools.js';
11
11
  import { exportPerScene } from './scene-cache-export.js';
12
+ import { boundedStructured, reportedIgnoredPaths } from './reply-fields.js';
12
13
  import { codeGenerator, generateQueryMutationsCode, SHEET_RUBRIC } from '../types/code-generator.js';
13
14
  import { expandBatchMacros, BatchMacroError } from './batch-macros.js';
14
15
  import { CreateItemInputSchema, ModifyItemInputSchema, DeleteItemInputSchema, AddRelationInputSchema, RemoveRelationInputSchema, QueryRelationsInputSchema, AnimateItemInputSchema, ImportMotionCaptureInputSchema, KeyframeAnimateInputSchema, ExecuteGeneratorInputSchema, ApplyEffectInputSchema, GetItemsInputSchema, PlayTimelineInputSchema, SetBackgroundColorInputSchema, SetCanvasSizeInputSchema, ImportSVGInputSchema, ImportMermaidInputSchema, AddFilterInputSchema, CreateGlossySphereInputSchema, CreateDiagonalStripesInputSchema, BatchCreateInputSchema, BatchModifyInputSchema, CreateGridInputSchema, GeometryInputSchema, EquationPathInputSchema, EventInputSchema, ConstructionSequenceInputSchema, ValidateSceneInputSchema, CaptureFramesInputSchema, InstantiateOntologyInputSchema, LintSceneInputSchema, MediaInputSchema, TextStyleInputSchema, TextEffectInputSchema, DesignMediumInputSchema, RelightInputSchema, ChoreographInputSchema, EmitterInputSchema, MorphSequenceInputSchema, OriginalCharacterInputSchema, LookInputSchema, RenderHookInputSchema, ShaderGraphInputSchema, ShatterImageInputSchema, ImportLayeredCharacterInputSchema, CharacterInputSchema, GameInputSchema, World3DInputSchema, BrandKitInputSchema, ComponentInputSchema, ArtboardInputSchema, CommentInputSchema, ProvenanceInputSchema, SceneDiffInputSchema, AudioBeatsInputSchema, TemplateParamsInputSchema, RenderBatchInputSchema, AccessibilityCheckInputSchema, QueryMutationsInputSchema, ComposeInputSchema, CropImageInputSchema, PathOpInputSchema, MotionInputSchema, SoundInputSchema, InterchangeInputSchema, StickInputSchema, DesignSystemInputSchema, StoryInputSchema, ChromaKeyInputSchema, RiggingInputSchema, GroupInputSchema, CameraDirectorInputSchema, DetectObjectsInputSchema, ExtractObjectInputSchema, ArrangeInputSchema, CreateSceneInputSchema, GetPerformanceMetricsInputSchema, DiagnosticReportInputSchema, SearchAssetsInputSchema, ImportAssetInputSchema, P5DrawInputSchema,
@@ -161,11 +162,11 @@ function exportGovernorTimeoutMs() {
161
162
  * size cap. This reads the user's disk on their instruction, the same as the
162
163
  * export side writes to it, and neither should be a wildcard.
163
164
  */
164
- const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp', '.avif']);
165
+ const IMAGE_EXTENSIONS = new Set(['.png', '.apng', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp', '.avif']);
165
166
  const MAX_LOCAL_IMAGE_BYTES = 32 * 1024 * 1024;
166
167
  const IMAGE_MIME = {
167
168
  '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif',
168
- '.webp': 'image/webp', '.svg': 'image/svg+xml', '.bmp': 'image/bmp', '.avif': 'image/avif',
169
+ '.webp': 'image/webp', '.apng': 'image/apng', '.svg': 'image/svg+xml', '.bmp': 'image/bmp', '.avif': 'image/avif',
169
170
  };
170
171
  /**
171
172
  * THE PAGE CANNOT FETCH A THIRD-PARTY IMAGE. THIS PROCESS CAN.
@@ -421,6 +422,7 @@ async function saveAdExport(r, input, platform) {
421
422
  if (warnings.length && cleanResult.fidelity.note)
422
423
  delete cleanResult.fidelity.note;
423
424
  return {
425
+ structuredContent: boundedStructured(cleanResult),
424
426
  content: [{
425
427
  type: 'text',
426
428
  text: `Ad saved:\n\n${Object.entries(files).map(([k, v]) => `${k}: ${v}`).join('\n')}\nFormat: ${r.format}\nSize: ${r.adSize.width}x${r.adSize.height}, upload ${(uploadBytes / 1024).toFixed(1)} KB\n\nResult: ${JSON.stringify(cleanResult, null, 2)}`,
@@ -1909,22 +1911,60 @@ export function batchOpIgnoredPaths(args) {
1909
1911
  return out.slice(0, 50);
1910
1912
  }
1911
1913
  export const IGNORED_ARGUMENTS_META_KEY = 'pinepaper.studio/ignoredArguments';
1912
- /** Adds fields to a reply whose first block is a JSON object; false when it is not one (or there is nothing to add). */
1914
+ /**
1915
+ * Adds fields to a reply whose first block is a JSON object, and to its structuredContent when it already has one (a
1916
+ * saved export: prose first, data beside it). False when neither took them (or nothing to add is true).
1917
+ */
1913
1918
  function withReplyFields(result, fields) {
1914
1919
  if (!Object.keys(fields).length)
1915
1920
  return true;
1921
+ const hadStructured = !!result.structuredContent;
1922
+ if (result.structuredContent)
1923
+ result.structuredContent = { ...result.structuredContent, ...fields };
1916
1924
  const first = result.content?.[0];
1917
1925
  if (first?.type !== 'text')
1918
- return false;
1926
+ return hadStructured;
1919
1927
  try {
1920
1928
  const obj = JSON.parse(first.text);
1921
1929
  if (!obj || typeof obj !== 'object' || Array.isArray(obj))
1922
- return false;
1930
+ return hadStructured;
1923
1931
  result.content = [{ ...first, text: JSON.stringify({ ...obj, ...fields }, null, 2) }, ...result.content.slice(1)];
1924
1932
  return true;
1925
1933
  }
1926
1934
  catch {
1927
- return false;
1935
+ return hadStructured;
1936
+ }
1937
+ }
1938
+ /** How many ops a written batch op becomes; a macro that does not expand is refused before anything runs. */
1939
+ function expandedOpCount(op) {
1940
+ try {
1941
+ return expandBatchMacros([op]).length;
1942
+ }
1943
+ catch {
1944
+ return 1;
1945
+ }
1946
+ }
1947
+ /**
1948
+ * The tools that hand back something the studio rendered. Render behaviour follows the live studio, not this package,
1949
+ * so a result is traced by the build that made it. agent_export's generated code says so itself; the rest are asked
1950
+ * here, once, after the call.
1951
+ */
1952
+ const RENDERING_TOOLS = new Set(['pinepaper_agent_export', 'pinepaper_capture_frames', 'pinepaper_export_scene', 'pinepaper_export_svg',
1953
+ 'pinepaper_export_training_data', 'pinepaper_export_widget', 'pinepaper_export_widget_html', 'pinepaper_render_batch', 'pinepaper_sprite_sheet']);
1954
+ async function renderingBuildField(toolName, args, result, options) {
1955
+ if (!RENDERING_TOOLS.has(toolName) || args.estimateOnly === true || result.isError || !options.executeInBrowser || result.structuredContent?.studioBuild)
1956
+ return {};
1957
+ if ((options.executionMode ?? getExecutionMode()) === 'code')
1958
+ return {};
1959
+ const controller = (options.browserController ?? peekBrowserController());
1960
+ if (!controller?.connected || typeof controller.executeCode !== 'function')
1961
+ return {};
1962
+ try {
1963
+ const r = await controller.executeCode(`(function () { return typeof app !== 'undefined' && app.buildId ? app.buildId : null; })();`, false, { bypassGovernor: true });
1964
+ return r?.success && typeof r.result === 'string' ? { studioBuild: r.result } : {};
1965
+ }
1966
+ catch {
1967
+ return {}; // the export stands
1928
1968
  }
1929
1969
  }
1930
1970
  export async function handleToolCall(toolName, args, options = {}) {
@@ -1955,10 +1995,12 @@ export async function handleToolCall(toolName, args, options = {}) {
1955
1995
  }
1956
1996
  }
1957
1997
  const ignored = await ignoredArgumentsNoteFor(toolName, args);
1958
- if (ignored) {
1998
+ if (ignored)
1959
1999
  result.content = [...(result.content ?? []), { type: 'text', text: ignored.note }];
1960
- result._meta = { ...(result._meta ?? {}), [IGNORED_ARGUMENTS_META_KEY]: ignored.ignored };
1961
- }
2000
+ // What only the run can tell: an option or property the op's own warning named. Its sentence is already in the reply.
2001
+ const ignoredPaths = [...new Set([...(ignored?.ignored ?? []), ...reportedIgnoredPaths(toolName, args, result, expandedOpCount)])];
2002
+ if (ignoredPaths.length)
2003
+ result._meta = { ...(result._meta ?? {}), [IGNORED_ARGUMENTS_META_KEY]: ignoredPaths };
1962
2004
  if (renamed.length)
1963
2005
  result._meta = { ...(result._meta ?? {}), [RENAMED_ARGUMENTS_META_KEY]: renamed };
1964
2006
  // IN THE REPLY'S OWN JSON. A client that has structuredContent reads that, and it is built from the first block
@@ -1966,7 +2008,11 @@ export async function handleToolCall(toolName, args, options = {}) {
1966
2008
  const folded = withReplyFields(result, {
1967
2009
  ...(renamed.length ? { translatedArguments: renamed } : {}),
1968
2010
  ...(dropped.length ? { droppedArguments: dropped } : {}),
1969
- ...(ignored ? { ignoredArguments: ignored.ignored } : {}),
2011
+ ...(ignoredPaths.length ? { ignoredArguments: ignoredPaths } : {}),
2012
+ ...await renderingBuildField(toolName, args, result, options),
2013
+ // The engine draws an APNG's first frame; a GIF or WebP plays. Said by name, since nothing about the reply would show it.
2014
+ ...(toolName === 'pinepaper_import_image' && !result.isError && typeof args.url === 'string' && /\.apng([?#]|$)/i.test(args.url)
2015
+ ? { animation: 'an APNG draws its first frame and does not play. Import a GIF or WebP to play it.' } : {}),
1970
2016
  });
1971
2017
  if (!folded) {
1972
2018
  const lines = [...(renamed.length ? [renamedArgumentsNote(renamed)] : []), ...(dropped.length ? [`Not sent to the studio, as its private state: ${dropped.join(', ')}.`] : [])];
@@ -4229,6 +4275,7 @@ You can now start creating new items on a clean canvas.${sizeNote}`,
4229
4275
  const receipt = await exportFileReceiptOrReason(filePath, exportResult);
4230
4276
  const cleanResult = { ...exportResult, data: undefined, filePath, fileSize, chunks, file: receipt };
4231
4277
  return withEngineReport({
4278
+ structuredContent: boundedStructured(cleanResult),
4232
4279
  content: [{
4233
4280
  type: 'text',
4234
4281
  text: `Export saved to file:\n\nFile: ${filePath}\nFormat: ${format}\nSize: ${(fileSize / 1024).toFixed(1)} KB\nPlatform: ${input.platform}\nPaged out of the studio's export store in ${chunks} chunk${chunks === 1 ? '' : 's'}\n${receiptErrorLine(receipt)}${'receiptError' in receipt ? '' : probeLines(receipt)}${lintLines(exportResult)}\nResult: ${JSON.stringify(cleanResult, null, 2)}`,
@@ -4265,7 +4312,7 @@ You can now start creating new items on a clean canvas.${sizeNote}`,
4265
4312
  const { sccCues: _c, ...rest } = exportResult;
4266
4313
  void _c;
4267
4314
  const cleanResult = { ...rest, filePath, fileSize, frameRate: '29.97 drop-frame', fidelity: { warnings } };
4268
- return withEngineReport({ content: [{ type: 'text', text: `Export saved to file:\n\nFile: ${filePath}\nFormat: scc (CEA-608, CC1, pop-on)\nSize: ${fileSize} bytes\n\nResult: ${JSON.stringify(cleanResult, null, 2)}` }, exportFileLink(filePath, fileSize)] }, exportBrowserResult.report);
4315
+ return withEngineReport({ structuredContent: boundedStructured(cleanResult), content: [{ type: 'text', text: `Export saved to file:\n\nFile: ${filePath}\nFormat: scc (CEA-608, CC1, pop-on)\nSize: ${fileSize} bytes\n\nResult: ${JSON.stringify(cleanResult, null, 2)}` }, exportFileLink(filePath, fileSize)] }, exportBrowserResult.report);
4269
4316
  }
4270
4317
  if ((format === 'html5-ad' || format === 'playable') && typeof exportResult?.adPage === 'string') {
4271
4318
  try {
@@ -4295,6 +4342,7 @@ You can now start creating new items on a clean canvas.${sizeNote}`,
4295
4342
  // it has to be said out loud here rather than left in the JSON.
4296
4343
  const dropNote = exportResult.incomplete;
4297
4344
  return withEngineReport({
4345
+ structuredContent: boundedStructured(cleanResult),
4298
4346
  content: [{
4299
4347
  type: 'text',
4300
4348
  text: `Export saved to file:\n\nFile: ${filePath}\nFormat: ${format}\nSize: ${(fileSize / 1024).toFixed(1)} KB\nPlatform: ${input.platform}\n`