nudge-ui 0.2.0 → 0.2.2

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 (231) hide show
  1. package/bin/nudge-ui.mjs +3 -1
  2. package/dist/cli/agentCommand.d.ts +8 -0
  3. package/dist/cli/agentCommand.js +59 -0
  4. package/dist/client.mjs +878 -11
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
  6. package/dist/hosts/astro/clientTransport.js +63 -11
  7. package/dist/hosts/astro/integration.d.ts +2 -2
  8. package/dist/hosts/astro/integration.js +4 -3
  9. package/dist/hosts/environment.d.ts +15 -0
  10. package/dist/hosts/environment.js +16 -0
  11. package/dist/hosts/next/sidecar.d.ts +4 -0
  12. package/dist/hosts/next/sidecar.js +56 -7
  13. package/dist/hosts/next/wrapper.d.ts +2 -0
  14. package/dist/hosts/next/wrapper.js +32 -3
  15. package/dist/hosts/projectBridge.d.ts +21 -0
  16. package/dist/hosts/projectBridge.js +98 -0
  17. package/dist/hosts/static/cli.d.ts +1 -1
  18. package/dist/hosts/static/cli.js +6 -1
  19. package/dist/hosts/static/manifest.d.ts +1 -1
  20. package/dist/hosts/static/manifest.js +3 -1
  21. package/dist/hosts/static/server.js +45 -3
  22. package/dist/hosts/vite/vite.d.ts +6 -1
  23. package/dist/hosts/vite/vite.js +82 -15
  24. package/dist/inspector/agent/McpConnectionDialog.d.ts +5 -4
  25. package/dist/inspector/agent/McpConnectionDialog.js +20 -37
  26. package/dist/inspector/agent/client.d.ts +16 -3
  27. package/dist/inspector/agent/client.js +114 -11
  28. package/dist/inspector/agent/connectionStatus.d.ts +3 -3
  29. package/dist/inspector/agent/connectionStatus.js +11 -4
  30. package/dist/inspector/agent/httpTransport.d.ts +8 -0
  31. package/dist/inspector/agent/httpTransport.js +78 -18
  32. package/dist/inspector/agent/protocol.d.ts +11 -3
  33. package/dist/inspector/agent/protocol.js +1 -1
  34. package/dist/inspector/agent/verification.d.ts +1 -1
  35. package/dist/inspector/agent/verification.js +69 -12
  36. package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
  37. package/dist/inspector/canvas/CanvasCard.js +181 -91
  38. package/dist/inspector/canvas/CanvasElementOverlay.js +288 -52
  39. package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
  40. package/dist/inspector/canvas/CanvasToolbar.js +24 -0
  41. package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
  42. package/dist/inspector/canvas/CanvasWorkspace.js +410 -72
  43. package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
  44. package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
  45. package/dist/inspector/canvas/canvasResize.d.ts +30 -0
  46. package/dist/inspector/canvas/canvasResize.js +52 -0
  47. package/dist/inspector/canvas/canvasStore.d.ts +30 -6
  48. package/dist/inspector/canvas/canvasStore.js +128 -61
  49. package/dist/inspector/canvas/frameProtocol.d.ts +82 -5
  50. package/dist/inspector/canvas/frameProtocol.js +63 -2
  51. package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -0
  52. package/dist/inspector/canvas/keyboardShortcuts.js +16 -0
  53. package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
  54. package/dist/inspector/canvas/linkEligibility.js +0 -29
  55. package/dist/inspector/canvas/projection.d.ts +7 -0
  56. package/dist/inspector/canvas/projection.js +118 -25
  57. package/dist/inspector/canvas/rendererBootstrap.js +42 -29
  58. package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
  59. package/dist/inspector/canvas/rendererElementSelector.js +385 -58
  60. package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
  61. package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
  62. package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
  63. package/dist/inspector/canvas/sessionStore.d.ts +1 -0
  64. package/dist/inspector/canvas/sessionStore.js +12 -4
  65. package/dist/inspector/canvas/staleChangeDetector.js +16 -14
  66. package/dist/inspector/changes/changesLog.js +31 -1
  67. package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
  68. package/dist/inspector/changes/managedStyleProjection.js +3 -3
  69. package/dist/inspector/changes/structuralTypes.d.ts +4 -0
  70. package/dist/inspector/changes/workspaceChanges.d.ts +4 -0
  71. package/dist/inspector/changes/workspaceChanges.js +29 -3
  72. package/dist/inspector/clientManifest.d.ts +7 -0
  73. package/dist/inspector/clientManifest.js +12 -0
  74. package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
  75. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
  76. package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
  77. package/dist/inspector/index.d.ts +2 -0
  78. package/dist/inspector/index.js +80 -32
  79. package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
  80. package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
  81. package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
  82. package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
  83. package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
  84. package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
  85. package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
  86. package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
  87. package/dist/inspector/inline-text/textEditChange.js +165 -0
  88. package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
  89. package/dist/inspector/inspection/selectionProperty.js +1 -0
  90. package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
  91. package/dist/inspector/overlay/DomNavigator.js +40 -0
  92. package/dist/inspector/overlay/InspectorOverlay.js +6 -2
  93. package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
  94. package/dist/inspector/overlay/clickPolicy.js +34 -3
  95. package/dist/inspector/overlay/elementSelector.d.ts +1 -1
  96. package/dist/inspector/overlay/elementSelector.js +18 -7
  97. package/dist/inspector/overlay/selectedGeometry.js +21 -2
  98. package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
  99. package/dist/inspector/overlay/spacingGestures.js +351 -0
  100. package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
  101. package/dist/inspector/overlay/structuralGestures.js +12 -10
  102. package/dist/inspector/projection/managedStylesheet.js +4 -1
  103. package/dist/inspector/projection/renderedInstance.d.ts +2 -0
  104. package/dist/inspector/projection/renderedInstance.js +5 -1
  105. package/dist/inspector/projection/structuralProjection.d.ts +2 -0
  106. package/dist/inspector/projection/structuralProjection.js +85 -1
  107. package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
  108. package/dist/inspector/projection/textProjection.d.ts +2 -0
  109. package/dist/inspector/projection/textProjection.js +10 -4
  110. package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
  111. package/dist/inspector/prompt/clipboardHandoff.js +3 -3
  112. package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
  113. package/dist/inspector/prompt/generatePrompt.js +19 -2
  114. package/dist/inspector/runtime/editorShell.d.ts +2 -0
  115. package/dist/inspector/runtime/editorShell.js +4 -0
  116. package/dist/inspector/runtime/runtimeConfig.d.ts +5 -1
  117. package/dist/inspector/runtime/runtimeConfig.js +4 -0
  118. package/dist/inspector/selection/hierarchy.d.ts +6 -0
  119. package/dist/inspector/selection/hierarchy.js +8 -0
  120. package/dist/inspector/selection/selectionStore.js +2 -0
  121. package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
  122. package/dist/inspector/settings/SettingsDialog.js +12 -2
  123. package/dist/inspector/shell/AppShell.js +5 -1
  124. package/dist/inspector/shell/ChangesLog.js +26 -22
  125. package/dist/inspector/shell/CopyPromptButton.js +102 -19
  126. package/dist/inspector/shell/EmptyState.js +1 -1
  127. package/dist/inspector/shell/InspectorShell.d.ts +0 -1
  128. package/dist/inspector/shell/InspectorShell.js +38 -42
  129. package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
  130. package/dist/inspector/shell/PeekOriginalButton.js +57 -0
  131. package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
  132. package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
  133. package/dist/inspector/shell/originalPreview.d.ts +9 -0
  134. package/dist/inspector/shell/originalPreview.js +61 -0
  135. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  136. package/dist/inspector/shell/shortcuts.js +15 -0
  137. package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
  138. package/dist/inspector/sketch/SketchChanges.js +72 -0
  139. package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
  140. package/dist/inspector/sketch/SketchEditor.js +402 -0
  141. package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
  142. package/dist/inspector/sketch/SketchEntryButton.js +11 -0
  143. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
  144. package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
  145. package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
  146. package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
  147. package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
  148. package/dist/inspector/sketch/SketchOverlay.js +420 -0
  149. package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
  150. package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
  151. package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
  152. package/dist/inspector/sketch/SketchWorkspace.js +152 -0
  153. package/dist/inspector/sketch/capture.d.ts +55 -0
  154. package/dist/inspector/sketch/capture.js +350 -0
  155. package/dist/inspector/sketch/freehand.d.ts +13 -0
  156. package/dist/inspector/sketch/freehand.js +64 -0
  157. package/dist/inspector/sketch/geometry.d.ts +17 -0
  158. package/dist/inspector/sketch/geometry.js +98 -0
  159. package/dist/inspector/sketch/handoff.d.ts +21 -0
  160. package/dist/inspector/sketch/handoff.js +72 -0
  161. package/dist/inspector/sketch/interaction.d.ts +21 -0
  162. package/dist/inspector/sketch/interaction.js +98 -0
  163. package/dist/inspector/sketch/model.d.ts +104 -0
  164. package/dist/inspector/sketch/model.js +282 -0
  165. package/dist/inspector/sketch/persistence.d.ts +21 -0
  166. package/dist/inspector/sketch/persistence.js +205 -0
  167. package/dist/inspector/sketch/raster.d.ts +22 -0
  168. package/dist/inspector/sketch/raster.js +203 -0
  169. package/dist/inspector/sketch/sketchNote.d.ts +10 -0
  170. package/dist/inspector/sketch/sketchNote.js +30 -0
  171. package/dist/inspector/sketch/store.d.ts +47 -0
  172. package/dist/inspector/sketch/store.js +434 -0
  173. package/dist/inspector/styleEditors/BorderEditor.js +15 -19
  174. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
  175. package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
  176. package/dist/inspector/styleEditors/ColorPicker.js +11 -36
  177. package/dist/inspector/styleEditors/GapField.d.ts +3 -2
  178. package/dist/inspector/styleEditors/GapField.js +2 -2
  179. package/dist/inspector/styleEditors/GridSection.js +1 -1
  180. package/dist/inspector/styleEditors/LayoutSection.js +11 -8
  181. package/dist/inspector/styleEditors/SpacingBox.js +31 -33
  182. package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
  183. package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
  184. package/dist/inspector/styleEditors/layoutValue.js +3 -0
  185. package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
  186. package/dist/inspector/styleEditors/nudgeValue.js +93 -5
  187. package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
  188. package/dist/inspector/styleEditors/stylePresence.js +22 -0
  189. package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
  190. package/dist/inspector/styleEditors/textRelevance.js +35 -0
  191. package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
  192. package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
  193. package/dist/inspector/tokens/TokenField.js +130 -7
  194. package/dist/inspector/tokens/TokensPanel.js +2 -1
  195. package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
  196. package/dist/inspector/tokens/dragPointerLock.js +70 -0
  197. package/dist/inspector/tokens/editActions.d.ts +2 -0
  198. package/dist/inspector/tokens/editActions.js +2 -2
  199. package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
  200. package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
  201. package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
  202. package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
  203. package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
  204. package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
  205. package/dist/inspector/tokens/resolution.d.ts +3 -26
  206. package/dist/inspector/tokens/resolution.js +44 -735
  207. package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
  208. package/dist/inspector/tokens/tokenAvailability.js +204 -0
  209. package/dist/inspector/ui/Button.d.ts +1 -1
  210. package/dist/inspector/ui/Disclosure.d.ts +22 -0
  211. package/dist/inspector/ui/Disclosure.js +13 -0
  212. package/dist/inspector/ui/SideValuesField.js +2 -2
  213. package/dist/inspector/ui/StatusCallout.d.ts +4 -1
  214. package/dist/inspector/ui/StatusCallout.js +2 -2
  215. package/dist/inspector/ui/dragCursor.d.ts +8 -0
  216. package/dist/inspector/ui/dragCursor.js +21 -0
  217. package/dist/inspector/ui/styles.js +21 -17
  218. package/dist/internal/agent-protocol.d.ts +65 -1
  219. package/dist/internal/agent-protocol.js +183 -3
  220. package/dist/nudge-ui.mjs +276 -58
  221. package/dist/transport/editor.d.ts +47 -0
  222. package/dist/transport/editor.js +163 -0
  223. package/dist/transport/index.d.ts +1 -0
  224. package/dist/transport/index.js +1 -0
  225. package/dist/transport/routes.d.ts +10 -0
  226. package/dist/transport/routes.js +10 -0
  227. package/package.json +3 -1
  228. package/dist/inspector/shell/DomNavigation.d.ts +0 -8
  229. package/dist/inspector/shell/DomNavigation.js +0 -30
  230. package/dist/inspector/shell/panelLayout.d.ts +0 -2
  231. package/dist/inspector/shell/panelLayout.js +0 -29
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useMemo, useSyncExternalStore } from "react";
2
2
  import { isNudgeUiDev } from "../runtime/devFlag.js";
