@particle-academy/agent-integrations 0.26.0 → 0.28.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.
Files changed (123) hide show
  1. package/dist/bridges/artboard.d.cts +3 -3
  2. package/dist/bridges/artboard.d.ts +3 -3
  3. package/dist/bridges/catalog.d.cts +3 -3
  4. package/dist/bridges/catalog.d.ts +3 -3
  5. package/dist/bridges/charts.d.cts +3 -3
  6. package/dist/bridges/charts.d.ts +3 -3
  7. package/dist/bridges/cms.d.cts +50 -0
  8. package/dist/bridges/cms.d.ts +50 -0
  9. package/dist/bridges/code.d.cts +3 -3
  10. package/dist/bridges/code.d.ts +3 -3
  11. package/dist/bridges/doc.d.cts +68 -0
  12. package/dist/bridges/doc.d.ts +68 -0
  13. package/dist/bridges/features.d.cts +3 -3
  14. package/dist/bridges/features.d.ts +3 -3
  15. package/dist/bridges/files.d.cts +3 -3
  16. package/dist/bridges/files.d.ts +3 -3
  17. package/dist/bridges/flow.d.cts +3 -3
  18. package/dist/bridges/flow.d.ts +3 -3
  19. package/dist/bridges/forms.d.cts +3 -3
  20. package/dist/bridges/forms.d.ts +3 -3
  21. package/dist/bridges/map.d.cts +3 -3
  22. package/dist/bridges/map.d.ts +3 -3
  23. package/dist/bridges/scene.d.cts +3 -3
  24. package/dist/bridges/scene.d.ts +3 -3
  25. package/dist/bridges/screens.d.cts +3 -3
  26. package/dist/bridges/screens.d.ts +3 -3
  27. package/dist/bridges/sheets.d.cts +3 -3
  28. package/dist/bridges/sheets.d.ts +3 -3
  29. package/dist/bridges/slides.d.cts +3 -3
  30. package/dist/bridges/slides.d.ts +3 -3
  31. package/dist/bridges/terminal.d.cts +3 -3
  32. package/dist/bridges/terminal.d.ts +3 -3
  33. package/dist/bridges/whiteboard.d.cts +3 -3
  34. package/dist/bridges/whiteboard.d.ts +3 -3
  35. package/dist/bridges-artboard.js +1 -1
  36. package/dist/bridges-catalog.js +2 -2
  37. package/dist/bridges-charts.js +2 -2
  38. package/dist/bridges-cms.cjs +437 -0
  39. package/dist/bridges-cms.cjs.map +1 -0
  40. package/dist/bridges-cms.js +8 -0
  41. package/dist/bridges-cms.js.map +1 -0
  42. package/dist/bridges-code.js +2 -2
  43. package/dist/bridges-doc.cjs +364 -0
  44. package/dist/bridges-doc.cjs.map +1 -0
  45. package/dist/bridges-doc.js +7 -0
  46. package/dist/bridges-doc.js.map +1 -0
  47. package/dist/bridges-features.js +2 -2
  48. package/dist/bridges-files.js +2 -2
  49. package/dist/bridges-flow.js +1 -1
  50. package/dist/bridges-forms.js +2 -2
  51. package/dist/bridges-map.js +2 -2
  52. package/dist/bridges-scene.js +2 -2
  53. package/dist/bridges-screens.js +2 -2
  54. package/dist/bridges-sheets.js +2 -2
  55. package/dist/bridges-slides.js +2 -2
  56. package/dist/bridges-terminal.js +2 -2
  57. package/dist/bridges-whiteboard.js +2 -2
  58. package/dist/{chunk-HKQBG2HZ.js → chunk-5KLUYF4D.js} +3 -3
  59. package/dist/{chunk-HKQBG2HZ.js.map → chunk-5KLUYF4D.js.map} +1 -1
  60. package/dist/{chunk-YMX7G3XM.js → chunk-66WOHZZJ.js} +3 -3
  61. package/dist/{chunk-YMX7G3XM.js.map → chunk-66WOHZZJ.js.map} +1 -1
  62. package/dist/chunk-A57CRGEK.js +79 -0
  63. package/dist/chunk-A57CRGEK.js.map +1 -0
  64. package/dist/{chunk-XQZGB4FP.js → chunk-AAD3Z5E7.js} +3 -3
  65. package/dist/{chunk-XQZGB4FP.js.map → chunk-AAD3Z5E7.js.map} +1 -1
  66. package/dist/{chunk-3QJSOS7G.js → chunk-AAMQOXZC.js} +3 -3
  67. package/dist/{chunk-3QJSOS7G.js.map → chunk-AAMQOXZC.js.map} +1 -1
  68. package/dist/{chunk-EP3G4LYW.js → chunk-AROVENCE.js} +3 -3
  69. package/dist/{chunk-EP3G4LYW.js.map → chunk-AROVENCE.js.map} +1 -1
  70. package/dist/chunk-FF5F526J.js +245 -0
  71. package/dist/chunk-FF5F526J.js.map +1 -0
  72. package/dist/{chunk-57KAMBAR.js → chunk-HWVVS6HE.js} +3 -3
  73. package/dist/{chunk-57KAMBAR.js.map → chunk-HWVVS6HE.js.map} +1 -1
  74. package/dist/{chunk-FUI7KXE7.js → chunk-KMSRG5KH.js} +35 -2
  75. package/dist/chunk-KMSRG5KH.js.map +1 -0
  76. package/dist/{chunk-QDF37J3V.js → chunk-NN3ELTVO.js} +3 -3
  77. package/dist/{chunk-QDF37J3V.js.map → chunk-NN3ELTVO.js.map} +1 -1
  78. package/dist/{chunk-775LJJTJ.js → chunk-PCW2EK5G.js} +3 -3
  79. package/dist/{chunk-775LJJTJ.js.map → chunk-PCW2EK5G.js.map} +1 -1
  80. package/dist/{chunk-46H4C5AP.js → chunk-QDD4WHIV.js} +3 -3
  81. package/dist/{chunk-46H4C5AP.js.map → chunk-QDD4WHIV.js.map} +1 -1
  82. package/dist/{chunk-V5BXBX7U.js → chunk-QRDVJW6Q.js} +3 -3
  83. package/dist/{chunk-V5BXBX7U.js.map → chunk-QRDVJW6Q.js.map} +1 -1
  84. package/dist/{chunk-267JS64O.js → chunk-VFKO2VUR.js} +3 -3
  85. package/dist/{chunk-267JS64O.js.map → chunk-VFKO2VUR.js.map} +1 -1
  86. package/dist/{chunk-CLFDQ2BW.js → chunk-XMMCSQ4I.js} +3 -3
  87. package/dist/{chunk-CLFDQ2BW.js.map → chunk-XMMCSQ4I.js.map} +1 -1
  88. package/dist/{chunk-K3S6WSHZ.js → chunk-YGD2VTUM.js} +3 -3
  89. package/dist/{chunk-K3S6WSHZ.js.map → chunk-YGD2VTUM.js.map} +1 -1
  90. package/dist/{chunk-NKRSLCS7.js → chunk-ZX6UQURN.js} +3 -3
  91. package/dist/{chunk-NKRSLCS7.js.map → chunk-ZX6UQURN.js.map} +1 -1
  92. package/dist/components-shared-whiteboard.cjs +32 -0
  93. package/dist/components-shared-whiteboard.cjs.map +1 -1
  94. package/dist/components-shared-whiteboard.js +4 -4
  95. package/dist/index.cjs +376 -9
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.d.cts +14 -7
  98. package/dist/index.d.ts +14 -7
  99. package/dist/index.js +54 -29
  100. package/dist/index.js.map +1 -1
  101. package/dist/mcp/index.d.cts +5 -5
  102. package/dist/mcp/index.d.ts +5 -5
  103. package/dist/presence/index.d.cts +1 -1
  104. package/dist/presence/index.d.ts +1 -1
  105. package/dist/{server-BsSwfemr.d.cts → server-Dr3j48CU.d.cts} +2 -2
  106. package/dist/{server-Du3-IGqM.d.ts → server-FFZpYPyM.d.ts} +2 -2
  107. package/dist/sharing/index.d.cts +4 -3
  108. package/dist/sharing/index.d.ts +4 -3
  109. package/dist/sharing.cjs +32 -0
  110. package/dist/sharing.cjs.map +1 -1
  111. package/dist/sharing.js +2 -1
  112. package/dist/sheets-adapter.d.cts +3 -3
  113. package/dist/sheets-adapter.d.ts +3 -3
  114. package/dist/{tool-host-C8JMMGYq.d.ts → tool-host-C_Vh17J_.d.ts} +1 -1
  115. package/dist/{tool-host-BQuUygLF.d.cts → tool-host-IDecPrF1.d.cts} +1 -1
  116. package/dist/{types-CCSBGW9T.d.cts → types-B0rmWyoR.d.cts} +1 -1
  117. package/dist/{types-aOQLTW0E.d.cts → types-BDI_g5UG.d.cts} +1 -1
  118. package/dist/{types-aOQLTW0E.d.ts → types-BDI_g5UG.d.ts} +1 -1
  119. package/dist/{types-DIVNcIQO.d.ts → types-CBoQhR8L.d.ts} +1 -1
  120. package/dist/undo/index.d.cts +2 -2
  121. package/dist/undo/index.d.ts +2 -2
  122. package/package.json +23 -2
  123. package/dist/chunk-FUI7KXE7.js.map +0 -1
