@pinepaper.studio/mcp-server 1.5.0 → 1.5.3
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.
- package/README.md +0 -25
- package/dist/agent/export-manager.d.ts.map +1 -1
- package/dist/agent/export-manager.js +24 -14
- package/dist/agent/export-manager.js.map +1 -1
- package/dist/agent/types.d.ts +6 -6
- package/dist/browser/puppeteer-controller.d.ts +5 -1
- package/dist/browser/puppeteer-controller.d.ts.map +1 -1
- package/dist/browser/puppeteer-controller.js +11 -3
- package/dist/browser/puppeteer-controller.js.map +1 -1
- package/dist/cli.js +61 -66
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +145 -122
- package/dist/index.js.map +1 -1
- package/dist/metrics/types.d.ts +3 -1
- package/dist/metrics/types.d.ts.map +1 -1
- package/dist/ontology/design-graph.d.ts +85 -0
- package/dist/ontology/design-graph.d.ts.map +1 -0
- package/dist/ontology/design-graph.js +944 -0
- package/dist/ontology/design-graph.js.map +1 -0
- package/dist/ontology/index.d.ts +11 -0
- package/dist/ontology/index.d.ts.map +1 -0
- package/dist/ontology/index.js +14 -0
- package/dist/ontology/index.js.map +1 -0
- package/dist/ontology/semantic-vocabulary.d.ts +12 -0
- package/dist/ontology/semantic-vocabulary.d.ts.map +1 -0
- package/dist/ontology/semantic-vocabulary.js +26 -0
- package/dist/ontology/semantic-vocabulary.js.map +1 -0
- package/dist/ontology/types.d.ts +255 -0
- package/dist/ontology/types.d.ts.map +1 -0
- package/dist/ontology/types.js +10 -0
- package/dist/ontology/types.js.map +1 -0
- package/dist/ontology/validator.d.ts +36 -0
- package/dist/ontology/validator.d.ts.map +1 -0
- package/dist/ontology/validator.js +374 -0
- package/dist/ontology/validator.js.map +1 -0
- package/dist/ontology/vocabulary.d.ts +34 -0
- package/dist/ontology/vocabulary.d.ts.map +1 -0
- package/dist/ontology/vocabulary.js +395 -0
- package/dist/ontology/vocabulary.js.map +1 -0
- package/dist/prompts/index.d.ts +3 -12
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +5 -96
- package/dist/prompts/index.js.map +1 -1
- package/dist/tools/compact-descriptions.d.ts +3 -3
- package/dist/tools/compact-descriptions.d.ts.map +1 -1
- package/dist/tools/compact-descriptions.js +58 -215
- package/dist/tools/compact-descriptions.js.map +1 -1
- package/dist/tools/definitions.d.ts +6 -4
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +789 -65
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +616 -86
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts +9 -0
- package/dist/tools/minimal-descriptions.d.ts.map +1 -0
- package/dist/tools/minimal-descriptions.js +178 -0
- package/dist/tools/minimal-descriptions.js.map +1 -0
- package/dist/tools/toolkits.d.ts +51 -0
- package/dist/tools/toolkits.d.ts.map +1 -0
- package/dist/tools/toolkits.js +366 -0
- package/dist/tools/toolkits.js.map +1 -0
- package/dist/types/code-generator.d.ts +14 -6
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +654 -196
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +429 -147
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +159 -49
- package/dist/types/schemas.js.map +1 -1
- package/manifest.json +30 -6
- package/package.json +2 -2
package/dist/tools/handlers.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Supports browser execution via Puppeteer.
|
|
7
7
|
*/
|
|
8
8
|
import { codeGenerator } from '../types/code-generator.js';
|
|
9
|
-
import { CreateItemInputSchema, ModifyItemInputSchema, DeleteItemInputSchema, AddRelationInputSchema, RemoveRelationInputSchema, QueryRelationsInputSchema, AnimateItemInputSchema, KeyframeAnimateInputSchema, ExecuteGeneratorInputSchema, ApplyEffectInputSchema, GetItemsInputSchema, PlayTimelineInputSchema, SetBackgroundColorInputSchema, SetCanvasSizeInputSchema, ExportTrainingDataInputSchema, ImportSVGInputSchema, AddFilterInputSchema, CreateGlossySphereInputSchema, CreateDiagonalStripesInputSchema, BatchCreateInputSchema, BatchModifyInputSchema, CreateGridInputSchema, CreateSceneInputSchema, GetPerformanceMetricsInputSchema, SearchAssetsInputSchema, ImportAssetInputSchema, P5DrawInputSchema,
|
|
9
|
+
import { CreateItemInputSchema, ModifyItemInputSchema, DeleteItemInputSchema, AddRelationInputSchema, RemoveRelationInputSchema, QueryRelationsInputSchema, AnimateItemInputSchema, KeyframeAnimateInputSchema, ExecuteGeneratorInputSchema, ApplyEffectInputSchema, GetItemsInputSchema, PlayTimelineInputSchema, SetBackgroundColorInputSchema, SetCanvasSizeInputSchema, ExportTrainingDataInputSchema, ImportSVGInputSchema, AddFilterInputSchema, CreateGlossySphereInputSchema, CreateDiagonalStripesInputSchema, BatchCreateInputSchema, BatchModifyInputSchema, CreateGridInputSchema, CreateSceneInputSchema, GetPerformanceMetricsInputSchema, DiagnosticReportInputSchema, SearchAssetsInputSchema, ImportAssetInputSchema, P5DrawInputSchema,
|
|
10
10
|
// Diagram schemas
|
|
11
11
|
CreateDiagramShapeInputSchema, ConnectInputSchema, ConnectPortsInputSchema, AddPortsInputSchema, AutoLayoutInputSchema, GetDiagramShapesInputSchema, UpdateConnectorInputSchema, RemoveConnectorInputSchema, DiagramModeInputSchema,
|
|
12
12
|
// Agent flow mode schemas
|
|
@@ -28,12 +28,20 @@ ApplyAnimatedMaskInputSchema, ApplyCustomMaskInputSchema, RemoveMaskInputSchema,
|
|
|
28
28
|
// Template schemas
|
|
29
29
|
ApplyTemplateInputSchema,
|
|
30
30
|
// Image import schemas
|
|
31
|
-
ImportImageInputSchema,
|
|
31
|
+
ImportImageInputSchema,
|
|
32
|
+
// Tool guide schema
|
|
33
|
+
ToolGuideInputSchema,
|
|
34
|
+
// Ontology schemas
|
|
35
|
+
AnalyzeDesignInputSchema, ValidateDesignInputSchema, QueryOntologyInputSchema,
|
|
36
|
+
// Scene management schemas
|
|
37
|
+
ManageScenesInputSchema, ScenePlaybackInputSchema,
|
|
38
|
+
// New consolidated tool schemas
|
|
39
|
+
SelectionInputSchema, TransformInputSchema, HistoryInputSchema, ImageFilterInputSchema, LassoInputSchema, CutoutStyleInputSchema, PrecompInputSchema, ViewInputSchema, BackgroundInputSchema, QueryInputSchema, ErrorCodes, } from '../types/schemas.js';
|
|
32
40
|
import { ZodError } from 'zod';
|
|
33
41
|
import { writeFile, mkdir } from 'node:fs/promises';
|
|
34
42
|
import { tmpdir } from 'node:os';
|
|
35
43
|
import { join } from 'node:path';
|
|
36
|
-
import { getBrowserController, } from '../browser/puppeteer-controller.js';
|
|
44
|
+
import { getBrowserController, resetBrowserController, } from '../browser/puppeteer-controller.js';
|
|
37
45
|
import { getPerformanceTracker } from '../metrics/index.js';
|
|
38
46
|
import { formatErrorContext, captureCanvasState } from '../execution/index.js';
|
|
39
47
|
import { getSessionManager } from '../agent/session-manager.js';
|
|
@@ -111,18 +119,13 @@ function successResult(code, description) {
|
|
|
111
119
|
return { content };
|
|
112
120
|
}
|
|
113
121
|
function executedResult(code, result, screenshot, description) {
|
|
122
|
+
const resultObj = { success: true, result };
|
|
123
|
+
if (description)
|
|
124
|
+
resultObj.description = description;
|
|
114
125
|
const content = [
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
text: `Executed PinePaper code:\n\n\`\`\`javascript\n${code}\n\`\`\`\n\nResult: ${JSON.stringify(result, null, 2)}`,
|
|
118
|
-
},
|
|
126
|
+
{ type: 'text', text: JSON.stringify(resultObj, null, 2) },
|
|
127
|
+
{ type: 'text', text: `Generated code:\n\`\`\`javascript\n${code}\n\`\`\`` },
|
|
119
128
|
];
|
|
120
|
-
if (description) {
|
|
121
|
-
content.push({
|
|
122
|
-
type: 'text',
|
|
123
|
-
text: `\n${description}`,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
129
|
if (screenshot) {
|
|
127
130
|
content.push({
|
|
128
131
|
type: 'image',
|
|
@@ -412,10 +415,43 @@ async function executeToolWithTracking(toolName, args, options, validator, codeG
|
|
|
412
415
|
// =============================================================================
|
|
413
416
|
// TOOL HANDLERS
|
|
414
417
|
// =============================================================================
|
|
418
|
+
/**
|
|
419
|
+
* Measure the approximate byte size of a CallToolResult payload.
|
|
420
|
+
* Text content is measured directly; image content estimates base64 overhead.
|
|
421
|
+
*/
|
|
422
|
+
function measureResponseBytes(result) {
|
|
423
|
+
let bytes = 0;
|
|
424
|
+
if (result.content) {
|
|
425
|
+
for (const item of result.content) {
|
|
426
|
+
if (item.type === 'text') {
|
|
427
|
+
bytes += Buffer.byteLength(item.text, 'utf-8');
|
|
428
|
+
}
|
|
429
|
+
else if (item.type === 'image') {
|
|
430
|
+
// Base64 image data
|
|
431
|
+
bytes += (item.data?.length ?? 0);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return bytes;
|
|
436
|
+
}
|
|
415
437
|
export async function handleToolCall(toolName, args, options = {}) {
|
|
416
|
-
const { i18n } = options;
|
|
417
438
|
const tracker = getPerformanceTracker();
|
|
418
439
|
const baseTimerId = `${toolName}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
440
|
+
const result = await handleToolCallInner(toolName, args, options, tracker, baseTimerId);
|
|
441
|
+
// Record response payload size for token estimation
|
|
442
|
+
const responseBytes = measureResponseBytes(result);
|
|
443
|
+
tracker.recordMetric({
|
|
444
|
+
toolName,
|
|
445
|
+
phase: 'response_size',
|
|
446
|
+
duration: 0,
|
|
447
|
+
timestamp: Date.now(),
|
|
448
|
+
success: !result.isError,
|
|
449
|
+
responseBytes,
|
|
450
|
+
});
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
453
|
+
async function handleToolCallInner(toolName, args, options, tracker, baseTimerId) {
|
|
454
|
+
const { i18n } = options;
|
|
419
455
|
try {
|
|
420
456
|
switch (toolName) {
|
|
421
457
|
// -----------------------------------------------------------------------
|
|
@@ -499,6 +535,78 @@ export async function handleToolCall(toolName, args, options = {}) {
|
|
|
499
535
|
const description = `Creates complete scene with ${itemCount} items, ${relationCount} relations, ${animationCount} animations`;
|
|
500
536
|
return executeOrGenerate(code, description, options, 'pinepaper_create_scene');
|
|
501
537
|
}
|
|
538
|
+
case 'pinepaper_manage_scenes': {
|
|
539
|
+
const input = ManageScenesInputSchema.parse(args);
|
|
540
|
+
const code = codeGenerator.generateManageScenes(input);
|
|
541
|
+
return executeOrGenerate(code, `Scene: ${input.action}`, options, 'pinepaper_manage_scenes');
|
|
542
|
+
}
|
|
543
|
+
case 'pinepaper_scene_playback': {
|
|
544
|
+
const input = ScenePlaybackInputSchema.parse(args);
|
|
545
|
+
const code = codeGenerator.generateScenePlayback(input);
|
|
546
|
+
return executeOrGenerate(code, `Scene playback: ${input.action}`, options, 'pinepaper_scene_playback');
|
|
547
|
+
}
|
|
548
|
+
// -----------------------------------------------------------------------
|
|
549
|
+
// SELECTION, TRANSFORM & HISTORY TOOLS
|
|
550
|
+
// -----------------------------------------------------------------------
|
|
551
|
+
case 'pinepaper_selection': {
|
|
552
|
+
const input = SelectionInputSchema.parse(args);
|
|
553
|
+
const code = codeGenerator.generateSelection(input);
|
|
554
|
+
return executeOrGenerate(code, `Selection: ${input.action}`, options, 'pinepaper_selection');
|
|
555
|
+
}
|
|
556
|
+
case 'pinepaper_transform': {
|
|
557
|
+
const input = TransformInputSchema.parse(args);
|
|
558
|
+
const code = codeGenerator.generateTransform(input);
|
|
559
|
+
return executeOrGenerate(code, `Transform: ${input.action}`, options, 'pinepaper_transform');
|
|
560
|
+
}
|
|
561
|
+
case 'pinepaper_history': {
|
|
562
|
+
const input = HistoryInputSchema.parse(args);
|
|
563
|
+
const code = codeGenerator.generateHistory(input);
|
|
564
|
+
return executeOrGenerate(code, `History: ${input.action}`, options, 'pinepaper_history');
|
|
565
|
+
}
|
|
566
|
+
// -----------------------------------------------------------------------
|
|
567
|
+
// IMAGE PROCESSING TOOLS
|
|
568
|
+
// -----------------------------------------------------------------------
|
|
569
|
+
case 'pinepaper_image_filter': {
|
|
570
|
+
const input = ImageFilterInputSchema.parse(args);
|
|
571
|
+
const code = codeGenerator.generateImageFilter(input);
|
|
572
|
+
return executeOrGenerate(code, `Image filter: ${input.action}`, options, 'pinepaper_image_filter');
|
|
573
|
+
}
|
|
574
|
+
case 'pinepaper_lasso': {
|
|
575
|
+
const input = LassoInputSchema.parse(args);
|
|
576
|
+
const code = codeGenerator.generateLasso(input);
|
|
577
|
+
return executeOrGenerate(code, `Lasso: ${input.action}`, options, 'pinepaper_lasso');
|
|
578
|
+
}
|
|
579
|
+
case 'pinepaper_cutout_style': {
|
|
580
|
+
const input = CutoutStyleInputSchema.parse(args);
|
|
581
|
+
const code = codeGenerator.generateCutoutStyle(input);
|
|
582
|
+
return executeOrGenerate(code, `Cutout style: ${input.action}`, options, 'pinepaper_cutout_style');
|
|
583
|
+
}
|
|
584
|
+
// -----------------------------------------------------------------------
|
|
585
|
+
// COMPOSITION & VIEW TOOLS
|
|
586
|
+
// -----------------------------------------------------------------------
|
|
587
|
+
case 'pinepaper_precomp': {
|
|
588
|
+
const input = PrecompInputSchema.parse(args);
|
|
589
|
+
const code = codeGenerator.generatePrecomp(input);
|
|
590
|
+
return executeOrGenerate(code, `Precomp: ${input.action}`, options, 'pinepaper_precomp');
|
|
591
|
+
}
|
|
592
|
+
case 'pinepaper_view': {
|
|
593
|
+
const input = ViewInputSchema.parse(args);
|
|
594
|
+
const code = codeGenerator.generateView(input);
|
|
595
|
+
return executeOrGenerate(code, `View: ${input.action}`, options, 'pinepaper_view');
|
|
596
|
+
}
|
|
597
|
+
case 'pinepaper_background': {
|
|
598
|
+
const input = BackgroundInputSchema.parse(args);
|
|
599
|
+
const code = codeGenerator.generateBackground(input);
|
|
600
|
+
return executeOrGenerate(code, `Background: ${input.action}`, options, 'pinepaper_background');
|
|
601
|
+
}
|
|
602
|
+
// -----------------------------------------------------------------------
|
|
603
|
+
// CANVAS QUERY TOOLS
|
|
604
|
+
// -----------------------------------------------------------------------
|
|
605
|
+
case 'pinepaper_query': {
|
|
606
|
+
const input = QueryInputSchema.parse(args);
|
|
607
|
+
const code = codeGenerator.generateQuery(input);
|
|
608
|
+
return executeOrGenerate(code, `Query: ${input.action}`, options, 'pinepaper_query');
|
|
609
|
+
}
|
|
502
610
|
// -----------------------------------------------------------------------
|
|
503
611
|
// BATCH OPERATION TOOLS
|
|
504
612
|
// -----------------------------------------------------------------------
|
|
@@ -924,86 +1032,35 @@ You can now start creating new items on a clean canvas.`,
|
|
|
924
1032
|
// BROWSER CONTROL TOOLS
|
|
925
1033
|
// -----------------------------------------------------------------------
|
|
926
1034
|
case 'pinepaper_browser_connect': {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
const
|
|
930
|
-
|
|
931
|
-
if (controller.connected) {
|
|
1035
|
+
// Default to headless: false — if you explicitly call browser_connect, you likely want to see it
|
|
1036
|
+
const headless = args.headless ?? false;
|
|
1037
|
+
const controller = getBrowserController({ headless });
|
|
1038
|
+
// Already connected with same headless mode → nothing to do
|
|
1039
|
+
if (controller.connected && controller.isHeadless === headless) {
|
|
932
1040
|
return {
|
|
933
|
-
content: [
|
|
934
|
-
{
|
|
935
|
-
type: 'text',
|
|
936
|
-
text: `Already connected to PinePaper Studio at ${controller.actualUrl}. Do NOT call this tool again — proceed directly with pinepaper_agent_start_job.`,
|
|
937
|
-
},
|
|
938
|
-
],
|
|
1041
|
+
content: [{ type: 'text', text: `Already connected to PinePaper Studio (headless: ${headless}).` }],
|
|
939
1042
|
};
|
|
940
1043
|
}
|
|
1044
|
+
// Already connected but different headless mode → reconnect
|
|
1045
|
+
if (controller.connected && controller.isHeadless !== headless) {
|
|
1046
|
+
await resetBrowserController();
|
|
1047
|
+
}
|
|
941
1048
|
try {
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1049
|
+
const newController = controller.connected ? controller : getBrowserController({ headless });
|
|
1050
|
+
if (!newController.connected)
|
|
1051
|
+
await newController.connect();
|
|
1052
|
+
const canvasSizeResult = await newController.executeCode(`const size = app.getCanvasSize ? app.getCanvasSize() : { width: 800, height: 600 }; ({ width: size.width || 800, height: size.height || 600 });`, false);
|
|
945
1053
|
const canvasSize = canvasSizeResult.success
|
|
946
1054
|
? canvasSizeResult.result
|
|
947
1055
|
: { width: 800, height: 600 };
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
type: 'text',
|
|
952
|
-
text: `✅ Connected to PinePaper Studio at ${controller.actualUrl}
|
|
953
|
-
|
|
954
|
-
**Agent mode enabled** (agent=1&mode=agent parameters added automatically)
|
|
955
|
-
**Current canvas size: ${canvasSize.width}x${canvasSize.height}**
|
|
956
|
-
|
|
957
|
-
⚠️ IMPORTANT NOTES:
|
|
958
|
-
1. **Welcome Template**: First-time visitors see a welcome template with example items. Use \`pinepaper_clear_canvas\` or \`pinepaper_refresh_page\` to start with an empty canvas.
|
|
959
|
-
2. **Canvas Size**: For complex designs (cards, invitations, detailed graphics), call \`pinepaper_set_canvas_size\` FIRST to set an appropriate size like 1080x1080 or larger. The default 800x600 may be too small.
|
|
960
|
-
3. **Verify with Screenshots**: After making changes, use \`pinepaper_browser_screenshot\` to confirm your work.
|
|
961
|
-
|
|
962
|
-
Browser is ready. You can now use other pinepaper tools to create and animate graphics.`,
|
|
963
|
-
},
|
|
964
|
-
];
|
|
965
|
-
if (screenshot) {
|
|
966
|
-
content.push({
|
|
967
|
-
type: 'image',
|
|
968
|
-
data: screenshot,
|
|
969
|
-
mimeType: 'image/png',
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
|
-
return { content };
|
|
1056
|
+
return {
|
|
1057
|
+
content: [{ type: 'text', text: `Connected to PinePaper Studio (headless: ${headless}, canvas: ${canvasSize.width}x${canvasSize.height}).` }],
|
|
1058
|
+
};
|
|
973
1059
|
}
|
|
974
1060
|
catch (error) {
|
|
975
|
-
const errorMessage = error instanceof Error ? error.message : 'Failed to connect
|
|
976
|
-
// Provide helpful fallback instructions instead of just failing
|
|
1061
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to connect';
|
|
977
1062
|
return {
|
|
978
|
-
content: [
|
|
979
|
-
{
|
|
980
|
-
type: 'text',
|
|
981
|
-
text: `⚠️ **Browser Connection Failed**
|
|
982
|
-
|
|
983
|
-
**Error:** ${errorMessage}
|
|
984
|
-
|
|
985
|
-
## Don't worry! You can still use PinePaper manually:
|
|
986
|
-
|
|
987
|
-
### Option 1: Manual Browser Mode
|
|
988
|
-
1. Open **PinePaper Studio** in your browser: https://pinepaper.studio/editor?agent=1&mode=agent
|
|
989
|
-
2. Continue using PinePaper tools - the MCP server will generate code for each action
|
|
990
|
-
3. Copy the generated code and paste it into the **Code Console** (</> icon in toolbar)
|
|
991
|
-
|
|
992
|
-
### Option 2: Troubleshoot Browser Connection
|
|
993
|
-
- **Check Chrome/Chromium**: Puppeteer requires Chrome or Chromium installed
|
|
994
|
-
- **Try headless: false**: Call \`pinepaper_browser_connect\` with \`headless: false\` to see the browser window
|
|
995
|
-
- **Check permissions**: Ensure the process has permission to launch browsers
|
|
996
|
-
- **Firewall**: Check if firewall is blocking browser launch
|
|
997
|
-
|
|
998
|
-
### What happens now?
|
|
999
|
-
All PinePaper tools will work in **code-only mode**:
|
|
1000
|
-
- Each tool generates JavaScript code
|
|
1001
|
-
- You copy the code and paste it in PinePaper's Code Console
|
|
1002
|
-
- The code executes and creates your graphics
|
|
1003
|
-
|
|
1004
|
-
**The MCP server is fully functional** - just without automatic browser control.`,
|
|
1005
|
-
},
|
|
1006
|
-
],
|
|
1063
|
+
content: [{ type: 'text', text: `Browser connection failed: ${errorMessage}` }],
|
|
1007
1064
|
};
|
|
1008
1065
|
}
|
|
1009
1066
|
}
|
|
@@ -1094,6 +1151,286 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1094
1151
|
};
|
|
1095
1152
|
}
|
|
1096
1153
|
// -----------------------------------------------------------------------
|
|
1154
|
+
// DIAGNOSTIC REPORT
|
|
1155
|
+
// -----------------------------------------------------------------------
|
|
1156
|
+
case 'pinepaper_diagnostic_report': {
|
|
1157
|
+
const input = DiagnosticReportInputSchema.parse(args);
|
|
1158
|
+
const includeMetrics = input.includeMetrics !== false;
|
|
1159
|
+
const includeCanvas = input.includeCanvas !== false;
|
|
1160
|
+
const metricsLimit = input.metricsLimit ?? 100;
|
|
1161
|
+
// --- Server info ---
|
|
1162
|
+
const { detectToolkitFromEnvironment: detectToolkit, detectVerbosityFromEnvironment: detectVerbosity } = await import('./toolkits.js');
|
|
1163
|
+
const serverSection = {
|
|
1164
|
+
version: '1.5.1',
|
|
1165
|
+
toolkit: detectToolkit(),
|
|
1166
|
+
verbosity: detectVerbosity(),
|
|
1167
|
+
locale: process.env.PINEPAPER_LOCALE || 'en',
|
|
1168
|
+
executionMode: options.executionMode || getExecutionMode(),
|
|
1169
|
+
screenshotMode: options.screenshotMode || getScreenshotMode(),
|
|
1170
|
+
};
|
|
1171
|
+
// --- Browser state ---
|
|
1172
|
+
let browserSection = { connected: false };
|
|
1173
|
+
try {
|
|
1174
|
+
const ctrl = options.browserController || getBrowserController();
|
|
1175
|
+
browserSection = {
|
|
1176
|
+
connected: ctrl.connected,
|
|
1177
|
+
agentMode: ctrl.agentMode,
|
|
1178
|
+
studioUrl: ctrl.studioUrl,
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
catch { /* browser not initialized */ }
|
|
1182
|
+
// --- Session state ---
|
|
1183
|
+
let sessionSection = {};
|
|
1184
|
+
try {
|
|
1185
|
+
const session = getSessionManager();
|
|
1186
|
+
sessionSection = session.getStats();
|
|
1187
|
+
}
|
|
1188
|
+
catch { /* session not initialized */ }
|
|
1189
|
+
// --- Canvas state ---
|
|
1190
|
+
let canvasSection = null;
|
|
1191
|
+
if (includeCanvas) {
|
|
1192
|
+
try {
|
|
1193
|
+
const ctrl = options.browserController || getBrowserController();
|
|
1194
|
+
if (ctrl.connected) {
|
|
1195
|
+
canvasSection = await captureCanvasState(ctrl);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
catch { /* canvas capture failed */ }
|
|
1199
|
+
}
|
|
1200
|
+
// --- Performance metrics ---
|
|
1201
|
+
let metricsSection = null;
|
|
1202
|
+
if (includeMetrics) {
|
|
1203
|
+
try {
|
|
1204
|
+
const perfTracker = getPerformanceTracker();
|
|
1205
|
+
const recentMetrics = perfTracker.exportMetrics('json', { limit: metricsLimit });
|
|
1206
|
+
const stats = perfTracker.getStats();
|
|
1207
|
+
metricsSection = { entries: recentMetrics, stats };
|
|
1208
|
+
}
|
|
1209
|
+
catch { /* metrics not available */ }
|
|
1210
|
+
}
|
|
1211
|
+
// --- Tool definitions overhead (fixed cost per API turn) ---
|
|
1212
|
+
let toolDefsSection = null;
|
|
1213
|
+
try {
|
|
1214
|
+
const { PINEPAPER_TOOLS, getToolsForVerbosity } = await import('./definitions.js');
|
|
1215
|
+
const { getToolsForToolkit } = await import('./toolkits.js');
|
|
1216
|
+
const toolkit = detectToolkit();
|
|
1217
|
+
const verbosity = detectVerbosity();
|
|
1218
|
+
// Measure what actually gets sent to the LLM
|
|
1219
|
+
const activeTools = getToolsForToolkit(getToolsForVerbosity(verbosity), toolkit);
|
|
1220
|
+
const activeJson = JSON.stringify(activeTools);
|
|
1221
|
+
const activeBytes = Buffer.byteLength(activeJson, 'utf-8');
|
|
1222
|
+
const activeTokens = Math.ceil(activeBytes / 4);
|
|
1223
|
+
// Also measure full set for comparison
|
|
1224
|
+
const fullJson = JSON.stringify(PINEPAPER_TOOLS);
|
|
1225
|
+
const fullBytes = Buffer.byteLength(fullJson, 'utf-8');
|
|
1226
|
+
const fullTokens = Math.ceil(fullBytes / 4);
|
|
1227
|
+
// Measure other profiles for comparison recommendations
|
|
1228
|
+
const agentCompactTools = getToolsForToolkit(getToolsForVerbosity('compact'), 'agent');
|
|
1229
|
+
const agentCompactTokens = Math.ceil(Buffer.byteLength(JSON.stringify(agentCompactTools), 'utf-8') / 4);
|
|
1230
|
+
const agentMinimalTools = getToolsForToolkit(getToolsForVerbosity('minimal'), 'agent');
|
|
1231
|
+
const agentMinimalTokens = Math.ceil(Buffer.byteLength(JSON.stringify(agentMinimalTools), 'utf-8') / 4);
|
|
1232
|
+
// Per-tool breakdown of active set (top 10 heaviest)
|
|
1233
|
+
const perToolDef = activeTools.map((t) => ({
|
|
1234
|
+
tool: t.name,
|
|
1235
|
+
bytes: Buffer.byteLength(JSON.stringify(t), 'utf-8'),
|
|
1236
|
+
descBytes: Buffer.byteLength(t.description || '', 'utf-8'),
|
|
1237
|
+
})).sort((a, b) => b.bytes - a.bytes);
|
|
1238
|
+
const totalDescBytes = perToolDef.reduce((s, t) => s + t.descBytes, 0);
|
|
1239
|
+
toolDefsSection = {
|
|
1240
|
+
toolkit,
|
|
1241
|
+
verbosity,
|
|
1242
|
+
activeToolCount: activeTools.length,
|
|
1243
|
+
activeBytes,
|
|
1244
|
+
activeTokens,
|
|
1245
|
+
fullToolCount: PINEPAPER_TOOLS.length,
|
|
1246
|
+
fullBytes,
|
|
1247
|
+
fullTokens,
|
|
1248
|
+
descriptionBytes: totalDescBytes,
|
|
1249
|
+
descriptionPct: ((totalDescBytes / activeBytes) * 100).toFixed(1) + '%',
|
|
1250
|
+
top10: perToolDef.slice(0, 10).map((t) => ({
|
|
1251
|
+
tool: t.tool,
|
|
1252
|
+
bytes: t.bytes,
|
|
1253
|
+
tokens: Math.ceil(t.bytes / 4),
|
|
1254
|
+
})),
|
|
1255
|
+
profiles: {
|
|
1256
|
+
'full+verbose': fullTokens,
|
|
1257
|
+
'agent+compact': agentCompactTokens,
|
|
1258
|
+
'agent+minimal': agentMinimalTokens,
|
|
1259
|
+
},
|
|
1260
|
+
recommendations: [],
|
|
1261
|
+
};
|
|
1262
|
+
// Add actionable recommendations
|
|
1263
|
+
const recs = toolDefsSection.recommendations;
|
|
1264
|
+
if (toolkit === 'full') {
|
|
1265
|
+
recs.push(`Switch to PINEPAPER_TOOLKIT=agent to reduce to ~${agentCompactTokens.toLocaleString()} tokens/turn (agent+compact)`);
|
|
1266
|
+
}
|
|
1267
|
+
if (verbosity === 'verbose') {
|
|
1268
|
+
recs.push(`Set PINEPAPER_VERBOSITY=compact to reduce tool definitions overhead`);
|
|
1269
|
+
}
|
|
1270
|
+
if (verbosity !== 'minimal') {
|
|
1271
|
+
recs.push(`Set PINEPAPER_VERBOSITY=minimal for maximum savings (~${agentMinimalTokens.toLocaleString()} tokens with agent toolkit) — use pinepaper_tool_guide for on-demand docs`);
|
|
1272
|
+
}
|
|
1273
|
+
if (activeTokens > 20000) {
|
|
1274
|
+
recs.push(`Tool definitions alone consume ~${activeTokens.toLocaleString()} tokens per API turn — this is a major context budget item`);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
catch { /* definitions not available */ }
|
|
1278
|
+
// --- Token usage estimate (response payloads) ---
|
|
1279
|
+
let tokenSection = null;
|
|
1280
|
+
try {
|
|
1281
|
+
const perfTracker = getPerformanceTracker();
|
|
1282
|
+
const responseSizeMetrics = perfTracker.getMetrics({ phase: 'response_size' });
|
|
1283
|
+
// Aggregate per-tool response sizes
|
|
1284
|
+
const perTool = new Map();
|
|
1285
|
+
let totalResponseBytes = 0;
|
|
1286
|
+
for (const m of responseSizeMetrics) {
|
|
1287
|
+
const bytes = m.responseBytes ?? 0;
|
|
1288
|
+
totalResponseBytes += bytes;
|
|
1289
|
+
const entry = perTool.get(m.toolName) ?? { calls: 0, totalBytes: 0 };
|
|
1290
|
+
entry.calls++;
|
|
1291
|
+
entry.totalBytes += bytes;
|
|
1292
|
+
perTool.set(m.toolName, entry);
|
|
1293
|
+
}
|
|
1294
|
+
// ~4 chars per token is a common estimate for English text
|
|
1295
|
+
const estimatedTokens = Math.ceil(totalResponseBytes / 4);
|
|
1296
|
+
// Sort by totalBytes descending
|
|
1297
|
+
const perToolSorted = [...perTool.entries()]
|
|
1298
|
+
.sort((a, b) => b[1].totalBytes - a[1].totalBytes)
|
|
1299
|
+
.map(([tool, data]) => ({
|
|
1300
|
+
tool,
|
|
1301
|
+
calls: data.calls,
|
|
1302
|
+
totalBytes: data.totalBytes,
|
|
1303
|
+
estimatedTokens: Math.ceil(data.totalBytes / 4),
|
|
1304
|
+
avgBytesPerCall: Math.round(data.totalBytes / data.calls),
|
|
1305
|
+
}));
|
|
1306
|
+
tokenSection = {
|
|
1307
|
+
totalToolCalls: responseSizeMetrics.length,
|
|
1308
|
+
totalResponseBytes,
|
|
1309
|
+
estimatedResponseTokens: estimatedTokens,
|
|
1310
|
+
perTool: perToolSorted,
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
catch { /* metrics not available */ }
|
|
1314
|
+
// --- Assemble report ---
|
|
1315
|
+
const report = {
|
|
1316
|
+
timestamp: new Date().toISOString(),
|
|
1317
|
+
server: serverSection,
|
|
1318
|
+
browser: browserSection,
|
|
1319
|
+
session: sessionSection,
|
|
1320
|
+
canvas: canvasSection,
|
|
1321
|
+
metrics: metricsSection,
|
|
1322
|
+
toolDefinitions: toolDefsSection,
|
|
1323
|
+
tokenUsage: tokenSection,
|
|
1324
|
+
};
|
|
1325
|
+
// --- Save to disk ---
|
|
1326
|
+
const exportDir = getExportDir();
|
|
1327
|
+
await mkdir(exportDir, { recursive: true });
|
|
1328
|
+
const fileName = `pinepaper_diagnostic_${Date.now()}.json`;
|
|
1329
|
+
const filePath = join(exportDir, fileName);
|
|
1330
|
+
const jsonContent = JSON.stringify(report, null, 2);
|
|
1331
|
+
await writeFile(filePath, jsonContent, 'utf-8');
|
|
1332
|
+
// --- Build summary (token budget first — it's what matters most) ---
|
|
1333
|
+
const lines = [
|
|
1334
|
+
`Diagnostic report saved to: ${filePath}`,
|
|
1335
|
+
``,
|
|
1336
|
+
];
|
|
1337
|
+
// Token budget section — prominent at top
|
|
1338
|
+
if (toolDefsSection) {
|
|
1339
|
+
const td = toolDefsSection;
|
|
1340
|
+
lines.push(`TOKEN BUDGET`);
|
|
1341
|
+
lines.push(` Tool definitions: ~${td.activeTokens.toLocaleString()} tokens/turn (${td.activeToolCount} tools, ${(td.activeBytes / 1024).toFixed(0)} KB)`);
|
|
1342
|
+
lines.push(` Descriptions are ${td.descriptionPct} of that overhead`);
|
|
1343
|
+
lines.push(` Config: toolkit=${td.toolkit}, verbosity=${td.verbosity}`);
|
|
1344
|
+
if (tokenSection) {
|
|
1345
|
+
const tk = tokenSection;
|
|
1346
|
+
lines.push(` Response payloads: ~${tk.estimatedResponseTokens.toLocaleString()} tokens across ${tk.totalToolCalls} tool calls (${(tk.totalResponseBytes / 1024).toFixed(1)} KB)`);
|
|
1347
|
+
const totalPerTurn = td.activeTokens + Math.ceil(tk.estimatedResponseTokens / Math.max(tk.totalToolCalls, 1));
|
|
1348
|
+
lines.push(` Estimated per-turn cost: ~${totalPerTurn.toLocaleString()} tokens (definitions + avg response)`);
|
|
1349
|
+
}
|
|
1350
|
+
// Top 3 heaviest tool definitions
|
|
1351
|
+
const topDefs = td.top10.slice(0, 3);
|
|
1352
|
+
if (topDefs.length > 0) {
|
|
1353
|
+
lines.push(` Heaviest definitions: ${topDefs.map(t => `${t.tool} (~${t.tokens.toLocaleString()}t)`).join(', ')}`);
|
|
1354
|
+
}
|
|
1355
|
+
// Top 3 heaviest response consumers
|
|
1356
|
+
if (tokenSection) {
|
|
1357
|
+
const tk = tokenSection;
|
|
1358
|
+
const topResp = tk.perTool.slice(0, 3);
|
|
1359
|
+
if (topResp.length > 0) {
|
|
1360
|
+
lines.push(` Heaviest responses: ${topResp.map(t => `${t.tool} (${t.calls}x, ~${t.estimatedTokens.toLocaleString()}t)`).join(', ')}`);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (td.recommendations.length > 0) {
|
|
1364
|
+
lines.push(``);
|
|
1365
|
+
lines.push(`RECOMMENDATIONS`);
|
|
1366
|
+
for (const rec of td.recommendations) {
|
|
1367
|
+
lines.push(` - ${rec}`);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
lines.push(``);
|
|
1371
|
+
}
|
|
1372
|
+
// Brief state summary
|
|
1373
|
+
lines.push(`Server: v${serverSection.version} | toolkit=${serverSection.toolkit} | mode=${serverSection.executionMode}`);
|
|
1374
|
+
lines.push(`Browser: ${browserSection.connected ? 'connected' : 'disconnected'}`);
|
|
1375
|
+
lines.push(`Session: ${sessionSection.activeJob ? `active job (${sessionSection.currentJobId})` : 'no active job'} | ${sessionSection.completedJobs ?? 0} completed | ${sessionSection.totalItemsCreated ?? 0} items total`);
|
|
1376
|
+
if (canvasSection) {
|
|
1377
|
+
const cs = canvasSection;
|
|
1378
|
+
lines.push(`Canvas: ${cs.itemCount ?? 0} items | ${JSON.stringify(cs.canvasSize ?? {})}`);
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
// -----------------------------------------------------------------------
|
|
1385
|
+
// TOOL GUIDE (on-demand documentation)
|
|
1386
|
+
// -----------------------------------------------------------------------
|
|
1387
|
+
case 'pinepaper_tool_guide': {
|
|
1388
|
+
const input = ToolGuideInputSchema.parse(args);
|
|
1389
|
+
const { PINEPAPER_TOOLS, AI_AGENT_GUIDE } = await import('./definitions.js');
|
|
1390
|
+
const { TOOL_TAGS } = await import('./toolkits.js');
|
|
1391
|
+
// No args → full AI Agent Guide
|
|
1392
|
+
if (!input.tool && !input.category) {
|
|
1393
|
+
return {
|
|
1394
|
+
content: [{ type: 'text', text: AI_AGENT_GUIDE }],
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
// Specific tool → full verbose description
|
|
1398
|
+
if (input.tool) {
|
|
1399
|
+
const tool = PINEPAPER_TOOLS.find((t) => t.name === input.tool);
|
|
1400
|
+
if (!tool) {
|
|
1401
|
+
return {
|
|
1402
|
+
content: [{ type: 'text', text: `Unknown tool: ${input.tool}. Use pinepaper_tool_guide with no args to see the AI Agent Guide.` }],
|
|
1403
|
+
isError: true,
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
return {
|
|
1407
|
+
content: [{ type: 'text', text: `# ${tool.name}\n\n${tool.description}` }],
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
// Category → all tools in that tag with descriptions
|
|
1411
|
+
if (input.category) {
|
|
1412
|
+
const toolNames = TOOL_TAGS[input.category];
|
|
1413
|
+
if (!toolNames) {
|
|
1414
|
+
const available = Object.keys(TOOL_TAGS).join(', ');
|
|
1415
|
+
return {
|
|
1416
|
+
content: [{ type: 'text', text: `Unknown category: ${input.category}. Available: ${available}` }],
|
|
1417
|
+
isError: true,
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
const lines = [`# Category: ${input.category}\n`];
|
|
1421
|
+
for (const name of toolNames) {
|
|
1422
|
+
const tool = PINEPAPER_TOOLS.find((t) => t.name === name);
|
|
1423
|
+
if (tool) {
|
|
1424
|
+
lines.push(`## ${tool.name}\n${tool.description}\n`);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
return {
|
|
1428
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
return { content: [{ type: 'text', text: AI_AGENT_GUIDE }] };
|
|
1432
|
+
}
|
|
1433
|
+
// -----------------------------------------------------------------------
|
|
1097
1434
|
// ASSET TOOLS
|
|
1098
1435
|
// -----------------------------------------------------------------------
|
|
1099
1436
|
case 'pinepaper_search_assets': {
|
|
@@ -1306,8 +1643,13 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1306
1643
|
}
|
|
1307
1644
|
}
|
|
1308
1645
|
const code = codeGenerator.generateAgentStartJob(input);
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1646
|
+
const descriptionText = `Started agent job${input.name ? ` "${input.name}"` : ''} with ${input.screenshotPolicy || 'on_complete'} screenshot policy`;
|
|
1647
|
+
const result = await executeOrGenerate(code, descriptionText, options, 'pinepaper_agent_start_job');
|
|
1648
|
+
// Append a 1-line workflow hint
|
|
1649
|
+
if (result.content && result.content.length > 0 && result.content[0].type === 'text') {
|
|
1650
|
+
result.content[0].text += `\n\nNEXT: Call batch_execute with all operations, then end_job.`;
|
|
1651
|
+
}
|
|
1652
|
+
return result;
|
|
1311
1653
|
}
|
|
1312
1654
|
case 'pinepaper_agent_end_job': {
|
|
1313
1655
|
const input = AgentEndJobInputSchema.parse(args);
|
|
@@ -1704,6 +2046,170 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1704
2046
|
return executeOrGenerate(code, 'Imports image', options, 'pinepaper_import_image');
|
|
1705
2047
|
}
|
|
1706
2048
|
// -----------------------------------------------------------------------
|
|
2049
|
+
// ONTOLOGY TOOLS
|
|
2050
|
+
// -----------------------------------------------------------------------
|
|
2051
|
+
case 'pinepaper_analyze_design': {
|
|
2052
|
+
const input = AnalyzeDesignInputSchema.parse(args);
|
|
2053
|
+
const { DesignGraph } = await import('../ontology/index.js');
|
|
2054
|
+
const dg = new DesignGraph();
|
|
2055
|
+
const graph = dg.extractFromDefinition(input.definition);
|
|
2056
|
+
const jsonLd = dg.toJsonLd(graph);
|
|
2057
|
+
return {
|
|
2058
|
+
content: [{
|
|
2059
|
+
type: 'text',
|
|
2060
|
+
text: JSON.stringify({
|
|
2061
|
+
templateId: graph.templateId,
|
|
2062
|
+
templateName: graph.templateName,
|
|
2063
|
+
category: graph.category,
|
|
2064
|
+
dimensions: graph.dimensions,
|
|
2065
|
+
duration: graph.duration,
|
|
2066
|
+
nodeCount: graph.nodes.length,
|
|
2067
|
+
edgeCount: graph.edges.length,
|
|
2068
|
+
nodeTypes: Object.fromEntries(Object.entries(graph.fingerprint?.nodeTypeCounts || {}).sort((a, b) => b[1] - a[1])),
|
|
2069
|
+
edgeTypes: Object.fromEntries(Object.entries(graph.fingerprint?.edgeTypeCounts || {}).sort((a, b) => b[1] - a[1])),
|
|
2070
|
+
patterns: graph.patterns,
|
|
2071
|
+
mathFunctions: graph.mathFunctions,
|
|
2072
|
+
generator: graph.generator,
|
|
2073
|
+
semantics: graph.semantics,
|
|
2074
|
+
fingerprint: graph.fingerprint,
|
|
2075
|
+
jsonLd,
|
|
2076
|
+
}, null, 2),
|
|
2077
|
+
}],
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
case 'pinepaper_validate_design': {
|
|
2081
|
+
const input = ValidateDesignInputSchema.parse(args);
|
|
2082
|
+
const { DesignGraph, KnowledgeGraphValidator } = await import('../ontology/index.js');
|
|
2083
|
+
const dg = new DesignGraph();
|
|
2084
|
+
const validator = new KnowledgeGraphValidator();
|
|
2085
|
+
const definition = input.definition;
|
|
2086
|
+
const validation = validator.validateTemplate(definition);
|
|
2087
|
+
let quality = null;
|
|
2088
|
+
if (validation.valid) {
|
|
2089
|
+
const graph = dg.extractFromDefinition(definition);
|
|
2090
|
+
quality = validator.scoreQuality(definition, graph);
|
|
2091
|
+
}
|
|
2092
|
+
return {
|
|
2093
|
+
content: [{
|
|
2094
|
+
type: 'text',
|
|
2095
|
+
text: JSON.stringify({
|
|
2096
|
+
validation,
|
|
2097
|
+
quality,
|
|
2098
|
+
}, null, 2),
|
|
2099
|
+
}],
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
// -----------------------------------------------------------------------
|
|
2103
|
+
// ONTOLOGY QUERY (read-only graph navigation)
|
|
2104
|
+
// -----------------------------------------------------------------------
|
|
2105
|
+
case 'pinepaper_query_ontology': {
|
|
2106
|
+
const input = QueryOntologyInputSchema.parse(args);
|
|
2107
|
+
const { PP_VOCABULARY, ITEM_TYPE_MAP, RELATION_TYPE_MAP } = await import('../ontology/vocabulary.js');
|
|
2108
|
+
const { DesignGraph } = await import('../ontology/design-graph.js');
|
|
2109
|
+
const dg = new DesignGraph();
|
|
2110
|
+
let result;
|
|
2111
|
+
switch (input.query) {
|
|
2112
|
+
case 'list_types': {
|
|
2113
|
+
const entries = Object.entries(PP_VOCABULARY.types);
|
|
2114
|
+
const filtered = entries.filter(([key, def]) => {
|
|
2115
|
+
if (input.category && def.category !== input.category)
|
|
2116
|
+
return false;
|
|
2117
|
+
if (!input.includeAbstract && def.abstract)
|
|
2118
|
+
return false;
|
|
2119
|
+
return true;
|
|
2120
|
+
});
|
|
2121
|
+
result = Object.fromEntries(filtered);
|
|
2122
|
+
break;
|
|
2123
|
+
}
|
|
2124
|
+
case 'list_edges': {
|
|
2125
|
+
const entries = Object.entries(PP_VOCABULARY.edges);
|
|
2126
|
+
const filtered = input.category
|
|
2127
|
+
? entries.filter(([, def]) => def.category === input.category)
|
|
2128
|
+
: entries;
|
|
2129
|
+
result = Object.fromEntries(filtered);
|
|
2130
|
+
break;
|
|
2131
|
+
}
|
|
2132
|
+
case 'list_generators': {
|
|
2133
|
+
const entries = Object.entries(PP_VOCABULARY.generators);
|
|
2134
|
+
const filtered = input.category
|
|
2135
|
+
? entries.filter(([, def]) => def.category === input.category)
|
|
2136
|
+
: entries;
|
|
2137
|
+
result = Object.fromEntries(filtered);
|
|
2138
|
+
break;
|
|
2139
|
+
}
|
|
2140
|
+
case 'list_effects': {
|
|
2141
|
+
result = ['sparkle', 'blast', 'smoke', 'fire', 'rain', 'snow', 'confetti', 'ripple', 'glow', 'electric'];
|
|
2142
|
+
break;
|
|
2143
|
+
}
|
|
2144
|
+
case 'list_patterns': {
|
|
2145
|
+
result = PP_VOCABULARY.patterns;
|
|
2146
|
+
break;
|
|
2147
|
+
}
|
|
2148
|
+
case 'list_math_functions': {
|
|
2149
|
+
result = PP_VOCABULARY.mathFunctions;
|
|
2150
|
+
break;
|
|
2151
|
+
}
|
|
2152
|
+
case 'type_hierarchy': {
|
|
2153
|
+
if (!input.ppType)
|
|
2154
|
+
return errorResult('INVALID_INPUT', 'ppType is required for type_hierarchy');
|
|
2155
|
+
result = dg.getTypeHierarchy(input.ppType);
|
|
2156
|
+
break;
|
|
2157
|
+
}
|
|
2158
|
+
case 'type_children': {
|
|
2159
|
+
if (!input.ppType)
|
|
2160
|
+
return errorResult('INVALID_INPUT', 'ppType is required for type_children');
|
|
2161
|
+
result = dg.getTypeChildren(input.ppType);
|
|
2162
|
+
break;
|
|
2163
|
+
}
|
|
2164
|
+
case 'type_properties': {
|
|
2165
|
+
if (!input.ppType)
|
|
2166
|
+
return errorResult('INVALID_INPUT', 'ppType is required for type_properties');
|
|
2167
|
+
result = dg.getPropertiesFor(input.ppType);
|
|
2168
|
+
break;
|
|
2169
|
+
}
|
|
2170
|
+
case 'animatable_properties': {
|
|
2171
|
+
if (!input.ppType)
|
|
2172
|
+
return errorResult('INVALID_INPUT', 'ppType is required for animatable_properties');
|
|
2173
|
+
result = dg.getAnimatableProperties(input.ppType);
|
|
2174
|
+
break;
|
|
2175
|
+
}
|
|
2176
|
+
case 'is_subtype': {
|
|
2177
|
+
if (!input.typeA || !input.typeB)
|
|
2178
|
+
return errorResult('INVALID_INPUT', 'typeA and typeB are required for is_subtype');
|
|
2179
|
+
result = { isSubtype: dg.isSubtypeOf(input.typeA, input.typeB) };
|
|
2180
|
+
break;
|
|
2181
|
+
}
|
|
2182
|
+
case 'edge_info': {
|
|
2183
|
+
const edgeName = input.relationName || input.ppType;
|
|
2184
|
+
if (!edgeName)
|
|
2185
|
+
return errorResult('INVALID_INPUT', 'relationName or ppType is required for edge_info');
|
|
2186
|
+
const ppEdge = PP_VOCABULARY.edges[edgeName] || PP_VOCABULARY.edges[RELATION_TYPE_MAP[edgeName] || ''];
|
|
2187
|
+
result = ppEdge || { error: `Unknown edge: ${edgeName}` };
|
|
2188
|
+
break;
|
|
2189
|
+
}
|
|
2190
|
+
case 'node_type': {
|
|
2191
|
+
if (!input.itemType)
|
|
2192
|
+
return errorResult('INVALID_INPUT', 'itemType is required for node_type');
|
|
2193
|
+
result = { ppType: dg.getNodeType(input.itemType) };
|
|
2194
|
+
break;
|
|
2195
|
+
}
|
|
2196
|
+
case 'edge_type': {
|
|
2197
|
+
if (!input.relationName)
|
|
2198
|
+
return errorResult('INVALID_INPUT', 'relationName is required for edge_type');
|
|
2199
|
+
result = { ppEdge: dg.getEdgeType(input.relationName) };
|
|
2200
|
+
break;
|
|
2201
|
+
}
|
|
2202
|
+
default:
|
|
2203
|
+
return errorResult('INVALID_INPUT', `Unknown query: ${input.query}`);
|
|
2204
|
+
}
|
|
2205
|
+
return {
|
|
2206
|
+
content: [{
|
|
2207
|
+
type: 'text',
|
|
2208
|
+
text: JSON.stringify(result, null, 2),
|
|
2209
|
+
}],
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
// -----------------------------------------------------------------------
|
|
1707
2213
|
// UNKNOWN TOOL
|
|
1708
2214
|
// -----------------------------------------------------------------------
|
|
1709
2215
|
default: {
|
|
@@ -1762,6 +2268,7 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1762
2268
|
'pinepaper_browser_screenshot',
|
|
1763
2269
|
'pinepaper_browser_status',
|
|
1764
2270
|
'pinepaper_get_performance_metrics',
|
|
2271
|
+
'pinepaper_diagnostic_report',
|
|
1765
2272
|
'pinepaper_search_assets',
|
|
1766
2273
|
'pinepaper_import_asset',
|
|
1767
2274
|
'pinepaper_p5_draw',
|
|
@@ -1777,6 +2284,8 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1777
2284
|
'pinepaper_diagram_mode',
|
|
1778
2285
|
// Scene tools
|
|
1779
2286
|
'pinepaper_create_scene',
|
|
2287
|
+
'pinepaper_manage_scenes',
|
|
2288
|
+
'pinepaper_scene_playback',
|
|
1780
2289
|
// Agent flow mode tools
|
|
1781
2290
|
'pinepaper_agent_start_job',
|
|
1782
2291
|
'pinepaper_agent_end_job',
|
|
@@ -1846,6 +2355,27 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1846
2355
|
'pinepaper_import_image',
|
|
1847
2356
|
// Paper.js direct access tools
|
|
1848
2357
|
'pinepaper_register_item',
|
|
2358
|
+
// On-demand guide & runtime config
|
|
2359
|
+
'pinepaper_tool_guide',
|
|
2360
|
+
'pinepaper_set_toolkit',
|
|
2361
|
+
// Ontology tools
|
|
2362
|
+
'pinepaper_analyze_design',
|
|
2363
|
+
'pinepaper_validate_design',
|
|
2364
|
+
'pinepaper_query_ontology',
|
|
2365
|
+
// Selection, transform & history tools
|
|
2366
|
+
'pinepaper_selection',
|
|
2367
|
+
'pinepaper_transform',
|
|
2368
|
+
'pinepaper_history',
|
|
2369
|
+
// Image processing tools
|
|
2370
|
+
'pinepaper_image_filter',
|
|
2371
|
+
'pinepaper_lasso',
|
|
2372
|
+
'pinepaper_cutout_style',
|
|
2373
|
+
// Composition & view tools
|
|
2374
|
+
'pinepaper_precomp',
|
|
2375
|
+
'pinepaper_view',
|
|
2376
|
+
'pinepaper_background',
|
|
2377
|
+
// Canvas query tools
|
|
2378
|
+
'pinepaper_query',
|
|
1849
2379
|
],
|
|
1850
2380
|
});
|
|
1851
2381
|
}
|