3
3
  import { isDemoRuntime } from "../runtime/runtimeConfig.js";
4
- import { HttpAgentBridgeTransport } from "./httpTransport.js";
4
+ import { AgentBridgeHttpError, HttpAgentBridgeTransport, shouldAutoConnectAgentBridge, } from "./httpTransport.js";
5
5
  import { AGENT_PROTOCOL_VERSION, } from "./protocol.js";
6
6
  const DISABLED_SNAPSHOT = Object.freeze({
7
7
  state: "disabled",
@@ -9,10 +9,12 @@ const DISABLED_SNAPSHOT = Object.freeze({
9
9
  listenerActive: false,
10
10
  companionReachable: false,
11
11
  paired: false,
12
+ pairedElsewhere: false,
12
13
  request: null,
13
14
  });
14
15
  const DEFAULT_DISCOVERY_INTERVAL_MS = 2_000;
15
16
  const SESSION_STORAGE_PREFIX = "nudge-ui-agent-session:";
17
+ const AUTO_CONNECT_DISABLED_PREFIX = "nudge-ui-agent-auto-connect-disabled:";
16
18
  function randomId(prefix) {
17
19
  const random = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
18
20
  ? crypto.randomUUID()
@@ -68,6 +70,31 @@ function clearStoredSession(projectId) {
68
70
  // Ignore storage failures; disconnect still clears the live connection.
69
71
  }
70
72
  }
73
+ function autoConnectDisabled(projectId) {
74
+ if (typeof localStorage === "undefined")
75
+ return false;
76
+ try {
77
+ return localStorage.getItem(`${AUTO_CONNECT_DISABLED_PREFIX}${encodeURIComponent(projectId)}`) === "true";
78
+ }
79
+ catch {
80
+ return false;
81
+ }
82
+ }
83
+ function setAutoConnectDisabled(projectId, disabled) {
84
+ if (typeof localStorage === "undefined")
85
+ return;
86
+ try {
87
+ const key = `${AUTO_CONNECT_DISABLED_PREFIX}${encodeURIComponent(projectId)}`;
88
+ if (disabled)
89
+ localStorage.setItem(key, "true");
90
+ else
91
+ localStorage.removeItem(key);
92
+ }
93
+ catch {
94
+ // Privacy settings can disable storage. The current document still keeps
95
+ // the explicit disconnect preference in memory.
96
+ }
97
+ }
71
98
  function statusForConnection(status) {
72
99
  if (status.connection === "working" || status.request?.status === "working")
73
100
  return "working";
@@ -90,10 +117,10 @@ function hashRevision(value) {
90
117
  return (hash >>> 0) & 0x7fff_ffff;
91
118
  }
92
119
  /** Creates a stable numeric revision for one prompt snapshot. */
93
- export function createPromptRevision(changes, structuralChanges = []) {
120
+ export function createPromptRevision(changes, structuralChanges = [], sketches = []) {
94
121
  let serialized;
95
122
  try {
96
- serialized = JSON.stringify({ changes, structuralChanges }) ?? "";
123
+ serialized = JSON.stringify({ changes, structuralChanges, sketches }) ?? "";
97
124
  }
98
125
  catch {
99
126
  serialized = `${changes.length}:${structuralChanges.length}`;
@@ -128,13 +155,18 @@ export class AgentClient {
128
155
  listenerActive = false;
129
156
  companionReachable = false;
130
157
  paired = false;
158
+ pairedElsewhere = false;
131
159
  lastError;
160
+ autoConnect;
161
+ autoConnectSuppressed = false;
132
162
  constructor(options) {
133
163
  this.projectId = options.projectId;
134
164
  this.origin = options.origin ?? (typeof window !== "undefined" ? window.location.origin : "http://localhost");
135
165
  this.transport = options.transport ?? new HttpAgentBridgeTransport(options.endpoint);
136
166
  this.discoveryIntervalMs = options.discoveryIntervalMs ?? DEFAULT_DISCOVERY_INTERVAL_MS;
137
167
  this.canvasCommandHandler = options.canvasCommandHandler;
168
+ this.autoConnect = options.autoConnect ?? shouldAutoConnectAgentBridge();
169
+ this.autoConnectSuppressed = this.autoConnect && autoConnectDisabled(this.projectId);
138
170
  // The public demo renders the real inspector but intentionally keeps the
139
171
  // local MCP/agent bridge inert.
140
172
  this.enabled = isNudgeUiDev() && !isDemoRuntime();
@@ -181,6 +213,7 @@ export class AgentClient {
181
213
  this.listenerActive = false;
182
214
  this.companionReachable = false;
183
215
  this.paired = false;
216
+ this.pairedElsewhere = false;
184
217
  this.publish();
185
218
  }
186
219
  async restoreOrDiscover() {
@@ -261,14 +294,26 @@ export class AgentClient {
261
294
  this.listenerActive = false;
262
295
  this.companionReachable = false;
263
296
  this.paired = false;
297
+ this.pairedElsewhere = false;
264
298
  this.lastError = undefined;
265
299
  this.publish();
266
300
  void this.restoreOrDiscover();
267
301
  }
268
302
  /** Pairs the browser with a reachable companion, including an idle agent. */
269
303
  async connect() {
304
+ return this.pairWith((request, signal) => this.transport.pair(request, signal));
305
+ }
306
+ /** Replaces an existing browser pairing after an explicit user action. */
307
+ async takeOver() {
308
+ if (!this.transport.takeOver)
309
+ return false;
310
+ return this.pairWith((request, signal) => this.transport.takeOver(request, signal));
311
+ }
312
+ async pairWith(pair) {
270
313
  if (!this.enabled || !this.started || this.pairingAbort || this.paired || !this.companionReachable)
271
314
  return false;
315
+ this.autoConnectSuppressed = false;
316
+ setAutoConnectDisabled(this.projectId, false);
272
317
  if (this.connection === "working" || this.activeRequest?.status === "working")
273
318
  return false;
274
319
  const idleListenerActive = this.listenerActive;
@@ -285,7 +330,7 @@ export class AgentClient {
285
330
  this.discoveryAbort?.abort();
286
331
  this.pairingAbort = abortController;
287
332
  try {
288
- const pairing = await this.transport.pair(request, abortController.signal);
333
+ const pairing = await pair(request, abortController.signal);
289
334
  if (!this.started || abortController.signal.aborted)
290
335
  return false;
291
336
  if (pairing.protocolVersion !== undefined && pairing.protocolVersion !== AGENT_PROTOCOL_VERSION) {
@@ -301,6 +346,7 @@ export class AgentClient {
301
346
  this.connection = "paired";
302
347
  this.listenerActive = pairing.status.listenerActive;
303
348
  this.paired = true;
349
+ this.pairedElsewhere = false;
304
350
  if (this.discoveryTimer !== null) {
305
351
  clearTimeout(this.discoveryTimer);
306
352
  this.discoveryTimer = null;
@@ -319,6 +365,9 @@ export class AgentClient {
319
365
  this.listenerActive = idleListenerActive;
320
366
  this.connection = idleListenerActive ? "listening" : "offline";
321
367
  this.paired = false;
368
+ this.pairedElsewhere = error instanceof AgentBridgeHttpError
369
+ && error.status === 409
370
+ && error.code === "already_paired";
322
371
  this.publish(idleListenerActive ? "available" : "disconnected");
323
372
  return false;
324
373
  }
@@ -351,7 +400,7 @@ export class AgentClient {
351
400
  * synchronously, before the transport promise resolves, so the UI cannot
352
401
  * issue a second prompt while the first one is in flight.
353
402
  */
354
- async dispatchPrompt(prompt, changeRevision) {
403
+ async dispatchPrompt(prompt, changeRevision, options = {}) {
355
404
  if (!this.enabled || !this.started || !this.paired || !this.listenerActive || !this.sessionToken)
356
405
  return null;
357
406
  if (!prompt || this.activeRequest?.status === "working")
@@ -362,12 +411,17 @@ export class AgentClient {
362
411
  sessionToken: this.sessionToken,
363
412
  prompt,
364
413
  ...(changeRevision === undefined ? {} : { changeRevision }),
414
+ ...(options.clientDispatchId === undefined ? {} : { clientDispatchId: options.clientDispatchId }),
415
+ ...(options.attachments === undefined ? {} : { attachments: options.attachments }),
365
416
  };
417
+ const sketches = options.attachments?.map(({ data: _data, ...metadata }) => metadata);
366
418
  this.activeRequest = {
367
419
  localRequestId,
368
420
  requestId: localRequestId,
369
421
  prompt,
370
422
  ...(changeRevision === undefined ? {} : { changeRevision }),
423
+ ...(options.clientDispatchId === undefined ? {} : { clientDispatchId: options.clientDispatchId }),
424
+ ...(sketches === undefined ? {} : { sketches }),
371
425
  status: "working",
372
426
  };
373
427
  this.connection = "working";
@@ -381,7 +435,12 @@ export class AgentClient {
381
435
  return null;
382
436
  if (response?.request?.requestId) {
383
437
  this.activeRequest = this.activeRequest && this.activeRequest.localRequestId === localRequestId
384
- ? { ...this.activeRequest, requestId: response.request.requestId }
438
+ ? {
439
+ ...this.activeRequest,
440
+ requestId: response.request.requestId,
441
+ ...(response.request.clientDispatchId === undefined ? {} : { clientDispatchId: response.request.clientDispatchId }),
442
+ ...(response.request.sketches === undefined ? {} : { sketches: response.request.sketches }),
443
+ }
385
444
  : this.activeRequest;
386
445
  }
387
446
  if (response?.status && response.status !== "working") {
@@ -405,6 +464,9 @@ export class AgentClient {
405
464
  }
406
465
  /** Interrupts local request state and asks the companion to revoke pairing. */
407
466
  disconnect(reason = "browser_disconnected") {
467
+ this.autoConnectSuppressed = true;
468
+ if (this.autoConnect)
469
+ setAutoConnectDisabled(this.projectId, true);
408
470
  const token = this.sessionToken;
409
471
  const active = this.activeRequest;
410
472
  if (active?.status === "working") {
@@ -422,6 +484,7 @@ export class AgentClient {
422
484
  this.connection = "offline";
423
485
  this.listenerActive = false;
424
486
  this.paired = false;
487
+ this.pairedElsewhere = false;
425
488
  this.lastError = reason === "reconnect" || reason === "browser_disconnected" ? undefined : reason;
426
489
  this.publish();
427
490
  if (token) {
@@ -454,7 +517,22 @@ export class AgentClient {
454
517
  else {
455
518
  this.companionReachable = true;
456
519
  if (this.connection !== "paired" && this.connection !== "working" && !this.pairingAbort) {
457
- this.applyStatus(status);
520
+ // Discovery is unauthenticated. Another browser tab can own the
521
+ // pairing, so do not adopt `paired` until this tab restores the
522
+ // shared token. The next poll runs restore first in case a sibling
523
+ // tab has just stored that token.
524
+ const pairedElsewhere = status.paired && !this.sessionToken;
525
+ this.applyStatus(status.paired ? {
526
+ ...status,
527
+ connection: status.listenerActive ? "listening" : "offline",
528
+ paired: false,
529
+ request: null,
530
+ } : status, pairedElsewhere);
531
+ if (this.autoConnect
532
+ && !status.paired
533
+ && !this.autoConnectSuppressed
534
+ && !autoConnectDisabled(this.projectId))
535
+ void this.connect();
458
536
  }
459
537
  }
460
538
  }
@@ -463,6 +541,7 @@ export class AgentClient {
463
541
  this.listenerActive = false;
464
542
  this.companionReachable = false;
465
543
  this.paired = false;
544
+ this.pairedElsewhere = false;
466
545
  this.lastError = undefined;
467
546
  this.publish();
468
547
  }
@@ -473,6 +552,7 @@ export class AgentClient {
473
552
  this.listenerActive = false;
474
553
  this.companionReachable = false;
475
554
  this.paired = false;
555
+ this.pairedElsewhere = false;
476
556
  // Discovery failures are expected while the companion is not running;
477
557
  // retain a quiet disconnected state rather than flashing an error.
478
558
  this.lastError = undefined;
@@ -491,7 +571,7 @@ export class AgentClient {
491
571
  return;
492
572
  this.discoveryTimer = setTimeout(() => {
493
573
  this.discoveryTimer = null;
494
- void this.discover();
574
+ void this.restoreOrDiscover();
495
575
  }, this.discoveryIntervalMs);
496
576
  }
497
577
  scheduleReconnect() {
@@ -538,6 +618,12 @@ export class AgentClient {
538
618
  },
539
619
  };
540
620
  }
621
+ // Every tab with the shared browser session receives the SSE command, but
622
+ // only the Canvas lease owner may answer it. A read-only tab reports this
623
+ // local sentinel; leave the command pending for the owning tab instead of
624
+ // racing its acknowledgement with a false failure.
625
+ if (!acknowledgement.ok && acknowledgement.error?.code === "workspace-locked")
626
+ return;
541
627
  if (acknowledgement.commandId !== command.commandId) {
542
628
  acknowledgement = {
543
629
  commandId: command.commandId,
@@ -559,18 +645,19 @@ export class AgentClient {
559
645
  // bounded timeout and reports any expired command to its agent host.
560
646
  }
561
647
  }
562
- applyStatus(status) {
648
+ applyStatus(status, pairedElsewhere = false) {
563
649
  this.companionReachable = true;
564
650
  this.lastError = undefined;
565
651
  this.listenerActive = status.listenerActive;
566
652
  this.paired = status.paired;
653
+ this.pairedElsewhere = pairedElsewhere;
567
654
  this.connection = statusForConnection(status);
568
655
  if (status.paired && this.discoveryTimer !== null) {
569
656
  clearTimeout(this.discoveryTimer);
570
657
  this.discoveryTimer = null;
571
658
  }
572
659
  if (status.request) {
573
- this.applyRequestStatus(status.request.status, status.request.requestId, status.request.summary, status.request.error, status.request.changeRevision);
660
+ this.applyRequestStatus(status.request.status, status.request.requestId, status.request.summary, status.request.error, status.request.changeRevision, status.request.clientDispatchId, status.request.sketches);
574
661
  }
575
662
  else if (status.listenerActive && status.paired && this.activeRequest?.status !== "working") {
576
663
  // A re-armed MCP listen call is the lifecycle boundary for the previous
@@ -596,9 +683,10 @@ export class AgentClient {
596
683
  this.listenerActive = false;
597
684
  this.companionReachable = false;
598
685
  this.paired = false;
686
+ this.pairedElsewhere = false;
599
687
  this.publish();
600
688
  }
601
- applyRequestStatus(status, requestId, summary, error, changeRevision) {
689
+ applyRequestStatus(status, requestId, summary, error, changeRevision, clientDispatchId, sketches) {
602
690
  const active = this.activeRequest;
603
691
  if (active && active.requestId !== requestId && active.localRequestId !== requestId) {
604
692
  // A fresh server id can arrive before the POST response. Associate it
@@ -613,6 +701,8 @@ export class AgentClient {
613
701
  requestId,
614
702
  prompt: "",
615
703
  ...(changeRevision === undefined ? {} : { changeRevision }),
704
+ ...(clientDispatchId === undefined ? {} : { clientDispatchId }),
705
+ ...(sketches === undefined ? {} : { sketches }),
616
706
  status,
617
707
  ...(summary === undefined ? {} : { summary }),
618
708
  ...(error === undefined ? {} : { error }),
@@ -624,6 +714,8 @@ export class AgentClient {
624
714
  requestId,
625
715
  status,
626
716
  ...(changeRevision === undefined ? {} : { changeRevision }),
717
+ ...(clientDispatchId === undefined ? {} : { clientDispatchId }),
718
+ ...(sketches === undefined ? {} : { sketches }),
627
719
  ...(summary === undefined ? {} : { summary }),
628
720
  ...(error === undefined ? {} : { error }),
629
721
  };
@@ -643,7 +735,15 @@ export class AgentClient {
643
735
  this.listenerActive = false;
644
736
  this.companionReachable = false;
645
737
  this.paired = false;
738
+ this.pairedElsewhere = false;
646
739
  this.lastError = reason;
740
+ if (reason === "browser_disconnected") {
741
+ // A remote takeover deliberately invalidates this browser's token. Do
742
+ // not immediately race the new browser by auto-pairing again.
743
+ this.autoConnectSuppressed = true;
744
+ if (this.autoConnect)
745
+ setAutoConnectDisabled(this.projectId, true);
746
+ }
647
747
  this.publish();
648
748
  this.scheduleReconnect();
649
749
  }
@@ -656,9 +756,12 @@ export class AgentClient {
656
756
  listenerActive: this.listenerActive,
657
757
  companionReachable: this.companionReachable,
658
758
  paired: this.paired,
759
+ pairedElsewhere: this.pairedElsewhere,
659
760
  request: request ? {
660
761
  requestId: request.requestId,
661
762
  ...(request.changeRevision === undefined ? {} : { changeRevision: request.changeRevision }),
763
+ ...(request.clientDispatchId === undefined ? {} : { clientDispatchId: request.clientDispatchId }),
764
+ ...(request.sketches === undefined ? {} : { sketches: request.sketches }),
662
765
  status: request.status,
663
766
  ...(request.summary === undefined ? {} : { summary: request.summary }),
664
767
  ...(request.error === undefined ? {} : { error: request.error }),
@@ -1,12 +1,12 @@
1
1
  import type { AgentClientSnapshot } from "./client.js";
2
- export type AgentConnectionStatusKind = "unavailable" | "connecting" | "working-disconnected" | "working" | "not-found" | "ready-to-connect" | "connected-not-listening" | "listening";
2
+ export type AgentConnectionStatusKind = "unavailable" | "connecting" | "working-disconnected" | "working" | "paired-elsewhere" | "not-found" | "ready-to-connect" | "connected-not-listening" | "listening";
3
3
  export interface AgentConnectionStatus {
4
4
  readonly kind: AgentConnectionStatusKind;
5
5
  readonly label: string;
6
6
  readonly tone: "neutral" | "accent" | "warning" | "danger";
7
7
  readonly action?: {
8
- readonly kind: "connect" | "setup";
9
- readonly label: "Connect" | "Set up";
8
+ readonly kind: "connect" | "takeover";
9
+ readonly label: "Connect" | "Take over";
10
10
  };
11
11
  }
12
12
  /** Returns the user-facing connection state shared by the prompt UI and MCP panel. */
@@ -6,6 +6,14 @@ export function getAgentConnectionStatus(snapshot) {
6
6
  if (snapshot.state === "pairing") {
7
7
  return { kind: "connecting", label: "Connecting…", tone: "accent" };
8
8
  }
9
+ if (snapshot.pairedElsewhere) {
10
+ return {
11
+ kind: "paired-elsewhere",
12
+ label: "MCP active in another tab or window",
13
+ tone: "warning",
14
+ action: { kind: "takeover", label: "Take over" },
15
+ };
16
+ }
9
17
  if (!snapshot.paired && (snapshot.state === "working" || snapshot.request?.status === "working")) {
10
18
  return { kind: "working-disconnected", label: "Disconnected · Last work status unknown", tone: "warning" };
11
19
  }
@@ -15,20 +23,19 @@ export function getAgentConnectionStatus(snapshot) {
15
23
  if (snapshot.paired && !snapshot.listenerActive) {
16
24
  return {
17
25
  kind: "connected-not-listening",
18
- label: "Connected, not listening",
26
+ label: "Project connected · Ask agent to listen",
19
27
  tone: "accent",
20
- action: { kind: "setup", label: "Set up" },
21
28
  };
22
29
  }
23
30
  if (!snapshot.companionReachable) {
24
- return { kind: "not-found", label: "Companion not found", tone: "neutral" };
31
+ return { kind: "not-found", label: "Project bridge unavailable", tone: "neutral" };
25
32
  }
26
33
  if (snapshot.listenerActive) {
27
34
  return { kind: "listening", label: "Agent listening", tone: "accent" };
28
35
  }
29
36
  return {
30
37
  kind: "ready-to-connect",
31
- label: "Ready to connect agent",
38
+ label: "Project available · Connect this page",
32
39
  tone: "accent",
33
40
  action: { kind: "connect", label: "Connect" },
34
41
  };
@@ -1,4 +1,11 @@
1
1
  import { type AgentBridgeTransport, type AgentBridgeEndpointConfig, type AgentDiscoveryRequest, type AgentEventHandlers, type AgentEventSubscription, type AgentEventsRequest, type AgentPairRequest, type AgentPromptDispatch, type AgentDisconnectRequest, type AgentCanvasAcknowledgementRequest, type AgentSessionRequest, type AgentStatusSnapshot, type PairingResponse, type PromptDispatchResponse } from "./protocol.js";
2
+ /** Returns whether the dev host supplied a trusted project bridge endpoint. */
3
+ export declare function shouldAutoConnectAgentBridge(): boolean;
4
+ export declare class AgentBridgeHttpError extends Error {
5
+ readonly status: number;
6
+ readonly code?: string;
7
+ constructor(status: number, message: string, code?: string);
8
+ }
2
9
  /**
3
10
  * HTTP/SSE browser adapter for the companion's canonical endpoints.
4
11
  *
@@ -11,6 +18,7 @@ export declare class HttpAgentBridgeTransport implements AgentBridgeTransport {
11
18
  constructor(config?: AgentBridgeEndpointConfig);
12
19
  discover(request: AgentDiscoveryRequest, signal?: AbortSignal): Promise<AgentStatusSnapshot | null>;
13
20
  pair(request: AgentPairRequest, signal?: AbortSignal): Promise<PairingResponse>;
21
+ takeOver(request: AgentPairRequest, signal?: AbortSignal): Promise<PairingResponse>;
14
22
  restore(request: AgentSessionRequest, signal?: AbortSignal): Promise<AgentStatusSnapshot | null>;
15
23
  openEvents(request: AgentEventsRequest, handlers: AgentEventHandlers): AgentEventSubscription;
16
24
  dispatch(request: AgentPromptDispatch, signal?: AbortSignal): Promise<PromptDispatchResponse>;
@@ -1,4 +1,5 @@
1
- import { AGENT_PROTOCOL_VERSION, defaultBridgePort, } from "./protocol.js";
1
+ import { AGENT_PROTOCOL_VERSION, defaultBridgePort, isAgentPromptRequest, } from "./protocol.js";
2
+ import { getActiveCanvasDocument } from "../canvas/activeCanvasDocument.js";
2
3
  function isRecord(value) {
3
4
  return typeof value === "object" && value !== null && !Array.isArray(value);
4
5
  }
@@ -17,21 +18,36 @@ function endpointValue(value) {
17
18
  statusUrl: stringValue(value.statusUrl),
18
19
  canvasAckUrl: stringValue(value.canvasAckUrl),
19
20
  disconnectUrl: stringValue(value.disconnectUrl),
21
+ takeoverUrl: stringValue(value.takeoverUrl),
22
+ ...(value.autoConnect === true ? { autoConnect: true } : {}),
20
23
  };
21
24
  return Object.values(config).some((entry) => entry !== undefined) ? config : undefined;
22
25
  }
23
- function configuredEndpoint() {
24
- if (typeof window === "undefined")
26
+ function configuredEndpointInDocument(doc) {
27
+ const target = doc.defaultView;
28
+ if (!target)
25
29
  return undefined;
26
- const target = window;
27
30
  const globalConfig = endpointValue(target.__NUDGE_UI_AGENT_BRIDGE__)
28
31
  ?? endpointValue(target.__NUDGE_UI_AGENT__);
29
32
  if (globalConfig)
30
33
  return globalConfig;
31
- const meta = document.querySelector('meta[name="nudge-ui-agent-bridge"]');
34
+ const meta = doc.querySelector('meta[name="nudge-ui-agent-bridge"]');
32
35
  const content = meta?.content.trim();
33
36
  return content ? { baseUrl: content } : undefined;
34
37
  }
38
+ function configuredEndpoint() {
39
+ if (typeof document === "undefined")
40
+ return undefined;
41
+ const controllerConfig = configuredEndpointInDocument(document);
42
+ if (controllerConfig)
43
+ return controllerConfig;
44
+ const previewDocument = getActiveCanvasDocument();
45
+ return previewDocument ? configuredEndpointInDocument(previewDocument) : undefined;
46
+ }
47
+ /** Returns whether the dev host supplied a trusted project bridge endpoint. */
48
+ export function shouldAutoConnectAgentBridge() {
49
+ return configuredEndpoint()?.autoConnect === true;
50
+ }
35
51
  function isLoopback(hostname) {
36
52
  const normalized = hostname.toLowerCase();
37
53
  return normalized === "localhost"
@@ -81,6 +97,16 @@ function headers() {
81
97
  Accept: "application/json",
82
98
  };
83
99
  }
100
+ export class AgentBridgeHttpError extends Error {
101
+ status;
102
+ code;
103
+ constructor(status, message, code) {
104
+ super(message);
105
+ this.name = "AgentBridgeHttpError";
106
+ this.status = status;
107
+ this.code = code;
108
+ }
109
+ }
84
110
  async function readJson(response) {
85
111
  let payload;
86
112
  try {
@@ -92,7 +118,8 @@ async function readJson(response) {
92
118
  if (!response.ok) {
93
119
  const error = isRecord(payload) && isRecord(payload.error) ? payload.error : payload;
94
120
  const message = isRecord(error) ? stringValue(error.message) : undefined;
95
- throw new Error(message ?? `Agent bridge request failed (${response.status}).`);
121
+ const code = isRecord(error) ? stringValue(error.code) : undefined;
122
+ throw new AgentBridgeHttpError(response.status, message ?? `Agent bridge request failed (${response.status}).`, code);
96
123
  }
97
124
  return isRecord(payload) ? payload : {};
98
125
  }
@@ -114,20 +141,24 @@ function statusPayload(value) {
114
141
  let request = null;
115
142
  if (rawRequest !== null && rawRequest !== undefined) {
116
143
  if (!isRecord(rawRequest)
117
- || typeof rawRequest.requestId !== "string"
118
- || typeof rawRequest.projectId !== "string"
119
- || typeof rawRequest.prompt !== "string"
120
144
  || (rawRequest.status !== "working"
121
145
  && rawRequest.status !== "completed"
122
146
  && rawRequest.status !== "failed"
123
147
  && rawRequest.status !== "interrupted"))
124
148
  return null;
125
- request = {
149
+ const requestCandidate = {
126
150
  requestId: rawRequest.requestId,
127
151
  projectId: rawRequest.projectId,
128
152
  prompt: rawRequest.prompt,
129
- status: rawRequest.status,
130
153
  ...(typeof rawRequest.changeRevision === "number" ? { changeRevision: rawRequest.changeRevision } : {}),
154
+ ...(rawRequest.clientDispatchId === undefined ? {} : { clientDispatchId: rawRequest.clientDispatchId }),
155
+ ...(rawRequest.sketches === undefined ? {} : { sketches: rawRequest.sketches }),
156
+ };
157
+ if (!isAgentPromptRequest(requestCandidate))
158
+ return null;
159
+ request = {
160
+ ...requestCandidate,
161
+ status: rawRequest.status,
131
162
  ...(typeof rawRequest.summary === "string" ? { summary: rawRequest.summary } : {}),
132
163
  ...(typeof rawRequest.error === "string" ? { error: rawRequest.error } : {}),
133
164
  };
@@ -234,6 +265,34 @@ export class HttpAgentBridgeTransport {
234
265
  status,
235
266
  };
236
267
  }
268
+ async takeOver(request, signal) {
269
+ const url = endpointUrl(this.config, request.projectId, "takeoverUrl", "/takeover", pageOrigin());
270
+ if (!url || typeof fetch !== "function")
271
+ throw new Error("Agent bridge is unavailable.");
272
+ const response = await fetch(url, {
273
+ method: "POST",
274
+ headers: { ...headers(), "Content-Type": "application/json" },
275
+ body: JSON.stringify(request),
276
+ signal,
277
+ });
278
+ const payload = await readJson(response);
279
+ if (payload.protocolVersion !== AGENT_PROTOCOL_VERSION
280
+ || payload.projectId !== request.projectId
281
+ || payload.origin !== request.origin
282
+ || typeof payload.sessionToken !== "string") {
283
+ throw new Error("Agent bridge returned an invalid takeover response.");
284
+ }
285
+ const status = statusPayload(payload.status);
286
+ if (!status)
287
+ throw new Error("Agent bridge returned an invalid status.");
288
+ return {
289
+ protocolVersion: AGENT_PROTOCOL_VERSION,
290
+ projectId: request.projectId,
291
+ origin: request.origin,
292
+ sessionToken: payload.sessionToken,
293
+ status,
294
+ };
295
+ }
237
296
  async restore(request, signal) {
238
297
  const url = endpointUrl(this.config, request.projectId, "statusUrl", "/status", pageOrigin());
239
298
  if (!url || typeof fetch !== "function")
@@ -299,20 +358,21 @@ export class HttpAgentBridgeTransport {
299
358
  signal,
300
359
  });
301
360
  const payload = await readJson(response);
302
- if (!isRecord(payload.request)
303
- || typeof payload.request.requestId !== "string"
304
- || typeof payload.request.projectId !== "string"
305
- || typeof payload.request.prompt !== "string"
306
- || payload.status !== "working") {
361
+ if (!isRecord(payload.request) || payload.status !== "working") {
307
362
  throw new Error("Agent bridge returned an invalid prompt response.");
308
363
  }
309
- const requestBody = {
364
+ const requestCandidate = {
310
365
  requestId: payload.request.requestId,
311
366
  projectId: payload.request.projectId,
312
367
  prompt: payload.request.prompt,
313
368
  ...(typeof payload.request.changeRevision === "number" ? { changeRevision: payload.request.changeRevision } : {}),
369
+ ...(payload.request.clientDispatchId === undefined ? {} : { clientDispatchId: payload.request.clientDispatchId }),
370
+ ...(payload.request.sketches === undefined ? {} : { sketches: payload.request.sketches }),
314
371
  };
315
- return { request: requestBody, status: "working" };
372
+ if (!isAgentPromptRequest(requestCandidate) || requestCandidate.projectId !== request.projectId) {
373
+ throw new Error("Agent bridge returned an invalid prompt response.");
374
+ }
375
+ return { request: requestCandidate, status: "working" };
316
376
  }
317
377
  async disconnect(request, signal) {
318
378
  const url = endpointUrl(this.config, request.projectId, "disconnectUrl", "/disconnect", pageOrigin());
@@ -1,4 +1,4 @@
1
- import type { AgentStatusSnapshot, CanvasCommand, CanvasCommandAcknowledgement, CanvasCommandResult, PairingResponse, PromptDispatchResponse } from "../../internal/agent-protocol.js";
1
+ import type { AgentStatusSnapshot, CanvasCommand, CanvasCommandAcknowledgement, CanvasCommandResult, AgentSketchAttachment, PairingResponse, PromptDispatchResponse } from "../../internal/agent-protocol.js";
2
2
  /**
3
3
  * Browser transport port for the shared Nudge agent protocol.
4
4
  *
@@ -7,8 +7,8 @@ import type { AgentStatusSnapshot, CanvasCommand, CanvasCommandAcknowledgement,
7
7
  * boundary local lets inspector tests use a deterministic fake while the
8
8
  * companion remains the owner of MCP and Node-specific code.
9
9
  */
10
- export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, defaultBridgePort, CANVAS_COMMAND_TYPES, canonicalOrigin, isAgentPromptRequest, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateRoutes, } from "../../internal/agent-protocol.js";
11
- export type { AgentConnectionState, AgentProjectIdentity, AgentPromptRequest, AgentRequestOutcome, AgentRequestStatus, AgentRoute, AgentStatusSnapshot, AgentStatusUpdate, BridgeCanvasCommandEvent, BridgeConnectedEvent, BridgeDisconnectedEvent, BridgeEnvelope, BridgeEvent, BridgeEventType, BridgeStatusEvent, CanvasCommand, CanvasCommandAcknowledgement, CanvasCommandError, CanvasCommandResult, CanvasGroup, CanvasGroupOwner, CanvasState, FitCanvasCommand, FocusCanvasGroupCommand, PairingRequest, PairingResponse, PresentRoutesCommand, PromptDispatchRequest, PromptDispatchResponse, ReadCanvasStateCommand, RemoveCanvasGroupCommand, StatusEventRequest, AgentProtocolVersion, } from "../../internal/agent-protocol.js";
10
+ export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, defaultBridgePort, CANVAS_COMMAND_TYPES, canonicalOrigin, isAgentPromptRequest, isAgentSketchAttachment, isAgentSketchAnnotation, isAgentSketchCaptureMetadata, isAgentSketchMetadata, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateSketchAttachments, validateRoutes, } from "../../internal/agent-protocol.js";
11
+ export type { AgentConnectionState, AgentProjectIdentity, AgentPromptRequest, AgentDeliveredPrompt, AgentSketchAttachment, AgentSketchAnnotation, AgentSketchCaptureMetadata, AgentSketchMetadata, AgentRequestOutcome, AgentRequestStatus, AgentRoute, AgentStatusSnapshot, AgentStatusUpdate, BridgeCanvasCommandEvent, BridgeConnectedEvent, BridgeDisconnectedEvent, BridgeEnvelope, BridgeEvent, BridgeEventType, BridgeStatusEvent, CanvasCommand, CanvasCommandAcknowledgement, CanvasCommandError, CanvasCommandResult, CanvasGroup, CanvasGroupOwner, CanvasState, FitCanvasCommand, FocusCanvasGroupCommand, PairingRequest, PairingResponse, PresentRoutesCommand, PromptDispatchRequest, PromptDispatchResponse, ReadCanvasStateCommand, RemoveCanvasGroupCommand, StatusEventRequest, AgentProtocolVersion, } from "../../internal/agent-protocol.js";
12
12
  export interface AgentBridgeEndpointConfig {
13
13
  readonly baseUrl?: string;
14
14
  readonly healthUrl?: string;
@@ -18,6 +18,9 @@ export interface AgentBridgeEndpointConfig {
18
18
  readonly statusUrl?: string;
19
19
  readonly canvasAckUrl?: string;
20
20
  readonly disconnectUrl?: string;
21
+ readonly takeoverUrl?: string;
22
+ /** Pair automatically when this endpoint came from the trusted dev host. */
23
+ readonly autoConnect?: boolean;
21
24
  }
22
25
  export interface AgentDiscoveryRequest {
23
26
  readonly projectId: string;
@@ -43,6 +46,8 @@ export interface AgentPromptDispatch {
43
46
  readonly sessionToken: string;
44
47
  readonly prompt: string;
45
48
  readonly changeRevision?: number;
49
+ readonly clientDispatchId?: string;
50
+ readonly attachments?: readonly AgentSketchAttachment[];
46
51
  }
47
52
  export interface AgentDisconnectRequest {
48
53
  readonly projectId: string;
@@ -83,6 +88,8 @@ export interface AgentBridgeTransport {
83
88
  readonly openEvents: (request: AgentEventsRequest, handlers: AgentEventHandlers) => AgentEventSubscription;
84
89
  readonly dispatch: (request: AgentPromptDispatch, signal?: AbortSignal) => Promise<PromptDispatchResponse | null>;
85
90
  readonly disconnect?: (request: AgentDisconnectRequest, signal?: AbortSignal) => Promise<void>;
91
+ /** Deliberately revokes another browser pairing before pairing this page. */
92
+ readonly takeOver?: (request: AgentPairRequest, signal?: AbortSignal) => Promise<PairingResponse>;
86
93
  readonly acknowledgeCanvasCommand?: (request: AgentCanvasAcknowledgementRequest, signal?: AbortSignal) => Promise<void>;
87
94
  }
88
95
  export interface AgentClientOptions {
@@ -90,6 +97,7 @@ export interface AgentClientOptions {
90
97
  readonly origin?: string;
91
98
  readonly transport?: AgentBridgeTransport;
92
99
  readonly endpoint?: AgentBridgeEndpointConfig;
100
+ readonly autoConnect?: boolean;
93
101
  readonly discoveryIntervalMs?: number;
94
102
  readonly canvasCommandHandler?: (command: CanvasCommand) => CanvasCommandResult | Promise<CanvasCommandResult>;
95
103
  }
@@ -6,4 +6,4 @@
6
6
  * boundary local lets inspector tests use a deterministic fake while the
7
7
  * companion remains the owner of MCP and Node-specific code.
8
8
  */
9
- export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, defaultBridgePort, CANVAS_COMMAND_TYPES, canonicalOrigin, isAgentPromptRequest, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateRoutes, } from "../../internal/agent-protocol.js";
9
+ export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, defaultBridgePort, CANVAS_COMMAND_TYPES, canonicalOrigin, isAgentPromptRequest, isAgentSketchAttachment, isAgentSketchAnnotation, isAgentSketchCaptureMetadata, isAgentSketchMetadata, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateSketchAttachments, validateRoutes, } from "../../internal/agent-protocol.js";
@@ -32,7 +32,7 @@ export declare function getAgentDispatchSize(revision: number): number;
32
32
  * observable after removing Nudge's own preview. Newer edits and ambiguous
33
33
  * records are preserved.
34
34
  */
35
- export declare function verifyAndReconcileHandoff(snapshot: HandoffSnapshot): Promise<number>;
35
+ export declare function verifyAndReconcileHandoff(snapshot: HandoffSnapshot, doc?: Document | null): Promise<number>;
36
36
  /** Reconciles the captured records for one completed connected-agent request. */
37
37
  export declare function verifyAndReconcileAgentDispatch(revision: number): Promise<number>;
38
38
  /** Test/session reset for in-memory dispatch snapshots. */