@@ -1,9 +1,9 @@
1
- import { ShareControls, AgentCursor, AgentActivityHighlight, AgentPanel } from './chunk-HKQBG2HZ.js';
2
- import { createSessionDescriptor, attachSseRelay } from './chunk-FUI7KXE7.js';
1
+ import { ShareControls, AgentCursor, AgentActivityHighlight, AgentPanel } from './chunk-5KLUYF4D.js';
2
+ import { createSessionDescriptor, attachSseRelay } from './chunk-KMSRG5KH.js';
3
3
  import { attachInProcess } from './chunk-AFUULW5E.js';
4
- import { registerWhiteboardBridge } from './chunk-3QJSOS7G.js';
5
- import './chunk-KJ5AOOV7.js';
4
+ import { registerWhiteboardBridge } from './chunk-AAMQOXZC.js';
6
5
  import './chunk-ULJL53DL.js';
6
+ import './chunk-KJ5AOOV7.js';
7
7
  import './chunk-C3TYI5TJ.js';
8
8
  import { MicroMcpServer } from './chunk-4KAIV6OD.js';
9
9
  import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
package/dist/index.cjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var fancyAutoCommon = require('@particle-academy/fancy-auto-common');
4
4
  var react = require('react');
5
+ var fancyDocCommons = require('@particle-academy/fancy-doc-commons');
5
6
  var jsxRuntime = require('react/jsx-runtime');
