synabun 2026.3.31007 → 2026.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +51 -52
  2. package/hooks/claude-code/prompt-submit.mjs +6 -6
  3. package/mcp-server/dist/services/neural-interface.d.ts +20 -19
  4. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  5. package/mcp-server/dist/services/neural-interface.js +52 -45
  6. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  7. package/mcp-server/dist/tools/browser-advanced.d.ts +4 -0
  8. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
  9. package/mcp-server/dist/tools/browser-advanced.js +7 -4
  10. package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
  11. package/mcp-server/dist/tools/browser-interact.d.ts +16 -0
  12. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
  13. package/mcp-server/dist/tools/browser-interact.js +25 -16
  14. package/mcp-server/dist/tools/browser-interact.js.map +1 -1
  15. package/mcp-server/dist/tools/browser-navigate.d.ts +8 -0
  16. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
  17. package/mcp-server/dist/tools/browser-navigate.js +13 -8
  18. package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
  19. package/mcp-server/dist/tools/browser-observe.d.ts +48 -0
  20. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
  21. package/mcp-server/dist/tools/browser-observe.js +74 -49
  22. package/mcp-server/dist/tools/browser-observe.js.map +1 -1
  23. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +20 -0
  24. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
  25. package/mcp-server/dist/tools/leonardo-browser-tools.js +39 -34
  26. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
  27. package/mcp-server/src/services/neural-interface.ts +58 -46
  28. package/mcp-server/src/tools/browser-advanced.ts +10 -5
  29. package/mcp-server/src/tools/browser-interact.ts +34 -24
  30. package/mcp-server/src/tools/browser-navigate.ts +83 -77
  31. package/mcp-server/src/tools/browser-observe.ts +99 -73
  32. package/mcp-server/src/tools/leonardo-browser-tools.ts +47 -37
  33. package/neural-interface/public/shared/styles.css +49 -0
  34. package/neural-interface/public/shared/ui-automation-studio.js +71 -3
  35. package/neural-interface/public/shared/ui-claude-panel.js +29 -23
  36. package/neural-interface/public/shared/ui-settings.js +10 -10
  37. package/neural-interface/public/shared/ui-terminal.js +9 -7
  38. package/neural-interface/server.js +732 -386
  39. package/package.json +1 -1
@@ -8,10 +8,14 @@
8
8
  import { z } from 'zod';
9
9
  export declare const browserNavigateSchema: {
10
10
  page: z.ZodEnum<["home", "library", "image", "video", "upscaler", "blueprints", "flow-state", "models"]>;
11
+ sessionId: z.ZodOptional<z.ZodString>;
12
+ tabId: z.ZodOptional<z.ZodString>;
11
13
  };
12
14
  export declare const browserNavigateDescription = "Navigate the browser to a specific Leonardo.ai page. Always use this as the first step before any browser-based generation.";
