@darbotlabs/darbot-browser-mcp 0.2.0 → 1.3.0

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 (80) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +222 -161
  3. package/cli.js +1 -1
  4. package/config.d.ts +77 -1
  5. package/index.d.ts +1 -1
  6. package/index.js +1 -1
  7. package/lib/ai/context.js +150 -0
  8. package/lib/ai/guardrails.js +382 -0
  9. package/lib/ai/integration.js +397 -0
  10. package/lib/ai/intent.js +237 -0
  11. package/lib/ai/manualPromise.js +111 -0
  12. package/lib/ai/memory.js +273 -0
  13. package/lib/ai/ml-scorer.js +265 -0
  14. package/lib/ai/orchestrator-tools.js +292 -0
  15. package/lib/ai/orchestrator.js +473 -0
  16. package/lib/ai/planner.js +300 -0
  17. package/lib/ai/reporter.js +493 -0
  18. package/lib/ai/workflow.js +407 -0
  19. package/lib/auth/apiKeyAuth.js +46 -0
  20. package/lib/auth/entraAuth.js +110 -0
  21. package/lib/auth/entraJwtVerifier.js +117 -0
  22. package/lib/auth/index.js +210 -0
  23. package/lib/auth/managedIdentityAuth.js +175 -0
  24. package/lib/auth/mcpOAuthProvider.js +186 -0
  25. package/lib/auth/tunnelAuth.js +120 -0
  26. package/lib/browserContextFactory.js +1 -1
  27. package/lib/browserServer.js +1 -1
  28. package/lib/cdpRelay.js +2 -2
  29. package/lib/common.js +68 -0
  30. package/lib/config.js +62 -3
  31. package/lib/connection.js +1 -1
  32. package/lib/context.js +1 -1
  33. package/lib/fileUtils.js +1 -1
  34. package/lib/guardrails.js +382 -0
  35. package/lib/health.js +178 -0
  36. package/lib/httpServer.js +1 -1
  37. package/lib/index.js +1 -1
  38. package/lib/javascript.js +1 -1
  39. package/lib/manualPromise.js +1 -1
  40. package/lib/memory.js +273 -0
  41. package/lib/openapi.js +373 -0
  42. package/lib/orchestrator.js +473 -0
  43. package/lib/package.js +1 -1
  44. package/lib/pageSnapshot.js +17 -2
  45. package/lib/planner.js +302 -0
  46. package/lib/program.js +17 -5
  47. package/lib/reporter.js +493 -0
  48. package/lib/resources/resource.js +1 -1
  49. package/lib/server.js +5 -3
  50. package/lib/tab.js +1 -1
  51. package/lib/tools/ai-native.js +298 -0
  52. package/lib/tools/autonomous.js +147 -0
  53. package/lib/tools/clock.js +183 -0
  54. package/lib/tools/common.js +1 -1
  55. package/lib/tools/console.js +1 -1
  56. package/lib/tools/diagnostics.js +132 -0
  57. package/lib/tools/dialogs.js +1 -1
  58. package/lib/tools/emulation.js +155 -0
  59. package/lib/tools/files.js +1 -1
  60. package/lib/tools/install.js +1 -1
  61. package/lib/tools/keyboard.js +1 -1
  62. package/lib/tools/navigate.js +1 -1
  63. package/lib/tools/network.js +1 -1
  64. package/lib/tools/pageSnapshot.js +58 -0
  65. package/lib/tools/pdf.js +1 -1
  66. package/lib/tools/profiles.js +76 -25
  67. package/lib/tools/screenshot.js +1 -1
  68. package/lib/tools/scroll.js +93 -0
  69. package/lib/tools/snapshot.js +1 -1
  70. package/lib/tools/storage.js +328 -0
  71. package/lib/tools/tab.js +16 -0
  72. package/lib/tools/tabs.js +1 -1
  73. package/lib/tools/testing.js +1 -1
  74. package/lib/tools/tool.js +1 -1
  75. package/lib/tools/utils.js +1 -1
  76. package/lib/tools/vision.js +1 -1
  77. package/lib/tools/wait.js +1 -1
  78. package/lib/tools.js +22 -1
  79. package/lib/transport.js +251 -31
  80. package/package.json +28 -22
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Copyright (c) DarbotLabs.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { z } from 'zod';
17
+ import { defineTool } from './tool.js';
18
+ /**
19
+ * Enhanced console with filtering - builds on existing console tool
20
+ */
21
+ const consoleFiltered = defineTool({
22
+ capability: 'core',
23
+ schema: {
24
+ name: 'browser_console_filtered',
25
+ title: 'Autonomous filtered console',
26
+ description: 'Autonomously retrieve console messages filtered by type (log, error, warning, info, debug). Useful for focused debugging.',
27
+ inputSchema: z.object({
28
+ type: z.enum(['log', 'error', 'warning', 'info', 'debug', 'all']).optional().default('all').describe('Type of console messages to retrieve'),
29
+ limit: z.number().optional().default(100).describe('Maximum number of messages to return'),
30
+ }),
31
+ type: 'readOnly',
32
+ },
33
+ handle: async (context, params) => {
34
+ const tab = context.currentTabOrDie();
35
+ const messages = tab.consoleMessages();
36
+ let filtered = messages;
37
+ if (params.type && params.type !== 'all') {
38
+ filtered = messages.filter(msg => msg.type === params.type);
39
+ }
40
+ // Apply limit
41
+ filtered = filtered.slice(-params.limit);
42
+ const log = filtered.length > 0
43
+ ? filtered.map(msg => `[${(msg.type || 'unknown').toUpperCase()}] ${msg.text}`).join('\n')
44
+ : `No ${params.type === 'all' ? '' : params.type + ' '}console messages found.`;
45
+ return {
46
+ code: [`// <internal code to get filtered console messages: type=${params.type}, limit=${params.limit}>`],
47
+ action: async () => {
48
+ return {
49
+ content: [{ type: 'text', text: log }]
50
+ };
51
+ },
52
+ captureSnapshot: false,
53
+ waitForNetwork: false,
54
+ };
55
+ },
56
+ });
57
+ /**
58
+ * Performance metrics tool - get performance timing information
59
+ */
60
+ const performanceMetrics = defineTool({
61
+ capability: 'core',
62
+ schema: {
63
+ name: 'browser_performance_metrics',
64
+ title: 'Autonomous performance analysis',
65
+ description: 'Autonomously retrieve performance metrics including page load times, DOM content loaded, and other timing data.',
66
+ inputSchema: z.object({}),
67
+ type: 'readOnly',
68
+ },
69
+ handle: async (context) => {
70
+ const tab = context.currentTabOrDie();
71
+ const code = [
72
+ `// Get performance metrics`,
73
+ `const metrics = await page.evaluate(() => JSON.stringify(performance.timing));`,
74
+ ];
75
+ const action = async () => {
76
+ const metrics = await tab.page.evaluate(() => {
77
+ const timing = performance.timing;
78
+ const navigation = performance.getEntriesByType('navigation')[0];
79
+ return {
80
+ // Core Web Vitals related
81
+ domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart,
82
+ loadComplete: timing.loadEventEnd - timing.navigationStart,
83
+ domInteractive: timing.domInteractive - timing.navigationStart,
84
+ // Network timing
85
+ dnsLookup: timing.domainLookupEnd - timing.domainLookupStart,
86
+ tcpConnection: timing.connectEnd - timing.connectStart,
87
+ serverResponse: timing.responseEnd - timing.requestStart,
88
+ // Additional metrics
89
+ firstByte: timing.responseStart - timing.navigationStart,
90
+ domParsing: timing.domComplete - timing.domLoading,
91
+ // Navigation type
92
+ navigationType: navigation?.type || 'unknown',
93
+ redirectCount: navigation?.redirectCount || 0,
94
+ };
95
+ });
96
+ const output = [
97
+ '=== Performance Metrics ===',
98
+ '',
99
+ '📊 Core Timings:',
100
+ ` • DOM Content Loaded: ${metrics.domContentLoaded}ms`,
101
+ ` • Load Complete: ${metrics.loadComplete}ms`,
102
+ ` • DOM Interactive: ${metrics.domInteractive}ms`,
103
+ '',
104
+ '🌐 Network Timing:',
105
+ ` • DNS Lookup: ${metrics.dnsLookup}ms`,
106
+ ` • TCP Connection: ${metrics.tcpConnection}ms`,
107
+ ` • Server Response: ${metrics.serverResponse}ms`,
108
+ ` • Time to First Byte: ${metrics.firstByte}ms`,
109
+ '',
110
+ '📄 DOM Parsing:',
111
+ ` • DOM Parsing Time: ${metrics.domParsing}ms`,
112
+ '',
113
+ '🔄 Navigation:',
114
+ ` • Type: ${metrics.navigationType}`,
115
+ ` • Redirect Count: ${metrics.redirectCount}`,
116
+ ].join('\n');
117
+ return {
118
+ content: [{ type: 'text', text: output }]
119
+ };
120
+ };
121
+ return {
122
+ code,
123
+ action,
124
+ captureSnapshot: false,
125
+ waitForNetwork: false,
126
+ };
127
+ },
128
+ });
129
+ export default [
130
+ consoleFiltered,
131
+ performanceMetrics,
132
+ ];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Copyright (c) DarbotLabs.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { z } from 'zod';
17
+ import { defineTool } from './tool.js';
18
+ /**
19
+ * Media emulation tools - uses Playwright emulateMedia API
20
+ * Extended support for:
21
+ * - Color scheme emulation (light/dark)
22
+ * - Reduced motion emulation
23
+ * - Contrast emulation (v1.51+)
24
+ * - Print/screen media
25
+ */
26
+ const emulateMedia = captureSnapshot => defineTool({
27
+ capability: 'core',
28
+ schema: {
29
+ name: 'browser_emulate_media',
30
+ title: 'Autonomous media emulation',
31
+ description: 'Autonomously emulate media features like color scheme, reduced motion, contrast preference, and media type for accessibility and responsive testing.',
32
+ inputSchema: z.object({
33
+ colorScheme: z.enum(['light', 'dark', 'no-preference', 'null']).optional().describe('Emulate color scheme preference: light, dark, no-preference, or null to reset'),
34
+ reducedMotion: z.enum(['reduce', 'no-preference', 'null']).optional().describe('Emulate prefers-reduced-motion: reduce, no-preference, or null to reset'),
35
+ contrast: z.enum(['more', 'less', 'no-preference', 'null']).optional().describe('Emulate prefers-contrast: more, less, no-preference, or null to reset'),
36
+ media: z.enum(['screen', 'print', 'null']).optional().describe('Emulate media type: screen, print, or null to reset'),
37
+ forcedColors: z.enum(['active', 'none', 'null']).optional().describe('Emulate forced-colors: active, none, or null to reset'),
38
+ }),
39
+ type: 'readOnly',
40
+ },
41
+ handle: async (context, params) => {
42
+ const tab = context.currentTabOrDie();
43
+ const options = {};
44
+ if (params.colorScheme !== undefined)
45
+ options.colorScheme = params.colorScheme === 'null' ? null : params.colorScheme;
46
+ if (params.reducedMotion !== undefined)
47
+ options.reducedMotion = params.reducedMotion === 'null' ? null : params.reducedMotion;
48
+ if (params.contrast !== undefined)
49
+ options.contrast = params.contrast === 'null' ? null : params.contrast;
50
+ if (params.media !== undefined)
51
+ options.media = params.media === 'null' ? null : params.media;
52
+ if (params.forcedColors !== undefined)
53
+ options.forcedColors = params.forcedColors === 'null' ? null : params.forcedColors;
54
+ const optionsStr = Object.entries(options)
55
+ .filter(([_, v]) => v !== undefined)
56
+ .map(([k, v]) => `${k}: ${v === null ? 'null' : `'${v}'`}`)
57
+ .join(', ');
58
+ const code = [
59
+ `// Emulate media features: ${optionsStr}`,
60
+ `await page.emulateMedia({ ${optionsStr} });`,
61
+ ];
62
+ const action = async () => {
63
+ await tab.page.emulateMedia(options);
64
+ };
65
+ return {
66
+ code,
67
+ action,
68
+ captureSnapshot,
69
+ waitForNetwork: false,
70
+ };
71
+ },
72
+ });
73
+ /**
74
+ * Geolocation emulation
75
+ */
76
+ const emulateGeolocation = captureSnapshot => defineTool({
77
+ capability: 'core',
78
+ schema: {
79
+ name: 'browser_emulate_geolocation',
80
+ title: 'Autonomous geolocation emulation',
81
+ description: 'Autonomously emulate a geographic location for location-based testing.',
82
+ inputSchema: z.object({
83
+ latitude: z.number().min(-90).max(90).describe('Latitude between -90 and 90'),
84
+ longitude: z.number().min(-180).max(180).describe('Longitude between -180 and 180'),
85
+ accuracy: z.number().optional().describe('Accuracy in meters. Defaults to 0.'),
86
+ }),
87
+ type: 'destructive',
88
+ },
89
+ handle: async (context, params) => {
90
+ const tab = context.currentTabOrDie();
91
+ const browserContext = tab.page.context();
92
+ const code = [
93
+ `// Emulate geolocation: ${params.latitude}, ${params.longitude}`,
94
+ `await context.setGeolocation({ latitude: ${params.latitude}, longitude: ${params.longitude}${params.accuracy ? `, accuracy: ${params.accuracy}` : ''} });`,
95
+ ];
96
+ const action = async () => {
97
+ await browserContext.setGeolocation({
98
+ latitude: params.latitude,
99
+ longitude: params.longitude,
100
+ accuracy: params.accuracy,
101
+ });
102
+ };
103
+ return {
104
+ code,
105
+ action,
106
+ captureSnapshot,
107
+ waitForNetwork: false,
108
+ };
109
+ },
110
+ });
111
+ /**
112
+ * Timezone emulation
113
+ */
114
+ const emulateTimezone = captureSnapshot => defineTool({
115
+ capability: 'core',
116
+ schema: {
117
+ name: 'browser_emulate_timezone',
118
+ title: 'Autonomous timezone emulation',
119
+ description: 'Autonomously change the browser timezone for testing time-sensitive features.',
120
+ inputSchema: z.object({
121
+ timezoneId: z.string().describe('Timezone ID (e.g., "America/New_York", "Europe/London", "Asia/Tokyo")'),
122
+ }),
123
+ type: 'destructive',
124
+ },
125
+ handle: async (context, params) => {
126
+ const tab = context.currentTabOrDie();
127
+ const browserContext = tab.page.context();
128
+ const code = [
129
+ `// Emulate timezone: ${params.timezoneId}`,
130
+ `await context.setDefaultTimezone('${params.timezoneId}');`,
131
+ ];
132
+ // Note: Playwright doesn't have setDefaultTimezone - we need to use emulateTimezone differently
133
+ // This would need to be set at context creation time, so we'll add JavaScript injection instead
134
+ const action = async () => {
135
+ await browserContext.addInitScript(`
136
+ const originalDateTimeFormat = Intl.DateTimeFormat;
137
+ Intl.DateTimeFormat = function(locale, options = {}) {
138
+ options.timeZone = '${params.timezoneId}';
139
+ return new originalDateTimeFormat(locale, options);
140
+ };
141
+ `);
142
+ };
143
+ return {
144
+ code,
145
+ action,
146
+ captureSnapshot,
147
+ waitForNetwork: false,
148
+ };
149
+ },
150
+ });
151
+ export default (captureSnapshot) => [
152
+ emulateMedia(captureSnapshot),
153
+ emulateGeolocation(captureSnapshot),
154
+ emulateTimezone(captureSnapshot),
155
+ ];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Copyright (c) DarbotLabs.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { callOnPageNoTrace } from './utils.js';
17
+ export class PageSnapshot {
18
+ _page;
19
+ _text;
20
+ constructor(page) {
21
+ this._page = page;
22
+ }
23
+ static async create(page) {
24
+ const snapshot = new PageSnapshot(page);
25
+ await snapshot._build();
26
+ return snapshot;
27
+ }
28
+ text() {
29
+ return this._text;
30
+ }
31
+ async _build() {
32
+ const snapshotResult = await callOnPageNoTrace(this._page, page => page._snapshotForAI());
33
+ // Handle both old (string) and new (object with full property) Playwright snapshot formats
34
+ let snapshot;
35
+ if (typeof snapshotResult === 'string') {
36
+ snapshot = snapshotResult;
37
+ }
38
+ else if (snapshotResult && typeof snapshotResult === 'object') {
39
+ // Try different known property names for the snapshot text
40
+ snapshot = snapshotResult.full
41
+ ?? snapshotResult.text
42
+ ?? snapshotResult.snapshot
43
+ ?? JSON.stringify(snapshotResult, null, 2);
44
+ }
45
+ else {
46
+ snapshot = String(snapshotResult);
47
+ }
48
+ this._text = [
49
+ `- Page Snapshot`,
50
+ '```yaml',
51
+ snapshot,
52
+ '```',
53
+ ].join('\n');
54
+ }
55
+ refLocator(params) {
56
+ return this._page.locator(`aria-ref=${params.ref}`).describe(params.element);
57
+ }
58
+ }
package/lib/tools/pdf.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -20,15 +20,15 @@ import { z } from 'zod';
20
20
  import { defineTool } from './tool.js';
