@particle-academy/agent-integrations 0.33.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/README.md +9 -0
- package/dist/bridges/passkeys.d.cts +185 -0
- package/dist/bridges/passkeys.d.ts +185 -0
- package/dist/bridges-passkeys.cjs +314 -0
- package/dist/bridges-passkeys.cjs.map +1 -0
- package/dist/bridges-passkeys.js +7 -0
- package/dist/bridges-passkeys.js.map +1 -0
- package/dist/chunk-7THGQUX3.js +193 -0
- package/dist/chunk-7THGQUX3.js.map +1 -0
- 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 +266 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -3
- package/dist/index.d.ts +25 -3
- package/dist/index.js +54 -11
- 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 +11 -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
|
@@ -3896,10 +3896,196 @@ function registerFeaturesBridge(host, options) {
|
|
|
3896
3896
|
}
|
|
3897
3897
|
};
|
|
3898
3898
|
}
|
|
3899
|
+
|
|
3900
|
+
// src/bridges/passkeys.ts
|
|
3899
3901
|
var DEFAULT_AGENT14 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
3902
|
+
var str6 = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
3903
|
+
function toSummary(record) {
|
|
3904
|
+
return {
|
|
3905
|
+
id: str6(record?.id),
|
|
3906
|
+
name: typeof record?.name === "string" ? record.name : null,
|
|
3907
|
+
createdAt: str6(record?.createdAt),
|
|
3908
|
+
lastUsedAt: typeof record?.lastUsedAt === "string" ? record.lastUsedAt : null,
|
|
3909
|
+
transports: Array.isArray(record?.transports) ? record.transports.map((t) => String(t)) : [],
|
|
3910
|
+
backedUp: record?.backedUp === true,
|
|
3911
|
+
aaguid: str6(record?.aaguid),
|
|
3912
|
+
clonedAt: typeof record?.clonedAt === "string" ? record.clonedAt : null
|
|
3913
|
+
};
|
|
3914
|
+
}
|
|
3915
|
+
var PASSKEY_BRIDGE_TOOLS = [
|
|
3916
|
+
"passkey_list",
|
|
3917
|
+
"passkey_status",
|
|
3918
|
+
"passkey_rename",
|
|
3919
|
+
"passkey_revoke",
|
|
3920
|
+
"passkey_begin_enrollment"
|
|
3921
|
+
];
|
|
3922
|
+
function registerPasskeyBridge(host, options) {
|
|
3923
|
+
const { adapter } = options;
|
|
3924
|
+
const agent = { ...DEFAULT_AGENT14, ...options.agent ?? {} };
|
|
3925
|
+
const surfaceId = adapter.id ?? "passkeys";
|
|
3926
|
+
const disposers = [];
|
|
3927
|
+
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
3928
|
+
const target = (elementId, label) => ({
|
|
3929
|
+
kind: "custom",
|
|
3930
|
+
screenId: adapter.screenId,
|
|
3931
|
+
elementId,
|
|
3932
|
+
label: label ?? surfaceId
|
|
3933
|
+
});
|
|
3934
|
+
const reg = (name, description, properties, required, handler, resolveTarget) => {
|
|
3935
|
+
const wrapped = async (args) => {
|
|
3936
|
+
try {
|
|
3937
|
+
return await handler(args);
|
|
3938
|
+
} catch (e) {
|
|
3939
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
3940
|
+
}
|
|
3941
|
+
};
|
|
3942
|
+
const final = resolveTarget ? wrapToolWithActivity(wrapped, {
|
|
3943
|
+
toolName: name,
|
|
3944
|
+
agent,
|
|
3945
|
+
kind: "custom",
|
|
3946
|
+
screenId: adapter.screenId,
|
|
3947
|
+
resolveTarget: ({ args }) => resolveTarget(args)
|
|
3948
|
+
}) : wrapped;
|
|
3949
|
+
disposers.push(
|
|
3950
|
+
host.registerTool(
|
|
3951
|
+
{
|
|
3952
|
+
name,
|
|
3953
|
+
description,
|
|
3954
|
+
inputSchema: {
|
|
3955
|
+
type: "object",
|
|
3956
|
+
properties,
|
|
3957
|
+
required,
|
|
3958
|
+
additionalProperties: false
|
|
3959
|
+
}
|
|
3960
|
+
},
|
|
3961
|
+
final
|
|
3962
|
+
)
|
|
3963
|
+
);
|
|
3964
|
+
};
|
|
3965
|
+
const findById = async (id) => (await adapter.list()).map(toSummary).find((p) => p.id === id) ?? null;
|
|
3966
|
+
reg(
|
|
3967
|
+
"passkey_list",
|
|
3968
|
+
"List the signed-in user's passkeys: credential id, label, when it was added and last used, transports, whether it is a synced passkey, and whether the server flagged it as a possible clone. Read-only. Public keys, user handles and signature counters are never returned.",
|
|
3969
|
+
{},
|
|
3970
|
+
[],
|
|
3971
|
+
async () => {
|
|
3972
|
+
const passkeys = (await adapter.list()).map(toSummary);
|
|
3973
|
+
return textResult(JSON.stringify(passkeys, null, 2), { passkeys, count: passkeys.length });
|
|
3974
|
+
},
|
|
3975
|
+
false
|
|
3976
|
+
);
|
|
3977
|
+
reg(
|
|
3978
|
+
"passkey_status",
|
|
3979
|
+
"Read what this browser can do (passkey support, a built-in authenticator, autofill sign-in) and what the management surface is currently showing (busy/error, a revoke awaiting confirmation, a rename in progress).",
|
|
3980
|
+
{},
|
|
3981
|
+
[],
|
|
3982
|
+
async () => {
|
|
3983
|
+
const support = adapter.support ? await adapter.support() : null;
|
|
3984
|
+
const state = adapter.state ? await adapter.state() : null;
|
|
3985
|
+
const out = {
|
|
3986
|
+
support,
|
|
3987
|
+
state,
|
|
3988
|
+
// Said in the payload, not only in the docs: an agent reading this to
|
|
3989
|
+
// work out "how do I sign the user in" should find the answer here.
|
|
3990
|
+
canCompleteCeremony: false,
|
|
3991
|
+
note: "A passkey ceremony needs a user gesture and a biometric or PIN. No tool on this bridge can perform one; ask the human to complete it on their device."
|
|
3992
|
+
};
|
|
3993
|
+
return textResult(JSON.stringify(out, null, 2), out);
|
|
3994
|
+
},
|
|
3995
|
+
false
|
|
3996
|
+
);
|
|
3997
|
+
reg(
|
|
3998
|
+
"passkey_rename",
|
|
3999
|
+
"Rename one passkey, by credential id (from passkey_list). Cosmetic and undoable with agent_undo.",
|
|
4000
|
+
{
|
|
4001
|
+
id: { type: "string", description: "base64url credential id." },
|
|
4002
|
+
name: { type: "string", description: "New label, e.g. 'MacBook Touch ID'. Empty string clears it." }
|
|
4003
|
+
},
|
|
4004
|
+
["id", "name"],
|
|
4005
|
+
async (args) => {
|
|
4006
|
+
const id = str6(args.id);
|
|
4007
|
+
const name = str6(args.name);
|
|
4008
|
+
if (!id) return errorResult("id is required.");
|
|
4009
|
+
const existing = await findById(id);
|
|
4010
|
+
if (!existing) return errorResult(`No passkey with id ${id}.`);
|
|
4011
|
+
if (options.confirmRename) {
|
|
4012
|
+
if (!options.confirm) {
|
|
4013
|
+
return errorResult(
|
|
4014
|
+
"passkey_rename requires confirmation (confirmRename), but the host wired no confirm hook."
|
|
4015
|
+
);
|
|
4016
|
+
}
|
|
4017
|
+
const ok = await options.confirm({ action: "passkey_rename", id, name });
|
|
4018
|
+
if (!ok) return errorResult(`Declined: rename ${id} (human did not confirm).`);
|
|
4019
|
+
}
|
|
4020
|
+
const previous = existing.name ?? "";
|
|
4021
|
+
await adapter.rename({ id, name });
|
|
4022
|
+
fancyAutoCommon.pushUndoEntry(agent.id, {
|
|
4023
|
+
timestamp: Date.now(),
|
|
4024
|
+
bridgeId: surfaceId,
|
|
4025
|
+
action: "passkey_rename",
|
|
4026
|
+
label: `Renamed passkey to ${name || "(unnamed)"}`,
|
|
4027
|
+
undo: () => void adapter.rename({ id, name: previous }),
|
|
4028
|
+
redo: () => void adapter.rename({ id, name })
|
|
4029
|
+
});
|
|
4030
|
+
return textResult(`Renamed passkey ${id} to ${name || "(unnamed)"}`, { id, name, previous });
|
|
4031
|
+
},
|
|
4032
|
+
(args) => target(str6(args.id), `rename ${str6(args.id)}`)
|
|
4033
|
+
);
|
|
4034
|
+
reg(
|
|
4035
|
+
"passkey_revoke",
|
|
4036
|
+
"PROPOSE revoking one passkey. This does NOT revoke it: the request is staged on the surface and the human confirms or cancels there. Revoking the last passkey can lock the user out, and the response says when that is the case so you can tell them before they confirm.",
|
|
4037
|
+
{ id: { type: "string", description: "base64url credential id." } },
|
|
4038
|
+
["id"],
|
|
4039
|
+
async (args) => {
|
|
4040
|
+
const id = str6(args.id);
|
|
4041
|
+
if (!id) return errorResult("id is required.");
|
|
4042
|
+
const existing = await findById(id);
|
|
4043
|
+
if (!existing) return errorResult(`No passkey with id ${id}.`);
|
|
4044
|
+
const proposal = await adapter.proposeRevoke({ id });
|
|
4045
|
+
const isLastPasskey = proposal?.isLastPasskey === true;
|
|
4046
|
+
const out = {
|
|
4047
|
+
id,
|
|
4048
|
+
name: existing.name,
|
|
4049
|
+
staged: true,
|
|
4050
|
+
awaitingHuman: true,
|
|
4051
|
+
isLastPasskey
|
|
4052
|
+
};
|
|
4053
|
+
return textResult(
|
|
4054
|
+
isLastPasskey ? `Staged a revoke of ${existing.name ?? id}. This is the account's LAST passkey \u2014 confirming it removes passkey sign-in entirely. Waiting for the human to confirm or cancel.` : `Staged a revoke of ${existing.name ?? id}. Waiting for the human to confirm or cancel on the surface.`,
|
|
4055
|
+
out
|
|
4056
|
+
);
|
|
4057
|
+
},
|
|
4058
|
+
(args) => target(str6(args.id), `propose revoke ${str6(args.id)}`)
|
|
4059
|
+
);
|
|
4060
|
+
reg(
|
|
4061
|
+
"passkey_begin_enrollment",
|
|
4062
|
+
"Ask the surface to start enrolling a new passkey. This only opens the prompt: the ceremony completes on the human's authenticator (Touch ID, Windows Hello, phone, security key) or it does not complete at all. Returns immediately.",
|
|
4063
|
+
{ name: { type: "string", description: "Optional label to pre-fill for the new passkey." } },
|
|
4064
|
+
[],
|
|
4065
|
+
async (args) => {
|
|
4066
|
+
if (!adapter.beginEnrollment) return errorResult("Host did not wire beginEnrollment().");
|
|
4067
|
+
const name = args.name === void 0 ? void 0 : str6(args.name);
|
|
4068
|
+
await adapter.beginEnrollment(name === void 0 ? {} : { name });
|
|
4069
|
+
const out = { awaitingHuman: true, ...name === void 0 ? {} : { name } };
|
|
4070
|
+
return textResult(
|
|
4071
|
+
"Enrollment prompt opened. The human has to complete it on their authenticator \u2014 there is no tool that can finish it for them.",
|
|
4072
|
+
out
|
|
4073
|
+
);
|
|
4074
|
+
},
|
|
4075
|
+
(args) => target(void 0, `begin enrollment${args.name ? ` (${str6(args.name)})` : ""}`)
|
|
4076
|
+
);
|
|
4077
|
+
return {
|
|
4078
|
+
id: `passkeys:${surfaceId}`,
|
|
4079
|
+
title: adapter.title ?? "Passkeys",
|
|
4080
|
+
dispose: () => {
|
|
4081
|
+
for (const d of disposers.splice(0)) d();
|
|
4082
|
+
}
|
|
4083
|
+
};
|
|
4084
|
+
}
|
|
4085
|
+
var DEFAULT_AGENT15 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
3900
4086
|
function registerDocBridge(host, options) {
|
|
3901
4087
|
const { adapter, surface } = options;
|
|
3902
|
-
const agent = { ...
|
|
4088
|
+
const agent = { ...DEFAULT_AGENT15, ...options.agent ?? {} };
|
|
3903
4089
|
const reducer = options.reducer ?? fancyDocCommons.treeReducer();
|
|
3904
4090
|
const stage = options.stagePolicy ?? fancyDocCommons.autoApply;
|
|
3905
4091
|
const makeNode = options.makeNode ?? (({ id, type, parent, order }, props) => ({ id, type, parent, order, props }));
|
|
@@ -4271,6 +4457,7 @@ var SseRelayTransport = class {
|
|
|
4271
4457
|
this.listeners = /* @__PURE__ */ new Set();
|
|
4272
4458
|
this.state = "idle";
|
|
4273
4459
|
this.peers = /* @__PURE__ */ new Set();
|
|
4460
|
+
this.peerListeners = /* @__PURE__ */ new Set();
|
|
4274
4461
|
this.opts = options;
|
|
4275
4462
|
this.expectedToken = options.token;
|
|
4276
4463
|
}
|
|
@@ -4331,6 +4518,8 @@ var SseRelayTransport = class {
|
|
|
4331
4518
|
this.es?.close();
|
|
4332
4519
|
this.es = void 0;
|
|
4333
4520
|
this.connected = false;
|
|
4521
|
+
this.peers.clear();
|
|
4522
|
+
this.notifyPeers();
|
|
4334
4523
|
this.setState("closed");
|
|
4335
4524
|
}
|
|
4336
4525
|
onStateChange(listener) {
|
|
@@ -4338,6 +4527,28 @@ var SseRelayTransport = class {
|
|
|
4338
4527
|
listener(this.state);
|
|
4339
4528
|
return () => this.listeners.delete(listener);
|
|
4340
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
|
+
}
|
|
4341
4552
|
/**
|
|
4342
4553
|
* For relays that wrap each frame with auth metadata: hosts can call this
|
|
4343
4554
|
* directly when a frame arrives via a non-SSE path. The transport will
|
|
@@ -4372,6 +4583,7 @@ var SseRelayTransport = class {
|
|
|
4372
4583
|
if ("method" in message && message.method === "notifications/peer_joined") {
|
|
4373
4584
|
const subscriberId = String(message.params?.subscriberId ?? "peer");
|
|
4374
4585
|
this.peers.add(subscriberId);
|
|
4586
|
+
this.notifyPeers();
|
|
4375
4587
|
fancyAutoCommon.emitActivity({
|
|
4376
4588
|
agentId: "agent",
|
|
4377
4589
|
agentName: "Agent",
|
|
@@ -4386,6 +4598,7 @@ var SseRelayTransport = class {
|
|
|
4386
4598
|
if ("method" in message && message.method === "notifications/peer_left") {
|
|
4387
4599
|
const subscriberId = String(message.params?.subscriberId ?? "peer");
|
|
4388
4600
|
this.peers.delete(subscriberId);
|
|
4601
|
+
this.notifyPeers();
|
|
4389
4602
|
if (this.peers.size === 0) {
|
|
4390
4603
|
fancyAutoCommon.emitActivity({
|
|
4391
4604
|
agentId: "agent",
|
|
@@ -4431,11 +4644,11 @@ function attachSseRelay(server, options) {
|
|
|
4431
4644
|
|
|
4432
4645
|
// src/sharing/use-co-browse-session.ts
|
|
4433
4646
|
init_registry();
|
|
4434
|
-
var
|
|
4647
|
+
var DEFAULT_AGENT16 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
4435
4648
|
var USER = { id: "human", name: "You" };
|
|
4436
4649
|
function useCoBrowseSession(options) {
|
|
4437
4650
|
const { adapter, extraBridges } = options;
|
|
4438
|
-
const agent = { ...
|
|
4651
|
+
const agent = { ...DEFAULT_AGENT16, ...options.agent ?? {} };
|
|
4439
4652
|
const relayBaseUrl = options.relayBaseUrl ?? "/agent-relay";
|
|
4440
4653
|
const serverRef = react.useRef(null);
|
|
4441
4654
|
const relayRef = react.useRef(null);
|
|
@@ -4443,6 +4656,7 @@ function useCoBrowseSession(options) {
|
|
|
4443
4656
|
const disposeBridge = react.useRef(null);
|
|
4444
4657
|
const [session, setSession] = react.useState(null);
|
|
4445
4658
|
const [relayState, setRelayState] = react.useState("idle");
|
|
4659
|
+
const [agentCount, setAgentCount] = react.useState(0);
|
|
4446
4660
|
react.useEffect(() => {
|
|
4447
4661
|
const server = new MicroMcpServer({
|
|
4448
4662
|
info: options.info ?? { name: "fancy-co-browse", version: "0.1.0" },
|
|
@@ -4477,6 +4691,7 @@ function useCoBrowseSession(options) {
|
|
|
4477
4691
|
}
|
|
4478
4692
|
const relay = attachSseRelay(server, { baseUrl: relayBaseUrl, sessionId: descriptor.id, token: descriptor.token });
|
|
4479
4693
|
relay.onStateChange(setRelayState);
|
|
4694
|
+
relay.onPeersChange(setAgentCount);
|
|
4480
4695
|
relayRef.current = relay;
|
|
4481
4696
|
setSession(descriptor);
|
|
4482
4697
|
}, [relayBaseUrl, options]);
|
|
@@ -4485,6 +4700,7 @@ function useCoBrowseSession(options) {
|
|
|
4485
4700
|
relayRef.current?.close();
|
|
4486
4701
|
relayRef.current = null;
|
|
4487
4702
|
setRelayState("idle");
|
|
4703
|
+
setAgentCount(0);
|
|
4488
4704
|
setSession(null);
|
|
4489
4705
|
if (current) {
|
|
4490
4706
|
const csrf = options.csrfToken?.() ?? "";
|
|
@@ -4507,7 +4723,16 @@ function useCoBrowseSession(options) {
|
|
|
4507
4723
|
meta: event
|
|
4508
4724
|
});
|
|
4509
4725
|
}, []);
|
|
4510
|
-
return {
|
|
4726
|
+
return {
|
|
4727
|
+
server: serverRef.current,
|
|
4728
|
+
session,
|
|
4729
|
+
relayState,
|
|
4730
|
+
agentConnected: agentCount > 0,
|
|
4731
|
+
agentCount,
|
|
4732
|
+
startShare,
|
|
4733
|
+
stopShare,
|
|
4734
|
+
observeUser
|
|
4735
|
+
};
|
|
4511
4736
|
}
|
|
4512
4737
|
function AgentPanel({ agent, activity, onSubmit, busy, actions, className, style }) {
|
|
4513
4738
|
const scrollRef = react.useRef(null);
|
|
@@ -5230,10 +5455,12 @@ function ManualPopover({
|
|
|
5230
5455
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "fai-connect__copy-btn", onClick: onCopy, children: copied ? "Copied" : "Copy snippet" })
|
|
5231
5456
|
] });
|
|
5232
5457
|
}
|
|
5458
|
+
var LIFECYCLE_ACTIONS = /* @__PURE__ */ new Set(["agent_connected", "agent_disconnected"]);
|
|
5233
5459
|
function CoBrowsePresence({ session, connectUrl, shareBaseUrl, className }) {
|
|
5234
5460
|
const { events } = useAgentActivity(void 0, { capacity: 40 });
|
|
5235
|
-
const lastAgentAction = [...events].reverse().find((e) => (e.source ?? "agent") !== "user");
|
|
5236
|
-
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]);
|
|
5237
5464
|
if (!session.session) {
|
|
5238
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" }) });
|
|
5239
5466
|
}
|
|
@@ -5251,12 +5478,25 @@ function CoBrowsePresence({ session, connectUrl, shareBaseUrl, className }) {
|
|
|
5251
5478
|
onStart: () => void session.startShare(),
|
|
5252
5479
|
onStop: session.stopShare,
|
|
5253
5480
|
status: session.relayState,
|
|
5481
|
+
agentConnected: connected,
|
|
5482
|
+
activity,
|
|
5254
5483
|
shareBaseUrl
|
|
5255
5484
|
}
|
|
5256
5485
|
),
|
|
5257
5486
|
connectUrl && /* @__PURE__ */ jsxRuntime.jsx(ConnectorButtons, { serverName: "Fancy UI co-browse", mcpUrl: connectUrl })
|
|
5258
5487
|
] });
|
|
5259
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
|
+
}
|
|
5260
5500
|
CoBrowsePresence.displayName = "CoBrowsePresence";
|
|
5261
5501
|
var CURSOR_EDGE_INSET = 24;
|
|
5262
5502
|
var FLUID_CURSOR_SPEED_PX_PER_SECOND = 1050;
|
|
@@ -5275,19 +5515,26 @@ function fluidGlideDuration(from, to) {
|
|
|
5275
5515
|
const distance = Math.hypot(to.x - from.x, to.y - from.y);
|
|
5276
5516
|
return Math.round(Math.min(MAX_GLIDE_MS, Math.max(MIN_GLIDE_MS, distance / FLUID_CURSOR_SPEED_PX_PER_SECOND * 1e3)));
|
|
5277
5517
|
}
|
|
5278
|
-
|
|
5518
|
+
var LIFECYCLE_ACTIONS2 = /* @__PURE__ */ new Set(["agent_connected", "agent_disconnected"]);
|
|
5519
|
+
function CoBrowseCursorLayer({
|
|
5520
|
+
active = true,
|
|
5521
|
+
zIndex = 2147483e3,
|
|
5522
|
+
idleAfterMs = 15e3
|
|
5523
|
+
}) {
|
|
5279
5524
|
const { latest } = useAgentActivity(void 0, { capacity: 12 });
|
|
5280
5525
|
const [cursor, setCursor] = react.useState(
|
|
5281
5526
|
null
|
|
5282
5527
|
);
|
|
5283
5528
|
const [pulse, setPulse] = react.useState(null);
|
|
5529
|
+
const [lastActionAt, setLastActionAt] = react.useState(0);
|
|
5284
5530
|
react.useEffect(() => {
|
|
5285
5531
|
if (!latest || (latest.source ?? "agent") === "user") return;
|
|
5286
|
-
if (latest.action
|
|
5287
|
-
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);
|
|
5288
5534
|
setPulse(null);
|
|
5289
5535
|
return;
|
|
5290
5536
|
}
|
|
5537
|
+
setLastActionAt(latest.timestamp);
|
|
5291
5538
|
const color = latest.agentColor ?? "#a855f7";
|
|
5292
5539
|
const name = latest.agentName ?? "Agent";
|
|
5293
5540
|
const status = latest.target?.label ?? latest.action;
|
|
@@ -5309,6 +5556,14 @@ function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
|
|
|
5309
5556
|
);
|
|
5310
5557
|
}
|
|
5311
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]);
|
|
5312
5567
|
if (!active || !cursor || typeof document === "undefined") return null;
|
|
5313
5568
|
return reactDom.createPortal(
|
|
5314
5569
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-co-browse-cursor-layer": "", style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex }, children: [
|
|
@@ -5654,6 +5909,7 @@ exports.MCPB_MIN_NODE = MCPB_MIN_NODE;
|
|
|
5654
5909
|
exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
|
|
5655
5910
|
exports.MemoryHumanPlusEventStore = MemoryHumanPlusEventStore;
|
|
5656
5911
|
exports.MicroMcpServer = MicroMcpServer;
|
|
5912
|
+
exports.PASSKEY_BRIDGE_TOOLS = PASSKEY_BRIDGE_TOOLS;
|
|
5657
5913
|
exports.RelayTransport = RelayTransport;
|
|
5658
5914
|
exports.ScreensActivityBridge = ScreensActivityBridge;
|
|
5659
5915
|
exports.ShareControls = ShareControls;
|
|
@@ -5695,6 +5951,7 @@ exports.registerFormBridge = registerFormBridge;
|
|
|
5695
5951
|
exports.registerGitBridge = registerGitBridge;
|
|
5696
5952
|
exports.registerMapBridge = registerMapBridge;
|
|
5697
5953
|
exports.registerNavigationBridge = registerNavigationBridge;
|
|
5954
|
+
exports.registerPasskeyBridge = registerPasskeyBridge;
|
|
5698
5955
|
exports.registerSceneBridge = registerSceneBridge;
|
|
5699
5956
|
exports.registerScreensBridge = registerScreensBridge;
|
|
5700
5957
|
exports.registerSheetsBridge = registerSheetsBridge;
|