6
7
  var reactDom = require('react-dom');
7
8
 
@@ -2819,7 +2820,7 @@ function registerNavigationBridge(host, options) {
2819
2820
  ].join("\n");
2820
2821
  return textResult(text, snap);
2821
2822
  },
2822
- false
2823
+ () => target("Inspect page")
2823
2824
  );
2824
2825
  reg(
2825
2826
  "page_read",
@@ -2827,7 +2828,23 @@ function registerNavigationBridge(host, options) {
2827
2828
  {},
2828
2829
  [],
2829
2830
  () => textResult(adapter.read ? adapter.read() : "(host did not provide page text)"),
2830
- false
2831
+ () => target("Read page")
2832
+ );
2833
+ reg(
2834
+ "page_focus",
2835
+ "Move the agent's visible focus to an interactive element by stable handle without activating it.",
2836
+ { handle: { type: "string" } },
2837
+ ["handle"],
2838
+ (args) => {
2839
+ const handle = String(args.handle ?? "");
2840
+ const action = adapter.describe().actions.find((candidate) => candidate.handle === handle);
2841
+ if (!action) return errorResult(`No element for handle "${handle}"`);
2842
+ return textResult(`Focused ${handle}`, {
2843
+ handle,
2844
+ rect: adapter.rectFor?.(handle) ?? void 0
2845
+ });
2846
+ },
2847
+ (args) => target(`Focus ${String(args.handle ?? "")}`, String(args.handle ?? ""))
2831
2848
  );
2832
2849
  reg(
2833
2850
  "nav_visit",
@@ -3486,6 +3503,314 @@ function registerFeaturesBridge(host, options) {
3486
3503
  }
3487
3504
  };
3488
3505
  }