21
21
  import { sanitizeForFilePath } from './utils.js';
22
22
  const saveProfileSchema = z.object({
23
- name: z.string().describe('Name for the work profile'),
24
- description: z.string().optional().describe('Optional description for the work profile'),
23
+ name: z.string().describe('Name for the session state'),
24
+ description: z.string().optional().describe('Optional description for the session state'),
25
25
  });
26
26
  const switchProfileSchema = z.object({
27
- name: z.string().describe('Name of the work profile to switch to'),
27
+ name: z.string().describe('Name of the session state to restore'),
28
28
  });
29
29
  const listProfilesSchema = z.object({});
30
30
  const deleteProfileSchema = z.object({
31
- name: z.string().describe('Name of the work profile to delete'),
31
+ name: z.string().describe('Name of the session state to delete'),
32
32
  });
33
33
  async function getProfilesDir() {
34
34
  let profilesDir;
@@ -40,7 +40,7 @@ async function getProfilesDir() {
40
40
  profilesDir = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
41
41
  else
42
42
  throw new Error('Unsupported platform: ' + process.platform);
43
- const result = path.join(profilesDir, 'darbot-browser-mcp', 'work-profiles');
43
+ const result = path.join(profilesDir, 'darbot-browser-mcp', 'session-states');
44
44
  await fs.promises.mkdir(result, { recursive: true });
45
45
  return result;
46
46
  }
@@ -53,8 +53,27 @@ async function saveCurrentProfile(context, profileName, description) {
53
53
  const tab = context.currentTabOrDie();
54
54
  const url = tab.page.url();
55
55
  const title = await tab.title();
56
- // Save profile metadata
56
+ // Detect Edge profile info from environment/config
57
+ const edgeProfile = process.env.DARBOT_EDGE_PROFILE || 'default';
58
+ const edgeProfileEmail = process.env.DARBOT_EDGE_PROFILE_EMAIL || undefined;
59
+ const workspacePath = process.env.VSCODE_WORKSPACE_FOLDER || process.env.PWD || undefined;
60
+ const workspaceName = workspacePath ? path.basename(workspacePath) : undefined;
61
+ // Save session state metadata with unified header
57
62
  const profileData = {
63
+ // Unified header
64
+ version: '2.0',
65
+ type: 'darbot-session-state',
66
+ // Edge profile context
67
+ edgeProfile: {
68
+ name: edgeProfile,
69
+ email: edgeProfileEmail,
70
+ },
71
+ // VS Code workspace context (if launched from VS Code)
72
+ workspace: workspacePath ? {
73
+ path: workspacePath,
74
+ name: workspaceName,
75
+ } : undefined,
76
+ // Session state details
58
77
  name: profileName,
59
78
  description: description || '',
60
79
  created: new Date().toISOString(),
@@ -82,7 +101,7 @@ async function loadProfile(context, profileName) {
82
101
  await fs.promises.access(profileDir);
83
102
  }
84
103
  catch {
85
- throw new Error(`Work profile "${profileName}" not found`);
104
+ throw new Error(`Session state "${profileName}" not found`);
86
105
  }
87
106
  // Load profile metadata
88
107
  const profileDataPath = path.join(profileDir, 'profile.json');
@@ -95,9 +114,8 @@ async function loadProfile(context, profileName) {
95
114
  // Create new context with the stored state
96
115
  const tab = await context.ensureTab();
97
116
  const currentContext = tab.page.context();
98
- if (currentContext) {
117
+ if (currentContext)
99
118
  await currentContext.close();
100
- }
101
119
  const newContext = await tab.page.context().browser()?.newContext({
102
120
  storageState,
103
121
  viewport: null,
@@ -151,7 +169,7 @@ async function deleteProfile(profileName) {
151
169
  await fs.promises.access(profileDir);
152
170
  }
153
171
  catch {
154
- throw new Error(`Work profile "${profileName}" not found`);
172
+ throw new Error(`Session state "${profileName}" not found`);
155
173
  }
156
174
  await fs.promises.rm(profileDir, { recursive: true, force: true });
157
175
  }
@@ -159,13 +177,25 @@ export const browserSaveProfile = defineTool({
159
177
  capability: 'core',
160
178
  schema: {
161
179
  name: 'browser_save_profile',
162
- title: 'Autonomous profile saving',
163
- description: 'Autonomously save the current browser state as a reusable work profile for later restoration',
180
+ title: 'Save session state',
181
+ description: 'Save the current browser session state (cookies, localStorage, URL) for later restoration. Includes Edge profile and VS Code workspace context.',
164
182
  inputSchema: saveProfileSchema,
165
183
  type: 'destructive',
166
184
  },
167
185
  handle: async (context, { name, description }) => {
168
186
  const profileData = await saveCurrentProfile(context, name, description);
187
+ let text = `Session state "${name}" saved successfully.\n\n`;
188
+ text += `### Session State Details\n`;
189
+ text += `- **Name:** ${profileData.name}\n`;
190
+ if (profileData.description)
191
+ text += `- **Description:** ${profileData.description}\n`;
192
+ text += `- **URL:** ${profileData.url}\n`;
193
+ text += `- **Title:** ${profileData.title}\n`;
194
+ text += `- **Created:** ${profileData.created}\n\n`;
195
+ text += `### Context\n`;
196
+ text += `- **Edge Profile:** ${profileData.edgeProfile.name}${profileData.edgeProfile.email ? ` (${profileData.edgeProfile.email})` : ''}\n`;
197
+ if (profileData.workspace)
198
+ text += `- **VS Code Workspace:** ${profileData.workspace.name} (${profileData.workspace.path})\n`;
169
199
  return {
170
200
  code: [`await browser_save_profile({ name: '${name}', description: '${description || ''}' })`],
171
201
  action: async () => ({ content: [] }),
@@ -174,7 +204,7 @@ export const browserSaveProfile = defineTool({
174
204
  resultOverride: {
175
205
  content: [{
176
206
  type: 'text',
177
- text: `Work profile "${name}" saved successfully.\n\nProfile details:\n- Name: ${profileData.name}\n- Description: ${profileData.description}\n- URL: ${profileData.url}\n- Title: ${profileData.title}\n- Created: ${profileData.created}`,
207
+ text,
178
208
  }],
179
209
  },
180
210
  };
@@ -184,13 +214,28 @@ export const browserSwitchProfile = defineTool({
184
214
  capability: 'core',
185
215
  schema: {
186
216
  name: 'browser_switch_profile',
187
- title: 'Autonomous profile switching',
188
- description: 'Autonomously switch to a previously saved work profile, restoring browser state and session',
217
+ title: 'Restore session state',
218
+ description: 'Restore a previously saved session state, including cookies, localStorage, and navigate to the saved URL',
189
219
  inputSchema: switchProfileSchema,
190
220
  type: 'destructive',
191
221
  },
192
222
  handle: async (context, { name }) => {
193
223
  const result = await loadProfile(context, name);
224
+ const pd = result.profileData;
225
+ let text = `Session state "${name}" restored.\n\n`;
226
+ text += `### Session State Details\n`;
227
+ text += `- **Name:** ${pd.name}\n`;
228
+ if (pd.description)
229
+ text += `- **Description:** ${pd.description}\n`;
230
+ text += `- **URL:** ${pd.url}\n`;
231
+ text += `- **Title:** ${pd.title}\n`;
232
+ text += `- **Storage:** ${result.restored ? 'Fully restored' : 'URL only (storage not available)'}\n\n`;
233
+ if (pd.edgeProfile) {
234
+ text += `### Original Context\n`;
235
+ text += `- **Edge Profile:** ${pd.edgeProfile.name}${pd.edgeProfile.email ? ` (${pd.edgeProfile.email})` : ''}\n`;
236
+ }
237
+ if (pd.workspace)
238
+ text += `- **VS Code Workspace:** ${pd.workspace.name}\n`;
194
239
  return {
195
240
  code: [`await browser_switch_profile({ name: '${name}' })`],
196
241
  action: async () => ({ content: [] }),
@@ -199,7 +244,7 @@ export const browserSwitchProfile = defineTool({
199
244
  resultOverride: {
200
245
  content: [{
201
246
  type: 'text',
202
- text: `Switched to work profile "${name}".\n\nProfile details:\n- Name: ${result.profileData.name}\n- Description: ${result.profileData.description}\n- URL: ${result.profileData.url}\n- Title: ${result.profileData.title}\n- Storage state ${result.restored ? 'restored' : 'not available'}`,
247
+ text,
203
248
  }],
204
249
  },
205
250
  };
@@ -209,16 +254,16 @@ export const browserListProfiles = defineTool({
209
254
  capability: 'core',
210
255
  schema: {
211
256
  name: 'browser_list_profiles',
212
- title: 'Autonomous profile listing',
213
- description: 'Autonomously list all saved work profiles with their details and creation information',
257
+ title: 'List session states',
258
+ description: 'List all saved Darbot session states with their Edge profile context and workspace information',
214
259
  inputSchema: listProfilesSchema,
215
260
  type: 'readOnly',
216
261
  },
217
262
  handle: async (context, {}) => {
218
263
  const profiles = await listProfiles();
219
- let text = '### Saved Work Profiles\n\n';
264
+ let text = '### Saved Darbot Session States\n\n';
220
265
  if (profiles.length === 0) {
221
- text += 'No work profiles saved yet. Use the "browser_save_profile" tool to save your current browser state as a work profile.';
266
+ text += 'No session states saved yet. Use the "browser_save_profile" tool to save your current browser session state.';
222
267
  }
223
268
  else {
224
269
  for (const profile of profiles) {
@@ -227,7 +272,13 @@ export const browserListProfiles = defineTool({
227
272
  text += `- Description: ${profile.description}\n`;
228
273
  text += `- URL: ${profile.url}\n`;
229
274
  text += `- Title: ${profile.title}\n`;
230
- text += `- Created: ${new Date(profile.created).toLocaleString()}\n\n`;
275
+ text += `- Created: ${new Date(profile.created).toLocaleString()}\n`;
276
+ // Show Edge profile context if available (v2.0+ session states)
277
+ if (profile.edgeProfile)
278
+ text += `- Edge Profile: ${profile.edgeProfile.name}${profile.edgeProfile.email ? ` (${profile.edgeProfile.email})` : ''}\n`;
279
+ if (profile.workspace)
280
+ text += `- Workspace: ${profile.workspace.name}\n`;
281
+ text += '\n';
231
282
  }
232
283
  }
233
284
  return {
@@ -248,8 +299,8 @@ export const browserDeleteProfile = defineTool({
248
299
  capability: 'core',
249
300
  schema: {
250
301
  name: 'browser_delete_profile',
251
- title: 'Autonomous profile deletion',
252
- description: 'Autonomously delete a saved work profile permanently from storage',
302
+ title: 'Delete session state',
303
+ description: 'Permanently delete a saved session state from storage',
253
304
  inputSchema: deleteProfileSchema,
254
305
  type: 'destructive',
255
306
  },
@@ -263,7 +314,7 @@ export const browserDeleteProfile = defineTool({
263
314
  resultOverride: {
264
315
  content: [{
265
316
  type: 'text',
266
- text: `Work profile "${name}" deleted successfully.`,
317
+ text: `Session state "${name}" deleted successfully.`,
267
318
  }],
268
319
  },
269
320
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.