@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/bridges-cms.cjs +32 -16
- package/dist/bridges-cms.cjs.map +1 -1
- package/dist/bridges-cms.js +2 -2
- package/dist/bridges-doc.cjs +32 -16
- package/dist/bridges-doc.cjs.map +1 -1
- package/dist/bridges-doc.js +1 -1
- 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/{chunk-DKSR4R4O.js → chunk-JJUABMAP.js} +3 -3
- package/dist/{chunk-DKSR4R4O.js.map → chunk-JJUABMAP.js.map} +1 -1
- package/dist/{chunk-LQZKRBWR.js → chunk-XY2TDE6E.js} +34 -18
- package/dist/chunk-XY2TDE6E.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 +193 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -6
- package/dist/index.d.ts +68 -6
- package/dist/index.js +142 -14
- 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-LQZKRBWR.js.map +0 -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
|
@@ -4093,7 +4093,15 @@ function registerDocBridge(host, options) {
|
|
|
4093
4093
|
const disposers = [];
|
|
4094
4094
|
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
4095
4095
|
let idSeq = 0;
|
|
4096
|
-
const mintId = () =>
|
|
4096
|
+
const mintId = () => {
|
|
4097
|
+
if (adapter.newId) return adapter.newId();
|
|
4098
|
+
const taken = adapter.get().nodes;
|
|
4099
|
+
let id;
|
|
4100
|
+
do
|
|
4101
|
+
id = `${surface}-${idSeq += 1}`;
|
|
4102
|
+
while (taken[id]);
|
|
4103
|
+
return id;
|
|
4104
|
+
};
|
|
4097
4105
|
const target = (elementId) => ({ kind: surface, screenId: adapter.screenId, elementId });
|
|
4098
4106
|
const pending = /* @__PURE__ */ new Map();
|
|
4099
4107
|
function commit(op, label, targetId) {
|
|
@@ -4112,10 +4120,10 @@ function registerDocBridge(host, options) {
|
|
|
4112
4120
|
}
|
|
4113
4121
|
function submit(op, label, targetId) {
|
|
4114
4122
|
if (stage(op) === "confirm") {
|
|
4115
|
-
const
|
|
4116
|
-
pending.set(
|
|
4117
|
-
options.onPending?.({ id, op, label });
|
|
4118
|
-
return { staged: true, id };
|
|
4123
|
+
const pendingId = `${surface}-pending-${idSeq += 1}`;
|
|
4124
|
+
pending.set(pendingId, { op, label, targetId });
|
|
4125
|
+
options.onPending?.({ id: pendingId, op, label });
|
|
4126
|
+
return { staged: true, id: targetId, pendingId };
|
|
4119
4127
|
}
|
|
4120
4128
|
commit(op, label, targetId);
|
|
4121
4129
|
return { staged: false, id: targetId };
|
|
@@ -4198,18 +4206,26 @@ function registerDocBridge(host, options) {
|
|
|
4198
4206
|
type: { type: "string", description: "Node type discriminant." },
|
|
4199
4207
|
parent: { type: ["string", "null"], description: "Parent id, or null for a root." },
|
|
4200
4208
|
props: { type: "object", description: "JSON props bag." },
|
|
4201
|
-
order: { type: "string", description: "Fractional order key (default: append)." }
|
|
4209
|
+
order: { type: "string", description: "Fractional order key (default: append)." },
|
|
4210
|
+
id: {
|
|
4211
|
+
type: "string",
|
|
4212
|
+
description: "Id for the new node. Supply a meaningful one if you intend to address this node later; omitted means one is minted for you. Must not already exist."
|
|
4213
|
+
}
|
|
4202
4214
|
},
|
|
4203
4215
|
["type"],
|
|
4204
4216
|
(args) => {
|
|
4205
4217
|
const tree = adapter.get();
|
|
4206
4218
|
const parent = typeof args.parent === "string" ? args.parent : null;
|
|
4207
|
-
const
|
|
4219
|
+
const requested = typeof args.id === "string" ? args.id : void 0;
|
|
4220
|
+
if (requested !== void 0 && tree.nodes[requested]) {
|
|
4221
|
+
throw new Error(`Node ${requested} already exists \u2014 pick a different id or update it instead`);
|
|
4222
|
+
}
|
|
4223
|
+
const id = requested ?? mintId();
|
|
4208
4224
|
const order = typeof args.order === "string" ? args.order : fancyDocCommons.appendOrder(tree, parent);
|
|
4209
4225
|
const props = args.props && typeof args.props === "object" ? args.props : {};
|
|
4210
4226
|
const node = makeNode({ id, type: String(args.type), parent, order }, props);
|
|
4211
4227
|
const res = submit({ t: "insert", node }, `add ${args.type} ${id}`, id);
|
|
4212
|
-
return { ...res, _text: res.staged ? `staged add ${id}` : `added ${id}` };
|
|
4228
|
+
return { ...res, _text: res.staged ? `staged add ${id} \u2014 confirm ${res.pendingId}` : `added ${id}` };
|
|
4213
4229
|
},
|
|
4214
4230
|
true,
|
|
4215
4231
|
(_args, result) => target(result.id)
|
|
@@ -4224,7 +4240,7 @@ function registerDocBridge(host, options) {
|
|
|
4224
4240
|
if (!adapter.get().nodes[id]) throw new Error(`No node ${id}`);
|
|
4225
4241
|
const patch = args.patch && typeof args.patch === "object" ? args.patch : {};
|
|
4226
4242
|
const res = submit({ t: "set_props", id, patch }, `update ${id}`, id);
|
|
4227
|
-
return { ...res, id, _text: res.staged ? `staged update ${id}` : `updated ${id}` };
|
|
4243
|
+
return { ...res, id, _text: res.staged ? `staged update ${id} \u2014 confirm ${res.pendingId}` : `updated ${id}` };
|
|
4228
4244
|
},
|
|
4229
4245
|
true,
|
|
4230
4246
|
(args) => target(String(args.id))
|
|
@@ -4240,7 +4256,7 @@ function registerDocBridge(host, options) {
|
|
|
4240
4256
|
if (!tree.nodes[id]) throw new Error(`No node ${id}`);
|
|
4241
4257
|
const count = fancyDocCommons.descendantsOf(tree, id).length + 1;
|
|
4242
4258
|
const res = submit({ t: "remove", id }, `remove ${id} (${count} node${count === 1 ? "" : "s"})`, id);
|
|
4243
|
-
return { ...res, id, removed: count, _text: res.staged ? `staged remove ${id}` : `removed ${id}` };
|
|
4259
|
+
return { ...res, id, removed: count, _text: res.staged ? `staged remove ${id} \u2014 confirm ${res.pendingId}` : `removed ${id}` };
|
|
4244
4260
|
},
|
|
4245
4261
|
true,
|
|
4246
4262
|
(args) => target(String(args.id))
|
|
@@ -4257,7 +4273,7 @@ function registerDocBridge(host, options) {
|
|
|
4257
4273
|
const parent = args.parent === null ? null : typeof args.parent === "string" ? args.parent : tree.nodes[id].parent;
|
|
4258
4274
|
const order = typeof args.order === "string" ? args.order : fancyDocCommons.appendOrder(tree, parent);
|
|
4259
4275
|
const res = submit({ t: "move", id, parent, order }, `move ${id}`, id);
|
|
4260
|
-
return { ...res, id, _text: res.staged ? `staged move ${id}` : `moved ${id}` };
|
|
4276
|
+
return { ...res, id, _text: res.staged ? `staged move ${id} \u2014 confirm ${res.pendingId}` : `moved ${id}` };
|
|
4261
4277
|
},
|
|
4262
4278
|
true,
|
|
4263
4279
|
(args) => target(String(args.id))
|
|
@@ -4273,7 +4289,7 @@ function registerDocBridge(host, options) {
|
|
|
4273
4289
|
if (built && typeof built === "object" && "error" in built) throw new Error(built.error);
|
|
4274
4290
|
const tid = t.targetId?.(args);
|
|
4275
4291
|
const res = submit(built, `${t.verb} ${tid ?? ""}`.trim(), tid);
|
|
4276
|
-
return { ...res, id: tid, _text: res.staged ? `staged ${t.verb}` : t.verb };
|
|
4292
|
+
return { ...res, id: tid, _text: res.staged ? `staged ${t.verb} \u2014 confirm ${res.pendingId}` : t.verb };
|
|
4277
4293
|
},
|
|
4278
4294
|
true,
|
|
4279
4295
|
(args) => target(t.targetId?.(args))
|
|
@@ -4281,8 +4297,8 @@ function registerDocBridge(host, options) {
|
|
|
4281
4297
|
}
|
|
4282
4298
|
reg(
|
|
4283
4299
|
"confirm",
|
|
4284
|
-
"Apply a staged op
|
|
4285
|
-
{ id: { type: "string" } },
|
|
4300
|
+
"Apply a staged op. Pass the `pendingId` a staged mutation returned \u2014 not the node `id` it also returns.",
|
|
4301
|
+
{ id: { type: "string", description: "The `pendingId` from a staged mutation." } },
|
|
4286
4302
|
["id"],
|
|
4287
4303
|
(args) => {
|
|
4288
4304
|
const id = String(args.id);
|
|
@@ -4297,8 +4313,8 @@ function registerDocBridge(host, options) {
|
|
|
4297
4313
|
);
|
|
4298
4314
|
reg(
|
|
4299
4315
|
"reject",
|
|
4300
|
-
"Discard a staged op
|
|
4301
|
-
{ id: { type: "string" } },
|
|
4316
|
+
"Discard a staged op. Pass the `pendingId` a staged mutation returned \u2014 not the node `id` it also returns.",
|
|
4317
|
+
{ id: { type: "string", description: "The `pendingId` from a staged mutation." } },
|
|
4302
4318
|
["id"],
|
|
4303
4319
|
(args) => {
|
|
4304
4320
|
const id = String(args.id);
|
|
@@ -4390,6 +4406,90 @@ function registerCmsBridge(host, options) {
|
|
|
4390
4406
|
]
|
|
4391
4407
|
});
|
|
4392
4408
|
}
|
|
4409
|
+
var SCREEN_TEXT_NODE_TYPE = "#text";
|
|
4410
|
+
function registerScreenDocBridge(host, options) {
|
|
4411
|
+
const { adapter } = options;
|
|
4412
|
+
const surface = options.surface ?? "screen";
|
|
4413
|
+
const setText = {
|
|
4414
|
+
verb: "set_text",
|
|
4415
|
+
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.",
|
|
4416
|
+
properties: {
|
|
4417
|
+
id: { type: "string", description: "Id of the element whose text to set." },
|
|
4418
|
+
text: { type: "string" }
|
|
4419
|
+
},
|
|
4420
|
+
required: ["id", "text"],
|
|
4421
|
+
build: (args, tree) => {
|
|
4422
|
+
const id = String(args.id);
|
|
4423
|
+
const node = tree.nodes[id];
|
|
4424
|
+
if (!node) return { error: `No node ${id}` };
|
|
4425
|
+
if (node.type === SCREEN_TEXT_NODE_TYPE) {
|
|
4426
|
+
return { t: "set_props", id, patch: { value: String(args.text) } };
|
|
4427
|
+
}
|
|
4428
|
+
const texts = fancyDocCommons.childrenOf(tree, id).filter((c) => c.type === SCREEN_TEXT_NODE_TYPE);
|
|
4429
|
+
if (texts.length === 1) {
|
|
4430
|
+
return { t: "set_props", id: texts[0].id, patch: { value: String(args.text) } };
|
|
4431
|
+
}
|
|
4432
|
+
if (texts.length === 0) {
|
|
4433
|
+
return {
|
|
4434
|
+
t: "insert",
|
|
4435
|
+
node: {
|
|
4436
|
+
id: `${id}-text`,
|
|
4437
|
+
type: SCREEN_TEXT_NODE_TYPE,
|
|
4438
|
+
parent: id,
|
|
4439
|
+
order: fancyDocCommons.appendOrder(tree, id),
|
|
4440
|
+
props: { value: String(args.text) }
|
|
4441
|
+
}
|
|
4442
|
+
};
|
|
4443
|
+
}
|
|
4444
|
+
return {
|
|
4445
|
+
error: `Node ${id} has ${texts.length} text children (${texts.map((t) => t.id).join(", ")}) \u2014 set_text on the one you mean`
|
|
4446
|
+
};
|
|
4447
|
+
},
|
|
4448
|
+
targetId: (args) => String(args.id)
|
|
4449
|
+
};
|
|
4450
|
+
const bridge = registerDocBridge(host, {
|
|
4451
|
+
adapter,
|
|
4452
|
+
surface,
|
|
4453
|
+
agent: options.agent,
|
|
4454
|
+
stagePolicy: options.stagePolicy,
|
|
4455
|
+
onPending: options.onPending,
|
|
4456
|
+
// Anything the AGENT adds is authored by definition — it chose or was given
|
|
4457
|
+
// the id — so it must not inherit the synthetic flag from its neighbours.
|
|
4458
|
+
makeNode: ({ id, type, parent, order }, props) => ({ id, type, parent, order, props }),
|
|
4459
|
+
describeNode: (n) => ({
|
|
4460
|
+
id: n.id,
|
|
4461
|
+
type: n.type,
|
|
4462
|
+
parent: n.parent,
|
|
4463
|
+
...n.synthetic ? { synthetic: true } : {},
|
|
4464
|
+
...n.type === SCREEN_TEXT_NODE_TYPE ? { text: String(n.props?.value ?? "") } : { props: Object.keys(n.props ?? {}) }
|
|
4465
|
+
}),
|
|
4466
|
+
opTools: [setText]
|
|
4467
|
+
});
|
|
4468
|
+
const disposeAddressable = host.registerTool(
|
|
4469
|
+
{
|
|
4470
|
+
name: `${surface}_addressable`,
|
|
4471
|
+
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.",
|
|
4472
|
+
inputSchema: { type: "object", properties: {}, required: [], additionalProperties: false }
|
|
4473
|
+
},
|
|
4474
|
+
(async () => {
|
|
4475
|
+
const nodes = Object.values(adapter.get().nodes);
|
|
4476
|
+
const authored = nodes.filter((n) => !n.synthetic).map((n) => ({ id: n.id, type: n.type }));
|
|
4477
|
+
const syntheticCount = nodes.length - authored.length;
|
|
4478
|
+
return textResult(
|
|
4479
|
+
authored.length ? `${authored.length} addressable, ${syntheticCount} positional` : `no addressable ids \u2014 all ${syntheticCount} nodes are positional`,
|
|
4480
|
+
{ addressable: authored, syntheticCount }
|
|
4481
|
+
);
|
|
4482
|
+
})
|
|
4483
|
+
);
|
|
4484
|
+
return {
|
|
4485
|
+
id: surface,
|
|
4486
|
+
title: "Screen content",
|
|
4487
|
+
dispose: () => {
|
|
4488
|
+
disposeAddressable();
|
|
4489
|
+
bridge.dispose();
|
|
4490
|
+
}
|
|
4491
|
+
};
|
|
4492
|
+
}
|
|
4393
4493
|
|
|
4394
4494
|
// src/sharing/token.ts
|
|
4395
4495
|
var TOKEN_BYTES = 24;
|
|
@@ -4457,6 +4557,7 @@ var SseRelayTransport = class {
|
|
|
4457
4557
|
this.listeners = /* @__PURE__ */ new Set();
|
|
4458
4558
|
this.state = "idle";
|
|
4459
4559
|
this.peers = /* @__PURE__ */ new Set();
|
|
4560
|
+
this.peerListeners = /* @__PURE__ */ new Set();
|
|
4460
4561
|
this.opts = options;
|
|
4461
4562
|
this.expectedToken = options.token;
|
|
4462
4563
|
}
|
|
@@ -4517,6 +4618,8 @@ var SseRelayTransport = class {
|
|
|
4517
4618
|
this.es?.close();
|
|
4518
4619
|
this.es = void 0;
|
|
4519
4620
|
this.connected = false;
|
|
4621
|
+
this.peers.clear();
|
|
4622
|
+
this.notifyPeers();
|
|
4520
4623
|
this.setState("closed");
|
|
4521
4624
|
}
|
|
4522
4625
|
onStateChange(listener) {
|
|
@@ -4524,6 +4627,28 @@ var SseRelayTransport = class {
|
|
|
4524
4627
|
listener(this.state);
|
|
4525
4628
|
return () => this.listeners.delete(listener);
|
|
4526
4629
|
}
|
|
4630
|
+
/** How many remote peers (agents) are currently attached to this session. */
|
|
4631
|
+
peerCount() {
|
|
4632
|
+
return this.peers.size;
|
|
4633
|
+
}
|
|
4634
|
+
/**
|
|
4635
|
+
* Subscribe to peer arrivals/departures. Fires immediately with the current
|
|
4636
|
+
* count.
|
|
4637
|
+
*
|
|
4638
|
+
* This is the ONLY honest answer to "is an agent actually here?".
|
|
4639
|
+
* `onStateChange` reports the BROWSER's own channel to the relay, which opens
|
|
4640
|
+
* the instant sharing starts — so a UI keyed on it announces "Agent is
|
|
4641
|
+
* driving" to a human who has not yet handed the link to anybody, and can
|
|
4642
|
+
* never tell them when one really arrives.
|
|
4643
|
+
*/
|
|
4644
|
+
onPeersChange(listener) {
|
|
4645
|
+
this.peerListeners.add(listener);
|
|
4646
|
+
listener(this.peers.size);
|
|
4647
|
+
return () => this.peerListeners.delete(listener);
|
|
4648
|
+
}
|
|
4649
|
+
notifyPeers() {
|
|
4650
|
+
for (const l of this.peerListeners) l(this.peers.size);
|
|
4651
|
+
}
|
|
4527
4652
|
/**
|
|
4528
4653
|
* For relays that wrap each frame with auth metadata: hosts can call this
|
|
4529
4654
|
* directly when a frame arrives via a non-SSE path. The transport will
|
|
@@ -4558,6 +4683,7 @@ var SseRelayTransport = class {
|
|
|
4558
4683
|
if ("method" in message && message.method === "notifications/peer_joined") {
|
|
4559
4684
|
const subscriberId = String(message.params?.subscriberId ?? "peer");
|
|
4560
4685
|
this.peers.add(subscriberId);
|
|
4686
|
+
this.notifyPeers();
|
|
4561
4687
|
fancyAutoCommon.emitActivity({
|
|
4562
4688
|
agentId: "agent",
|
|
4563
4689
|
agentName: "Agent",
|
|
@@ -4572,6 +4698,7 @@ var SseRelayTransport = class {
|
|
|
4572
4698
|
if ("method" in message && message.method === "notifications/peer_left") {
|
|
4573
4699
|
const subscriberId = String(message.params?.subscriberId ?? "peer");
|
|
4574
4700
|
this.peers.delete(subscriberId);
|
|
4701
|
+
this.notifyPeers();
|
|
4575
4702
|
if (this.peers.size === 0) {
|
|
4576
4703
|
fancyAutoCommon.emitActivity({
|
|
4577
4704
|
agentId: "agent",
|
|
@@ -4629,6 +4756,7 @@ function useCoBrowseSession(options) {
|
|
|
4629
4756
|
const disposeBridge = react.useRef(null);
|
|
4630
4757
|
const [session, setSession] = react.useState(null);
|
|
4631
4758
|
const [relayState, setRelayState] = react.useState("idle");
|
|
4759
|
+
const [agentCount, setAgentCount] = react.useState(0);
|
|
4632
4760
|
react.useEffect(() => {
|
|
4633
4761
|
const server = new MicroMcpServer({
|
|
4634
4762
|
info: options.info ?? { name: "fancy-co-browse", version: "0.1.0" },
|
|
@@ -4663,6 +4791,7 @@ function useCoBrowseSession(options) {
|
|
|
4663
4791
|
}
|
|
4664
4792
|
const relay = attachSseRelay(server, { baseUrl: relayBaseUrl, sessionId: descriptor.id, token: descriptor.token });
|
|
4665
4793
|
relay.onStateChange(setRelayState);
|
|
4794
|
+
relay.onPeersChange(setAgentCount);
|
|
4666
4795
|
relayRef.current = relay;
|
|
4667
4796
|
setSession(descriptor);
|
|
4668
4797
|
}, [relayBaseUrl, options]);
|
|
@@ -4671,6 +4800,7 @@ function useCoBrowseSession(options) {
|
|
|
4671
4800
|
relayRef.current?.close();
|
|
4672
4801
|
relayRef.current = null;
|
|
4673
4802
|
setRelayState("idle");
|
|
4803
|
+
setAgentCount(0);
|
|
4674
4804
|
setSession(null);
|
|
4675
4805
|
if (current) {
|
|
4676
4806
|
const csrf = options.csrfToken?.() ?? "";
|
|
@@ -4693,7 +4823,16 @@ function useCoBrowseSession(options) {
|
|
|
4693
4823
|
meta: event
|
|
4694
4824
|
});
|
|
4695
4825
|
}, []);
|
|
4696
|
-
return {
|
|
4826
|
+
return {
|
|
4827
|
+
server: serverRef.current,
|
|
4828
|
+
session,
|
|
4829
|
+
relayState,
|
|
4830
|
+
agentConnected: agentCount > 0,
|
|
4831
|
+
agentCount,
|
|
4832
|
+
startShare,
|
|
4833
|
+
stopShare,
|
|
4834
|
+
observeUser
|
|
4835
|
+
};
|
|
4697
4836
|
}
|
|
4698
4837
|
function AgentPanel({ agent, activity, onSubmit, busy, actions, className, style }) {
|
|
4699
4838
|
const scrollRef = react.useRef(null);
|
|
@@ -5416,10 +5555,12 @@ function ManualPopover({
|
|
|
5416
5555
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "fai-connect__copy-btn", onClick: onCopy, children: copied ? "Copied" : "Copy snippet" })
|
|
5417
5556
|
] });
|
|
5418
5557
|
}
|
|
5558
|
+
var LIFECYCLE_ACTIONS = /* @__PURE__ */ new Set(["agent_connected", "agent_disconnected"]);
|
|
5419
5559
|
function CoBrowsePresence({ session, connectUrl, shareBaseUrl, className }) {
|
|
5420
5560
|
const { events } = useAgentActivity(void 0, { capacity: 40 });
|
|
5421
|
-
const lastAgentAction = [...events].reverse().find((e) => (e.source ?? "agent") !== "user");
|
|
5422
|
-
const connected = session.
|
|
5561
|
+
const lastAgentAction = [...events].reverse().find((e) => (e.source ?? "agent") !== "user" && !LIFECYCLE_ACTIONS.has(e.action));
|
|
5562
|
+
const connected = session.agentConnected;
|
|
5563
|
+
const activity = react.useMemo(() => events.map(toPanelActivity), [events]);
|
|
5423
5564
|
if (!session.session) {
|
|
5424
5565
|
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
5566
|
}
|
|
@@ -5437,12 +5578,25 @@ function CoBrowsePresence({ session, connectUrl, shareBaseUrl, className }) {
|
|
|
5437
5578
|
onStart: () => void session.startShare(),
|
|
5438
5579
|
onStop: session.stopShare,
|
|
5439
5580
|
status: session.relayState,
|
|
5581
|
+
agentConnected: connected,
|
|
5582
|
+
activity,
|
|
5440
5583
|
shareBaseUrl
|
|
5441
5584
|
}
|
|
5442
5585
|
),
|
|
5443
5586
|
connectUrl && /* @__PURE__ */ jsxRuntime.jsx(ConnectorButtons, { serverName: "Fancy UI co-browse", mcpUrl: connectUrl })
|
|
5444
5587
|
] });
|
|
5445
5588
|
}
|
|
5589
|
+
function toPanelActivity(event) {
|
|
5590
|
+
const who = (event.source ?? "agent") === "user" ? "You" : event.agentName ?? "Agent";
|
|
5591
|
+
return {
|
|
5592
|
+
id: `${event.timestamp}-${event.action}-${event.target?.elementId ?? event.target?.label ?? ""}`,
|
|
5593
|
+
at: event.timestamp,
|
|
5594
|
+
kind: LIFECYCLE_ACTIONS.has(event.action) ? "info" : "tool",
|
|
5595
|
+
source: who,
|
|
5596
|
+
text: event.target?.label ?? event.action,
|
|
5597
|
+
detail: event.meta
|
|
5598
|
+
};
|
|
5599
|
+
}
|
|
5446
5600
|
CoBrowsePresence.displayName = "CoBrowsePresence";
|
|
5447
5601
|
var CURSOR_EDGE_INSET = 24;
|
|
5448
5602
|
var FLUID_CURSOR_SPEED_PX_PER_SECOND = 1050;
|
|
@@ -5461,19 +5615,26 @@ function fluidGlideDuration(from, to) {
|
|
|
5461
5615
|
const distance = Math.hypot(to.x - from.x, to.y - from.y);
|
|
5462
5616
|
return Math.round(Math.min(MAX_GLIDE_MS, Math.max(MIN_GLIDE_MS, distance / FLUID_CURSOR_SPEED_PX_PER_SECOND * 1e3)));
|
|
5463
5617
|
}
|
|
5464
|
-
|
|
5618
|
+
var LIFECYCLE_ACTIONS2 = /* @__PURE__ */ new Set(["agent_connected", "agent_disconnected"]);
|
|
5619
|
+
function CoBrowseCursorLayer({
|
|
5620
|
+
active = true,
|
|
5621
|
+
zIndex = 2147483e3,
|
|
5622
|
+
idleAfterMs = 15e3
|
|
5623
|
+
}) {
|
|
5465
5624
|
const { latest } = useAgentActivity(void 0, { capacity: 12 });
|
|
5466
5625
|
const [cursor, setCursor] = react.useState(
|
|
5467
5626
|
null
|
|
5468
5627
|
);
|
|
5469
5628
|
const [pulse, setPulse] = react.useState(null);
|
|
5629
|
+
const [lastActionAt, setLastActionAt] = react.useState(0);
|
|
5470
5630
|
react.useEffect(() => {
|
|
5471
5631
|
if (!latest || (latest.source ?? "agent") === "user") return;
|
|
5472
|
-
if (latest.action
|
|
5473
|
-
setCursor((prev) => prev ? { ...prev, status: "Standing by", glideMs: 0 } :
|
|
5632
|
+
if (LIFECYCLE_ACTIONS2.has(latest.action)) {
|
|
5633
|
+
setCursor((prev) => prev ? { ...prev, status: "Standing by", glideMs: 0 } : null);
|
|
5474
5634
|
setPulse(null);
|
|
5475
5635
|
return;
|
|
5476
5636
|
}
|
|
5637
|
+
setLastActionAt(latest.timestamp);
|
|
5477
5638
|
const color = latest.agentColor ?? "#a855f7";
|
|
5478
5639
|
const name = latest.agentName ?? "Agent";
|
|
5479
5640
|
const status = latest.target?.label ?? latest.action;
|
|
@@ -5495,6 +5656,14 @@ function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
|
|
|
5495
5656
|
);
|
|
5496
5657
|
}
|
|
5497
5658
|
}, [latest?.timestamp]);
|
|
5659
|
+
react.useEffect(() => {
|
|
5660
|
+
if (!idleAfterMs || !lastActionAt) return;
|
|
5661
|
+
const timer = setTimeout(() => {
|
|
5662
|
+
setCursor(null);
|
|
5663
|
+
setPulse(null);
|
|
5664
|
+
}, idleAfterMs);
|
|
5665
|
+
return () => clearTimeout(timer);
|
|
5666
|
+
}, [lastActionAt, idleAfterMs]);
|
|
5498
5667
|
if (!active || !cursor || typeof document === "undefined") return null;
|
|
5499
5668
|
return reactDom.createPortal(
|
|
5500
5669
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-co-browse-cursor-layer": "", style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex }, children: [
|
|
@@ -5842,6 +6011,7 @@ exports.MemoryHumanPlusEventStore = MemoryHumanPlusEventStore;
|
|
|
5842
6011
|
exports.MicroMcpServer = MicroMcpServer;
|
|
5843
6012
|
exports.PASSKEY_BRIDGE_TOOLS = PASSKEY_BRIDGE_TOOLS;
|
|
5844
6013
|
exports.RelayTransport = RelayTransport;
|
|
6014
|
+
exports.SCREEN_TEXT_NODE_TYPE = SCREEN_TEXT_NODE_TYPE;
|
|
5845
6015
|
exports.ScreensActivityBridge = ScreensActivityBridge;
|
|
5846
6016
|
exports.ShareControls = ShareControls;
|
|
5847
6017
|
exports.SimulateUsersButton = SimulateUsersButton;
|
|
@@ -5884,6 +6054,7 @@ exports.registerMapBridge = registerMapBridge;
|
|
|
5884
6054
|
exports.registerNavigationBridge = registerNavigationBridge;
|
|
5885
6055
|
exports.registerPasskeyBridge = registerPasskeyBridge;
|
|
5886
6056
|
exports.registerSceneBridge = registerSceneBridge;
|
|
6057
|
+
exports.registerScreenDocBridge = registerScreenDocBridge;
|
|
5887
6058
|
exports.registerScreensBridge = registerScreensBridge;
|
|
5888
6059
|
exports.registerSheetsBridge = registerSheetsBridge;
|
|
5889
6060
|
exports.registerSlidesBridge = registerSlidesBridge;
|