@compilr-dev/sdk 0.18.3 → 0.18.4

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.
@@ -243,10 +243,11 @@ export const CAPABILITY_PACKS = {
243
243
  'canvas_get',
244
244
  'canvas_delete',
245
245
  'canvas_validate',
246
+ 'canvas_screenshot',
246
247
  ],
247
248
  readOnly: false,
248
249
  promptModules: ['platform-tool-hints'],
249
- promptSnippet: "Visual canvases (infographic/carousel/board) rendered from sandboxed HTML/SVG. Create/replace with canvas_write (raw HTML only — host injects the CSP; inline <style>/<script> only, no network/remote assets; optional Tweaks controls manifest). EDIT via canvas_get (outline=true or startLine/maxLines) then canvas_edit (str_replace/append/prepend) — never re-send the whole document. After authoring, canvas_validate to catch won't-render content, off-canvas board nodes, and un-themed colors.",
250
+ promptSnippet: 'Visual canvases (infographic/carousel/board) rendered from sandboxed HTML/SVG. Create/replace with canvas_write (raw HTML only — host injects the CSP; inline <style>/<script> only, no network/remote assets; optional Tweaks controls manifest). EDIT via canvas_get (outline=true or startLine/maxLines) then canvas_edit (str_replace/append/prepend) — never re-send the whole document. After authoring, canvas_validate (static) then canvas_screenshot to SEE the render (vision models) — fix overflow/readability with canvas_edit.',
250
251
  estimatedPromptTokens: 150,
251
252
  estimatedToolTokens: 1500,
252
253
  },
@@ -5,7 +5,7 @@
5
5
  * underlying storage backend (SQLite in CLI, PostgreSQL in web/API).
6
6
  */
7
7
  import type { IProjectRepository, IWorkItemRepository, IDocumentRepository, IPlanRepository, ICommentRepository, ICanvasRepository } from './repositories.js';
8
- import type { IAnchorService, IArtifactService, IEpisodeService } from './services.js';
8
+ import type { IAnchorService, IArtifactService, IEpisodeService, ICanvasRenderer } from './services.js';
9
9
  export interface PlatformContext {
10
10
  readonly projects: IProjectRepository;
11
11
  readonly workItems: IWorkItemRepository;
@@ -17,6 +17,8 @@ export interface PlatformContext {
17
17
  readonly episodes?: IEpisodeService;
18
18
  readonly canvases?: ICanvasRepository;
19
19
  readonly comments?: ICommentRepository;
20
+ /** Host-provided canvas → image renderer (for canvas_screenshot). */
21
+ readonly canvasRenderer?: ICanvasRenderer;
20
22
  }
21
23
  export interface PlatformHooks {
22
24
  /** Called when a project is resolved/selected as the current project */
@@ -80,3 +80,27 @@ export interface IEpisodeService {
80
80
  getRecent(count: number): Promise<WorkEpisode[]>;
81
81
  getWorkSummary(): Promise<ProjectWorkSummary>;
82
82
  }
83
+ /** A rendered canvas image, for the agent's visual self-review. */
84
+ export interface RenderedCanvasImage {
85
+ /** Base64-encoded image data (no `data:` prefix). */
86
+ data: string;
87
+ /** MIME type, e.g. "image/png". */
88
+ mediaType: string;
89
+ width?: number;
90
+ height?: number;
91
+ }
92
+ /**
93
+ * Renders a canvas's HTML/SVG to a raster image so a vision-capable agent can
94
+ * SEE what it authored (`canvas_screenshot`). Host-provided — the SDK can't
95
+ * render HTML (the host does it via, e.g., an offscreen Electron window +
96
+ * capturePage). Absent in hosts that can't render canvases (e.g. the CLI),
97
+ * in which case canvas_screenshot degrades to a "not available here" note.
98
+ */
99
+ export interface ICanvasRenderer {
100
+ renderToImage(input: {
101
+ /** Raw canvas HTML/SVG (the stored content). */
102
+ content: string;
103
+ /** Canvas type — the host sizes/wraps accordingly. */
104
+ type: 'infographic' | 'carousel' | 'board';
105
+ }): Promise<RenderedCanvasImage>;
106
+ }
@@ -576,6 +576,55 @@ export function createCanvasTools(config) {
576
576
  }
577
577
  },
578
578
  });
