@particle-academy/agent-integrations 0.34.0 → 0.36.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.
package/dist/index.d.cts CHANGED
@@ -24,8 +24,10 @@ export { TerminalBridge, TerminalBridgeAdapter, TerminalBridgeOptions, TerminalR
24
24
  export { CatalogBridgeAdapter, CatalogBridgeOptions, CatalogCheckoutArgs, CatalogPrice, CatalogProduct, registerCatalogBridge } from './bridges/catalog.cjs';
25
25
  export { FeatureDefinition, FeatureGrant, FeaturesBridgeAdapter, FeaturesBridgeOptions, registerFeaturesBridge } from './bridges/features.cjs';
26
26
  export { PASSKEY_BRIDGE_TOOLS, PasskeyBridgeAdapter, PasskeyBridgeOptions, PasskeyProposal, PasskeySummaryLike, PasskeySupport, PasskeySurfaceState, registerPasskeyBridge } from './bridges/passkeys.cjs';
27
- export { DocAdapter, DocBridgeOptions, DocOpTool, registerDocBridge } from './bridges/doc.cjs';
27
+ import { DocAdapter } from './bridges/doc.cjs';
28
+ export { DocBridgeOptions, DocOpTool, registerDocBridge } from './bridges/doc.cjs';
28
29
  export { CmsBridgeOptions, CmsOp, cmsReducer, registerCmsBridge } from './bridges/cms.cjs';
30
+ import { DocNode, StagePolicy, TreeOp } from '@particle-academy/fancy-doc-commons';
29
31
  import { RelayState } from './sharing/index.cjs';
30
32
  export { SseRelayOptions, SseRelayTransport, attachSseRelay } from './sharing/index.cjs';
31
33
  import { S as SessionDescriptor } from './token-BZ8doMam.cjs';
@@ -40,7 +42,6 @@ export { CLAUDE_CONNECTORS_URL, CONNECTOR_GLYPHS, CONNECTOR_TARGETS, ClaudeMark,
40
42
  export { D as DEFAULT_MCPB_ENTRY_POINT, b as MCPB_MANIFEST_VERSION, c as MCPB_MIN_NODE, M as McpbManifestInput, a as McpbTool, d as buildMcpbManifest, e as buildMcpbProxyStub } from './mcpb-BXOrsRnv.cjs';
41
43
  import '@particle-academy/fancy-sheets';
42
44
  import '@particle-academy/fancy-slides';
43
- import '@particle-academy/fancy-doc-commons';
44
45
 
45
46
  /**
46
47
  * One interactive element the agent can act on, addressed by a STABLE handle
@@ -159,6 +160,46 @@ type NavigationBridgeOptions = {
159
160
  */
160
161
  declare function registerNavigationBridge(host: ToolHost, options: NavigationBridgeOptions): Bridge;
161
162
 
163
+ /**
164
+ * Reserved node type for a literal string child. Must match fancy-screens'
165
+ * `TEXT_NODE_TYPE`; duplicated rather than imported to keep this bridge free of
166
+ * a fancy-screens dependency.
167
+ */
168
+ declare const SCREEN_TEXT_NODE_TYPE = "#text";
169
+ /** A screen document node — a `DocNode` plus the minted-id flag. */
170
+ interface ScreenDocNode extends DocNode {
171
+ /**
172
+ * True when fancy-screens minted this id because the author supplied none.
173
+ * A synthetic id is derived from position, so it is not a durable handle.
174
+ */
175
+ synthetic?: boolean;
176
+ }
177
+ type ScreenDocOp = TreeOp<ScreenDocNode>;
178
+ interface ScreenDocBridgeOptions {
179
+ adapter: DocAdapter<ScreenDocNode>;
180
+ /**
181
+ * Tool-name prefix. Default `"screen"` → `screen_add`, `screen_set_text`, …
182
+ *
183
+ * Tool names are global, so a host wiring more than one doc-driven screen at
184
+ * once must give each a distinct prefix (`"screen_checkout"`).
185
+ */
186
+ surface?: string;
187
+ agent?: {
188
+ id: string;
189
+ name?: string;
190
+ color?: string;
191
+ };
192
+ /** Auto-apply vs. confirm per op (trust-but-verify). Default: auto. */
193
+ stagePolicy?: StagePolicy<ScreenDocOp>;
194
+ onPending?: (staged: {
195
+ id: string;
196
+ op: ScreenDocOp;
197
+ label: string;
198
+ }) => void;
199
+ }
200
+ /** Register the screen-content bridge (prefix `screen_`) over a screen's `DocTree`. */
201
+ declare function registerScreenDocBridge(host: ToolHost, options: ScreenDocBridgeOptions): Bridge;
202
+
162
203
  /** A thing the human did, surfaced so the connected agent stays aware. */
163
204
  type CoBrowseUserEvent = {
164
205
  kind: "navigation";
@@ -213,6 +254,18 @@ type CoBrowseSession = {
213
254
  server: MicroMcpServer | null;
214
255
  session: SessionDescriptor | null;
215
256
  relayState: RelayState;
257
+ /**
258
+ * True once at least one remote agent is actually attached to the session.
259
+ *
260
+ * Distinct from `relayState === "open"`, which only says the BROWSER reached
261
+ * the relay — true the moment sharing starts, before the link has been handed
262
+ * to anyone. A UI that keys "Agent is driving" off the relay state therefore
263
+ * announces a driver who does not exist, and stays silent when a real one
264
+ * arrives.
265
+ */
266
+ agentConnected: boolean;
267
+ /** How many remote agents are attached (0 when nobody has joined). */
268
+ agentCount: number;
216
269
  startShare: () => Promise<void>;
217
270
  stopShare: () => void;
218
271
  /**
@@ -440,8 +493,8 @@ type CoBrowsePresenceProps = {
440
493
  };
441
494
  /**
442
495
  * The human's view of a site-wide co-browsing session: a "Let an agent drive"
443
- * starter, the share/connect surface once started, a live "agent is driving"
444
- * status with the latest action, and a Stop / take-back-control button.
496
+ * starter, the share/connect surface until an agent arrives, a live log of what
497
+ * it is doing once one has, and a Stop / take-back-control button.
445
498
  *
446
499
  * Staged-action confirms are rendered by the HOST (via the navigation adapter's
447
500
  * `confirm`), so this component stays presentation-only.
@@ -456,6 +509,15 @@ type CoBrowseCursorLayerProps = {
456
509
  active?: boolean;
457
510
  /** Stacking order of the overlay. Default just under the max. */
458
511
  zIndex?: number;
512
+ /**
513
+ * Retire the cursor after this long without a tool call. Default 15s;
514
+ * `0` keeps it on screen indefinitely.
515
+ *
516
+ * A cursor that sits still is not neutral — it reads as "the agent is
517
+ * broken", which is worse than showing nothing. Continuous presence belongs
518
+ * in the activity log; the cursor is for *motion*.
519
+ */
520
+ idleAfterMs?: number;
459
521
  };
460
522
  /**
461
523
  * Page-wide agent presence for co-browsing — the missing "all actors present"
@@ -469,7 +531,7 @@ type CoBrowseCursorLayerProps = {
469
531
  * Mount it once near the app root (e.g. in your CoBrowseProvider), gated on an
470
532
  * active session. SSR-safe: renders null on the server / until the first action.
471
533
  */
472
- declare function CoBrowseCursorLayer({ active, zIndex }: CoBrowseCursorLayerProps): react.ReactPortal | null;
534
+ declare function CoBrowseCursorLayer({ active, zIndex, idleAfterMs, }: CoBrowseCursorLayerProps): react.ReactPortal | null;
473
535
  declare namespace CoBrowseCursorLayer {
474
536
  var displayName: string;
475
537
  }
@@ -504,4 +566,4 @@ type SimulateUsersButtonProps = {
504
566
  */
505
567
  declare function SimulateUsersButton({ endpoint, count, label, csrfToken, onTriggered, onError, className, style, }: SimulateUsersButtonProps): react.JSX.Element;
506
568
 
507
- export { type AgentActivity, AgentActivityHighlight, type AgentActivityHighlightProps, AgentCursor, type AgentCursorProps, AgentPanel, type AgentPanelProps, Bridge, BridgedForm, type BridgedFormProps, CoBrowseCursorLayer, type CoBrowseCursorLayerProps, CoBrowsePresence, type CoBrowsePresenceProps, type CoBrowseSession, type CoBrowseUserEvent, FormFieldDescriptor, MicroMcpServer, type NavigationBridgeAdapter, type NavigationBridgeOptions, type NavigationConfirmRequest, type PageAction, type PageSnapshot, RelayState, ScreensActivityBridge, type ScreensActivityBridgeProps, SessionDescriptor, ShareControls, type ShareControlsProps, SimulateUsersButton, type SimulateUsersButtonProps, ToolHost, type UseCoBrowseSessionOptions, buildAgentPrompt, registerNavigationBridge, useCoBrowseSession };
569
+ export { type AgentActivity, AgentActivityHighlight, type AgentActivityHighlightProps, AgentCursor, type AgentCursorProps, AgentPanel, type AgentPanelProps, Bridge, BridgedForm, type BridgedFormProps, CoBrowseCursorLayer, type CoBrowseCursorLayerProps, CoBrowsePresence, type CoBrowsePresenceProps, type CoBrowseSession, type CoBrowseUserEvent, DocAdapter, FormFieldDescriptor, MicroMcpServer, type NavigationBridgeAdapter, type NavigationBridgeOptions, type NavigationConfirmRequest, type PageAction, type PageSnapshot, RelayState, SCREEN_TEXT_NODE_TYPE, type ScreenDocBridgeOptions, type ScreenDocNode, type ScreenDocOp, ScreensActivityBridge, type ScreensActivityBridgeProps, SessionDescriptor, ShareControls, type ShareControlsProps, SimulateUsersButton, type SimulateUsersButtonProps, ToolHost, type UseCoBrowseSessionOptions, buildAgentPrompt, registerNavigationBridge, registerScreenDocBridge, useCoBrowseSession };
package/dist/index.d.ts CHANGED
@@ -24,8 +24,10 @@ export { TerminalBridge, TerminalBridgeAdapter, TerminalBridgeOptions, TerminalR
24
24
  export { CatalogBridgeAdapter, CatalogBridgeOptions, CatalogCheckoutArgs, CatalogPrice, CatalogProduct, registerCatalogBridge } from './bridges/catalog.js';
25
25
  export { FeatureDefinition, FeatureGrant, FeaturesBridgeAdapter, FeaturesBridgeOptions, registerFeaturesBridge } from './bridges/features.js';
26
26
  export { PASSKEY_BRIDGE_TOOLS, PasskeyBridgeAdapter, PasskeyBridgeOptions, PasskeyProposal, PasskeySummaryLike, PasskeySupport, PasskeySurfaceState, registerPasskeyBridge } from './bridges/passkeys.js';
27
- export { DocAdapter, DocBridgeOptions, DocOpTool, registerDocBridge } from './bridges/doc.js';
27
+ import { DocAdapter } from './bridges/doc.js';
28
+ export { DocBridgeOptions, DocOpTool, registerDocBridge } from './bridges/doc.js';
28
29
  export { CmsBridgeOptions, CmsOp, cmsReducer, registerCmsBridge } from './bridges/cms.js';
30
+ import { DocNode, StagePolicy, TreeOp } from '@particle-academy/fancy-doc-commons';
29
31
  import { RelayState } from './sharing/index.js';
30
32
  export { SseRelayOptions, SseRelayTransport, attachSseRelay } from './sharing/index.js';
31
33
  import { S as SessionDescriptor } from './token-BZ8doMam.js';
@@ -40,7 +42,6 @@ export { CLAUDE_CONNECTORS_URL, CONNECTOR_GLYPHS, CONNECTOR_TARGETS, ClaudeMark,
40
42
  export { D as DEFAULT_MCPB_ENTRY_POINT, b as MCPB_MANIFEST_VERSION, c as MCPB_MIN_NODE, M as McpbManifestInput, a as McpbTool, d as buildMcpbManifest, e as buildMcpbProxyStub } from './mcpb-BXOrsRnv.js';
41
43
  import '@particle-academy/fancy-sheets';
42
44
  import '@particle-academy/fancy-slides';
43
- import '@particle-academy/fancy-doc-commons';
44
45
 
45
46
  /**
46
47
  * One interactive element the agent can act on, addressed by a STABLE handle
@@ -159,6 +160,46 @@ type NavigationBridgeOptions = {
159
160
  */
160
161
  declare function registerNavigationBridge(host: ToolHost, options: NavigationBridgeOptions): Bridge;
161
162
 
163
+ /**
164
+ * Reserved node type for a literal string child. Must match fancy-screens'
165
+ * `TEXT_NODE_TYPE`; duplicated rather than imported to keep this bridge free of
166
+ * a fancy-screens dependency.
167
+ */
168
+ declare const SCREEN_TEXT_NODE_TYPE = "#text";
169
+ /** A screen document node — a `DocNode` plus the minted-id flag. */
170
+ interface ScreenDocNode extends DocNode {
171
+ /**
172
+ * True when fancy-screens minted this id because the author supplied none.
173
+ * A synthetic id is derived from position, so it is not a durable handle.
174
+ */
175
+ synthetic?: boolean;
176
+ }
177
+ type ScreenDocOp = TreeOp<ScreenDocNode>;
178
+ interface ScreenDocBridgeOptions {
179
+ adapter: DocAdapter<ScreenDocNode>;
180
+ /**
181
+ * Tool-name prefix. Default `"screen"` → `screen_add`, `screen_set_text`, …
182
+ *
183
+ * Tool names are global, so a host wiring more than one doc-driven screen at
184
+ * once must give each a distinct prefix (`"screen_checkout"`).
185
+ */
186
+ surface?: string;
187
+ agent?: {
188
+ id: string;
189
+ name?: string;
190
+ color?: string;
191
+ };
192
+ /** Auto-apply vs. confirm per op (trust-but-verify). Default: auto. */
193
+ stagePolicy?: StagePolicy<ScreenDocOp>;
194
+ onPending?: (staged: {
195
+ id: string;
196
+ op: ScreenDocOp;
197
+ label: string;
198
+ }) => void;
199
+ }
200
+ /** Register the screen-content bridge (prefix `screen_`) over a screen's `DocTree`. */
201
+ declare function registerScreenDocBridge(host: ToolHost, options: ScreenDocBridgeOptions): Bridge;
202
+
162
203
  /** A thing the human did, surfaced so the connected agent stays aware. */
163
204
  type CoBrowseUserEvent = {
164
205
  kind: "navigation";
@@ -213,6 +254,18 @@ type CoBrowseSession = {
213
254
  server: MicroMcpServer | null;
214
255
  session: SessionDescriptor | null;
215
256
  relayState: RelayState;
257
+ /**
258
+ * True once at least one remote agent is actually attached to the session.
259
+ *
260
+ * Distinct from `relayState === "open"`, which only says the BROWSER reached
261
+ * the relay — true the moment sharing starts, before the link has been handed
262
+ * to anyone. A UI that keys "Agent is driving" off the relay state therefore
263
+ * announces a driver who does not exist, and stays silent when a real one
264
+ * arrives.
265
+ */
266
+ agentConnected: boolean;
267
+ /** How many remote agents are attached (0 when nobody has joined). */
268
+ agentCount: number;
216
269
  startShare: () => Promise<void>;
217
270
  stopShare: () => void;
218
271
  /**
@@ -440,8 +493,8 @@ type CoBrowsePresenceProps = {
440
493
  };
441
494
  /**
442
495
  * The human's view of a site-wide co-browsing session: a "Let an agent drive"
443
- * starter, the share/connect surface once started, a live "agent is driving"
444
- * status with the latest action, and a Stop / take-back-control button.
496
+ * starter, the share/connect surface until an agent arrives, a live log of what
497
+ * it is doing once one has, and a Stop / take-back-control button.
445
498
  *
446
499
  * Staged-action confirms are rendered by the HOST (via the navigation adapter's
447
500
  * `confirm`), so this component stays presentation-only.
@@ -456,6 +509,15 @@ type CoBrowseCursorLayerProps = {
456
509
  active?: boolean;
457
510
  /** Stacking order of the overlay. Default just under the max. */
458
511
  zIndex?: number;
512
+ /**
513
+ * Retire the cursor after this long without a tool call. Default 15s;
514
+ * `0` keeps it on screen indefinitely.
515
+ *
516
+ * A cursor that sits still is not neutral — it reads as "the agent is
517
+ * broken", which is worse than showing nothing. Continuous presence belongs
518
+ * in the activity log; the cursor is for *motion*.
519
+ */
520
+ idleAfterMs?: number;
459
521
  };
460
522
  /**
461
523
  * Page-wide agent presence for co-browsing — the missing "all actors present"
@@ -469,7 +531,7 @@ type CoBrowseCursorLayerProps = {
469
531
  * Mount it once near the app root (e.g. in your CoBrowseProvider), gated on an
470
532
  * active session. SSR-safe: renders null on the server / until the first action.
471
533
  */
472
- declare function CoBrowseCursorLayer({ active, zIndex }: CoBrowseCursorLayerProps): react.ReactPortal | null;
534
+ declare function CoBrowseCursorLayer({ active, zIndex, idleAfterMs, }: CoBrowseCursorLayerProps): react.ReactPortal | null;
473
535
  declare namespace CoBrowseCursorLayer {
474
536
  var displayName: string;
475
537
  }
@@ -504,4 +566,4 @@ type SimulateUsersButtonProps = {
504
566
  */
505
567
  declare function SimulateUsersButton({ endpoint, count, label, csrfToken, onTriggered, onError, className, style, }: SimulateUsersButtonProps): react.JSX.Element;
506
568
 
507
- export { type AgentActivity, AgentActivityHighlight, type AgentActivityHighlightProps, AgentCursor, type AgentCursorProps, AgentPanel, type AgentPanelProps, Bridge, BridgedForm, type BridgedFormProps, CoBrowseCursorLayer, type CoBrowseCursorLayerProps, CoBrowsePresence, type CoBrowsePresenceProps, type CoBrowseSession, type CoBrowseUserEvent, FormFieldDescriptor, MicroMcpServer, type NavigationBridgeAdapter, type NavigationBridgeOptions, type NavigationConfirmRequest, type PageAction, type PageSnapshot, RelayState, ScreensActivityBridge, type ScreensActivityBridgeProps, SessionDescriptor, ShareControls, type ShareControlsProps, SimulateUsersButton, type SimulateUsersButtonProps, ToolHost, type UseCoBrowseSessionOptions, buildAgentPrompt, registerNavigationBridge, useCoBrowseSession };
569
+ export { type AgentActivity, AgentActivityHighlight, type AgentActivityHighlightProps, AgentCursor, type AgentCursorProps, AgentPanel, type AgentPanelProps, Bridge, BridgedForm, type BridgedFormProps, CoBrowseCursorLayer, type CoBrowseCursorLayerProps, CoBrowsePresence, type CoBrowsePresenceProps, type CoBrowseSession, type CoBrowseUserEvent, DocAdapter, FormFieldDescriptor, MicroMcpServer, type NavigationBridgeAdapter, type NavigationBridgeOptions, type NavigationConfirmRequest, type PageAction, type PageSnapshot, RelayState, SCREEN_TEXT_NODE_TYPE, type ScreenDocBridgeOptions, type ScreenDocNode, type ScreenDocOp, ScreensActivityBridge, type ScreensActivityBridgeProps, SessionDescriptor, ShareControls, type ShareControlsProps, SimulateUsersButton, type SimulateUsersButtonProps, ToolHost, type UseCoBrowseSessionOptions, buildAgentPrompt, registerNavigationBridge, registerScreenDocBridge, useCoBrowseSession };
package/dist/index.js CHANGED
@@ -2,18 +2,19 @@ export { useSheetsActivityHighlights, useSheetsAdapter } from './chunk-KHKSQEMC.
2
2
  import { ConnectorButtons } from './chunk-5HNTNIWY.js';
3
3
  export { CLAUDE_CONNECTORS_URL, CONNECTOR_GLYPHS, CONNECTOR_TARGETS, ClaudeMark, ConnectorButtons, CursorMark, DesktopMark, VscodeMark, WrenchMark, buildCursorDeeplink, buildManualConfig, buildManualConfigSnippet, buildVscodeDeeplink, connectorHref, encodeBase64Json, slugifyServerName } from './chunk-5HNTNIWY.js';
4
4
  export { DEFAULT_MCPB_ENTRY_POINT, MCPB_MANIFEST_VERSION, MCPB_MIN_NODE, buildMcpbManifest, buildMcpbProxyStub } from './chunk-GO2Y6H6U.js';
5
- import { ShareControls, AgentActivityHighlight, AgentCursor } from './chunk-SJTLTITO.js';
6
- export { AgentActivityHighlight, AgentCursor, AgentPanel, ShareControls, buildAgentPrompt } from './chunk-SJTLTITO.js';
5
+ import { ShareControls, AgentActivityHighlight, AgentCursor } from './chunk-ABP2THQV.js';
6
+ export { AgentActivityHighlight, AgentCursor, AgentPanel, ShareControls, buildAgentPrompt } from './chunk-ABP2THQV.js';
7
7
  import './chunk-IJ6JX5VC.js';
8
- import { createSessionDescriptor, attachSseRelay } from './chunk-N6RLSLOF.js';
9
- export { SseRelayTransport, attachSseRelay, buildShareConfig, buildShareUrl, createSessionDescriptor, describeSession, readSessionFromUrl } from './chunk-N6RLSLOF.js';
8
+ import { createSessionDescriptor, attachSseRelay } from './chunk-JDCB2NMM.js';
9
+ export { SseRelayTransport, attachSseRelay, buildShareConfig, buildShareUrl, createSessionDescriptor, describeSession, readSessionFromUrl } from './chunk-JDCB2NMM.js';
10
10
  export { attachHeuristicsSink, mapActivityToEvent as mapActivityToHeuristicsEvent } from './chunk-7R6RMROL.js';
11
11
  export { useUndoStack } from './chunk-BT7HM5I6.js';
12
12
  export { registerCatalogBridge } from './chunk-OAFJ53X3.js';
13
13
  export { registerFeaturesBridge } from './chunk-ZNGTC4CO.js';
14
14
  export { PASSKEY_BRIDGE_TOOLS, registerPasskeyBridge } from './chunk-7THGQUX3.js';
15
- export { cmsReducer, registerCmsBridge } from './chunk-DKSR4R4O.js';
16
- export { registerDocBridge } from './chunk-LQZKRBWR.js';
15
+ export { cmsReducer, registerCmsBridge } from './chunk-JJUABMAP.js';
16
+ import { registerDocBridge } from './chunk-XY2TDE6E.js';
17
+ export { registerDocBridge } from './chunk-XY2TDE6E.js';
17
18
  export { registerTuiBridge } from './chunk-MFMBR6LN.js';
18
19
  import { useAgentActivity } from './chunk-UCKJAUBY.js';
19
20
  export { useAgentActivity, useAgentActivityForScreen } from './chunk-UCKJAUBY.js';
@@ -39,8 +40,9 @@ import { wrapToolWithActivity } from './chunk-2VENWGLZ.js';
39
40
  export { wrapToolWithActivity } from './chunk-2VENWGLZ.js';
40
41
  import { emitActivity, onActivity } from './chunk-C3TYI5TJ.js';
41
42
  export { emitActivity, onActivity, readActivityHistory, resetActivityRegistry } from './chunk-C3TYI5TJ.js';
42
- import { MicroMcpServer, textResult, errorResult } from './chunk-DF3PLL2E.js';
43
+ import { textResult, MicroMcpServer, errorResult } from './chunk-DF3PLL2E.js';
43
44
  export { MCP_PROTOCOL_VERSION, MicroMcpServer, ToolRegistry, errorResult, rpcError, textResult } from './chunk-DF3PLL2E.js';
45
+ import { childrenOf, appendOrder } from '@particle-academy/fancy-doc-commons';
44
46
  import { useRef, useState, useEffect, useCallback, useMemo } from 'react';
45
47
  import { jsx, jsxs } from 'react/jsx-runtime';
46
48
  import { createPortal } from 'react-dom';
@@ -308,6 +310,90 @@ function registerNavigationBridge(host, options) {
308
310
  }
309
311
  };
310
312
  }
313
+ var SCREEN_TEXT_NODE_TYPE = "#text";
314
+ function registerScreenDocBridge(host, options) {
315
+ const { adapter } = options;
316
+ const surface = options.surface ?? "screen";
317
+ const setText = {
318
+ verb: "set_text",
319
+ description: "Replace the literal text of an element node \u2014 the natural way to change a label, heading or paragraph. Pass the ELEMENT's id, not the text node's.",
320
+ properties: {
321
+ id: { type: "string", description: "Id of the element whose text to set." },
322
+ text: { type: "string" }
323
+ },
324
+ required: ["id", "text"],
325
+ build: (args, tree) => {
326
+ const id = String(args.id);
327
+ const node = tree.nodes[id];
328
+ if (!node) return { error: `No node ${id}` };
329
+ if (node.type === SCREEN_TEXT_NODE_TYPE) {
330
+ return { t: "set_props", id, patch: { value: String(args.text) } };
331
+ }
332
+ const texts = childrenOf(tree, id).filter((c) => c.type === SCREEN_TEXT_NODE_TYPE);
333
+ if (texts.length === 1) {
334
+ return { t: "set_props", id: texts[0].id, patch: { value: String(args.text) } };
335
+ }
336
+ if (texts.length === 0) {
337
+ return {
338
+ t: "insert",
339
+ node: {
340
+ id: `${id}-text`,
341
+ type: SCREEN_TEXT_NODE_TYPE,
342
+ parent: id,
343
+ order: appendOrder(tree, id),
344
+ props: { value: String(args.text) }
345
+ }
346
+ };
347
+ }
348
+ return {
349
+ error: `Node ${id} has ${texts.length} text children (${texts.map((t) => t.id).join(", ")}) \u2014 set_text on the one you mean`
350
+ };
351
+ },
352
+ targetId: (args) => String(args.id)
353
+ };
354
+ const bridge = registerDocBridge(host, {
355
+ adapter,
356
+ surface,
357
+ agent: options.agent,
358
+ stagePolicy: options.stagePolicy,
359
+ onPending: options.onPending,
360
+ // Anything the AGENT adds is authored by definition — it chose or was given
361
+ // the id — so it must not inherit the synthetic flag from its neighbours.
362
+ makeNode: ({ id, type, parent, order }, props) => ({ id, type, parent, order, props }),
363
+ describeNode: (n) => ({
364
+ id: n.id,
365
+ type: n.type,
366
+ parent: n.parent,
367
+ ...n.synthetic ? { synthetic: true } : {},
368
+ ...n.type === SCREEN_TEXT_NODE_TYPE ? { text: String(n.props?.value ?? "") } : { props: Object.keys(n.props ?? {}) }
369
+ }),
370
+ opTools: [setText]
371
+ });
372
+ const disposeAddressable = host.registerTool(
373
+ {
374
+ name: `${surface}_addressable`,
375
+ description: "List the node ids that are safe to remember and use later. Ids NOT listed here were minted from a node's position: they resolve today and silently point at a different node once a sibling is inserted above them. Address those in the same turn you read them, and never store one.",
376
+ inputSchema: { type: "object", properties: {}, required: [], additionalProperties: false }
377
+ },
378
+ (async () => {
379
+ const nodes = Object.values(adapter.get().nodes);
380
+ const authored = nodes.filter((n) => !n.synthetic).map((n) => ({ id: n.id, type: n.type }));
381
+ const syntheticCount = nodes.length - authored.length;
382
+ return textResult(
383
+ authored.length ? `${authored.length} addressable, ${syntheticCount} positional` : `no addressable ids \u2014 all ${syntheticCount} nodes are positional`,
384
+ { addressable: authored, syntheticCount }
385
+ );
386
+ })
387
+ );
388
+ return {
389
+ id: surface,
390
+ title: "Screen content",
391
+ dispose: () => {
392
+ disposeAddressable();
393
+ bridge.dispose();
394
+ }
395
+ };
396
+ }
311
397
  var DEFAULT_AGENT2 = { id: "agent", name: "Agent", color: "#a855f7" };
312
398
  var USER = { id: "human", name: "You" };
313
399
  function useCoBrowseSession(options) {
@@ -320,6 +406,7 @@ function useCoBrowseSession(options) {
320
406
  const disposeBridge = useRef(null);
321
407
  const [session, setSession] = useState(null);
322
408
  const [relayState, setRelayState] = useState("idle");
409
+ const [agentCount, setAgentCount] = useState(0);
323
410
  useEffect(() => {
324
411
  const server = new MicroMcpServer({
325
412
  info: options.info ?? { name: "fancy-co-browse", version: "0.1.0" },
@@ -354,6 +441,7 @@ function useCoBrowseSession(options) {
354
441
  }
355
442
  const relay = attachSseRelay(server, { baseUrl: relayBaseUrl, sessionId: descriptor.id, token: descriptor.token });
356
443
  relay.onStateChange(setRelayState);
444
+ relay.onPeersChange(setAgentCount);
357
445
  relayRef.current = relay;
358
446
  setSession(descriptor);
359
447
  }, [relayBaseUrl, options]);
@@ -362,6 +450,7 @@ function useCoBrowseSession(options) {
362
450
  relayRef.current?.close();
363
451
  relayRef.current = null;
364
452
  setRelayState("idle");
453
+ setAgentCount(0);
365
454
  setSession(null);
366
455
  if (current) {
367
456
  const csrf = options.csrfToken?.() ?? "";
@@ -384,7 +473,16 @@ function useCoBrowseSession(options) {
384
473
  meta: event
385
474
  });
386
475
  }, []);
387
- return { server: serverRef.current, session, relayState, startShare, stopShare, observeUser };
476
+ return {
477
+ server: serverRef.current,
478
+ session,
479
+ relayState,
480
+ agentConnected: agentCount > 0,
481
+ agentCount,
482
+ startShare,
483
+ stopShare,
484
+ observeUser
485
+ };
388
486
  }
389
487
  function BridgedForm({
390
488
  id,
@@ -478,10 +576,12 @@ function ScreensActivityBridge({ system, fadeMs = 1500 }) {
478
576
  }, [system, fadeMs]);
479
577
  return null;
480
578
  }
579
+ var LIFECYCLE_ACTIONS = /* @__PURE__ */ new Set(["agent_connected", "agent_disconnected"]);
481
580
  function CoBrowsePresence({ session, connectUrl, shareBaseUrl, className }) {
482
581
  const { events } = useAgentActivity(void 0, { capacity: 40 });
483
- const lastAgentAction = [...events].reverse().find((e) => (e.source ?? "agent") !== "user");
484
- const connected = session.relayState === "open";
582
+ const lastAgentAction = [...events].reverse().find((e) => (e.source ?? "agent") !== "user" && !LIFECYCLE_ACTIONS.has(e.action));
583
+ const connected = session.agentConnected;
584
+ const activity = useMemo(() => events.map(toPanelActivity), [events]);
485
585
  if (!session.session) {
486
586
  return /* @__PURE__ */ jsx("div", { className, "data-co-browse-presence": "idle", children: /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void session.startShare(), "data-co-browse-start": true, children: "Let an agent drive" }) });
487
587
  }
@@ -499,12 +599,25 @@ function CoBrowsePresence({ session, connectUrl, shareBaseUrl, className }) {
499
599
  onStart: () => void session.startShare(),
500
600
  onStop: session.stopShare,
501
601
  status: session.relayState,
602
+ agentConnected: connected,
603
+ activity,
502
604
  shareBaseUrl
503
605
  }
504
606
  ),
505
607
  connectUrl && /* @__PURE__ */ jsx(ConnectorButtons, { serverName: "Fancy UI co-browse", mcpUrl: connectUrl })
506
608
  ] });
507
609
  }
610
+ function toPanelActivity(event) {
611
+ const who = (event.source ?? "agent") === "user" ? "You" : event.agentName ?? "Agent";
612
+ return {
613
+ id: `${event.timestamp}-${event.action}-${event.target?.elementId ?? event.target?.label ?? ""}`,
614
+ at: event.timestamp,
615
+ kind: LIFECYCLE_ACTIONS.has(event.action) ? "info" : "tool",
616
+ source: who,
617
+ text: event.target?.label ?? event.action,
618
+ detail: event.meta
619
+ };
620
+ }
508
621
  CoBrowsePresence.displayName = "CoBrowsePresence";
509
622
  var CURSOR_EDGE_INSET = 24;
510
623
  var FLUID_CURSOR_SPEED_PX_PER_SECOND = 1050;
@@ -523,19 +636,26 @@ function fluidGlideDuration(from, to) {
523
636
  const distance = Math.hypot(to.x - from.x, to.y - from.y);
524
637
  return Math.round(Math.min(MAX_GLIDE_MS, Math.max(MIN_GLIDE_MS, distance / FLUID_CURSOR_SPEED_PX_PER_SECOND * 1e3)));
525
638
  }
526
- function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
639
+ var LIFECYCLE_ACTIONS2 = /* @__PURE__ */ new Set(["agent_connected", "agent_disconnected"]);
640
+ function CoBrowseCursorLayer({
641
+ active = true,
642
+ zIndex = 2147483e3,
643
+ idleAfterMs = 15e3
644
+ }) {
527
645
  const { latest } = useAgentActivity(void 0, { capacity: 12 });
528
646
  const [cursor, setCursor] = useState(
529
647
  null
530
648
  );
531
649
  const [pulse, setPulse] = useState(null);
650
+ const [lastActionAt, setLastActionAt] = useState(0);
532
651
  useEffect(() => {
533
652
  if (!latest || (latest.source ?? "agent") === "user") return;
534
- if (latest.action === "agent_disconnected") {
535
- setCursor((prev) => prev ? { ...prev, status: "Standing by", glideMs: 0 } : prev);
653
+ if (LIFECYCLE_ACTIONS2.has(latest.action)) {
654
+ setCursor((prev) => prev ? { ...prev, status: "Standing by", glideMs: 0 } : null);
536
655
  setPulse(null);
537
656
  return;
538
657
  }
658
+ setLastActionAt(latest.timestamp);
539
659
  const color = latest.agentColor ?? "#a855f7";
540
660
  const name = latest.agentName ?? "Agent";
541
661
  const status = latest.target?.label ?? latest.action;
@@ -557,6 +677,14 @@ function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
557
677
  );
558
678
  }
559
679
  }, [latest?.timestamp]);
680
+ useEffect(() => {
681
+ if (!idleAfterMs || !lastActionAt) return;
682
+ const timer = setTimeout(() => {
683
+ setCursor(null);
684
+ setPulse(null);
685
+ }, idleAfterMs);
686
+ return () => clearTimeout(timer);
687
+ }, [lastActionAt, idleAfterMs]);
560
688
  if (!active || !cursor || typeof document === "undefined") return null;
561
689
  return createPortal(
562
690
  /* @__PURE__ */ jsxs("div", { "data-co-browse-cursor-layer": "", style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex }, children: [
@@ -660,6 +788,6 @@ function SimulateUsersButton({
660
788
  );
661
789
  }
662
790
 
663
- export { BridgedForm, CoBrowseCursorLayer, CoBrowsePresence, ScreensActivityBridge, SimulateUsersButton, registerNavigationBridge, useCoBrowseSession };
791
+ export { BridgedForm, CoBrowseCursorLayer, CoBrowsePresence, SCREEN_TEXT_NODE_TYPE, ScreensActivityBridge, SimulateUsersButton, registerNavigationBridge, registerScreenDocBridge, useCoBrowseSession };
664
792
  //# sourceMappingURL=index.js.map
665
793
  //# sourceMappingURL=index.js.map