13
15
  export declare function handleBrowserNavigate(args: {
14
16
  page: string;
17
+ sessionId?: string;
18
+ tabId?: string;
15
19
  }): Promise<{
16
20
  content: {
17
21
  type: "text";
@@ -21,11 +25,15 @@ export declare function handleBrowserNavigate(args: {
21
25
  export declare const browserGenerateSchema: {
22
26
  prompt: z.ZodString;
23
27
  type: z.ZodOptional<z.ZodEnum<["image", "video"]>>;
28
+ sessionId: z.ZodOptional<z.ZodString>;
29
+ tabId: z.ZodOptional<z.ZodString>;
24
30
  };
25
31
  export declare const browserGenerateDescription = "Fill the prompt field and click Generate on the current Leonardo.ai page. Set all other settings (model, style, dimensions, motion controls) BEFORE calling this \u2014 use browser_click, browser_fill, and browser_snapshot to configure the UI first. This tool only handles: navigate to correct page if needed \u2192 clear & fill prompt \u2192 click Generate.";
26
32
  export declare function handleBrowserGenerate(args: {
27
33
  prompt: string;
28
34
  type?: string;
35
+ sessionId?: string;
36
+ tabId?: string;
29
37
  }): Promise<{
30
38
  content: {
31
39
  type: "text";
@@ -35,11 +43,15 @@ export declare function handleBrowserGenerate(args: {
35
43
  export declare const browserLibrarySchema: {
36
44
  action: z.ZodEnum<["view", "search"]>;
37
45
  query: z.ZodOptional<z.ZodString>;
46
+ sessionId: z.ZodOptional<z.ZodString>;
47
+ tabId: z.ZodOptional<z.ZodString>;
38
48
  };
39
49
  export declare const browserLibraryDescription = "Open or search Leonardo.ai's library to view past generations. Use browser_snapshot after to see results.";
40
50
  export declare function handleBrowserLibrary(args: {
41
51
  action: string;
42
52
  query?: string;
53
+ sessionId?: string;
54
+ tabId?: string;
43
55
  }): Promise<{
44
56
  content: {
45
57
  type: "text";
@@ -48,10 +60,14 @@ export declare function handleBrowserLibrary(args: {
48
60
  }>;
49
61
  export declare const browserDownloadSchema: {
50
62
  action: z.ZodEnum<["screenshot"]>;
63
+ sessionId: z.ZodOptional<z.ZodString>;
64
+ tabId: z.ZodOptional<z.ZodString>;
51
65
  };
52
66
  export declare const browserDownloadDescription = "Capture a screenshot of the current Leonardo.ai page. Use to verify generation results or UI state.";
53
67
  export declare function handleBrowserDownload(args: {
54
68
  action: string;
69
+ sessionId?: string;
70
+ tabId?: string;
55
71
  }): Promise<{
56
72
  content: {
57
73
  type: "text";
@@ -62,12 +78,16 @@ export declare const browserReferenceSchema: {
62
78
  type: z.ZodEnum<["image_ref", "style_ref", "content_ref", "character_ref", "image_to_image", "start_frame", "end_frame"]>;
63
79
  filePaths: z.ZodArray<z.ZodString, "many">;
64
80
  autoClear: z.ZodOptional<z.ZodBoolean>;
81
+ sessionId: z.ZodOptional<z.ZodString>;
82
+ tabId: z.ZodOptional<z.ZodString>;
65
83
  };
66
84
  export declare const browserReferenceDescription: string;
67
85
  export declare function handleBrowserReference(args: {
68
86
  type: string;
69
87
  filePaths: string[];
70
88
  autoClear?: boolean;
89
+ sessionId?: string;
90
+ tabId?: string;
71
91
  }): Promise<{
72
92
  content: {
73
93
  type: "text";
@@ -1 +1 @@
1
- {"version":3,"file":"leonardo-browser-tools.d.ts","sourceRoot":"","sources":["../../src/tools/leonardo-browser-tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoBxB,eAAO,MAAM,qBAAqB;;CAKjC,CAAC;AAEF,eAAO,MAAM,0BAA0B,gIACwF,CAAC;AAEhI,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;;;;;GAuBjE;AAID,eAAO,MAAM,qBAAqB;;;CAGjC,CAAC;AAEF,eAAO,MAAM,0BAA0B,0WACmT,CAAC;AAE3V,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;;;;;GA4BA;AAID,eAAO,MAAM,oBAAoB;;;CAGhC,CAAC;AAEF,eAAO,MAAM,yBAAyB,8GACwE,CAAC;AAE/G,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;GAiBlF;AAID,eAAO,MAAM,qBAAqB;;CAEjC,CAAC;AAEF,eAAO,MAAM,0BAA0B,wGACgE,CAAC;AAExG,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE;;;;;GAWnE;AAID,eAAO,MAAM,sBAAsB;;;;CAclC,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAI+C,CAAC;AAExF,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;;;;;GA+FA"}
1
+ {"version":3,"file":"leonardo-browser-tools.d.ts","sourceRoot":"","sources":["../../src/tools/leonardo-browser-tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,eAAO,MAAM,qBAAqB;;;;CAOjC,CAAC;AAEF,eAAO,MAAM,0BAA0B,gIACwF,CAAC;AAEhI,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;GAuBrG;AAID,eAAO,MAAM,qBAAqB;;;;;CAKjC,CAAC;AAEF,eAAO,MAAM,0BAA0B,0WACmT,CAAC;AAE3V,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;GA4BA;AAID,eAAO,MAAM,oBAAoB;;;;;CAKhC,CAAC;AAEF,eAAO,MAAM,yBAAyB,8GACwE,CAAC;AAE/G,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;GAiBtH;AAID,eAAO,MAAM,qBAAqB;;;;CAIjC,CAAC;AAEF,eAAO,MAAM,0BAA0B,wGACgE,CAAC;AAExG,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;GAWvG;AAID,eAAO,MAAM,sBAAsB;;;;;;CAgBlC,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAI+C,CAAC;AAExF,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;GA+FA"}
@@ -8,14 +8,9 @@
8
8
  import { z } from 'zod';
9
9
  import * as ni from '../services/neural-interface.js';
10
10
  import { text } from './response.js';
11
+ const tabIdField = z.string().optional().describe('Target a specific tab within the session. Auto-resolved from environment if omitted.');
11
12
  const LEO_BASE = 'https://app.leonardo.ai';
12
- // ── Helper: resolve or create session ─────────────────────────
13
- async function getSession() {
14
- const resolved = await ni.resolveSession(undefined, { url: LEO_BASE });
15
- if ('error' in resolved)
16
- return null;
17
- return resolved.sessionId;
18
- }
13
+ // ── Helper ────────────────────────────────────────────────────
19
14
  async function wait(ms) {
20
15
  return new Promise(resolve => setTimeout(resolve, ms));
21
16
  }
@@ -25,11 +20,13 @@ export const browserNavigateSchema = {
25
20
  'home', 'library', 'image', 'video', 'upscaler',
26
21
  'blueprints', 'flow-state', 'models',
27
22
  ]).describe('Leonardo.ai page to navigate to.'),
23
+ sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
24
+ tabId: tabIdField,
28
25
  };
29
26
  export const browserNavigateDescription = 'Navigate the browser to a specific Leonardo.ai page. Always use this as the first step before any browser-based generation.';
30
27
  export async function handleBrowserNavigate(args) {
31
- const sessionId = await getSession();
32
- if (!sessionId)
28
+ const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
29
+ if ('error' in resolved)
33
30
  return text('No browser session available. Open Leonardo.ai in the browser first using browser_navigate or the Apps menu.');
34
31
  const paths = {
35
32
  'home': '/',
@@ -42,7 +39,7 @@ export async function handleBrowserNavigate(args) {
42
39
  'models': '/models-training',
43
40
  };
44
41
  const path = paths[args.page] || '/';
45
- const res = await ni.navigate(sessionId, `${LEO_BASE}${path}`);
42
+ const res = await ni.navigate(resolved.sessionId, `${LEO_BASE}${path}`, resolved.tabId);
46
43
  if (res.error)
47
44
  return text(`Navigation failed: ${res.error}`);
48
45
  // Wait for page to settle
@@ -53,30 +50,32 @@ export async function handleBrowserNavigate(args) {
53
50
  export const browserGenerateSchema = {
54
51
  prompt: z.string().describe('The generation prompt to type into Leonardo.ai.'),
55
52
  type: z.enum(['image', 'video']).optional().describe('Generation type. Default: image.'),
53
+ sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
54
+ tabId: tabIdField,
56
55
  };
57
56
  export const browserGenerateDescription = 'Fill the prompt field and click Generate on the current Leonardo.ai page. Set all other settings (model, style, dimensions, motion controls) BEFORE calling this — use browser_click, browser_fill, and browser_snapshot to configure the UI first. This tool only handles: navigate to correct page if needed → clear & fill prompt → click Generate.';
58
57
  export async function handleBrowserGenerate(args) {
59
- const sessionId = await getSession();
60
- if (!sessionId)
58
+ const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
59
+ if ('error' in resolved)
61
60
  return text('No browser session available. Open Leonardo.ai first.');
62
61
  const genType = args.type || 'image';
63
62
  // Check current URL — navigate only if not already on the right page
64
- const snapshot = await ni.snapshot(sessionId);
63
+ const snapshot = await ni.snapshot(resolved.sessionId, resolved.tabId);
65
64
  const currentUrl = snapshot?.url || '';
66
65
  const targetPath = genType === 'video' ? '/image-generation/video' : '/image-generation';
67
66
  if (!currentUrl.includes(targetPath)) {
68
- const navRes = await ni.navigate(sessionId, `${LEO_BASE}${targetPath}`);
67
+ const navRes = await ni.navigate(resolved.sessionId, `${LEO_BASE}${targetPath}`, resolved.tabId);
69
68
  if (navRes.error)
70
69
  return text(`Navigation failed: ${navRes.error}`);
71
70
  await wait(3000);
72
71
  }
73
72
  // Clear and fill the prompt textbox
74
- const fillRes = await ni.fill(sessionId, '[aria-label="Prompt"], textbox[name="Prompt"], textarea', args.prompt);
73
+ const fillRes = await ni.fill(resolved.sessionId, '[aria-label="Prompt"], textbox[name="Prompt"], textarea', args.prompt, undefined, resolved.tabId);
75
74
  if (fillRes.error)
76
75
  return text(`Failed to fill prompt: ${fillRes.error}`);
77
76
  await wait(500);
78
77
  // Click the Generate button
79
- const clickRes = await ni.click(sessionId, 'button[aria-label="Generate"], button:has-text("Generate")');
78
+ const clickRes = await ni.click(resolved.sessionId, 'button[aria-label="Generate"], button:has-text("Generate")', undefined, resolved.tabId);
80
79
  if (clickRes.error)
81
80
  return text(`Failed to click Generate: ${clickRes.error}`);
82
81
  return text(`Generation started (${genType}).\n\nPrompt: "${args.prompt.substring(0, 120)}${args.prompt.length > 120 ? '...' : ''}"\n\nThe generation is processing in Leonardo.ai. Use browser_snapshot or browser_screenshot to check progress and see results. Results also appear in the library.`);
@@ -85,21 +84,23 @@ export async function handleBrowserGenerate(args) {
85
84
  export const browserLibrarySchema = {
86
85
  action: z.enum(['view', 'search']).describe('Action: view (open library) or search (search for a term).'),
87
86
  query: z.string().optional().describe('Search query (for search action).'),
87
+ sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
88
+ tabId: tabIdField,
88
89
  };
89
90
  export const browserLibraryDescription = 'Open or search Leonardo.ai\'s library to view past generations. Use browser_snapshot after to see results.';
90
91
  export async function handleBrowserLibrary(args) {
91
- const sessionId = await getSession();
92
- if (!sessionId)
92
+ const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
93
+ if ('error' in resolved)
93
94
  return text('No browser session available.');
94
- const navRes = await ni.navigate(sessionId, `${LEO_BASE}/library`);
95
+ const navRes = await ni.navigate(resolved.sessionId, `${LEO_BASE}/library`, resolved.tabId);
95
96
  if (navRes.error)
96
97
  return text(`Navigation failed: ${navRes.error}`);
97
98
  await wait(2000);
98
99
  if (args.action === 'search' && args.query) {
99
- const fillRes = await ni.fill(sessionId, 'input[placeholder*="Search" i]', args.query);
100
+ const fillRes = await ni.fill(resolved.sessionId, 'input[placeholder*="Search" i]', args.query, undefined, resolved.tabId);
100
101
  if (fillRes.error)
101
102
  return text(`Failed to search: ${fillRes.error}`);
102
- await ni.pressKey(sessionId, 'Enter');
103
+ await ni.pressKey(resolved.sessionId, 'Enter', resolved.tabId);
103
104
  return text(`Searched Leonardo.ai library for: "${args.query}". Use browser_snapshot to see results.`);
104
105
  }
105
106
  return text('Leonardo.ai library opened. Use browser_snapshot to see recent generations.');
@@ -107,14 +108,16 @@ export async function handleBrowserLibrary(args) {
107
108
  // ── Browser Download / Screenshot ─────────────────────────────
108
109
  export const browserDownloadSchema = {
109
110
  action: z.enum(['screenshot']).describe('Action: screenshot (capture current Leonardo.ai page state).'),
111
+ sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
112
+ tabId: tabIdField,
110
113
  };
111
114
  export const browserDownloadDescription = 'Capture a screenshot of the current Leonardo.ai page. Use to verify generation results or UI state.';
112
115
  export async function handleBrowserDownload(args) {
113
- const sessionId = await getSession();
114
- if (!sessionId)
116
+ const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
117
+ if ('error' in resolved)
115
118
  return text('No browser session available.');
116
119
  if (args.action === 'screenshot') {
117
- const res = await ni.screenshot(sessionId);
120
+ const res = await ni.screenshot(resolved.sessionId, resolved.tabId);
118
121
  if (res.error)
119
122
  return text(`Screenshot failed: ${res.error}`);
120
123
  return text('Screenshot captured of the current Leonardo.ai page.');
@@ -130,14 +133,16 @@ export const browserReferenceSchema = {
130
133
  'Video generation: start_frame, end_frame. Availability depends on the selected model — check the model advisor.'),
131
134
  filePaths: z.array(z.string()).describe('Absolute file paths of images to upload as reference. Get paths from image_staged tool or from user-attached images.'),
132
135
  autoClear: z.boolean().optional().describe('Auto-delete the source images from data/images/ after successful upload. Default: true.'),
136
+ sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
137
+ tabId: tabIdField,
133
138
  };
134
139
  export const browserReferenceDescription = 'Upload reference image(s) to Leonardo.ai for guided generation. Supports image references, style/content/character references, ' +
135
140
  'image-to-image, and video start/end frames. Must be called AFTER selecting a model (reference type availability is model-dependent) ' +
136
141
  'and BEFORE calling leonardo_browser_generate. Uses browser automation to click the reference panel, upload the file(s), and confirm. ' +
137
142
  'By default, auto-clears uploaded images from the SynaBun image store after success.';
138
143
  export async function handleBrowserReference(args) {
139
- const sessionId = await getSession();
140
- if (!sessionId)
144
+ const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
145
+ if ('error' in resolved)
141
146
  return text('No browser session available. Open Leonardo.ai first.');
142
147
  const { type, filePaths, autoClear = true } = args;
143
148
  const isVideo = type === 'start_frame' || type === 'end_frame';
@@ -145,7 +150,7 @@ export async function handleBrowserReference(args) {
145
150
  let openResult;
146
151
  if (isVideo) {
147
152
  // Video: click "Add Image Guidance to generation" button
148
- openResult = await ni.evaluate(sessionId, `(() => {
153
+ openResult = await ni.evaluate(resolved.sessionId, `(() => {
149
154
  const btns = document.querySelectorAll('button');
150
155
  for (const b of btns) {
151
156
  if (b.textContent.includes('Image Guidance') || b.textContent.includes('Add Image')) {
@@ -153,11 +158,11 @@ export async function handleBrowserReference(args) {
153
158
  }
154
159
  }
155
160
  return 'not found';
156
- })()`);
161
+ })()`, resolved.tabId);
157
162
  }
158
163
  else {
159
164
  // Image: click "Add elements" button
160
- openResult = await ni.evaluate(sessionId, `(() => {
165
+ openResult = await ni.evaluate(resolved.sessionId, `(() => {
161
166
  const btns = document.querySelectorAll('button');
162
167
  for (const b of btns) {
163
168
  if (b.textContent.includes('Add elements') || b.textContent.includes('Add Elements')) {
@@ -165,7 +170,7 @@ export async function handleBrowserReference(args) {
165
170
  }
166
171
  }
167
172
  return 'not found';
168
- })()`);
173
+ })()`, resolved.tabId);
169
174
  }
170
175
  if (openResult.error)
171
176
  return text(`Failed to open reference panel: ${openResult.error}`);
@@ -181,22 +186,22 @@ export async function handleBrowserReference(args) {
181
186
  };
182
187
  const labels = typeLabels[type] || [type];
183
188
  const labelSearch = labels.map(l => `b.textContent.includes('${l}')`).join(' || ');
184
- const selectResult = await ni.evaluate(sessionId, `(() => {
189
+ const selectResult = await ni.evaluate(resolved.sessionId, `(() => {
185
190
  const btns = document.querySelectorAll('button, [role="tab"], [role="menuitem"]');
186
191
  for (const b of btns) {
187
192
  if (${labelSearch}) { b.click(); return 'selected'; }
188
193
  }
189
194
  return 'not found — use browser_snapshot to see available options';
190
- })()`);
195
+ })()`, resolved.tabId);
191
196
  if (selectResult.error)
192
197
  return text(`Failed to select reference type: ${selectResult.error}. Use browser_snapshot to see the panel.`);
193
198
  await wait(1000);
194
199
  }
195
200
  // Step 3: Find file input and upload
196
- const uploadResult = await ni.upload(sessionId, 'input[type="file"]', filePaths);
201
+ const uploadResult = await ni.upload(resolved.sessionId, 'input[type="file"]', filePaths, undefined, resolved.tabId);
197
202
  if (uploadResult.error) {
198
203
  // Fallback: try broader selector
199
- const fallback = await ni.upload(sessionId, 'input[accept*="image"]', filePaths);
204
+ const fallback = await ni.upload(resolved.sessionId, 'input[accept*="image"]', filePaths, undefined, resolved.tabId);
200
205
  if (fallback.error) {
201
206
  return text(`Failed to upload reference image(s): ${uploadResult.error}. ` +
202
207
  `The file input may not be visible yet — try browser_snapshot to inspect the panel, ` +
@@ -1 +1 @@
1
- {"version":3,"file":"leonardo-browser-tools.js","sourceRoot":"","sources":["../../src/tools/leonardo-browser-tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"leonardo-browser-tools.js","sourceRoot":"","sources":["../../src/tools/leonardo-browser-tools.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ const BASE_URL = process.env.NEURAL_INTERFACE_URL
8
8
  || `http://localhost:${process.env.NEURAL_PORT || '3344'}`;
9
9
  const DEFAULT_TIMEOUT = 10_000;
10
10
  const LONG_TIMEOUT = 30_000;
11
+ const SESSION_CREATE_TIMEOUT = 70_000;
11
12
 
12
13
  export interface BrowserSessionInfo {
13
14
  id: string;
@@ -67,8 +68,12 @@ async function request(
67
68
  */
68
69
  export async function resolveSession(
69
70
  sessionId?: string,
70
- autoCreate?: { url?: string }
71
- ): Promise<{ sessionId: string } | { error: string }> {
71
+ autoCreate?: { url?: string },
72
+ tabId?: string
73
+ ): Promise<{ sessionId: string; tabId?: string } | { error: string }> {
74
+ // Resolve tab ID from explicit param or environment variable
75
+ const resolvedTabId = tabId || process.env.SYNABUN_BROWSER_TAB || undefined;
76
+
72
77
  // Agent-scoped browser session — set by the agent orchestrator to pin
73
78
  // this MCP instance to a specific browser session (multi-agent isolation).
74
79
  // Verify the pinned session still exists; fall through to auto-select if gone.
@@ -76,14 +81,14 @@ export async function resolveSession(
76
81
  if (pinnedSession && !sessionId) {
77
82
  const check = await request('GET', '/api/browser/sessions');
78
83
  const active = ((check.sessions || []) as BrowserSessionInfo[]).find(s => s.id === pinnedSession);
79
- if (active) return { sessionId: pinnedSession };
80
- // Pinned session is gone — fall through to auto-select/create below
84
+ if (active) return { sessionId: pinnedSession, tabId: resolvedTabId };
85
+ return { error: `Pinned browser session ${pinnedSession} is no longer available. Launch the automation again so SynaBun can open the selected browser profile.` };
81
86
  }
82
87
 
83
88
  if (sessionId) {
84
89
  // Trust the server — it will 404 if the session doesn't exist.
85
90
  // Skipping the extra GET /api/browser/sessions verification call saves a full round-trip.
86
- return { sessionId };
91
+ return { sessionId, tabId: resolvedTabId };
87
92
  }
88
93
 
89
94
  // List sessions
@@ -92,16 +97,16 @@ export async function resolveSession(
92
97
  const sessions = (data.sessions || []) as BrowserSessionInfo[];
93
98
 
94
99
  if (sessions.length === 1) {
95
- return { sessionId: sessions[0].id };
100
+ return { sessionId: sessions[0].id, tabId: resolvedTabId };
96
101
  }
97
102
 
98
103
  if (sessions.length === 0) {
99
104
  if (autoCreate) {
100
105
  const created = await request('POST', '/api/browser/sessions', {
101
106
  url: autoCreate.url || 'about:blank',
102
- }, LONG_TIMEOUT);
107
+ }, SESSION_CREATE_TIMEOUT);
103
108
  if (created.error) return { error: `Failed to auto-create session: ${created.error}` };
104
- return { sessionId: created.sessionId as string };
109
+ return { sessionId: created.sessionId as string, tabId: resolvedTabId };
105
110
  }
106
111
  return { error: 'No browser sessions open. Use browser_session to create one first, or use browser_navigate with a URL to auto-create.' };
107
112
  }
@@ -130,7 +135,7 @@ export async function listSessions(): Promise<NiResponse> {
130
135
  export async function createSession(url?: string): Promise<NiResponse> {
131
136
  return request('POST', '/api/browser/sessions', {
132
137
  url: url || 'about:blank',
133
- }, LONG_TIMEOUT);
138
+ }, SESSION_CREATE_TIMEOUT);
134
139
  }
135
140
 
136
141
  export async function closeSession(sessionId: string): Promise<NiResponse> {
@@ -139,98 +144,105 @@ export async function closeSession(sessionId: string): Promise<NiResponse> {
139
144
 
140
145
  // ── Navigation ──
141
146
 
142
- export async function navigate(sessionId: string, url: string): Promise<NiResponse> {
143
- return request('POST', `/api/browser/sessions/${sessionId}/navigate`, { url }, LONG_TIMEOUT);
147
+ export async function navigate(sessionId: string, url: string, tabId?: string): Promise<NiResponse> {
148
+ return request('POST', `/api/browser/sessions/${sessionId}/navigate`, { url, ...(tabId && { tabId }) }, LONG_TIMEOUT);
144
149
  }
145
150
 
146
- export async function goBack(sessionId: string): Promise<NiResponse> {
147
- return request('POST', `/api/browser/sessions/${sessionId}/back`);
151
+ export async function goBack(sessionId: string, tabId?: string): Promise<NiResponse> {
152
+ return request('POST', `/api/browser/sessions/${sessionId}/back`, { ...(tabId && { tabId }) });
148
153
  }
149
154
 
150
- export async function goForward(sessionId: string): Promise<NiResponse> {
151
- return request('POST', `/api/browser/sessions/${sessionId}/forward`);
155
+ export async function goForward(sessionId: string, tabId?: string): Promise<NiResponse> {
156
+ return request('POST', `/api/browser/sessions/${sessionId}/forward`, { ...(tabId && { tabId }) });
152
157
  }
153
158
 
154
- export async function reload(sessionId: string): Promise<NiResponse> {
155
- return request('POST', `/api/browser/sessions/${sessionId}/reload`, undefined, LONG_TIMEOUT);
159
+ export async function reload(sessionId: string, tabId?: string): Promise<NiResponse> {
160
+ return request('POST', `/api/browser/sessions/${sessionId}/reload`, { ...(tabId && { tabId }) }, LONG_TIMEOUT);
156
161
  }
157
162
 
158
163
  // ── Interaction (selector-based) ──
159
164
 
160
- export async function click(sessionId: string, selector: string, nthMatch?: number): Promise<NiResponse> {
161
- return request('POST', `/api/browser/sessions/${sessionId}/click`, { selector, ...(nthMatch !== undefined && { nthMatch }) });
165
+ export async function click(sessionId: string, selector: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
166
+ return request('POST', `/api/browser/sessions/${sessionId}/click`, { selector, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
162
167
  }
163
168
 
164
- export async function fill(sessionId: string, selector: string, value: string, nthMatch?: number): Promise<NiResponse> {
165
- return request('POST', `/api/browser/sessions/${sessionId}/fill`, { selector, value, ...(nthMatch !== undefined && { nthMatch }) });
169
+ export async function fill(sessionId: string, selector: string, value: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
170
+ return request('POST', `/api/browser/sessions/${sessionId}/fill`, { selector, value, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
166
171
  }
167
172
 
168
- export async function type(sessionId: string, selector: string | null, text: string, nthMatch?: number): Promise<NiResponse> {
169
- return request('POST', `/api/browser/sessions/${sessionId}/type`, { selector, text, ...(nthMatch !== undefined && { nthMatch }) });
173
+ export async function type(sessionId: string, selector: string | null, text: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
174
+ return request('POST', `/api/browser/sessions/${sessionId}/type`, { selector, text, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
170
175
  }
171
176
 
172
- export async function hover(sessionId: string, selector: string, nthMatch?: number): Promise<NiResponse> {
173
- return request('POST', `/api/browser/sessions/${sessionId}/hover`, { selector, ...(nthMatch !== undefined && { nthMatch }) });
177
+ export async function hover(sessionId: string, selector: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
178
+ return request('POST', `/api/browser/sessions/${sessionId}/hover`, { selector, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
174
179
  }
175
180
 
176
- export async function selectOption(sessionId: string, selector: string, value: string, nthMatch?: number): Promise<NiResponse> {
177
- return request('POST', `/api/browser/sessions/${sessionId}/select`, { selector, value, ...(nthMatch !== undefined && { nthMatch }) });
181
+ export async function selectOption(sessionId: string, selector: string, value: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
182
+ return request('POST', `/api/browser/sessions/${sessionId}/select`, { selector, value, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
178
183
  }
179
184
 
180
- export async function pressKey(sessionId: string, key: string): Promise<NiResponse> {
181
- return request('POST', `/api/browser/sessions/${sessionId}/press`, { key });
185
+ export async function pressKey(sessionId: string, key: string, tabId?: string): Promise<NiResponse> {
186
+ return request('POST', `/api/browser/sessions/${sessionId}/press`, { key, ...(tabId && { tabId }) });
182
187
  }
183
188
 
184
189
  export async function scroll(
185
190
  sessionId: string,
186
- opts: { direction: string; distance?: number; selector?: string }
191
+ opts: { direction: string; distance?: number; selector?: string },
192
+ tabId?: string
187
193
  ): Promise<NiResponse> {
188
- return request('POST', `/api/browser/sessions/${sessionId}/scroll`, opts as Record<string, unknown>);
194
+ return request('POST', `/api/browser/sessions/${sessionId}/scroll`, { ...opts, ...(tabId && { tabId }) } as Record<string, unknown>);
189
195
  }
190
196
 
191
197
  export async function upload(
192
198
  sessionId: string,
193
199
  selector: string,
194
200
  filePaths: string[],
195
- nthMatch?: number
201
+ nthMatch?: number,
202
+ tabId?: string
196
203
  ): Promise<NiResponse> {
197
- return request('POST', `/api/browser/sessions/${sessionId}/upload`, { selector, filePaths, ...(nthMatch !== undefined && { nthMatch }) }, LONG_TIMEOUT);
204
+ return request('POST', `/api/browser/sessions/${sessionId}/upload`, { selector, filePaths, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) }, LONG_TIMEOUT);
198
205
  }
199
206
 
200
207
  // ── Observation ──
201
208
 
202
- export async function snapshot(sessionId: string, selector?: string): Promise<NiResponse> {
203
- if (selector) return request('POST', `/api/browser/sessions/${sessionId}/snapshot`, { selector });
204
- return request('GET', `/api/browser/sessions/${sessionId}/snapshot`);
209
+ export async function snapshot(sessionId: string, selector?: string, tabId?: string): Promise<NiResponse> {
210
+ if (selector) return request('POST', `/api/browser/sessions/${sessionId}/snapshot`, { selector, ...(tabId && { tabId }) });
211
+ const qs = tabId ? `?tabId=${tabId}` : '';
212
+ return request('GET', `/api/browser/sessions/${sessionId}/snapshot${qs}`);
205
213
  }
206
214
 
207
- export async function getContent(sessionId: string): Promise<NiResponse> {
208
- return request('GET', `/api/browser/sessions/${sessionId}/content`);
215
+ export async function getContent(sessionId: string, tabId?: string): Promise<NiResponse> {
216
+ const qs = tabId ? `?tabId=${tabId}` : '';
217
+ return request('GET', `/api/browser/sessions/${sessionId}/content${qs}`);
209
218
  }
210
219
 
211
- export async function getMarkdown(sessionId: string): Promise<NiResponse> {
212
- return request('GET', `/api/browser/sessions/${sessionId}/markdown`, undefined, LONG_TIMEOUT);
220
+ export async function getMarkdown(sessionId: string, tabId?: string): Promise<NiResponse> {
221
+ const qs = tabId ? `?tabId=${tabId}` : '';
222
+ return request('GET', `/api/browser/sessions/${sessionId}/markdown${qs}`, undefined, LONG_TIMEOUT);
213
223
  }
214
224
 
215
225
  export async function fetchMarkdown(url: string, timeout?: number): Promise<NiResponse> {
216
226
  return request('POST', '/api/fetch-markdown', { url, timeout }, LONG_TIMEOUT);
217
227
  }
218
228
 
219
- export async function screenshot(sessionId: string): Promise<NiResponse> {
220
- return request('GET', `/api/browser/sessions/${sessionId}/screenshot-base64`);
229
+ export async function screenshot(sessionId: string, tabId?: string): Promise<NiResponse> {
230
+ const qs = tabId ? `?tabId=${tabId}` : '';
231
+ return request('GET', `/api/browser/sessions/${sessionId}/screenshot-base64${qs}`);
221
232
  }
222
233
 
223
234
  // ── Advanced ──
224
235
 
225
- export async function evaluate(sessionId: string, script: string): Promise<NiResponse> {
226
- return request('POST', `/api/browser/sessions/${sessionId}/evaluate`, { script }, LONG_TIMEOUT);
236
+ export async function evaluate(sessionId: string, script: string, tabId?: string): Promise<NiResponse> {
237
+ return request('POST', `/api/browser/sessions/${sessionId}/evaluate`, { script, ...(tabId && { tabId }) }, LONG_TIMEOUT);
227
238
  }
228
239
 
229
240
  export async function waitFor(
230
241
  sessionId: string,
231
- opts: { selector?: string; state?: string; loadState?: string; timeout?: number }
242
+ opts: { selector?: string; state?: string; loadState?: string; timeout?: number },
243
+ tabId?: string
232
244
  ): Promise<NiResponse> {
233
- return request('POST', `/api/browser/sessions/${sessionId}/wait`, opts, LONG_TIMEOUT);
245
+ return request('POST', `/api/browser/sessions/${sessionId}/wait`, { ...opts, ...(tabId && { tabId }) }, LONG_TIMEOUT);
234
246
  }
235
247
 
236
248
  // ── Whiteboard ──
@@ -2,25 +2,28 @@ import { z } from 'zod';
2
2
  import * as ni from '../services/neural-interface.js';
3
3
  import { text } from './response.js';
4
4
 
5
+ const tabIdField = z.string().optional().describe('Target a specific tab within the session. Auto-resolved from environment if omitted.');
6
+
5
7
  // ── browser_evaluate ──
6
8
 
7
9
  export const browserEvaluateSchema = {
8
10
  script: z.string().optional().describe('JavaScript code to execute in the browser page context. The return value will be serialized to JSON.'),
9
11
  expression: z.string().optional().describe('Alias for "script" — accepted for compatibility with Playwright conventions.'),
10
12
  sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
13
+ tabId: tabIdField,
11
14
  };
12
15
 
13
16
  export const browserEvaluateDescription =
14
17
  'Execute JavaScript in the browser page context and return the result. Useful for reading DOM state, extracting data, or performing actions not covered by other tools.';
15
18
 
16
- export async function handleBrowserEvaluate(args: { script?: string; expression?: string; sessionId?: string }) {
19
+ export async function handleBrowserEvaluate(args: { script?: string; expression?: string; sessionId?: string; tabId?: string }) {
17
20
  const code = args.script || args.expression;
18
21
  if (!code) return text('Either "script" or "expression" is required.');
19
22
 
20
- const resolved = await ni.resolveSession(args.sessionId);
23
+ const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
21
24
  if ('error' in resolved) return text(resolved.error);
22
25
 
23
- const result = await ni.evaluate(resolved.sessionId, code);
26
+ const result = await ni.evaluate(resolved.sessionId, code, resolved.tabId);
24
27
  if (result.error) return text(`Evaluate failed: ${result.error}`);
25
28
 
26
29
  let msg: string;
@@ -45,6 +48,7 @@ export const browserWaitSchema = {
45
48
  ),
46
49
  timeout: z.coerce.number().optional().describe('Timeout in ms (default: 10000 for element waits, 15000 for loadState).'),
47
50
  sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
51
+ tabId: tabIdField,
48
52
  };
49
53
 
50
54
  export const browserWaitDescription =
@@ -56,8 +60,9 @@ export async function handleBrowserWait(args: {
56
60
  loadState?: string;
57
61
  timeout?: number;
58
62
  sessionId?: string;
63
+ tabId?: string;
59
64
  }) {
60
- const resolved = await ni.resolveSession(args.sessionId);
65
+ const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
61
66
  if ('error' in resolved) return text(resolved.error);
62
67
 
63
68
  const result = await ni.waitFor(resolved.sessionId, {
@@ -65,7 +70,7 @@ export async function handleBrowserWait(args: {
65
70
  state: args.state,
66
71
  loadState: args.loadState,
67
72
  timeout: args.timeout,
68
- });
73
+ }, resolved.tabId);
69
74
  if (result.error) return text(`Wait failed: ${result.error}`);
70
75
 
71
76
  if (args.loadState) {