3506
+ var DEFAULT_AGENT14 = { id: "agent", name: "Agent", color: "#a855f7" };
3507
+ function registerDocBridge(host, options) {
3508
+ const { adapter, surface } = options;
3509
+ const agent = { ...DEFAULT_AGENT14, ...options.agent ?? {} };
3510
+ const reducer = options.reducer ?? fancyDocCommons.treeReducer();
3511
+ const stage = options.stagePolicy ?? fancyDocCommons.autoApply;
3512
+ const makeNode = options.makeNode ?? (({ id, type, parent, order }, props) => ({ id, type, parent, order, props }));
3513
+ const describeNode = options.describeNode ?? ((n) => ({ id: n.id, type: n.type, parent: n.parent, props: Object.keys(n.props ?? {}) }));
3514
+ const disposers = [];
3515
+ ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
3516
+ let idSeq = 0;
3517
+ const mintId = () => adapter.newId?.() ?? `${surface}-${idSeq += 1}`;
3518
+ const target = (elementId) => ({ kind: surface, screenId: adapter.screenId, elementId });
3519
+ const pending = /* @__PURE__ */ new Map();
3520
+ function commit(op, label, targetId) {
3521
+ const prev = adapter.get();
3522
+ const next = reducer.reduce(prev, op);
3523
+ adapter.set(next);
3524
+ fancyAutoCommon.pushUndoEntry(agent.id, {
3525
+ timestamp: Date.now(),
3526
+ bridgeId: surface,
3527
+ action: label,
3528
+ label,
3529
+ undo: () => adapter.set(prev),
3530
+ redo: () => adapter.set(next)
3531
+ });
3532
+ return { ok: true, id: targetId };
3533
+ }
3534
+ function submit(op, label, targetId) {
3535
+ if (stage(op) === "confirm") {
3536
+ const id = `${surface}-pending-${idSeq += 1}`;
3537
+ pending.set(id, { op, label, targetId });
3538
+ options.onPending?.({ id, op, label });
3539
+ return { staged: true, id };
3540
+ }
3541
+ commit(op, label, targetId);
3542
+ return { staged: false, id: targetId };
3543
+ }
3544
+ const reg = (verb, description, properties, required, handler, isMutation, resolveTarget) => {
3545
+ const name = `${surface}_${verb}`;
3546
+ const wrapped = async (args) => {
3547
+ try {
3548
+ const { _text, ...structured } = await handler(args);
3549
+ return textResult(typeof _text === "string" ? _text : name, structured);
3550
+ } catch (e) {
3551
+ return errorResult(e instanceof Error ? e.message : String(e));
3552
+ }
3553
+ };
3554
+ const final = isMutation ? wrapToolWithActivity(wrapped, {
3555
+ toolName: name,
3556
+ agent,
3557
+ kind: surface,
3558
+ screenId: adapter.screenId,
3559
+ resolveTarget: ({ args, result }) => {
3560
+ if (result.structuredContent?.staged) return null;
3561
+ return resolveTarget?.(args, result.structuredContent ?? {}) ?? target();
3562
+ }
3563
+ }) : wrapped;
3564
+ disposers.push(
3565
+ host.registerTool(
3566
+ { name, description, inputSchema: { type: "object", properties, required, additionalProperties: false } },
3567
+ final
3568
+ )
3569
+ );
3570
+ };
3571
+ reg(
3572
+ "describe",
3573
+ `Summarize the ${surface} document: node count, roots, and the active surface. Call before reading or writing.`,
3574
+ {},
3575
+ [],
3576
+ () => {
3577
+ const tree = adapter.get();
3578
+ const ids = Object.keys(tree.nodes);
3579
+ return { nodeCount: ids.length, roots: fancyDocCommons.childrenOf(tree, null).map((n) => n.id), _text: `${ids.length} nodes` };
3580
+ },
3581
+ false
3582
+ );
3583
+ reg(
3584
+ "get",
3585
+ `Get the whole ${surface} document tree as JSON.`,
3586
+ {},
3587
+ [],
3588
+ () => ({ tree: adapter.get(), _text: "full tree" }),
3589
+ false
3590
+ );
3591
+ reg(
3592
+ "list",
3593
+ "List nodes (optionally the children of `parent`), in order.",
3594
+ { parent: { type: ["string", "null"], description: "Parent id, or null/omitted for roots." } },
3595
+ [],
3596
+ (args) => {
3597
+ const tree = adapter.get();
3598
+ const parent = typeof args.parent === "string" ? args.parent : null;
3599
+ return { nodes: fancyDocCommons.childrenOf(tree, parent).map(describeNode), _text: `children of ${parent ?? "root"}` };
3600
+ },
3601
+ false
3602
+ );
3603
+ reg(
3604
+ "get_node",
3605
+ "Get one node by id.",
3606
+ { id: { type: "string" } },
3607
+ ["id"],
3608
+ (args) => {
3609
+ const node = adapter.get().nodes[String(args.id)];
3610
+ if (!node) throw new Error(`No node ${args.id}`);
3611
+ return { node, _text: `node ${node.id}` };
3612
+ },
3613
+ false
3614
+ );
3615
+ reg(
3616
+ "add",
3617
+ "Insert a node. Returns its id. Appends under `parent` (or roots) unless `order` is given.",
3618
+ {
3619
+ type: { type: "string", description: "Node type discriminant." },
3620
+ parent: { type: ["string", "null"], description: "Parent id, or null for a root." },
3621
+ props: { type: "object", description: "JSON props bag." },
3622
+ order: { type: "string", description: "Fractional order key (default: append)." }
3623
+ },
3624
+ ["type"],
3625
+ (args) => {
3626
+ const tree = adapter.get();
3627
+ const parent = typeof args.parent === "string" ? args.parent : null;
3628
+ const id = mintId();
3629
+ const order = typeof args.order === "string" ? args.order : fancyDocCommons.appendOrder(tree, parent);
3630
+ const props = args.props && typeof args.props === "object" ? args.props : {};
3631
+ const node = makeNode({ id, type: String(args.type), parent, order }, props);
3632
+ const res = submit({ t: "insert", node }, `add ${args.type} ${id}`, id);
3633
+ return { ...res, _text: res.staged ? `staged add ${id}` : `added ${id}` };
3634
+ },
3635
+ true,
3636
+ (_args, result) => target(result.id)
3637
+ );
3638
+ reg(
3639
+ "update",
3640
+ "Merge a props patch into a node.",
3641
+ { id: { type: "string" }, patch: { type: "object" } },
3642
+ ["id", "patch"],
3643
+ (args) => {
3644
+ const id = String(args.id);
3645
+ if (!adapter.get().nodes[id]) throw new Error(`No node ${id}`);
3646
+ const patch = args.patch && typeof args.patch === "object" ? args.patch : {};
3647
+ const res = submit({ t: "set_props", id, patch }, `update ${id}`, id);
3648
+ return { ...res, id, _text: res.staged ? `staged update ${id}` : `updated ${id}` };
3649
+ },
3650
+ true,
3651
+ (args) => target(String(args.id))
3652
+ );
3653
+ reg(
3654
+ "remove",
3655
+ "Remove a node and its subtree.",
3656
+ { id: { type: "string" } },
3657
+ ["id"],
3658
+ (args) => {
3659
+ const id = String(args.id);
3660
+ const tree = adapter.get();
3661
+ if (!tree.nodes[id]) throw new Error(`No node ${id}`);
3662
+ const count = fancyDocCommons.descendantsOf(tree, id).length + 1;
3663
+ const res = submit({ t: "remove", id }, `remove ${id} (${count} node${count === 1 ? "" : "s"})`, id);
3664
+ return { ...res, id, removed: count, _text: res.staged ? `staged remove ${id}` : `removed ${id}` };
3665
+ },
3666
+ true,
3667
+ (args) => target(String(args.id))
3668
+ );
3669
+ reg(
3670
+ "move",
3671
+ "Reparent + reorder a node.",
3672
+ { id: { type: "string" }, parent: { type: ["string", "null"] }, order: { type: "string", description: "Fractional order key (default: append under the new parent)." } },
3673
+ ["id"],
3674
+ (args) => {
3675
+ const id = String(args.id);
3676
+ const tree = adapter.get();
3677
+ if (!tree.nodes[id]) throw new Error(`No node ${id}`);
3678
+ const parent = args.parent === null ? null : typeof args.parent === "string" ? args.parent : tree.nodes[id].parent;
3679
+ const order = typeof args.order === "string" ? args.order : fancyDocCommons.appendOrder(tree, parent);
3680
+ const res = submit({ t: "move", id, parent, order }, `move ${id}`, id);
3681
+ return { ...res, id, _text: res.staged ? `staged move ${id}` : `moved ${id}` };
3682
+ },
3683
+ true,
3684
+ (args) => target(String(args.id))
3685
+ );
3686
+ for (const t of options.opTools ?? []) {
3687
+ reg(
3688
+ t.verb,
3689
+ t.description,
3690
+ t.properties,
3691
+ t.required,
3692
+ (args) => {
3693
+ const built = t.build(args, adapter.get());
3694
+ if (built && typeof built === "object" && "error" in built) throw new Error(built.error);
3695
+ const tid = t.targetId?.(args);
3696
+ const res = submit(built, `${t.verb} ${tid ?? ""}`.trim(), tid);
3697
+ return { ...res, id: tid, _text: res.staged ? `staged ${t.verb}` : t.verb };
3698
+ },
3699
+ true,
3700
+ (args) => target(t.targetId?.(args))
3701
+ );
3702
+ }
3703
+ reg(
3704
+ "confirm",
3705
+ "Apply a staged op by its pending id (from a staged mutation).",
3706
+ { id: { type: "string" } },
3707
+ ["id"],
3708
+ (args) => {
3709
+ const id = String(args.id);
3710
+ const p = pending.get(id);
3711
+ if (!p) throw new Error(`No pending op ${id}`);
3712
+ pending.delete(id);
3713
+ commit(p.op, p.label, p.targetId);
3714
+ return { ok: true, applied: p.label, id: p.targetId, _text: `applied ${p.label}` };
3715
+ },
3716
+ true,
3717
+ (_args, result) => target(result.id)
3718
+ );
3719
+ reg(
3720
+ "reject",
3721
+ "Discard a staged op by its pending id.",
3722
+ { id: { type: "string" } },
3723
+ ["id"],
3724
+ (args) => {
3725
+ const id = String(args.id);
3726
+ const p = pending.get(id);
3727
+ if (!p) throw new Error(`No pending op ${id}`);
3728
+ pending.delete(id);
3729
+ return { ok: true, rejected: p.label, _text: `rejected ${p.label}` };
3730
+ },
3731
+ false
3732
+ );
3733
+ return {
3734
+ id: surface,
3735
+ title: surface,
3736
+ dispose: () => {
3737
+ for (const d of disposers) d();
3738
+ pending.clear();
3739
+ }
3740
+ };
3741
+ }
3742
+ function cmsReducer() {
3743
+ const base = fancyDocCommons.treeReducer();
3744
+ return {
3745
+ reduce(doc, op) {
3746
+ if (op.t === "set_style") {
3747
+ const node = doc.nodes[op.id];
3748
+ if (!node) return doc;
3749
+ return { nodes: { ...doc.nodes, [op.id]: { ...node, style: { ...node.style, base: { ...node.style.base, ...op.patch } } } } };
3750
+ }
3751
+ if (op.t === "set_layout") {
3752
+ const node = doc.nodes[op.id];
3753
+ if (!node) return doc;
3754
+ return { nodes: { ...doc.nodes, [op.id]: { ...node, layout: op.layout } } };
3755
+ }
3756
+ return base.reduce(doc, op);
3757
+ },
3758
+ invert(doc, op) {
3759
+ if (op.t === "set_style") {
3760
+ const node = doc.nodes[op.id];
3761
+ if (!node) return [];
3762
+ const prev = {};
3763
+ for (const k in op.patch) prev[k] = node.style.base[k];
3764
+ return [{ t: "set_style", id: op.id, patch: prev }];
3765
+ }
3766
+ if (op.t === "set_layout") {
3767
+ const node = doc.nodes[op.id];
3768
+ if (!node) return [];
3769
+ return [{ t: "set_layout", id: op.id, layout: node.layout }];
3770
+ }
3771
+ return base.invert(doc, op);
3772
+ }
3773
+ };
3774
+ }
3775
+ function registerCmsBridge(host, options) {
3776
+ return registerDocBridge(host, {
3777
+ adapter: options.adapter,
3778
+ surface: "cms",
3779
+ agent: options.agent,
3780
+ reducer: cmsReducer(),
3781
+ stagePolicy: options.stagePolicy,
3782
+ onPending: options.onPending,
3783
+ // New nodes get an empty base style so they satisfy StyledNode.
3784
+ makeNode: ({ id, type, parent, order }, props) => ({ id, type, parent, order, props, style: { base: {} } }),
3785
+ describeNode: (n) => ({ id: n.id, type: n.type, parent: n.parent, layout: n.layout }),
3786
+ opTools: [
3787
+ {
3788
+ verb: "set_style",
3789
+ description: "Merge a StyleProps patch into a node's base style (color, background, padding, fontSize, gap, \u2026).",
3790
+ properties: { id: { type: "string" }, patch: { type: "object", description: "Partial StyleProps." } },
3791
+ required: ["id", "patch"],
3792
+ build: (args, tree) => {
3793
+ const id = String(args.id);
3794
+ if (!tree.nodes[id]) return { error: `No node ${id}` };
3795
+ return { t: "set_style", id, patch: args.patch && typeof args.patch === "object" ? args.patch : {} };
3796
+ },
3797
+ targetId: (args) => String(args.id)
3798
+ },
3799
+ {
3800
+ verb: "set_layout",
3801
+ description: "Set a container node's layout mode (free | stack | grid).",
3802
+ properties: { id: { type: "string" }, layout: { type: "string", enum: ["free", "stack", "grid"] } },
3803
+ required: ["id"],
3804
+ build: (args, tree) => {
3805
+ const id = String(args.id);
3806
+ if (!tree.nodes[id]) return { error: `No node ${id}` };
3807
+ return { t: "set_layout", id, layout: args.layout };
3808
+ },
3809
+ targetId: (args) => String(args.id)
3810
+ }
3811
+ ]
3812
+ });
3813
+ }
3489
3814
 
