@particle-academy/agent-integrations 0.34.0 → 0.35.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/{chunk-SJTLTITO.js → chunk-ABP2THQV.js} +3 -3
- package/dist/{chunk-SJTLTITO.js.map → chunk-ABP2THQV.js.map} +1 -1
- package/dist/{chunk-N6RLSLOF.js → chunk-JDCB2NMM.js} +29 -2
- package/dist/chunk-JDCB2NMM.js.map +1 -0
- package/dist/components-shared-whiteboard.cjs +27 -0
- package/dist/components-shared-whiteboard.cjs.map +1 -1
- package/dist/components-shared-whiteboard.js +2 -2
- package/dist/index.cjs +75 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -3
- package/dist/index.d.ts +24 -3
- package/dist/index.js +52 -10
- package/dist/index.js.map +1 -1
- package/dist/sharing/index.d.cts +15 -0
- package/dist/sharing/index.d.ts +15 -0
- package/dist/sharing.cjs +27 -0
- package/dist/sharing.cjs.map +1 -1
- package/dist/sharing.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-N6RLSLOF.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ShareControls, AgentCursor, AgentActivityHighlight, AgentPanel } from './chunk-
|
|
2
|
-
import { createSessionDescriptor, attachSseRelay } from './chunk-
|
|
1
|
+
import { ShareControls, AgentCursor, AgentActivityHighlight, AgentPanel } from './chunk-ABP2THQV.js';
|
|
2
|
+
import { createSessionDescriptor, attachSseRelay } from './chunk-JDCB2NMM.js';
|
|
3
3
|
import { attachInProcess } from './chunk-AFUULW5E.js';
|
|
4
4
|
import { registerWhiteboardBridge } from './chunk-2U5JB6ZA.js';
|
|
5
5
|
import './chunk-CT3LRNH7.js';
|
package/dist/index.cjs
CHANGED
|
@@ -4457,6 +4457,7 @@ var SseRelayTransport = class {
|
|
|
4457
4457
|
this.listeners = /* @__PURE__ */ new Set();
|
|
4458
4458
|
this.state = "idle";
|
|
4459
4459
|
this.peers = /* @__PURE__ */ new Set();
|
|
4460
|
+
this.peerListeners = /* @__PURE__ */ new Set();
|
|
4460
4461
|
this.opts = options;
|
|
4461
4462
|
this.expectedToken = options.token;
|
|
4462
4463
|
}
|
|
@@ -4517,6 +4518,8 @@ var SseRelayTransport = class {
|
|
|
4517
4518
|
this.es?.close();
|
|
4518
4519
|
this.es = void 0;
|
|
4519
4520
|
this.connected = false;
|
|
4521
|
+
this.peers.clear();
|
|
4522
|
+
this.notifyPeers();
|
|
4520
4523
|
this.setState("closed");
|
|
4521
4524
|
}
|
|
4522
4525
|
onStateChange(listener) {
|
|
@@ -4524,6 +4527,28 @@ var SseRelayTransport = class {
|
|
|
4524
4527
|
listener(this.state);
|
|
4525
4528
|
return () => this.listeners.delete(listener);
|
|
4526
4529
|
}
|
|
4530
|
+
/** How many remote peers (agents) are currently attached to this session. */
|
|
4531
|
+
peerCount() {
|
|
4532
|
+
return this.peers.size;
|
|
4533
|
+
}
|
|
4534
|
+
/**
|
|
4535
|
+
* Subscribe to peer arrivals/departures. Fires immediately with the current
|
|
4536
|
+
* count.
|
|
4537
|
+
*
|
|
4538
|
+
* This is the ONLY honest answer to "is an agent actually here?".
|
|
4539
|
+
* `onStateChange` reports the BROWSER's own channel to the relay, which opens
|
|
4540
|
+
* the instant sharing starts — so a UI keyed on it announces "Agent is
|
|
4541
|
+
* driving" to a human who has not yet handed the link to anybody, and can
|
|
4542
|
+
* never tell them when one really arrives.
|
|
4543
|
+
*/
|
|
4544
|
+
onPeersChange(listener) {
|
|
4545
|
+
this.peerListeners.add(listener);
|
|
4546
|
+
listener(this.peers.size);
|
|
4547
|
+
return () => this.peerListeners.delete(listener);
|
|
4548
|
+
}
|
|
4549
|
+
notifyPeers() {
|
|
4550
|
+
for (const l of this.peerListeners) l(this.peers.size);
|
|
4551
|
+
}
|
|
4527
4552
|
/**
|
|
4528
4553
|
* For relays that wrap each frame with auth metadata: hosts can call this
|
|
4529
4554
|
* directly when a frame arrives via a non-SSE path. The transport will
|
|
@@ -4558,6 +4583,7 @@ var SseRelayTransport = class {
|
|
|
4558
4583
|
if ("method" in message && message.method === "notifications/peer_joined") {
|
|
4559
4584
|
const subscriberId = String(message.params?.subscriberId ?? "peer");
|
|
4560
4585
|
this.peers.add(subscriberId);
|
|
4586
|
+
this.notifyPeers();
|
|
4561
4587
|
fancyAutoCommon.emitActivity({
|
|
4562
4588
|
agentId: "agent",
|
|
4563
4589
|
agentName: "Agent",
|
|
@@ -4572,6 +4598,7 @@ var SseRelayTransport = class {
|
|
|
4572
4598
|
if ("method" in message && message.method === "notifications/peer_left") {
|
|
4573
4599
|
const subscriberId = String(message.params?.subscriberId ?? "peer");
|
|
4574
4600
|
this.peers.delete(subscriberId);
|
|
4601
|
+
this.notifyPeers();
|
|
4575
4602
|
if (this.peers.size === 0) {
|
|
4576
4603
|
fancyAutoCommon.emitActivity({
|
|
4577
4604
|
agentId: "agent",
|
|
@@ -4629,6 +4656,7 @@ function useCoBrowseSession(options) {
|
|
|
4629
4656
|
const disposeBridge = react.useRef(null);
|
|
4630
4657
|
const [session, setSession] = react.useState(null);
|
|
4631
4658
|
const [relayState, setRelayState] = react.useState("idle");
|
|
4659
|
+
const [agentCount, setAgentCount] = react.useState(0);
|
|
4632
4660
|
react.useEffect(() => {
|
|
4633
4661
|
const server = new MicroMcpServer({
|
|
4634
4662
|
info: options.info ?? { name: "fancy-co-browse", version: "0.1.0" },
|
|
@@ -4663,6 +4691,7 @@ function useCoBrowseSession(options) {
|
|
|
4663
4691
|
}
|
|
4664
4692
|
const relay = attachSseRelay(server, { baseUrl: relayBaseUrl, sessionId: descriptor.id, token: descriptor.token });
|
|
4665
4693
|
relay.onStateChange(setRelayState);
|
|
4694
|
+
relay.onPeersChange(setAgentCount);
|
|
4666
4695
|
relayRef.current = relay;
|
|
4667
4696
|
setSession(descriptor);
|
|
4668
4697
|
}, [relayBaseUrl, options]);
|
|
@@ -4671,6 +4700,7 @@ function useCoBrowseSession(options) {
|
|
|
4671
4700
|
relayRef.current?.close();
|
|
4672
4701
|
relayRef.current = null;
|
|
4673
4702
|
setRelayState("idle");
|
|
4703
|
+
setAgentCount(0);
|
|
4674
4704
|
setSession(null);
|
|
4675
4705
|
if (current) {
|
|
4676
4706
|
const csrf = options.csrfToken?.() ?? "";
|
|
@@ -4693,7 +4723,16 @@ function useCoBrowseSession(options) {
|
|
|
4693
4723
|
meta: event
|
|
4694
4724
|
});
|
|
4695
4725
|
}, []);
|
|
4696
|
-
return {
|
|
4726
|
+
return {
|
|
4727
|
+
server: serverRef.current,
|
|
4728
|
+
session,
|
|
4729
|
+
relayState,
|
|
4730
|
+
agentConnected: agentCount > 0,
|
|
4731
|
+
agentCount,
|
|
4732
|
+
startShare,
|
|
4733
|
+
stopShare,
|
|
4734
|
+
observeUser
|
|
4735
|
+
};
|
|
4697
4736
|
}
|
|
4698
4737
|
function AgentPanel({ agent, activity, onSubmit, busy, actions, className, style }) {
|
|
4699
4738
|
const scrollRef = react.useRef(null);
|
|
@@ -5416,10 +5455,12 @@ function ManualPopover({
|
|
|
5416
5455
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "fai-connect__copy-btn", onClick: onCopy, children: copied ? "Copied" : "Copy snippet" })
|
|
5417
5456
|
] });
|
|
5418
5457
|
}
|
|
5458
|
+
var LIFECYCLE_ACTIONS = /* @__PURE__ */ new Set(["agent_connected", "agent_disconnected"]);
|
|
5419
5459
|
function CoBrowsePresence({ session, connectUrl, shareBaseUrl, className }) {
|
|
5420
5460
|
const { events } = useAgentActivity(void 0, { capacity: 40 });
|
|
5421
|
-
const lastAgentAction = [...events].reverse().find((e) => (e.source ?? "agent") !== "user");
|
|
5422
|
-
const connected = session.
|
|
5461
|
+
const lastAgentAction = [...events].reverse().find((e) => (e.source ?? "agent") !== "user" && !LIFECYCLE_ACTIONS.has(e.action));
|
|
5462
|
+
const connected = session.agentConnected;
|
|
5463
|
+
const activity = react.useMemo(() => events.map(toPanelActivity), [events]);
|
|
5423
5464
|
if (!session.session) {
|
|
5424
5465
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, "data-co-browse-presence": "idle", children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => void session.startShare(), "data-co-browse-start": true, children: "Let an agent drive" }) });
|
|
5425
5466
|
}
|
|
@@ -5437,12 +5478,25 @@ function CoBrowsePresence({ session, connectUrl, shareBaseUrl, className }) {
|
|
|
5437
5478
|
onStart: () => void session.startShare(),
|
|
5438
5479
|
onStop: session.stopShare,
|
|
5439
5480
|
status: session.relayState,
|
|
5481
|
+
agentConnected: connected,
|
|
5482
|
+
activity,
|
|
5440
5483
|
shareBaseUrl
|
|
5441
5484
|
}
|
|
5442
5485
|
),
|
|
5443
5486
|
connectUrl && /* @__PURE__ */ jsxRuntime.jsx(ConnectorButtons, { serverName: "Fancy UI co-browse", mcpUrl: connectUrl })
|
|
5444
5487
|
] });
|
|
5445
5488
|
}
|
|
5489
|
+
function toPanelActivity(event) {
|
|
5490
|
+
const who = (event.source ?? "agent") === "user" ? "You" : event.agentName ?? "Agent";
|
|
5491
|
+
return {
|
|
5492
|
+
id: `${event.timestamp}-${event.action}-${event.target?.elementId ?? event.target?.label ?? ""}`,
|
|
5493
|
+
at: event.timestamp,
|
|
5494
|
+
kind: LIFECYCLE_ACTIONS.has(event.action) ? "info" : "tool",
|
|
5495
|
+
source: who,
|
|
5496
|
+
text: event.target?.label ?? event.action,
|
|
5497
|
+
detail: event.meta
|
|
5498
|
+
};
|
|
5499
|
+
}
|
|
5446
5500
|
CoBrowsePresence.displayName = "CoBrowsePresence";
|
|
5447
5501
|
var CURSOR_EDGE_INSET = 24;
|
|
5448
5502
|
var FLUID_CURSOR_SPEED_PX_PER_SECOND = 1050;
|
|
@@ -5461,19 +5515,26 @@ function fluidGlideDuration(from, to) {
|
|
|
5461
5515
|
const distance = Math.hypot(to.x - from.x, to.y - from.y);
|
|
5462
5516
|
return Math.round(Math.min(MAX_GLIDE_MS, Math.max(MIN_GLIDE_MS, distance / FLUID_CURSOR_SPEED_PX_PER_SECOND * 1e3)));
|
|
5463
5517
|
}
|
|
5464
|
-
|
|
5518
|
+
var LIFECYCLE_ACTIONS2 = /* @__PURE__ */ new Set(["agent_connected", "agent_disconnected"]);
|
|
5519
|
+
function CoBrowseCursorLayer({
|
|
5520
|
+
active = true,
|
|
5521
|
+
zIndex = 2147483e3,
|
|
5522
|
+
idleAfterMs = 15e3
|
|
5523
|
+
}) {
|
|
5465
5524
|
const { latest } = useAgentActivity(void 0, { capacity: 12 });
|
|
5466
5525
|
const [cursor, setCursor] = react.useState(
|
|
5467
5526
|
null
|
|
5468
5527
|
);
|
|
5469
5528
|
const [pulse, setPulse] = react.useState(null);
|
|
5529
|
+
const [lastActionAt, setLastActionAt] = react.useState(0);
|
|
5470
5530
|
react.useEffect(() => {
|
|
5471
5531
|
if (!latest || (latest.source ?? "agent") === "user") return;
|
|
5472
|
-
if (latest.action
|
|
5473
|
-
setCursor((prev) => prev ? { ...prev, status: "Standing by", glideMs: 0 } :
|
|
5532
|
+
if (LIFECYCLE_ACTIONS2.has(latest.action)) {
|
|
5533
|
+
setCursor((prev) => prev ? { ...prev, status: "Standing by", glideMs: 0 } : null);
|
|
5474
5534
|
setPulse(null);
|
|
5475
5535
|
return;
|
|
5476
5536
|
}
|
|
5537
|
+
setLastActionAt(latest.timestamp);
|
|
5477
5538
|
const color = latest.agentColor ?? "#a855f7";
|
|
5478
5539
|
const name = latest.agentName ?? "Agent";
|
|
5479
5540
|
const status = latest.target?.label ?? latest.action;
|
|
@@ -5495,6 +5556,14 @@ function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
|
|
|
5495
5556
|
);
|
|
5496
5557
|
}
|
|
5497
5558
|
}, [latest?.timestamp]);
|
|
5559
|
+
react.useEffect(() => {
|
|
5560
|
+
if (!idleAfterMs || !lastActionAt) return;
|
|
5561
|
+
const timer = setTimeout(() => {
|
|
5562
|
+
setCursor(null);
|
|
5563
|
+
setPulse(null);
|
|
5564
|
+
}, idleAfterMs);
|
|
5565
|
+
return () => clearTimeout(timer);
|
|
5566
|
+
}, [lastActionAt, idleAfterMs]);
|
|
5498
5567
|
if (!active || !cursor || typeof document === "undefined") return null;
|
|
5499
5568
|
return reactDom.createPortal(
|
|
5500
5569
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-co-browse-cursor-layer": "", style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex }, children: [
|