@nudge-ui/mcp 0.2.1 → 0.2.3

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.
@@ -1,13 +1,14 @@
1
+ import type { AgentActivity } from "./internal/agent-protocol.js";
1
2
  import type { AgentPromptRequest, AgentStatusSnapshot, AgentStatusUpdate, CanvasCommandResult } from "./internal/agent-protocol.js";
2
3
  import type { CanvasCommandInput } from "./bridge.js";
3
- import { type ProjectSession } from "./project.js";
4
+ import { type ProjectSession, type StoredProjectSession } from "./project.js";
4
5
  export interface DiscoveredProjectSession extends ProjectSession {
5
6
  readonly matchesWorkspace: boolean;
6
7
  readonly matchesApplication: boolean;
7
8
  readonly claimed: boolean;
8
9
  readonly status: Pick<AgentStatusSnapshot, "connection" | "listenerActive" | "paired">;
9
10
  }
10
- export declare function discoverProjectSessions(applicationRoot: string, registryRoot?: string): Promise<DiscoveredProjectSession[]>;
11
+ export declare function discoverProjectSessions(applicationRoot: string | undefined, registryRoot?: string): Promise<DiscoveredProjectSession[]>;
11
12
  /** Routes MCP tools to a live project in the adapter's exact worktree. */
12
13
  export declare class DiscoveredProjectRouter {
13
14
  readonly workspaceRoot: string;
@@ -16,14 +17,32 @@ export declare class DiscoveredProjectRouter {
16
17
  readonly agentId: string;
17
18
  private client;
18
19
  private releaseGeneration;
20
+ private connectionQueue;
19
21
  private constructor();
22
+ get selectedSessionId(): string | undefined;
23
+ static forSession(session: StoredProjectSession, registryRoot: string): DiscoveredProjectRouter;
20
24
  static create(workspaceRoot: string, registryRoot?: string): Promise<DiscoveredProjectRouter>;
21
25
  listSessions(): Promise<DiscoveredProjectSession[]>;
22
26
  private connectToSession;
23
- waitForPrompt(signal?: AbortSignal, sessionId?: string): Promise<AgentPromptRequest>;
27
+ private selectSession;
28
+ waitForPrompt(signal?: AbortSignal, sessionId?: string, agentClientName?: string): Promise<AgentPromptRequest>;
24
29
  getStatus(): Promise<AgentStatusSnapshot>;
30
+ reportActivity(activity: AgentActivity): Promise<void>;
25
31
  updateRequestStatus(update: AgentStatusUpdate): Promise<AgentStatusSnapshot>;
26
32
  dispatchCanvasCommand(command: CanvasCommandInput): Promise<CanvasCommandResult>;
27
33
  release(): Promise<void>;
28
34
  close(): Promise<void>;
29
35
  }
36
+ /** Returns diagnostic counts, scope, and public paths without exposing registry credentials. */
37
+ export declare function diagnoseProjectSessions(applicationRoot?: string, registryRoot?: string): Promise<{
38
+ registryRoot: string;
39
+ workspaceRoot: string | undefined;
40
+ applicationRoot: string | undefined;
41
+ descriptorCount: number;
42
+ invalidCount: number;
43
+ incompatibleCount: number;
44
+ reachableCount: number;
45
+ matchingReachableCount: number;
46
+ matchingUnreachableCount: number;
47
+ guidance: string[];
48
+ }>;
package/dist/discovery.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { realpath } from "node:fs/promises";
3
- import { resolve } from "node:path";
3
+ import { isAbsolute, resolve } from "node:path";
4
4
  import { ProjectSessionClient } from "./projectSessionClient.js";
5
- import { readLiveProjectSessions } from "./sessionRegistry.js";
5
+ import { inspectProjectSessions, readLiveProjectSessions } from "./sessionRegistry.js";
6
6
  import { matchesApplication, selectProjectSession } from "./sessionSelection.js";
7
7
  import { defaultSessionRegistryRoot, resolveWorkspaceRoot, } from "./project.js";
8
8
  function publicSession(session) {
@@ -10,15 +10,17 @@ function publicSession(session) {
10
10
  return visible;
11
11
  }
12
12
  export async function discoverProjectSessions(applicationRoot, registryRoot = defaultSessionRegistryRoot()) {
13
- const canonicalApplication = await realpath(resolve(applicationRoot));
14
- const canonicalWorkspace = await resolveWorkspaceRoot(canonicalApplication);
13
+ const canonicalApplication = applicationRoot === undefined
14
+ ? undefined
15
+ : await realpath(requireAbsoluteWorkspaceRoot(applicationRoot));
16
+ const canonicalWorkspace = canonicalApplication === undefined ? undefined : await resolveWorkspaceRoot(canonicalApplication);
15
17
  const live = await readLiveProjectSessions(resolve(registryRoot));
16
18
  return live.map(({ descriptor, health }) => {
17
19
  const matchesWorkspace = descriptor.workspaceRoot === canonicalWorkspace;
18
20
  return {
19
21
  ...publicSession(descriptor),
20
22
  matchesWorkspace,
21
- matchesApplication: matchesApplication(descriptor, {
23
+ matchesApplication: canonicalWorkspace !== undefined && canonicalApplication !== undefined && matchesApplication(descriptor, {
22
24
  workspaceRoot: canonicalWorkspace,
23
25
  applicationRoot: canonicalApplication,
24
26
  }),
@@ -39,20 +41,34 @@ export class DiscoveredProjectRouter {
39
41
  agentId;
40
42
  client = null;
41
43
  releaseGeneration = 0;
44
+ connectionQueue = Promise.resolve();
42
45
  constructor(workspaceRoot, applicationRoot, registryRoot, agentId) {
43
46
  this.workspaceRoot = workspaceRoot;
44
47
  this.applicationRoot = applicationRoot;
45
48
  this.registryRoot = registryRoot;
46
49
  this.agentId = agentId;
47
50
  }
51
+ get selectedSessionId() {
52
+ return this.client?.descriptor.sessionId;
53
+ }
54
+ static forSession(session, registryRoot) {
55
+ const router = new DiscoveredProjectRouter(session.workspaceRoot, session.appRoot, registryRoot, randomUUID());
56
+ router.client = new ProjectSessionClient(session, router.agentId);
57
+ return router;
58
+ }
48
59
  static async create(workspaceRoot, registryRoot = defaultSessionRegistryRoot()) {
49
- const applicationRoot = await realpath(resolve(workspaceRoot));
60
+ const applicationRoot = await realpath(requireAbsoluteWorkspaceRoot(workspaceRoot));
50
61
  return new DiscoveredProjectRouter(await resolveWorkspaceRoot(applicationRoot), applicationRoot, resolve(registryRoot), randomUUID());
51
62
  }
52
63
  async listSessions() {
53
64
  return await discoverProjectSessions(this.applicationRoot, this.registryRoot);
54
65
  }
55
- async connectToSession(sessionId) {
66
+ connectToSession(sessionId) {
67
+ const connection = this.connectionQueue.then(() => this.selectSession(sessionId));
68
+ this.connectionQueue = connection.then(() => undefined, () => undefined);
69
+ return connection;
70
+ }
71
+ async selectSession(sessionId) {
56
72
  const live = await readLiveProjectSessions(this.registryRoot);
57
73
  const selected = selectProjectSession(live.map(({ descriptor }) => descriptor), {
58
74
  workspaceRoot: this.workspaceRoot,
@@ -66,7 +82,7 @@ export class DiscoveredProjectRouter {
66
82
  this.client = new ProjectSessionClient(selected, this.agentId);
67
83
  return this.client;
68
84
  }
69
- async waitForPrompt(signal, sessionId) {
85
+ async waitForPrompt(signal, sessionId, agentClientName) {
70
86
  const releaseGeneration = this.releaseGeneration;
71
87
  const client = await this.connectToSession(sessionId);
72
88
  if (releaseGeneration !== this.releaseGeneration) {
@@ -77,7 +93,7 @@ export class DiscoveredProjectRouter {
77
93
  throw new Error("The Nudge project session was released.");
78
94
  }
79
95
  try {
80
- return await client.waitForPrompt(signal);
96
+ return await client.waitForPrompt(signal, agentClientName);
81
97
  }
82
98
  catch (error) {
83
99
  if (signal?.aborted || sessionId || releaseGeneration !== this.releaseGeneration)
@@ -85,13 +101,17 @@ export class DiscoveredProjectRouter {
85
101
  const replacement = await this.connectToSession();
86
102
  if (replacement === client)
87
103
  throw error;
88
- return await replacement.waitForPrompt(signal);
104
+ return await replacement.waitForPrompt(signal, agentClientName);
89
105
  }
90
106
  }
91
107
  async getStatus() {
92
108
  const client = await this.connectToSession();
93
109
  return await client.getStatus();
94
110
  }
111
+ async reportActivity(activity) {
112
+ const client = await this.connectToSession();
113
+ await client.reportActivity(activity);
114
+ }
95
115
  async updateRequestStatus(update) {
96
116
  const client = await this.connectToSession();
97
117
  return await client.updateRequestStatus(update);
@@ -102,6 +122,7 @@ export class DiscoveredProjectRouter {
102
122
  }
103
123
  async release() {
104
124
  this.releaseGeneration += 1;
125
+ await this.connectionQueue;
105
126
  const client = this.client;
106
127
  this.client = null;
107
128
  await client?.close();
@@ -110,3 +131,34 @@ export class DiscoveredProjectRouter {
110
131
  await this.release();
111
132
  }
112
133
  }
134
+ /** Returns diagnostic counts, scope, and public paths without exposing registry credentials. */
135
+ export async function diagnoseProjectSessions(applicationRoot, registryRoot = defaultSessionRegistryRoot()) {
136
+ const resolvedRegistryRoot = resolve(registryRoot);
137
+ const app = applicationRoot === undefined
138
+ ? undefined
139
+ : await realpath(requireAbsoluteWorkspaceRoot(applicationRoot));
140
+ const workspace = app === undefined ? undefined : await resolveWorkspaceRoot(app);
141
+ const inspection = await inspectProjectSessions(resolvedRegistryRoot);
142
+ const matching = (session) => app === undefined || (session.workspaceRoot === workspace && (app === workspace || session.appRoot === app));
143
+ return {
144
+ registryRoot: resolvedRegistryRoot,
145
+ workspaceRoot: workspace,
146
+ applicationRoot: app,
147
+ descriptorCount: inspection.descriptorCount,
148
+ invalidCount: inspection.invalidCount,
149
+ incompatibleCount: inspection.incompatibleCount,
150
+ reachableCount: inspection.live.length,
151
+ matchingReachableCount: inspection.live.filter(({ descriptor }) => matching(descriptor)).length,
152
+ matchingUnreachableCount: inspection.unreachable.filter(matching).length,
153
+ guidance: [
154
+ ...(inspection.invalidCount > 0 ? ["Some session descriptors or authenticated health responses are invalid. Check the adapter and project bridge versions, descriptor permissions, and bridge logs."] : []),
155
+ ...(inspection.unreachable.some(matching) ? ["Matching descriptors exist but their loopback endpoints are unreachable. The server may have stopped or local networking may be blocked. Retry outside the agent command sandbox; do not treat this as proof that no bridge registered."] : []),
156
+ ...(inspection.incompatibleCount > 0 ? ["Some registrations use an incompatible protocol. Upgrade the reusable adapter and the project's @nudge-ui/mcp dependency, then restart the development server and agent host."] : []),
157
+ ],
158
+ };
159
+ }
160
+ function requireAbsoluteWorkspaceRoot(workspaceRoot) {
161
+ if (!isAbsolute(workspaceRoot))
162
+ throw new TypeError("workspaceRoot must be an absolute path.");
163
+ return workspaceRoot;
164
+ }
@@ -177,6 +177,8 @@ export interface AgentStatusSnapshot {
177
177
  readonly projectId: string;
178
178
  readonly connection: AgentConnectionState;
179
179
  readonly listenerActive: boolean;
180
+ /** MCP client name supplied during initialization; absent for older adapters. */
181
+ readonly agentClientName?: string;
180
182
  readonly paired: boolean;
181
183
  /** The last paired page, retained only for this process lifetime. */
182
184
  readonly pageUrl?: string | null;
@@ -186,7 +188,7 @@ export interface AgentStatusSnapshot {
186
188
  readonly error?: string;
187
189
  }) | null;
188
190
  }
189
- export type BridgeEventType = "status" | "canvas-command" | "connected" | "disconnected";
191
+ export type BridgeEventType = "activity" | "status" | "canvas-command" | "connected" | "disconnected";
190
192
  export interface BridgeStatusEvent {
191
193
  readonly type: "status";
192
194
  readonly status: AgentStatusSnapshot;
@@ -203,8 +205,23 @@ export interface BridgeCanvasCommandEvent {
203
205
  readonly type: "canvas-command";
204
206
  readonly command: CanvasCommand;
205
207
  }
208
+ /** Temporary attribution for one file operation in an authoritative request. */
209
+ export interface AgentActivity {
210
+ readonly requestId: string;
211
+ /** Workspace-relative source path. */
212
+ readonly file: string;
213
+ readonly operation: "read" | "edit";
214
+ readonly line?: number;
215
+ readonly endLine?: number;
216
+ readonly componentId?: string;
217
+ }
218
+ export declare function isAgentActivity(value: unknown): value is AgentActivity;
219
+ export interface BridgeActivityEvent {
220
+ readonly type: "activity";
221
+ readonly activity: AgentActivity;
222
+ }
206
223
  /** Payload delivered to a browser SSE connection. */
207
- export type BridgeEvent = BridgeStatusEvent | BridgeConnectedEvent | BridgeDisconnectedEvent | BridgeCanvasCommandEvent;
224
+ export type BridgeEvent = BridgeActivityEvent | BridgeStatusEvent | BridgeConnectedEvent | BridgeDisconnectedEvent | BridgeCanvasCommandEvent;
208
225
  export interface BridgeEnvelope {
209
226
  readonly protocolVersion: AgentProtocolVersion;
210
227
  readonly projectId: string;
@@ -61,6 +61,19 @@ export const CANVAS_COMMAND_TYPES = [
61
61
  "fit-all",
62
62
  "remove-group",
63
63
  ];
64
+ export function isAgentActivity(value) {
65
+ if (!isRecord(value) || !hasOnlyKeys(value, ["requestId", "file", "operation", "line", "endLine", "componentId"]))
66
+ return false;
67
+ const line = (value) => Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= 10_000_000;
68
+ return nonEmptyString(value.requestId, AGENT_PROTOCOL_LIMITS.requestId)
69
+ && nonEmptyString(value.file, AGENT_PROTOCOL_LIMITS.workspaceRoot)
70
+ && !/^(?:[\\/]|[a-z]:)/i.test(value.file) && !/[\\\u0000-\u001f]/.test(value.file)
71
+ && !value.file.split("/").some((part, index, parts) => !part || part === "." || (part === ".." && parts.slice(0, index).some((previous) => previous !== "..")))
72
+ && (value.operation === "read" || value.operation === "edit")
73
+ && (value.line === undefined || line(value.line))
74
+ && (value.endLine === undefined || (line(value.endLine) && line(value.line) && Number(value.endLine) >= Number(value.line)))
75
+ && (value.componentId === undefined || nonEmptyString(value.componentId, 512));
76
+ }
64
77
  const ORIGIN_PROTOCOLS = new Set(["http:", "https:"]);
65
78
  function isRecord(value) {
66
79
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1,3 +1,4 @@
1
+ import type { AgentActivity } from "./internal/agent-protocol.js";
1
2
  import type { AgentPromptRequest, AgentStatusSnapshot, AgentStatusUpdate, CanvasCommandResult } from "./internal/agent-protocol.js";
2
3
  import type { CanvasCommandInput } from "./bridge.js";
3
4
  import type { StoredProjectSession } from "./project.js";
@@ -7,6 +8,11 @@ export interface SessionHealth {
7
8
  readonly claimed: boolean;
8
9
  readonly status: AgentStatusSnapshot;
9
10
  }
11
+ /** Represents an HTTP response from a project bridge that could be classified. */
12
+ export declare class ProjectSessionResponseError extends Error {
13
+ readonly status: number;
14
+ constructor(status: number, message: string);
15
+ }
10
16
  export declare function readSessionHealth(session: StoredProjectSession, timeoutMs?: number): Promise<SessionHealth>;
11
17
  /** Owns one project's claim, heartbeat, and authenticated control requests. */
12
18
  export declare class ProjectSessionClient {
@@ -20,8 +26,9 @@ export declare class ProjectSessionClient {
20
26
  private send;
21
27
  private acquireClaim;
22
28
  private claim;
23
- waitForPrompt(signal?: AbortSignal): Promise<AgentPromptRequest>;
29
+ waitForPrompt(signal?: AbortSignal, agentClientName?: string): Promise<AgentPromptRequest>;
24
30
  getStatus(): Promise<AgentStatusSnapshot>;
31
+ reportActivity(activity: AgentActivity): Promise<void>;
25
32
  updateRequestStatus(update: AgentStatusUpdate): Promise<AgentStatusSnapshot>;
26
33
  dispatchCanvasCommand(input: CanvasCommandInput): Promise<CanvasCommandResult>;
27
34
  close(): Promise<void>;
@@ -2,6 +2,15 @@ import { randomUUID } from "node:crypto";
2
2
  import { AGENT_CONTROL_ENDPOINTS } from "./protocol.js";
3
3
  const CONTROL_TIMEOUT_MS = 5_000;
4
4
  const CANVAS_TIMEOUT_MS = 65_000;
5
+ /** Represents an HTTP response from a project bridge that could be classified. */
6
+ export class ProjectSessionResponseError extends Error {
7
+ status;
8
+ constructor(status, message) {
9
+ super(message);
10
+ this.name = "ProjectSessionResponseError";
11
+ this.status = status;
12
+ }
13
+ }
5
14
  async function requestJson(session, path, init = {}, { signal, timeoutMs = CONTROL_TIMEOUT_MS } = {}) {
6
15
  const response = await fetch(`${session.endpoint}${path}`, {
7
16
  ...init,
@@ -14,8 +23,9 @@ async function requestJson(session, path, init = {}, { signal, timeoutMs = CONTR
14
23
  },
15
24
  });
16
25
  const body = await response.json().catch(() => undefined);
17
- if (!response.ok)
18
- throw new Error(body?.error?.message ?? `Nudge project bridge returned ${response.status}.`);
26
+ if (!response.ok) {
27
+ throw new ProjectSessionResponseError(response.status, body?.error?.message ?? `Nudge project bridge returned ${response.status}.`);
28
+ }
19
29
  return body;
20
30
  }
21
31
  export async function readSessionHealth(session, timeoutMs = CONTROL_TIMEOUT_MS) {
@@ -62,14 +72,18 @@ export class ProjectSessionClient {
62
72
  this.claimPromise = null;
63
73
  }
64
74
  }
65
- async waitForPrompt(signal) {
75
+ async waitForPrompt(signal, agentClientName) {
66
76
  await this.claim();
67
- return await this.send(AGENT_CONTROL_ENDPOINTS.listen, {}, { signal, timeoutMs: null });
77
+ return await this.send(AGENT_CONTROL_ENDPOINTS.listen, { agentClientName }, { signal, timeoutMs: null });
68
78
  }
69
79
  async getStatus() {
70
80
  const health = await readSessionHealth(this.descriptor);
71
81
  return health.status;
72
82
  }
83
+ async reportActivity(activity) {
84
+ await this.claim();
85
+ await this.send(AGENT_CONTROL_ENDPOINTS.activity, { activity });
86
+ }
73
87
  async updateRequestStatus(update) {
74
88
  await this.claim();
75
89
  return await this.send(AGENT_CONTROL_ENDPOINTS.reportStatus, { update });
@@ -5,12 +5,13 @@
5
5
  * subpath keeps transport names discoverable for Node consumers while
6
6
  * preserving one protocol vocabulary for browser and agent adapters.
7
7
  */
8
- export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, CANVAS_COMMAND_TYPES, canonicalOrigin, defaultBridgePort, isAgentPromptRequest, isAgentSketchAttachment, isAgentSketchCaptureMetadata, isAgentSketchMetadata, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateSketchAttachments, validateRoutes, } from "./internal/agent-protocol.js";
9
- export type { AgentConnectionState, AgentProjectIdentity, AgentPromptRequest, AgentDeliveredPrompt, AgentSketchAttachment, 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, } from "./internal/agent-protocol.js";
8
+ export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, CANVAS_COMMAND_TYPES, canonicalOrigin, defaultBridgePort, isAgentPromptRequest, isAgentSketchAttachment, isAgentSketchCaptureMetadata, isAgentSketchMetadata, isAgentStatusUpdate, isAgentActivity, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateSketchAttachments, validateRoutes, } from "./internal/agent-protocol.js";
9
+ export type { AgentActivity, BridgeActivityEvent, AgentConnectionState, AgentProjectIdentity, AgentPromptRequest, AgentDeliveredPrompt, AgentSketchAttachment, 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, } from "./internal/agent-protocol.js";
10
10
  /** Stable endpoint names used by the loopback browser bridge. */
11
11
  export declare const BRIDGE_ENDPOINTS: {
12
12
  readonly health: "/health";
13
13
  readonly pair: "/pair";
14
+ readonly takeover: "/takeover";
14
15
  readonly events: "/events";
15
16
  readonly prompt: "/prompt";
16
17
  readonly status: "/status";
@@ -26,6 +27,7 @@ export declare const AGENT_CONTROL_ENDPOINTS: {
26
27
  readonly listen: "/__nudge/agent/listen";
27
28
  readonly status: "/__nudge/agent/status";
28
29
  readonly reportStatus: "/__nudge/agent/report-status";
30
+ readonly activity: "/__nudge/agent/activity";
29
31
  readonly canvas: "/__nudge/agent/canvas";
30
32
  };
31
33
  export type BridgeEndpoint = (typeof BRIDGE_ENDPOINTS)[keyof typeof BRIDGE_ENDPOINTS];
package/dist/protocol.js CHANGED
@@ -5,11 +5,12 @@
5
5
  * subpath keeps transport names discoverable for Node consumers while
6
6
  * preserving one protocol vocabulary for browser and agent adapters.
7
7
  */
8
- export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, CANVAS_COMMAND_TYPES, canonicalOrigin, defaultBridgePort, isAgentPromptRequest, isAgentSketchAttachment, isAgentSketchCaptureMetadata, isAgentSketchMetadata, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateSketchAttachments, validateRoutes, } from "./internal/agent-protocol.js";
8
+ export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, CANVAS_COMMAND_TYPES, canonicalOrigin, defaultBridgePort, isAgentPromptRequest, isAgentSketchAttachment, isAgentSketchCaptureMetadata, isAgentSketchMetadata, isAgentStatusUpdate, isAgentActivity, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateSketchAttachments, validateRoutes, } from "./internal/agent-protocol.js";
9
9
  /** Stable endpoint names used by the loopback browser bridge. */
10
10
  export const BRIDGE_ENDPOINTS = {
11
11
  health: "/health",
12
12
  pair: "/pair",
13
+ takeover: "/takeover",
13
14
  events: "/events",
14
15
  prompt: "/prompt",
15
16
  status: "/status",
@@ -25,5 +26,6 @@ export const AGENT_CONTROL_ENDPOINTS = {
25
26
  listen: "/__nudge/agent/listen",
26
27
  status: "/__nudge/agent/status",
27
28
  reportStatus: "/__nudge/agent/report-status",
29
+ activity: "/__nudge/agent/activity",
28
30
  canvas: "/__nudge/agent/canvas",
29
31
  };
package/dist/server.d.ts CHANGED
@@ -2,6 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
3
3
  import type { AgentProjectIdentity } from "./internal/agent-protocol.js";
4
4
  import { type BrowserBridge, type BrowserBridgeOptions, type BridgeAddress } from "./bridge.js";
5
+ import { AdapterRouting } from "./adapterRouting.js";
5
6
  import { DiscoveredProjectRouter } from "./discovery.js";
6
7
  export declare const MCP_SERVER_NAME = "nudge-ui";
7
8
  export declare const MCP_SERVER_VERSION: string;
@@ -29,13 +30,13 @@ export interface AgentCompanion {
29
30
  export declare function createAgentCompanion(options: AgentCompanionOptions): AgentCompanion;
30
31
  export interface DiscoveredAgentAdapter {
31
32
  readonly mcpServer: McpServer;
32
- readonly router: DiscoveredProjectRouter;
33
+ readonly router: DiscoveredProjectRouter | AdapterRouting;
33
34
  start(transport?: Transport): Promise<void>;
34
35
  close(): Promise<void>;
35
36
  }
36
37
  /** Creates a stdio MCP adapter that discovers project-owned bridges. */
37
38
  export declare function createDiscoveredAgentAdapter(options: {
38
- readonly workspaceRoot: string;
39
+ readonly workspaceRoot?: string;
39
40
  readonly registryRoot?: string;
40
41
  readonly serverName?: string;
41
42
  readonly serverVersion?: string;