@pinepaper.studio/mcp-server 1.6.17 → 1.6.18
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 +92 -0
- package/dist/browser/puppeteer-controller.d.ts +61 -1
- package/dist/browser/puppeteer-controller.d.ts.map +1 -1
- package/dist/browser/puppeteer-controller.js +168 -6
- package/dist/browser/puppeteer-controller.js.map +1 -1
- package/dist/build-info.json +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +75 -10
- package/dist/index.js.map +1 -1
- package/dist/ontology/ontology-validator.d.ts.map +1 -1
- package/dist/ontology/ontology-validator.js +9 -5
- package/dist/ontology/ontology-validator.js.map +1 -1
- package/dist/ontology/upstream-vocabulary.d.ts +2 -2
- package/dist/ontology/upstream-vocabulary.js +2 -2
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +138 -35
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/engine-lists.d.ts +20 -0
- package/dist/tools/engine-lists.d.ts.map +1 -0
- package/dist/tools/engine-lists.js +86 -0
- package/dist/tools/engine-lists.js.map +1 -0
- package/dist/tools/engine-methods.d.ts.map +1 -1
- package/dist/tools/engine-methods.js +4 -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 +20 -4
- package/dist/tools/engine-surface.js.map +1 -1
- package/dist/tools/handlers/brand-intake.d.ts +95 -0
- package/dist/tools/handlers/brand-intake.d.ts.map +1 -0
- package/dist/tools/handlers/brand-intake.js +242 -0
- package/dist/tools/handlers/brand-intake.js.map +1 -0
- package/dist/tools/handlers.d.ts +23 -11
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +226 -10
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/minimal-descriptions.js +4 -4
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/shape-params.d.ts +2 -2
- package/dist/tools/shape-params.js +2 -2
- package/dist/tools/stick-vocabulary.d.ts +1 -1
- package/dist/tools/stick-vocabulary.js +1 -1
- package/dist/types/code-generator.d.ts +40 -2
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +260 -26
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +2170 -698
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +152 -36
- package/dist/types/schemas.js.map +1 -1
- package/dist/utils/mcp-log.d.ts +16 -0
- package/dist/utils/mcp-log.d.ts.map +1 -0
- package/dist/utils/mcp-log.js +10 -0
- package/dist/utils/mcp-log.js.map +1 -0
- package/dist/utils/progress.d.ts +28 -0
- package/dist/utils/progress.d.ts.map +1 -0
- package/dist/utils/progress.js +24 -0
- package/dist/utils/progress.js.map +1 -0
- package/dist/vendor/design/design-style.js +1 -1
- package/manifest.json +1 -1
- package/package.json +7 -4
package/dist/tools/handlers.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Supports i18n for localized error and success messages.
|
|
6
6
|
* Supports browser execution via Puppeteer.
|
|
7
7
|
*/
|
|
8
|
-
import { codeGenerator, generateQueryMutationsCode } from '../types/code-generator.js';
|
|
8
|
+
import { codeGenerator, generateQueryMutationsCode, SHEET_RUBRIC } from '../types/code-generator.js';
|
|
9
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, 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
10
|
// Diagram schemas
|
|
11
11
|
CreateDiagramShapeInputSchema, ConnectInputSchema, ConnectPortsInputSchema, AddPortsInputSchema, AutoLayoutInputSchema, GetDiagramShapesInputSchema, UpdateConnectorInputSchema, RemoveConnectorInputSchema, DiagramModeInputSchema,
|
|
@@ -85,13 +85,19 @@ export function getScreenshotMode() {
|
|
|
85
85
|
// =============================================================================
|
|
86
86
|
// EXPORT FILE SAVE HELPERS
|
|
87
87
|
// =============================================================================
|
|
88
|
-
function getExportDir() {
|
|
88
|
+
export function getExportDir() {
|
|
89
89
|
return process.env.PINEPAPER_EXPORT_DIR || join(tmpdir(), 'pinepaper-exports');
|
|
90
90
|
}
|
|
91
91
|
// wav joins them: a minute of 48kHz 16-bit is ~5.8 MB of base64 and the ten
|
|
92
92
|
// minutes the schema allows is ~77 MB. Deliverable across the bridge, useless
|
|
93
93
|
// pasted into a response.
|
|
94
|
-
|
|
94
|
+
// Stills join them too. The threshold below is in CHARACTERS of base64, and a
|
|
95
|
+
// 59 KB 1920x1080 PNG is ~79k of them: under the threshold, so it came back
|
|
96
|
+
// inline, and over the token limit of the client reading it (a Halloween
|
|
97
|
+
// look-frame session, 2026-09-29), while bigger PNGs saved to a file. Same
|
|
98
|
+
// format, two shapes, decided by file size. An image is never useful as
|
|
99
|
+
// base64 in a text result, so png/jpg/webp always save and name the path.
|
|
100
|
+
export const ALWAYS_SAVE_FORMATS = new Set(['mp4', 'webm', 'gif', 'apng', 'pdf', 'wav', 'srt', 'vtt', 'scc', 'html5-ad', 'playable', 'png', 'jpg', 'webp']);
|
|
95
101
|
// 500_000 was chosen against the bridge's limits, not the CALLER's. A pilot
|
|
96
102
|
// session hit a 263K-character end_job result — comfortably under this, so it
|
|
97
103
|
// was returned inline, and over the tool-result limit of the client reading
|
|
@@ -583,6 +589,33 @@ function successResult(code, description) {
|
|
|
583
589
|
function dataResult(value) {
|
|
584
590
|
return { content: [{ type: 'text', text: JSON.stringify(value, null, 2) }] };
|
|
585
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* A call that only succeeded after the browser session was recovered says so,
|
|
594
|
+
* in the result: the controller has reported `recovered` / `canvasReset` for
|
|
595
|
+
* a while, but nothing read them, so an agent kept using item ids from a
|
|
596
|
+
* scene that was gone (M6).
|
|
597
|
+
*/
|
|
598
|
+
function withSessionNote(out, r) {
|
|
599
|
+
if (!r.recovered)
|
|
600
|
+
return out;
|
|
601
|
+
const note = r.relaunched
|
|
602
|
+
? `The browser session had been lost; the browser was relaunched and this call ran on an EMPTY canvas${r.canvasSizeRestored ? ` (size restored to ${r.canvasSizeRestored.width}x${r.canvasSizeRestored.height})` : ' at the default size'}. Item ids from before are gone: rebuild the scene.`
|
|
603
|
+
: r.canvasReset
|
|
604
|
+
? 'The page had reloaded and the canvas came back EMPTY. Item ids from before are gone: rebuild the scene.'
|
|
605
|
+
: 'The page connection was re-established; the scene survived.';
|
|
606
|
+
const first = out.content[0];
|
|
607
|
+
if (first && first.type === 'text') {
|
|
608
|
+
try {
|
|
609
|
+
const obj = JSON.parse(first.text);
|
|
610
|
+
obj.session = { recovered: true, ...(r.relaunched ? { relaunched: true } : {}), ...(r.canvasReset !== undefined ? { canvasReset: r.canvasReset } : {}), note };
|
|
611
|
+
return { ...out, content: [{ type: 'text', text: JSON.stringify(obj, null, 2) }, ...out.content.slice(1)] };
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
return { ...out, content: [{ type: 'text', text: `${note}\n\n${first.text}` }, ...out.content.slice(1)] };
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return out;
|
|
618
|
+
}
|
|
586
619
|
function executedResult(code, result, screenshot, description, report) {
|
|
587
620
|
const resultObj = { success: true, result };
|
|
588
621
|
if (description)
|
|
@@ -740,6 +773,170 @@ function handleValidationError(error, i18n) {
|
|
|
740
773
|
*
|
|
741
774
|
* Default is 'on_request' for optimal performance per best practices.
|
|
742
775
|
*/
|
|
776
|
+
/** Page code: remember the id of every item on the canvas. */
|
|
777
|
+
const MARK_ITEMS_BEFORE_RUN = `(function () {
|
|
778
|
+
if (typeof paper === 'undefined' || !paper.project) return { ok: false };
|
|
779
|
+
const ids = new Set();
|
|
780
|
+
paper.project.getItems({}).forEach(function (i) { ids.add(i.id); });
|
|
781
|
+
window.__ppPreRunIds = ids;
|
|
782
|
+
return { ok: true };
|
|
783
|
+
})();`;
|
|
784
|
+
/** Page code: remove every item that was not there at the mark, outermost first. */
|
|
785
|
+
const REMOVE_ITEMS_CREATED_SINCE_MARK = `(function () {
|
|
786
|
+
const pre = window.__ppPreRunIds;
|
|
787
|
+
window.__ppPreRunIds = null;
|
|
788
|
+
if (!pre || typeof paper === 'undefined' || !paper.project) return { ok: false };
|
|
789
|
+
const fresh = paper.project.getItems({}).filter(function (i) { return !pre.has(i.id); });
|
|
790
|
+
const freshIds = new Set(fresh.map(function (i) { return i.id; }));
|
|
791
|
+
const tops = fresh.filter(function (i) { return !(i.parent && freshIds.has(i.parent.id)); });
|
|
792
|
+
let removed = 0;
|
|
793
|
+
tops.forEach(function (it) {
|
|
794
|
+
try { if (typeof app !== 'undefined' && typeof app.deleteItem === 'function' && app.deleteItem(it)) { removed++; return; } } catch (e) {}
|
|
795
|
+
try { if (it.parent) { it.remove(); removed++; } } catch (e) {}
|
|
796
|
+
});
|
|
797
|
+
const left = paper.project.getItems({}).filter(function (i) { return !pre.has(i.id); }).length;
|
|
798
|
+
try { if (paper.view) paper.view.update(); } catch (e) {}
|
|
799
|
+
return { ok: left === 0, removed: removed, left: left };
|
|
800
|
+
})();`;
|
|
801
|
+
/**
|
|
802
|
+
* Record which canvas items exist before agent-written code runs, so a failed
|
|
803
|
+
* run's leftovers can be told apart from the scene it started with. Returns
|
|
804
|
+
* false when the page could not be marked (the rollback is then skipped and
|
|
805
|
+
* the failure note says nothing about the scene it cannot vouch for).
|
|
806
|
+
*/
|
|
807
|
+
async function markItemsBeforeRun(controller) {
|
|
808
|
+
try {
|
|
809
|
+
const r = await controller.executeCode(MARK_ITEMS_BEFORE_RUN, false, { bypassGovernor: true });
|
|
810
|
+
return !!(r.success && r.result?.ok);
|
|
811
|
+
}
|
|
812
|
+
catch {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Remove what agent-written code created before it failed, and say so.
|
|
818
|
+
*
|
|
819
|
+
* A run stopped by the loop deadline after drawing 42 strokes left them on
|
|
820
|
+
* the canvas: a half-painted scene. Undo cannot fix that. The governor's one
|
|
821
|
+
* history entry only covers mutations that announce themselves, and raw
|
|
822
|
+
* `new paper.Path(...)` does not (measured: report.mutated false, 30 paths
|
|
823
|
+
* left behind). So the rollback is by identity instead: every item that was
|
|
824
|
+
* not on the canvas before the call goes, through app.deleteItem where the
|
|
825
|
+
* engine knows the item (registry, trackers) and item.remove() otherwise.
|
|
826
|
+
* Edits to items that already existed are NOT reverted; the note says so.
|
|
827
|
+
*/
|
|
828
|
+
export async function customCodeRollback(controller, error) {
|
|
829
|
+
let rolledBack = false;
|
|
830
|
+
let removed = 0;
|
|
831
|
+
let left = -1;
|
|
832
|
+
try {
|
|
833
|
+
const r = await controller.executeCode(REMOVE_ITEMS_CREATED_SINCE_MARK, false, { bypassGovernor: true });
|
|
834
|
+
const v = (r.result || {});
|
|
835
|
+
rolledBack = !!(r.success && v.ok);
|
|
836
|
+
removed = v.removed ?? 0;
|
|
837
|
+
left = typeof v.left === 'number' ? v.left : -1;
|
|
838
|
+
}
|
|
839
|
+
catch {
|
|
840
|
+
rolledBack = false;
|
|
841
|
+
}
|
|
842
|
+
const scene = rolledBack
|
|
843
|
+
? (removed > 0
|
|
844
|
+
? `The ${removed} item(s) this call created before it stopped were removed. Changes it made to items that already existed are not reverted.`
|
|
845
|
+
: 'This call created no items before it stopped. Changes it made to items that already existed are not reverted.')
|
|
846
|
+
: `Items this call created could not all be removed${left > 0 ? ` (${left} remain)` : ''}: check the canvas before continuing.`;
|
|
847
|
+
const how = /Synchronous execution exceeded|Loop budget exceeded/i.test(error || '')
|
|
848
|
+
? ' This is execute_custom_code\'s per-call guard, not a limit on the scene: its loops may run about 4 s (and 2,000,000 iterations per loop) so the studio tab stays responsive. Split the work across several calls, each of which gets a fresh budget. For example, paint 10 strokes per call instead of 40 in one.'
|
|
849
|
+
: '';
|
|
850
|
+
return { rolledBack, note: scene + how };
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* pinepaper_brand_kit from_url: the product's real brand, read from its own
|
|
854
|
+
* site (see handlers/brand-intake.ts). Runs in the MCP server's browser, in an
|
|
855
|
+
* isolated context, so code mode cannot do it and says so.
|
|
856
|
+
*/
|
|
857
|
+
async function brandFromUrl(url, shots, options) {
|
|
858
|
+
const effectiveExecMode = options.executionMode ?? getExecutionMode();
|
|
859
|
+
if (effectiveExecMode === 'code' || !options.executeInBrowser) {
|
|
860
|
+
return errorResult(ErrorCodes.EXECUTION_ERROR, 'brand_kit from_url reads the site in the MCP server\'s browser, so it needs browser mode; it cannot run as generated code.', {}, { toolName: 'pinepaper_brand_kit' });
|
|
861
|
+
}
|
|
862
|
+
const controller = options.browserController || getBrowserController();
|
|
863
|
+
if (!controller.connected) {
|
|
864
|
+
try {
|
|
865
|
+
await controller.connect();
|
|
866
|
+
}
|
|
867
|
+
catch (e) {
|
|
868
|
+
return errorResult(ErrorCodes.BROWSER_NOT_CONNECTED, `could not start the browser: ${e instanceof Error ? e.message : String(e)}`, {}, { toolName: 'pinepaper_brand_kit' });
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
const host = (() => { try {
|
|
872
|
+
return new URL(url).hostname.replace(/[^a-z0-9.-]/gi, '_');
|
|
873
|
+
}
|
|
874
|
+
catch {
|
|
875
|
+
return 'site';
|
|
876
|
+
} })();
|
|
877
|
+
const outDir = join(getExportDir(), `brand-${host}-${Date.now()}`);
|
|
878
|
+
try {
|
|
879
|
+
const { intakeBrand } = await import('./handlers/brand-intake.js');
|
|
880
|
+
const r = await controller
|
|
881
|
+
.withIsolatedPage((page) => intakeBrand(page, url, outDir, shots));
|
|
882
|
+
const text = [
|
|
883
|
+
`Brand read from ${url}. Files in ${outDir}:`,
|
|
884
|
+
...r.screenshots.map((f) => `- screenshot: ${f}`),
|
|
885
|
+
r.logo ? `- logo: ${r.logo.file} (from ${r.logo.source})` : '- logo: none found in the header, icons or og:image',
|
|
886
|
+
'',
|
|
887
|
+
'Review the proposed kit against the screenshots, then apply it with pinepaper_brand_kit { action: "apply", kit } (or "plan" first). Import the logo or a screenshot with pinepaper_import_image { url: <file> }.',
|
|
888
|
+
'',
|
|
889
|
+
JSON.stringify({ success: true, ...r }, null, 2),
|
|
890
|
+
].join('\n');
|
|
891
|
+
return { content: [{ type: 'text', text }] };
|
|
892
|
+
}
|
|
893
|
+
catch (e) {
|
|
894
|
+
return errorResult(ErrorCodes.EXECUTION_ERROR, `could not read ${url}: ${e instanceof Error ? e.message : String(e)}`, {}, { toolName: 'pinepaper_brand_kit' });
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* pinepaper_capture_frames with `sheet`: one tiled image of the film, saved
|
|
899
|
+
* to a file (an image is never useful inline, see ALWAYS_SAVE_FORMATS), with
|
|
900
|
+
* the rubric the agent scores it against. Code mode returns the code as usual.
|
|
901
|
+
*/
|
|
902
|
+
async function captureSheet(input, options) {
|
|
903
|
+
const code = codeGenerator.generateCaptureSheet(input);
|
|
904
|
+
const description = 'Contact sheet';
|
|
905
|
+
const effectiveExecMode = options.executionMode ?? getExecutionMode();
|
|
906
|
+
if (effectiveExecMode === 'code' || !options.executeInBrowser) {
|
|
907
|
+
return executeOrGenerate(code, description, options, 'pinepaper_capture_frames');
|
|
908
|
+
}
|
|
909
|
+
const controller = options.browserController || getBrowserController();
|
|
910
|
+
if (!controller.connected) {
|
|
911
|
+
try {
|
|
912
|
+
await controller.connect();
|
|
913
|
+
}
|
|
914
|
+
catch {
|
|
915
|
+
return executeOrGenerate(code, description, options, 'pinepaper_capture_frames');
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
// Frames wait for video seeks one at a time, so 48 of them can outlast the
|
|
919
|
+
// governor's 10 s default.
|
|
920
|
+
const run = await controller.executeCode(code, false, { governorTimeoutMs: 120_000 });
|
|
921
|
+
const value = (run.result ?? {});
|
|
922
|
+
if (!run.success || value.success === false || typeof value.dataUrl !== 'string') {
|
|
923
|
+
return errorResult(ErrorCodes.EXECUTION_ERROR, run.error || value.error || 'the contact sheet could not be made', { code, governorReport: run.report }, { toolName: 'pinepaper_capture_frames' });
|
|
924
|
+
}
|
|
925
|
+
const format = input.sheet?.format === 'png' ? 'png' : 'jpg';
|
|
926
|
+
const { filePath, fileSize } = await saveExportToFile(value.dataUrl, format, 'sheet');
|
|
927
|
+
const { dataUrl: _d, success: _s, ...facts } = value;
|
|
928
|
+
void _d;
|
|
929
|
+
void _s;
|
|
930
|
+
const result = { success: true, filePath, fileSize, format, ...facts, review: SHEET_RUBRIC };
|
|
931
|
+
return {
|
|
932
|
+
content: [{
|
|
933
|
+
type: 'text',
|
|
934
|
+
text: `Contact sheet saved: ${filePath} (${(fileSize / 1024).toFixed(1)} KB, ${String(facts.tiles)} tiles).\n`
|
|
935
|
+
+ 'Open the image and score it against review.criteria (1-10 each), then check review.checklist.\n\n'
|
|
936
|
+
+ JSON.stringify(result, null, 2),
|
|
937
|
+
}],
|
|
938
|
+
};
|
|
939
|
+
}
|
|
743
940
|
export async function executeOrGenerate(code, description, options, toolName,
|
|
744
941
|
/**
|
|
745
942
|
* Values too large to live inside the generated code. Written to
|
|
@@ -880,6 +1077,10 @@ ${code}
|
|
|
880
1077
|
if (shouldTakeScreenshot) {
|
|
881
1078
|
tracker.startTimer(`${timerId}_screenshot`);
|
|
882
1079
|
}
|
|
1080
|
+
// Agent-written code is rolled back when it fails part-way; a tool's own
|
|
1081
|
+
// emitter is not (its verdict already describes what it left behind).
|
|
1082
|
+
const rollbackOnFailure = toolName === 'pinepaper_execute_custom_code'
|
|
1083
|
+
&& await markItemsBeforeRun(controller);
|
|
883
1084
|
const result = await controller.executeCode(code, shouldTakeScreenshot, stage ? { stage } : {});
|
|
884
1085
|
const browserDuration = tracker.endTimer(`${timerId}_browser_execution`);
|
|
885
1086
|
tracker.recordMetric({
|
|
@@ -916,7 +1117,10 @@ ${code}
|
|
|
916
1117
|
return errorResult(ErrorCodes.EXECUTION_ERROR, result.error || 'Failed to execute code in browser',
|
|
917
1118
|
// errorCode surfaces the governor's structured code (PP_ITEM_BUDGET,
|
|
918
1119
|
// PP_LOOP_BUDGET, PP_TIMEOUT) so the model can correct on the next turn.
|
|
919
|
-
{
|
|
1120
|
+
{
|
|
1121
|
+
code, errorCode: result.errorCode, governorReport: result.report,
|
|
1122
|
+
...(rollbackOnFailure ? await customCodeRollback(controller, result.error) : {}),
|
|
1123
|
+
}, {
|
|
920
1124
|
toolName,
|
|
921
1125
|
canvasState: canvasState || undefined,
|
|
922
1126
|
});
|
|
@@ -936,9 +1140,12 @@ ${code}
|
|
|
936
1140
|
const inner = innerFailure(result.result);
|
|
937
1141
|
if (inner) {
|
|
938
1142
|
const canvasState = await captureCanvasState(controller);
|
|
939
|
-
return errorResult(ErrorCodes.EXECUTION_ERROR, inner, {
|
|
1143
|
+
return errorResult(ErrorCodes.EXECUTION_ERROR, inner, {
|
|
1144
|
+
code, result: result.result, governorReport: result.report,
|
|
1145
|
+
...(rollbackOnFailure ? await customCodeRollback(controller, inner) : {}),
|
|
1146
|
+
}, { toolName, canvasState: canvasState || undefined });
|
|
940
1147
|
}
|
|
941
|
-
return executedResult(code, result.result, result.screenshot, description, result.report);
|
|
1148
|
+
return withSessionNote(executedResult(code, result.result, result.screenshot, description, result.report), result);
|
|
942
1149
|
}
|
|
943
1150
|
/**
|
|
944
1151
|
* The emitted code's own verdict, when it reported one and it was a failure.
|
|
@@ -1502,8 +1709,10 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
1502
1709
|
}
|
|
1503
1710
|
case 'pinepaper_capture_frames': {
|
|
1504
1711
|
const input = CaptureFramesInputSchema.parse(args);
|
|
1712
|
+
if (input.sheet)
|
|
1713
|
+
return captureSheet(input, options);
|
|
1505
1714
|
const code = codeGenerator.generateCaptureFrames(input);
|
|
1506
|
-
const description = `Deterministic capture of ${input.times.length} frame(s)`;
|
|
1715
|
+
const description = `Deterministic capture of ${(input.times ?? []).length} frame(s)`;
|
|
1507
1716
|
return executeOrGenerate(code, description, options, 'pinepaper_capture_frames');
|
|
1508
1717
|
}
|
|
1509
1718
|
case 'pinepaper_instantiate_ontology': {
|
|
@@ -1563,6 +1772,8 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
1563
1772
|
// ─── 1.6.4: agent surface for the Tier-2 engine features ───
|
|
1564
1773
|
case 'pinepaper_brand_kit': {
|
|
1565
1774
|
const input = BrandKitInputSchema.parse(args);
|
|
1775
|
+
if (input.action === 'from_url')
|
|
1776
|
+
return brandFromUrl(input.url, input.screenshots ?? 3, options);
|
|
1566
1777
|
const code = codeGenerator.generateBrandKit(input);
|
|
1567
1778
|
return executeOrGenerate(code, `Brand kit: ${input.action}`, options, 'pinepaper_brand_kit');
|
|
1568
1779
|
}
|
|
@@ -2007,8 +2218,13 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
2007
2218
|
return errorResult(ErrorCodes.BROWSER_NOT_CONNECTED, 'Not connected to PinePaper Studio. Call pinepaper_browser_connect first.');
|
|
2008
2219
|
}
|
|
2009
2220
|
try {
|
|
2010
|
-
// Refresh the page
|
|
2011
|
-
await controller.refreshPage();
|
|
2221
|
+
// Refresh the page (recovers a dead session first; M6)
|
|
2222
|
+
const refreshed = await controller.refreshPage();
|
|
2223
|
+
const sizeNote = refreshed.relaunched
|
|
2224
|
+
? (refreshed.canvasSizeRestored
|
|
2225
|
+
? `\n\nThe browser session had been lost, so the browser was relaunched. The canvas size was restored to ${refreshed.canvasSizeRestored.width}x${refreshed.canvasSizeRestored.height}.`
|
|
2226
|
+
: '\n\nThe browser session had been lost, so the browser was relaunched at the default canvas size: set the size again if you need another.')
|
|
2227
|
+
: '';
|
|
2012
2228
|
// Wait a moment for the page to stabilize
|
|
2013
2229
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
2014
2230
|
// Take a screenshot to show the result
|
|
@@ -2020,7 +2236,7 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
2020
2236
|
|
|
2021
2237
|
The canvas is now completely empty. The welcome template will not appear (it only shows for first-time visitors).
|
|
2022
2238
|
|
|
2023
|
-
You can now start creating new items on a clean canvas
|
|
2239
|
+
You can now start creating new items on a clean canvas.${sizeNote}`,
|
|
2024
2240
|
},
|
|
2025
2241
|
];
|
|
2026
2242
|
if (screenshot) {
|