@pinepaper.studio/mcp-server 1.6.19 → 1.6.21
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.de.md +1 -1
- package/README.es.md +1 -1
- package/README.fr.md +1 -1
- package/README.hi.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +161 -5
- package/README.pt-BR.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/build-info.json +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/ontology/upstream-vocabulary.d.ts +2 -2
- package/dist/ontology/upstream-vocabulary.js +3 -3
- package/dist/ontology/upstream-vocabulary.js.map +1 -1
- package/dist/ontology/vocabulary.d.ts.map +1 -1
- package/dist/ontology/vocabulary.js +9 -1
- package/dist/ontology/vocabulary.js.map +1 -1
- package/dist/tools/choreograph.d.ts +64 -0
- package/dist/tools/choreograph.d.ts.map +1 -0
- package/dist/tools/choreograph.js +346 -0
- package/dist/tools/choreograph.js.map +1 -0
- package/dist/tools/compact-descriptions.js +1 -1
- package/dist/tools/craft-tools.d.ts +55 -0
- package/dist/tools/craft-tools.d.ts.map +1 -0
- package/dist/tools/craft-tools.js +139 -0
- package/dist/tools/craft-tools.js.map +1 -0
- package/dist/tools/definitions.d.ts +10 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +401 -32
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/effect-check.d.ts +26 -0
- package/dist/tools/effect-check.d.ts.map +1 -0
- package/dist/tools/effect-check.js +129 -0
- package/dist/tools/effect-check.js.map +1 -0
- package/dist/tools/effect-class.d.ts +31 -0
- package/dist/tools/effect-class.d.ts.map +1 -0
- package/dist/tools/effect-class.js +176 -0
- package/dist/tools/effect-class.js.map +1 -0
- package/dist/tools/engine-lists.d.ts +6 -6
- package/dist/tools/engine-lists.d.ts.map +1 -1
- package/dist/tools/engine-lists.js +11 -9
- package/dist/tools/engine-lists.js.map +1 -1
- package/dist/tools/engine-methods.d.ts.map +1 -1
- package/dist/tools/engine-methods.js +12 -0
- package/dist/tools/engine-methods.js.map +1 -1
- package/dist/tools/engine-surface.d.ts +1 -1
- package/dist/tools/engine-surface.d.ts.map +1 -1
- package/dist/tools/engine-surface.js +221 -10
- package/dist/tools/engine-surface.js.map +1 -1
- package/dist/tools/handlers/export.d.ts.map +1 -1
- package/dist/tools/handlers/export.js +8 -1
- package/dist/tools/handlers/export.js.map +1 -1
- package/dist/tools/handlers/generate.d.ts +28 -28
- package/dist/tools/handlers.d.ts +23 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +301 -6
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts.map +1 -1
- package/dist/tools/minimal-descriptions.js +16 -3
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/shape-params.d.ts +2 -2
- package/dist/tools/shape-params.d.ts.map +1 -1
- package/dist/tools/shape-params.js +25 -2
- package/dist/tools/shape-params.js.map +1 -1
- package/dist/tools/stick-vocabulary.d.ts +1 -1
- package/dist/tools/stick-vocabulary.js +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +15 -0
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +44 -1
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +1140 -128
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +3091 -584
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +308 -6
- package/dist/types/schemas.js.map +1 -1
- package/dist/vendor/design/design-style.d.ts +2 -2
- package/dist/vendor/design/design-style.d.ts.map +1 -1
- package/dist/vendor/design/design-style.js +7 -2
- package/dist/vendor/design/design-style.js.map +1 -1
- package/dist/vendor/design/subdivision-tool-schema.d.ts +16 -0
- package/dist/vendor/design/subdivision-tool-schema.d.ts.map +1 -0
- package/dist/vendor/design/subdivision-tool-schema.js +104 -0
- package/dist/vendor/design/subdivision-tool-schema.js.map +1 -0
- package/dist/vendor/design/textile-tool-schema.d.ts +14 -0
- package/dist/vendor/design/textile-tool-schema.d.ts.map +1 -0
- package/dist/vendor/design/textile-tool-schema.js +195 -0
- package/dist/vendor/design/textile-tool-schema.js.map +1 -0
- package/dist/vendor/design/ui-kit-runtime.d.ts +49 -0
- package/dist/vendor/design/ui-kit-runtime.d.ts.map +1 -0
- package/dist/vendor/design/ui-kit-runtime.js +761 -0
- package/dist/vendor/design/ui-kit-runtime.js.map +1 -0
- package/dist/vendor/design/ui-kit-tool-schema.d.ts +280 -0
- package/dist/vendor/design/ui-kit-tool-schema.d.ts.map +1 -0
- package/dist/vendor/design/ui-kit-tool-schema.js +209 -0
- package/dist/vendor/design/ui-kit-tool-schema.js.map +1 -0
- package/manifest.json +53 -1
- package/package.json +5 -4
- package/dist/assets/repositories/openclipart.d.ts +0 -41
- package/dist/assets/repositories/openclipart.d.ts.map +0 -1
- package/dist/assets/repositories/openclipart.js +0 -139
- package/dist/assets/repositories/openclipart.js.map +0 -1
- package/dist/assets/repositories/svgrepo.d.ts +0 -61
- package/dist/assets/repositories/svgrepo.d.ts.map +0 -1
- package/dist/assets/repositories/svgrepo.js +0 -276
- package/dist/assets/repositories/svgrepo.js.map +0 -1
package/dist/tools/handlers.js
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Supports i18n for localized error and success messages.
|
|
6
6
|
* Supports browser execution via Puppeteer.
|
|
7
7
|
*/
|
|
8
|
+
import { CRAFT_FRAME_JS, generateDrawWeave, generateDrawKnots, generateDrawSashiko, generateDrawSubdivision, generateUiKit, uiKitCallProblems, } from './craft-tools.js';
|
|
8
9
|
import { codeGenerator, generateQueryMutationsCode, SHEET_RUBRIC } from '../types/code-generator.js';
|
|
9
|
-
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, 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,
|
|
10
|
+
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,
|
|
10
11
|
// Diagram schemas
|
|
11
12
|
CreateDiagramShapeInputSchema, ConnectInputSchema, ConnectPortsInputSchema, AddPortsInputSchema, AutoLayoutInputSchema, GetDiagramShapesInputSchema, UpdateConnectorInputSchema, RemoveConnectorInputSchema, DiagramModeInputSchema,
|
|
12
13
|
// Agent flow mode schemas
|
|
@@ -27,8 +28,11 @@ ImportImageInputSchema,
|
|
|
27
28
|
// Scene management schemas
|
|
28
29
|
ManageScenesInputSchema, ScenePlaybackInputSchema, SceneGraphInputSchema, SequenceInputSchema, StaggerInputSchema, FlipInputSchema,
|
|
29
30
|
// New consolidated tool schemas
|
|
30
|
-
SelectionInputSchema, TransformInputSchema, HistoryInputSchema, ImageFilterInputSchema, LassoInputSchema, CutoutStyleInputSchema, PrecompInputSchema, BackgroundInputSchema, QueryInputSchema, QueryCapabilitiesInputSchema, DeformInputSchema, SpriteSheetInputSchema, InteractionInputSchema, ExportWidgetInputSchema, ExportWidgetHtmlInputSchema, CreateChartInputSchema, MagicInputSchema, PhysicsInputSchema, MeasurementInputSchema, PlaceOnSurfaceInputSchema, StyledSceneInputSchema, ErrorCodes, } from '../types/schemas.js';
|
|
31
|
+
SelectionInputSchema, TransformInputSchema, HistoryInputSchema, ImageFilterInputSchema, LassoInputSchema, CutoutStyleInputSchema, PrecompInputSchema, BackgroundInputSchema, QueryInputSchema, QueryCapabilitiesInputSchema, DeformInputSchema, HandDrawnInputSchema, PenFigureInputSchema, CreateCharacterInputSchema, CharacterActionInputSchema, CharacterPoseInputSchema, SpriteSheetInputSchema, InteractionInputSchema, ExportWidgetInputSchema, ExportWidgetHtmlInputSchema, CreateChartInputSchema, MagicInputSchema, PhysicsInputSchema, MeasurementInputSchema, PlaceOnSurfaceInputSchema, DrawWeaveInputSchema, DrawKnotsInputSchema, DrawSashikoInputSchema, DrawSubdivisionInputSchema, UiKitInputSchema, StyledSceneInputSchema, ErrorCodes, } from '../types/schemas.js';
|
|
31
32
|
import { ZodError, z } from 'zod';
|
|
33
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
34
|
+
import { effectClass } from './effect-class.js';
|
|
35
|
+
import { EFFECT_BEFORE_JS, effectAfterJs, namedItemIds, idMayBeGone, NO_CHANGE_NOTE } from './effect-check.js';
|
|
32
36
|
import { writeFile, mkdir, appendFile, unlink, readFile, stat } from 'node:fs/promises';
|
|
33
37
|
import { tmpdir } from 'node:os';
|
|
34
38
|
import { join, extname } from 'node:path';
|
|
@@ -624,6 +628,17 @@ function dataResult(value) {
|
|
|
624
628
|
* index.ts adds toolMs, the whole call, for every tool.
|
|
625
629
|
*/
|
|
626
630
|
export const TIMING_META_KEY = 'pinepaper.studio/timing';
|
|
631
|
+
export const EFFECT_META_KEY = 'pinepaper.studio/effect';
|
|
632
|
+
/** The tool and action of the call in flight, for executeOrGenerate's effect check. */
|
|
633
|
+
const callContext = new AsyncLocalStorage();
|
|
634
|
+
let READ_ONLY_TOOLS = null;
|
|
635
|
+
async function readOnlyTools() {
|
|
636
|
+
if (!READ_ONLY_TOOLS) {
|
|
637
|
+
const { PINEPAPER_TOOLS } = await import('./definitions.js');
|
|
638
|
+
READ_ONLY_TOOLS = new Set(PINEPAPER_TOOLS.filter((t) => t.annotations?.readOnlyHint).map((t) => t.name));
|
|
639
|
+
}
|
|
640
|
+
return READ_ONLY_TOOLS;
|
|
641
|
+
}
|
|
627
642
|
export function withTiming(out, t) {
|
|
628
643
|
const meta = (out._meta ?? {});
|
|
629
644
|
const prev = (meta[TIMING_META_KEY] ?? {});
|
|
@@ -844,6 +859,80 @@ const REMOVE_ITEMS_CREATED_SINCE_MARK = `(function () {
|
|
|
844
859
|
* false when the page could not be marked (the rollback is then skipped and
|
|
845
860
|
* the failure note says nothing about the scene it cannot vouch for).
|
|
846
861
|
*/
|
|
862
|
+
/**
|
|
863
|
+
* The studio's job gate (FxTool D92). PinePaperAgent.startJob is the one place
|
|
864
|
+
* a stale studio reloads, and only when the job is about to clear the canvas:
|
|
865
|
+
* it resolves {reloading: true, fromBuild, toBuild} and the PAGE reloads, so
|
|
866
|
+
* nothing running in it survives — the wait has to happen here. Poll the new
|
|
867
|
+
* page through waitForReady, open the job again, then let start_job's own
|
|
868
|
+
* setup (clear, canvas size) run on the fresh page.
|
|
869
|
+
*/
|
|
870
|
+
export async function studioJobGate(input, options, wait = (ms) => new Promise((r) => setTimeout(r, ms))) {
|
|
871
|
+
const mode = options.executionMode ?? getExecutionMode();
|
|
872
|
+
if (mode === 'code' || !options.executeInBrowser || options.deferred)
|
|
873
|
+
return {};
|
|
874
|
+
const controller = options.browserController || getBrowserController();
|
|
875
|
+
if (!controller.connected) {
|
|
876
|
+
try {
|
|
877
|
+
await controller.connect();
|
|
878
|
+
}
|
|
879
|
+
catch {
|
|
880
|
+
return {};
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
const args = JSON.stringify({ ...(input.name ? { name: input.name } : {}), clearCanvas: input.clearCanvas !== false });
|
|
884
|
+
const START = `(async function () {
|
|
885
|
+
const A = typeof window !== 'undefined' ? window.PinePaperAgent : null;
|
|
886
|
+
if (!A || !A.startJob) return { legacy: true };
|
|
887
|
+
return await A.startJob(${args});
|
|
888
|
+
})();`;
|
|
889
|
+
const call = async (code) => {
|
|
890
|
+
try {
|
|
891
|
+
const r = await controller.executeCode(code, false, { bypassGovernor: true });
|
|
892
|
+
return r.success ? r.result : null;
|
|
893
|
+
}
|
|
894
|
+
catch {
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
const first = await call(START);
|
|
899
|
+
if (!first || first.legacy || (first.jobOpen === undefined && first.reloading === undefined))
|
|
900
|
+
return {};
|
|
901
|
+
const build = (j) => ({ buildId: j.buildId, liveBuildId: j.liveBuildId, stale: !!j.stale });
|
|
902
|
+
if (first.reloading) {
|
|
903
|
+
const READY = `(async function () {
|
|
904
|
+
const A = typeof window !== 'undefined' ? window.PinePaperAgent : null;
|
|
905
|
+
if (!A || !A.waitForReady) return { ready: false };
|
|
906
|
+
await A.waitForReady();
|
|
907
|
+
return { ready: true };
|
|
908
|
+
})();`;
|
|
909
|
+
let ready = false;
|
|
910
|
+
for (let i = 0; i < 60 && !ready; i++) { // ~30 s: a reload is ~100 ms plus a fresh boot
|
|
911
|
+
await wait(500);
|
|
912
|
+
const r = await call(READY);
|
|
913
|
+
ready = !!(r && r.ready);
|
|
914
|
+
}
|
|
915
|
+
if (!ready)
|
|
916
|
+
return { warning: `the studio was reloading from build ${first.fromBuild} to ${first.toBuild} and did not come back within 30 s; this job may run on the old page.`, build: build(first) };
|
|
917
|
+
const second = await call(START);
|
|
918
|
+
if (!second)
|
|
919
|
+
return { warning: 'the studio reloaded but did not open the job again.', build: build(first) };
|
|
920
|
+
return {
|
|
921
|
+
note: `Studio was on build ${first.fromBuild}, reloaded to ${first.toBuild} before this job.`,
|
|
922
|
+
...(second.stale ? { warning: `the studio is still on an old build after reloading (${second.buildId}; live ${second.liveBuildId})${second.reason ? ': ' + second.reason : ''}.` } : {}),
|
|
923
|
+
build: build(second),
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
if (first.stale) {
|
|
927
|
+
return {
|
|
928
|
+
warning: input.clearCanvas === false
|
|
929
|
+
? `studio is on old build ${first.buildId} (live: ${first.liveBuildId}); finish, then start a new job to update.`
|
|
930
|
+
: `studio is on old build ${first.buildId} (live: ${first.liveBuildId}) and was not reloaded${first.reason ? ': ' + first.reason : ''}.`,
|
|
931
|
+
build: build(first),
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
return { build: build(first) };
|
|
935
|
+
}
|
|
847
936
|
async function markItemsBeforeRun(controller) {
|
|
848
937
|
try {
|
|
849
938
|
const r = await controller.executeCode(MARK_ITEMS_BEFORE_RUN, false, { bypassGovernor: true });
|
|
@@ -1011,6 +1100,24 @@ async function captureSheet(input, options) {
|
|
|
1011
1100
|
}],
|
|
1012
1101
|
};
|
|
1013
1102
|
}
|
|
1103
|
+
/**
|
|
1104
|
+
* The canvas the craft tools fill when the call gives no bounds. Asked of the
|
|
1105
|
+
* studio, so a portrait or square canvas is filled rather than a 1280x720
|
|
1106
|
+
* corner of it; undefined (the generators' own default) without a studio.
|
|
1107
|
+
*/
|
|
1108
|
+
async function craftFrame(options) {
|
|
1109
|
+
const ctl = options.browserController;
|
|
1110
|
+
if (!options.executeInBrowser || !ctl || !ctl.connected)
|
|
1111
|
+
return undefined;
|
|
1112
|
+
try {
|
|
1113
|
+
const r = await ctl.executeCode(CRAFT_FRAME_JS, false);
|
|
1114
|
+
const f = r.success ? r.result : null;
|
|
1115
|
+
return f && f.width > 0 && f.height > 0 ? f : undefined;
|
|
1116
|
+
}
|
|
1117
|
+
catch {
|
|
1118
|
+
return undefined;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1014
1121
|
export async function executeOrGenerate(code, description, options, toolName,
|
|
1015
1122
|
/**
|
|
1016
1123
|
* Values too large to live inside the generated code. Written to
|
|
@@ -1156,6 +1263,23 @@ ${code}
|
|
|
1156
1263
|
const rollbackOnFailure = toolName === 'pinepaper_execute_custom_code'
|
|
1157
1264
|
&& await markItemsBeforeRun(controller);
|
|
1158
1265
|
const governorTimeoutMs = LONG_GOVERNOR_BUDGET_MS[toolName];
|
|
1266
|
+
// NO SILENT SUCCESS (effect-class.ts / effect-check.ts): fingerprint a
|
|
1267
|
+
// mutating call before and after it runs.
|
|
1268
|
+
const ctx = callContext.getStore();
|
|
1269
|
+
const action = ctx && ctx.toolName === toolName ? ctx.action : undefined;
|
|
1270
|
+
const effect = effectClass(toolName, action, (await readOnlyTools()).has(toolName));
|
|
1271
|
+
const checkEffect = effect === 'mutates' && !rollbackOnFailure;
|
|
1272
|
+
let effectCheckMs = 0;
|
|
1273
|
+
let effectArmed = false;
|
|
1274
|
+
if (checkEffect) {
|
|
1275
|
+
const t0 = Date.now();
|
|
1276
|
+
try {
|
|
1277
|
+
const r = await controller.executeCode(EFFECT_BEFORE_JS, false, { bypassGovernor: true });
|
|
1278
|
+
effectArmed = !!(r.success && r.result?.ok);
|
|
1279
|
+
}
|
|
1280
|
+
catch { /* unchecked this time */ }
|
|
1281
|
+
effectCheckMs += Date.now() - t0;
|
|
1282
|
+
}
|
|
1159
1283
|
const result = await controller.executeCode(code, shouldTakeScreenshot, {
|
|
1160
1284
|
...(stage ? { stage } : {}),
|
|
1161
1285
|
...(governorTimeoutMs ? { governorTimeoutMs } : {}),
|
|
@@ -1226,7 +1350,15 @@ ${code}
|
|
|
1226
1350
|
const inner = innerVerdict;
|
|
1227
1351
|
if (inner) {
|
|
1228
1352
|
const canvasState = await captureCanvasState(controller);
|
|
1229
|
-
|
|
1353
|
+
// THE ENGINE'S OWN REASON, when it gave one. An emitter's refusal is often
|
|
1354
|
+
// generic ("the studio refused the time_expression relation") while the
|
|
1355
|
+
// engine said exactly why in a warning the governor captured ("unknown
|
|
1356
|
+
// property 'opacty' — did you mean 'opacity'?"; D78). Production strips
|
|
1357
|
+
// console.warn, so this is the only place the model can read it.
|
|
1358
|
+
const said = (Array.isArray(result.report?.warnings) ? result.report.warnings : [])
|
|
1359
|
+
.filter((w) => typeof w === 'string' && w.length > 0 && !inner.includes(w))
|
|
1360
|
+
.slice(0, 3);
|
|
1361
|
+
return withTiming(errorResult(ErrorCodes.EXECUTION_ERROR, said.length ? `${inner} The studio said: ${said.join(' | ')}` : inner, {
|
|
1230
1362
|
code, result: result.result, governorReport: result.report,
|
|
1231
1363
|
...(rollbackOnFailure ? await customCodeRollback(controller, inner) : {}),
|
|
1232
1364
|
}, { toolName, canvasState: canvasState || undefined }), timing);
|
|
@@ -1240,7 +1372,32 @@ ${code}
|
|
|
1240
1372
|
}
|
|
1241
1373
|
catch { /* no session */ }
|
|
1242
1374
|
}
|
|
1243
|
-
|
|
1375
|
+
let noChangeNote = null;
|
|
1376
|
+
let effectMeta = null;
|
|
1377
|
+
if (checkEffect && effectArmed) {
|
|
1378
|
+
const t0 = Date.now();
|
|
1379
|
+
const ids = idMayBeGone(toolName, action) ? [] : namedItemIds(result.result);
|
|
1380
|
+
const verdict = await controller.executeCode(effectAfterJs(ids), false, { bypassGovernor: true })
|
|
1381
|
+
.then((r) => (r.success ? r.result : null))
|
|
1382
|
+
.catch(() => null);
|
|
1383
|
+
effectCheckMs += Date.now() - t0;
|
|
1384
|
+
if (verdict && verdict.ok) {
|
|
1385
|
+
// A CONTRADICTION, not idempotence: the call names an item that is not there.
|
|
1386
|
+
if (verdict.missing && verdict.missing.length) {
|
|
1387
|
+
return withTiming(errorResult(ErrorCodes.EXECUTION_ERROR, `the call reported ${verdict.missing.join(', ')}, which ${verdict.missing.length === 1 ? 'is' : 'are'} not on the canvas: it did not make what it said it made.`, { result: result.result }, { toolName }), { ...timing, effectCheckMs });
|
|
1388
|
+
}
|
|
1389
|
+
const changed = verdict.changed ?? [];
|
|
1390
|
+
effectMeta = { class: effect, changed: changed.length > 0, signals: changed, checked: verdict.checked ?? [] };
|
|
1391
|
+
if (!changed.length)
|
|
1392
|
+
noChangeNote = NO_CHANGE_NOTE(verdict.checked ?? []);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
let out = withSessionNote(executedResult(code, result.result, result.screenshot, description, result.report), result);
|
|
1396
|
+
if (noChangeNote)
|
|
1397
|
+
out = { ...out, content: [...(out.content ?? []), { type: 'text', text: noChangeNote }] };
|
|
1398
|
+
if (effectMeta)
|
|
1399
|
+
out = { ...out, _meta: { ...(out._meta ?? {}), [EFFECT_META_KEY]: effectMeta } };
|
|
1400
|
+
return withTiming(out, checkEffect ? { ...timing, effectCheckMs } : timing);
|
|
1244
1401
|
}
|
|
1245
1402
|
/**
|
|
1246
1403
|
* The emitted code's own verdict, when it reported one and it was a failure.
|
|
@@ -1429,7 +1586,7 @@ export function ignoredArgumentsNote(toolName, args) {
|
|
|
1429
1586
|
export async function handleToolCall(toolName, args, options = {}) {
|
|
1430
1587
|
const tracker = getPerformanceTracker();
|
|
1431
1588
|
const baseTimerId = `${toolName}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1432
|
-
const result = await handleToolCallInner(toolName, args, options, tracker, baseTimerId);
|
|
1589
|
+
const result = await callContext.run({ toolName, action: typeof args.action === 'string' ? args.action : undefined }, () => handleToolCallInner(toolName, args, options, tracker, baseTimerId));
|
|
1433
1590
|
const ignoredNote = ignoredArgumentsNote(toolName, args);
|
|
1434
1591
|
if (ignoredNote)
|
|
1435
1592
|
result.content = [...(result.content ?? []), { type: 'text', text: ignoredNote }];
|
|
@@ -1650,6 +1807,17 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
1650
1807
|
const code = codeGenerator.generateDeform(input);
|
|
1651
1808
|
return executeOrGenerate(code, `Deform: ${input.action}`, options, 'pinepaper_deform');
|
|
1652
1809
|
}
|
|
1810
|
+
case 'pinepaper_hand_drawn': {
|
|
1811
|
+
const input = HandDrawnInputSchema.parse(args);
|
|
1812
|
+
const code = codeGenerator.generateHandDrawn(input);
|
|
1813
|
+
return executeOrGenerate(code, `Hand-drawn style: ${input.style ?? input.mode ?? 'sketchy'}`, options, 'pinepaper_hand_drawn');
|
|
1814
|
+
}
|
|
1815
|
+
case 'pinepaper_pen_figure': {
|
|
1816
|
+
const input = PenFigureInputSchema.parse(args);
|
|
1817
|
+
const code = codeGenerator.generatePenFigure(input);
|
|
1818
|
+
const what = input.action === 'list' ? 'List the pen figures' : `Draw ${typeof input.figure === 'string' ? input.figure : 'a figure'} with a pen`;
|
|
1819
|
+
return executeOrGenerate(code, what, options, 'pinepaper_pen_figure');
|
|
1820
|
+
}
|
|
1653
1821
|
case 'pinepaper_sprite_sheet': {
|
|
1654
1822
|
const input = SpriteSheetInputSchema.parse(args);
|
|
1655
1823
|
const code = codeGenerator.generateSpriteSheet(input);
|
|
@@ -1718,6 +1886,60 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
1718
1886
|
const description = `Places ${input.concept} as ${plan.parts.length} parts with ${plan.tracks} keyframe tracks`;
|
|
1719
1887
|
return executeOrGenerate(code, description, options, 'pinepaper_character');
|
|
1720
1888
|
}
|
|
1889
|
+
case 'pinepaper_create_character': {
|
|
1890
|
+
const input = CreateCharacterInputSchema.parse(args);
|
|
1891
|
+
// If layered bundle provided, use importLayeredCharacter
|
|
1892
|
+
if (input.bundle) {
|
|
1893
|
+
const bundleInfo = input.bundle.info || input.bundle;
|
|
1894
|
+
const bundleImages = input.bundle.images || {};
|
|
1895
|
+
const code = codeGenerator.generateImportLayeredCharacter({ info: bundleInfo, images: bundleImages });
|
|
1896
|
+
return executeOrGenerate(code, `Import layered character: ${input.name || input.characterId || 'bundle'}`, options, 'pinepaper_create_character');
|
|
1897
|
+
}
|
|
1898
|
+
// If concept/preset matches a planCharacter concept, plan it
|
|
1899
|
+
const conceptName = input.concept || input.preset;
|
|
1900
|
+
if (conceptName) {
|
|
1901
|
+
const formattedConcept = conceptName.startsWith('pp:') ? conceptName : `pp:${conceptName.charAt(0).toUpperCase() + conceptName.slice(1)}`;
|
|
1902
|
+
const plan = planCharacter({
|
|
1903
|
+
concept: formattedConcept,
|
|
1904
|
+
at: { x: input.x, y: input.y },
|
|
1905
|
+
scale: input.scale,
|
|
1906
|
+
});
|
|
1907
|
+
if (plan.ok) {
|
|
1908
|
+
const code = generateCharacterCode(plan, {
|
|
1909
|
+
generateCreateItem: (i) => codeGenerator.generateCreateItem(i),
|
|
1910
|
+
generateKeyframeAnimate: (i) => codeGenerator.generateKeyframeAnimate(i),
|
|
1911
|
+
}, {
|
|
1912
|
+
createItem: (a) => CreateItemInputSchema.parse(a),
|
|
1913
|
+
keyframeAnimate: (a) => KeyframeAnimateInputSchema.parse(a),
|
|
1914
|
+
});
|
|
1915
|
+
return executeOrGenerate(code, `Places character ${formattedConcept}`, options, 'pinepaper_create_character');
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
const code = codeGenerator.generateCreateCharacter(input);
|
|
1919
|
+
return executeOrGenerate(code, `Create character: ${input.name || input.characterId || 'Character'}`, options, 'pinepaper_create_character');
|
|
1920
|
+
}
|
|
1921
|
+
case 'pinepaper_character_action': {
|
|
1922
|
+
const input = CharacterActionInputSchema.parse(args);
|
|
1923
|
+
const act = String(input.action || 'walk').toLowerCase();
|
|
1924
|
+
const FACES = ['blink', 'frown', 'smile', 'surprise', 'talk', 'wink'];
|
|
1925
|
+
if (FACES.includes(act)) {
|
|
1926
|
+
return {
|
|
1927
|
+
content: [{
|
|
1928
|
+
type: 'text',
|
|
1929
|
+
text: `"${act}" is a facial expression — use pinepaper_character_pose with { expression: "${act}" }. Actions: walk, run, locomotion, wave, dance, jump, idle`,
|
|
1930
|
+
}],
|
|
1931
|
+
isError: true,
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
const code = codeGenerator.generateCharacterAction(input);
|
|
1935
|
+
return executeOrGenerate(code, `Character action: ${act}`, options, 'pinepaper_character_action');
|
|
1936
|
+
}
|
|
1937
|
+
case 'pinepaper_character_pose': {
|
|
1938
|
+
const input = CharacterPoseInputSchema.parse(args);
|
|
1939
|
+
const expr = String(input.expression || 'smile').toLowerCase();
|
|
1940
|
+
const code = codeGenerator.generateCharacterPose(input);
|
|
1941
|
+
return executeOrGenerate(code, `Character pose/expression: ${expr}`, options, 'pinepaper_character_pose');
|
|
1942
|
+
}
|
|
1721
1943
|
case 'pinepaper_batch_create': {
|
|
1722
1944
|
const input = BatchCreateInputSchema.parse(args);
|
|
1723
1945
|
const code = codeGenerator.generateBatchCreate(input);
|
|
@@ -1887,6 +2109,30 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
1887
2109
|
return executeOrGenerate(code, `Text effect: ${input.action}`, options, 'pinepaper_text_effect');
|
|
1888
2110
|
}
|
|
1889
2111
|
// ─── 1.6.4: agent surface for the Tier-2 engine features ───
|
|
2112
|
+
case 'pinepaper_look': {
|
|
2113
|
+
const input = LookInputSchema.parse(args);
|
|
2114
|
+
return executeOrGenerate(codeGenerator.generateLook(input), `Look: ${input.action}`, options, 'pinepaper_look');
|
|
2115
|
+
}
|
|
2116
|
+
case 'pinepaper_original_character': {
|
|
2117
|
+
const input = OriginalCharacterInputSchema.parse(args);
|
|
2118
|
+
return executeOrGenerate(codeGenerator.generateOriginalCharacter(input), `Original character: ${input.action}`, options, 'pinepaper_original_character');
|
|
2119
|
+
}
|
|
2120
|
+
case 'pinepaper_morph_sequence': {
|
|
2121
|
+
const input = MorphSequenceInputSchema.parse(args);
|
|
2122
|
+
return executeOrGenerate(codeGenerator.generateMorphSequence(input), `Morph sequence: ${input.states.length} states`, options, 'pinepaper_morph_sequence');
|
|
2123
|
+
}
|
|
2124
|
+
case 'pinepaper_emitter': {
|
|
2125
|
+
const input = EmitterInputSchema.parse(args);
|
|
2126
|
+
return executeOrGenerate(codeGenerator.generateEmitter(input), `Emitter: ${input.action}`, options, 'pinepaper_emitter');
|
|
2127
|
+
}
|
|
2128
|
+
case 'pinepaper_render_hook': {
|
|
2129
|
+
const input = RenderHookInputSchema.parse(args);
|
|
2130
|
+
return executeOrGenerate(codeGenerator.generateRenderHook(input), `Render hook: ${input.action}`, options, 'pinepaper_render_hook');
|
|
2131
|
+
}
|
|
2132
|
+
case 'pinepaper_choreograph': {
|
|
2133
|
+
const input = ChoreographInputSchema.parse(args);
|
|
2134
|
+
return executeOrGenerate(codeGenerator.generateChoreograph(input), `Choreograph: ${input.beats.map((b) => b.verb).join(' → ')}`, options, 'pinepaper_choreograph');
|
|
2135
|
+
}
|
|
1890
2136
|
case 'pinepaper_relight': {
|
|
1891
2137
|
const input = RelightInputSchema.parse(args);
|
|
1892
2138
|
return executeOrGenerate(codeGenerator.generateRelight(input), `Relight: ${input.action}`, options, 'pinepaper_relight');
|
|
@@ -1899,6 +2145,44 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
1899
2145
|
const input = StyledSceneInputSchema.parse(args);
|
|
1900
2146
|
return executeOrGenerate(codeGenerator.generateStyledScene(input), `Styled scene${input.style ? `: ${input.style}` : ''}`, options, 'pinepaper_styled_scene');
|
|
1901
2147
|
}
|
|
2148
|
+
// The cloud's craft tools, shared (craft-tools.ts says what differs locally).
|
|
2149
|
+
case 'pinepaper_draw_weave': {
|
|
2150
|
+
const input = DrawWeaveInputSchema.parse(args);
|
|
2151
|
+
const code = generateDrawWeave(input, await craftFrame(options));
|
|
2152
|
+
return executeOrGenerate(code, `Draw a ${input.weave ?? 'plain'} weave`, options, 'pinepaper_draw_weave');
|
|
2153
|
+
}
|
|
2154
|
+
case 'pinepaper_draw_knots': {
|
|
2155
|
+
const input = DrawKnotsInputSchema.parse(args);
|
|
2156
|
+
if (!input.points?.length) {
|
|
2157
|
+
return {
|
|
2158
|
+
content: [{ type: 'text', text: 'pinepaper_draw_knots: give points — [{x, y}, …], one per knot. The tool places knots where you say and draws nothing without them.' }],
|
|
2159
|
+
isError: true,
|
|
2160
|
+
};
|
|
2161
|
+
}
|
|
2162
|
+
return executeOrGenerate(generateDrawKnots(input), `Draw ${input.points.length} embroidered knot(s)`, options, 'pinepaper_draw_knots');
|
|
2163
|
+
}
|
|
2164
|
+
case 'pinepaper_draw_sashiko': {
|
|
2165
|
+
const input = DrawSashikoInputSchema.parse(args);
|
|
2166
|
+
const code = generateDrawSashiko(input, await craftFrame(options));
|
|
2167
|
+
return executeOrGenerate(code, `Draw ${input.pattern ?? 'seigaiha'} sashiko`, options, 'pinepaper_draw_sashiko');
|
|
2168
|
+
}
|
|
2169
|
+
case 'pinepaper_draw_subdivision': {
|
|
2170
|
+
const input = DrawSubdivisionInputSchema.parse(args);
|
|
2171
|
+
const code = generateDrawSubdivision(input, await craftFrame(options));
|
|
2172
|
+
return executeOrGenerate(code, `Draw a ${input.preset ?? 'mondrian'} subdivision`, options, 'pinepaper_draw_subdivision');
|
|
2173
|
+
}
|
|
2174
|
+
case 'pinepaper_ui_kit': {
|
|
2175
|
+
const input = UiKitInputSchema.parse(args);
|
|
2176
|
+
const problems = uiKitCallProblems(input);
|
|
2177
|
+
const code = problems.length ? null : generateUiKit(input);
|
|
2178
|
+
if (!code) {
|
|
2179
|
+
return {
|
|
2180
|
+
content: [{ type: 'text', text: `pinepaper_ui_kit: ${problems.join(' ') || 'nothing to draw'}` }],
|
|
2181
|
+
isError: true,
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
return executeOrGenerate(code, `UI kit: ${input.action}`, options, 'pinepaper_ui_kit');
|
|
2185
|
+
}
|
|
1902
2186
|
case 'pinepaper_place_on_surface': {
|
|
1903
2187
|
const input = PlaceOnSurfaceInputSchema.parse(args);
|
|
1904
2188
|
const code = codeGenerator.generatePlaceOnSurface(input);
|
|
@@ -2179,7 +2463,7 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
2179
2463
|
const description = getLocalizedSuccessMessage(i18n, 'relationAdded', {
|
|
2180
2464
|
relationType: input.relationType ?? `preset:${input.presetId}`,
|
|
2181
2465
|
sourceId: input.sourceId,
|
|
2182
|
-
targetId: input.targetId || '
|
|
2466
|
+
targetId: input.targetId || '(no target)',
|
|
2183
2467
|
});
|
|
2184
2468
|
return executeOrGenerate(code, description, options, 'pinepaper_add_relation');
|
|
2185
2469
|
}
|
|
@@ -3130,11 +3414,20 @@ You can now start creating new items on a clean canvas.${sizeNote}`,
|
|
|
3130
3414
|
}
|
|
3131
3415
|
const code = codeGenerator.generateAgentStartJob(input);
|
|
3132
3416
|
const descriptionText = `Started agent job${input.name ? ` "${input.name}"` : ''} with ${input.screenshotPolicy || 'on_complete'} screenshot policy`;
|
|
3417
|
+
// A STALE STUDIO IS RELOADED HERE, AND ONLY HERE (D92): the canvas is
|
|
3418
|
+
// about to be cleared anyway. The setup below runs on the new page.
|
|
3419
|
+
const gate = await studioJobGate(input, options);
|
|
3133
3420
|
const result = await executeOrGenerate(code, descriptionText, options, 'pinepaper_agent_start_job');
|
|
3134
3421
|
// Append a 1-line workflow hint
|
|
3135
3422
|
if (result.content && result.content.length > 0 && result.content[0].type === 'text') {
|
|
3136
3423
|
result.content[0].text += `\n\nNEXT: Call batch_execute with all operations, then end_job.`;
|
|
3424
|
+
if (gate.note)
|
|
3425
|
+
result.content[0].text = `${gate.note}\n\n${result.content[0].text}`;
|
|
3426
|
+
if (gate.warning)
|
|
3427
|
+
result.content[0].text = `⚠️ ${gate.warning}\n\n${result.content[0].text}`;
|
|
3137
3428
|
}
|
|
3429
|
+
if (gate.build)
|
|
3430
|
+
result._meta = { ...(result._meta ?? {}), 'pinepaper.studio/build': gate.build };
|
|
3138
3431
|
return result;
|
|
3139
3432
|
}
|
|
3140
3433
|
case 'pinepaper_agent_end_job': {
|
|
@@ -3883,6 +4176,8 @@ You can now start creating new items on a clean canvas.${sizeNote}`,
|
|
|
3883
4176
|
'pinepaper_query_capabilities',
|
|
3884
4177
|
// Deformation, sprite sheets, interaction
|
|
3885
4178
|
'pinepaper_deform',
|
|
4179
|
+
'pinepaper_hand_drawn',
|
|
4180
|
+
'pinepaper_pen_figure',
|
|
3886
4181
|
'pinepaper_sprite_sheet',
|
|
3887
4182
|
'pinepaper_interaction',
|
|
3888
4183
|
// Widget export
|