579
+ // ---------------------------------------------------------------------------
580
+ // canvas_screenshot — render + SEE the canvas (visual self-review)
581
+ // ---------------------------------------------------------------------------
582
+ const canvasScreenshotTool = defineTool({
583
+ name: 'canvas_screenshot',
584
+ description: 'RENDER a canvas and SEE it — a visual self-review that static checks can’t do. Renders the stored canvas ' +
585
+ 'to an image and returns it so you can inspect how it ACTUALLY looks: content overflow / cut-off, unreadable ' +
586
+ 'or low-contrast text, cramped or empty areas, misaligned or broken layout. Run it after canvas_write / ' +
587
+ 'canvas_edit (and canvas_validate) — if something is off, fix it with canvas_edit and screenshot again. ' +
588
+ 'Only useful on a vision-capable model; unavailable in hosts that can’t render canvases.',
589
+ inputSchema: {
590
+ type: 'object',
591
+ properties: {
592
+ canvas_id: { type: 'number', description: 'The stored canvas to render and view.' },
593
+ },
594
+ required: ['canvas_id'],
595
+ },
596
+ execute: async (input) => {
597
+ try {
598
+ const renderer = ctx.canvasRenderer;
599
+ if (!renderer) {
600
+ return createSuccessResult('Canvas rendering is not available in this environment — a visual self-review can’t be produced here. ' +
601
+ 'Rely on canvas_validate (static checks) instead.');
602
+ }
603
+ const c = await canvases.getById(input.canvas_id);
604
+ if (!c)
605
+ return createErrorResult(`Canvas ${String(input.canvas_id)} not found.`);
606
+ const img = await renderer.renderToImage({ content: c.content, type: c.type });
607
+ return {
608
+ success: true,
609
+ result: `Rendered "${c.title}" (${c.type}). Review the image below — check for overflow / cut-off content, ` +
610
+ 'unreadable or low-contrast text, cramped or empty areas, and broken layout. If anything is off, fix it ' +
611
+ 'with canvas_edit and screenshot again.',
612
+ imageBlocks: [
613
+ {
614
+ data: img.data,
615
+ mediaType: img.mediaType,
616
+ filename: `canvas-${String(c.id)}.png`,
617
+ width: img.width,
618
+ height: img.height,
619
+ },
620
+ ],
621
+ };
622
+ }
623
+ catch (error) {
624
+ return createErrorResult(`Failed to screenshot canvas: ${error instanceof Error ? error.message : String(error)}`);
625
+ }
626
+ },
627
+ });
579
628
  return [
580
629
  canvasWriteTool,
581
630
  canvasEditTool,
@@ -583,5 +632,6 @@ export function createCanvasTools(config) {
583
632
  canvasGetTool,
584
633
  canvasDeleteTool,
585
634
  canvasValidateTool,
635
+ canvasScreenshotTool,
586
636
  ];
587
637
  }
@@ -67,9 +67,11 @@ export const canvasSkill = defineSkill({
67
67
 
68
68
  4. **To EDIT an existing canvas, do NOT re-send the whole document.** First call \`canvas_get\` with \`outline=true\` (or a \`startLine\`/\`maxLines\` slice) to find the exact snippet, then \`canvas_edit\` with \`operation=str_replace\` (old_str must be unique — include surrounding context — or set replace_all=true), or append/prepend. Reserve \`canvas_write\` with \`canvas_id\` for a full intentional rewrite.
69
69
 
70
- 5. **Validate before you hand it off.** Once the canvas is composed, call \`canvas_validate\` (canvas_id) — a fast static check that catches content that won't render (Mermaid/Markdown/CDN), a board missing its \`data-board\` bounds or with nodes off-canvas, sparse output, and hardcoded colors that ignore the theme tokens. Fix any **errors** (they render broken) and address applicable **warnings**, then move on. (It doesn't judge looks — that's your eye.)
70
+ 5. **Validate before you hand it off.** Once the canvas is composed, call \`canvas_validate\` (canvas_id) — a fast static check that catches content that won't render (Mermaid/Markdown/CDN), a board missing its \`data-board\` bounds or with nodes off-canvas, sparse output, and hardcoded colors that ignore the theme tokens. Fix any **errors** (they render broken) and address applicable **warnings**, then move on.
71
71
 
72
- 6. **After writing, tell the user what you made in one line** and point them at the Tweaks they can adjust. The canvas opens in its own tab.
72
+ 6. **See it before you trust it (if you can).** If you're vision-capable, call \`canvas_screenshot\` (canvas_id) — it renders the canvas and returns the image so you can catch what static checks can't: overflow / cut-off content, unreadable or low-contrast text, cramped or empty areas, broken layout. If something's off, fix it with \`canvas_edit\` and screenshot again. (No-op on text-only models / hosts that can't render.)
73
+
74
+ 7. **After writing, tell the user what you made in one line** and point them at the Tweaks they can adjust. The canvas opens in its own tab.
73
75
 
74
76
  ## Rules
75
77
  - CALL THE TOOL — don't describe the HTML you "would" write and stop. A canvas only exists once the tool succeeds.
@@ -295,6 +295,7 @@ export const TOOL_GROUPS = {
295
295
  'canvas_get',
296
296
  'canvas_delete',
297
297
  'canvas_validate',
298
+ 'canvas_screenshot',
298
299
  ],
299
300
  readOnly: false,
300
301
  tier: 'meta',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.18.3",
3
+ "version": "0.18.4",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",