3490
3815
  // src/sharing/token.ts
3491
3816
  var TOKEN_BYTES = 24;
@@ -3544,6 +3869,7 @@ function constantTimeEqual(a, b) {
3544
3869
  }
3545
3870
 
3546
3871
  // src/sharing/sse-relay.ts
3872
+ init_registry();
3547
3873
  var SseRelayTransport = class {
3548
3874
  constructor(options) {
3549
3875
  this.disposed = false;
@@ -3551,6 +3877,7 @@ var SseRelayTransport = class {
3551
3877
  this.connected = false;
3552
3878
  this.listeners = /* @__PURE__ */ new Set();
3553
3879
  this.state = "idle";
3880
+ this.peers = /* @__PURE__ */ new Set();
3554
3881
  this.opts = options;
3555
3882
  this.expectedToken = options.token;
3556
3883
  }
@@ -3649,6 +3976,36 @@ var SseRelayTransport = class {
3649
3976
  } catch {
3650
3977
  return;
3651
3978
  }
3979
+ if ("method" in message && message.method === "notifications/peer_joined") {
3980
+ const subscriberId = String(message.params?.subscriberId ?? "peer");
3981
+ this.peers.add(subscriberId);
3982
+ fancyAutoCommon.emitActivity({
3983
+ agentId: "agent",
3984
+ agentName: "Agent",
3985
+ agentColor: "#a855f7",
3986
+ action: "agent_connected",
3987
+ timestamp: Date.now(),
3988
+ target: { kind: "navigation", label: "Agent connected" },
3989
+ meta: { subscriberId }
3990
+ });
3991
+ return;
3992
+ }
3993
+ if ("method" in message && message.method === "notifications/peer_left") {
3994
+ const subscriberId = String(message.params?.subscriberId ?? "peer");
3995
+ this.peers.delete(subscriberId);
3996
+ if (this.peers.size === 0) {
3997
+ fancyAutoCommon.emitActivity({
3998
+ agentId: "agent",
3999
+ agentName: "Agent",
4000
+ agentColor: "#a855f7",
4001
+ action: "agent_disconnected",
4002
+ timestamp: Date.now(),
4003
+ target: { kind: "navigation", label: "Agent disconnected" },
4004
+ meta: { subscriberId }
4005
+ });
4006
+ }
4007
+ return;
4008
+ }
3652
4009
  await this.server.receive(this, message);
3653
4010
  }
3654
4011
  setState(state) {
@@ -3681,11 +4038,11 @@ function attachSseRelay(server, options) {
3681
4038
 
3682
4039
  // src/sharing/use-co-browse-session.ts
3683
4040
  init_registry();
3684
- var DEFAULT_AGENT14 = { id: "agent", name: "Agent", color: "#a855f7" };
4041
+ var DEFAULT_AGENT15 = { id: "agent", name: "Agent", color: "#a855f7" };
3685
4042
  var USER = { id: "human", name: "You" };
3686
4043
  function useCoBrowseSession(options) {
3687
4044
  const { adapter, extraBridges } = options;
3688
- const agent = { ...DEFAULT_AGENT14, ...options.agent ?? {} };
4045
+ const agent = { ...DEFAULT_AGENT15, ...options.agent ?? {} };
3689
4046
  const relayBaseUrl = options.relayBaseUrl ?? "/agent-relay";
3690
4047
  const serverRef = react.useRef(null);
3691
4048
  const relayRef = react.useRef(null);
@@ -3717,11 +4074,14 @@ function useCoBrowseSession(options) {
3717
4074
  if (!server || relayRef.current) return;
3718
4075
  const descriptor = createSessionDescriptor();
3719
4076
  const csrf = options.csrfToken?.() ?? "";
3720
- await fetch(`${relayBaseUrl}/register`, {
4077
+ const response = await fetch(`${relayBaseUrl}/register`, {
3721
4078
  method: "POST",
3722
4079
  headers: { "content-type": "application/json", "x-csrf-token": csrf },
3723
4080
  body: JSON.stringify({ session: descriptor.id, token: descriptor.token })
3724
4081
  });
4082
+ if (!response.ok) {
4083
+ throw new Error(`Relay registration failed (${response.status})`);
4084
+ }
3725
4085
  const relay = attachSseRelay(server, { baseUrl: relayBaseUrl, sessionId: descriptor.id, token: descriptor.token });
3726
4086
  relay.onStateChange(setRelayState);
3727
4087
  relayRef.current = relay;
@@ -3735,16 +4095,15 @@ function useCoBrowseSession(options) {
3735
4095
  setSession(null);
3736
4096
  if (current) {
3737
4097
  const csrf = options.csrfToken?.() ?? "";
3738
- void fetch(`${relayBaseUrl}/${current.id}/unregister`, {
4098
+ void fetch(`${relayBaseUrl}/${current.id}/unregister?token=${encodeURIComponent(current.token)}`, {
3739
4099
  method: "POST",
3740
- headers: { "content-type": "application/json", "x-csrf-token": csrf },
3741
- body: JSON.stringify({ token: current.token })
4100
+ headers: { "content-type": "application/json", "x-csrf-token": csrf }
3742
4101
  }).catch(() => {
3743
4102
  });
3744
4103
  }
3745
4104
  }, [relayBaseUrl, options, session]);
3746
4105
  const observeUser = react.useCallback((event) => {
3747
- const label = event.kind === "navigation" ? `You navigated to ${event.url}` : event.kind === "scroll" ? "You scrolled" : `You edited ${event.handle}${event.masked ? " (hidden)" : ""}`;
4106
+ const label = event.kind === "navigation" ? `You navigated to ${event.url}` : event.kind === "scroll" ? "You scrolled" : event.kind === "click" ? `You clicked ${event.label ?? event.handle}` : `You edited ${event.handle}${event.masked ? " (hidden)" : ""}`;
3748
4107
  fancyAutoCommon.emitActivity({
3749
4108
  agentId: USER.id,
3750
4109
  agentName: USER.name,
@@ -4520,6 +4879,11 @@ function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
4520
4879
  const [pulse, setPulse] = react.useState(null);
4521
4880
  react.useEffect(() => {
4522
4881
  if (!latest || (latest.source ?? "agent") === "user") return;
4882
+ if (latest.action === "agent_disconnected") {
4883
+ setCursor(null);
4884
+ setPulse(null);
4885
+ return;
4886
+ }
4523
4887
  const color = latest.agentColor ?? "#a855f7";
4524
4888
  const name = latest.agentName ?? "Agent";
4525
4889
  const status = latest.target?.label ?? latest.action;
@@ -4899,6 +5263,7 @@ exports.buildMcpbProxyStub = buildMcpbProxyStub;
4899
5263
  exports.buildShareConfig = buildShareConfig;
4900
5264
  exports.buildShareUrl = buildShareUrl;
4901
5265
  exports.buildVscodeDeeplink = buildVscodeDeeplink;
5266
+ exports.cmsReducer = cmsReducer;
4902
5267
  exports.connectorHref = connectorHref;
4903
5268
  exports.createSessionDescriptor = createSessionDescriptor;
4904
5269
  exports.describeSession = describeSession;
@@ -4909,7 +5274,9 @@ exports.mapActivityToHeuristicsEvent = mapActivityToEvent;
4909
5274
  exports.readSessionFromUrl = readSessionFromUrl;
4910
5275
  exports.registerCatalogBridge = registerCatalogBridge;
4911
5276
  exports.registerChartsBridge = registerChartsBridge;
5277
+ exports.registerCmsBridge = registerCmsBridge;
4912
5278
  exports.registerCodeBridge = registerCodeBridge;
5279
+ exports.registerDocBridge = registerDocBridge;
4913
5280
  exports.registerFeaturesBridge = registerFeaturesBridge;
4914
5281
  exports.registerFilesBridge = registerFilesBridge;
4915
5282
  exports.registerFormBridge = registerFormBridge;