@particle-academy/agent-integrations 0.28.0 → 0.28.2

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.cjs CHANGED
@@ -2772,6 +2772,17 @@ function registerNavigationBridge(host, options) {
2772
2772
  const agent = { ...DEFAULT_AGENT11, ...options.agent ?? {} };
2773
2773
  const pendingMode = options.pendingMode ?? true;
2774
2774
  const disposers = [];
2775
+ let groundedRevision = null;
2776
+ const staleIntent = () => {
2777
+ const current = adapter.getRevision?.();
2778
+ if (current === void 0) return null;
2779
+ if (groundedRevision === null || groundedRevision !== current) {
2780
+ return errorResult(
2781
+ `Human took control at page revision ${current}. This agent action was cancelled; call page_describe before acting again.`
2782
+ );
2783
+ }
2784
+ return null;
2785
+ };
2775
2786
  ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
2776
2787
  const target = (label, elementId) => ({
2777
2788
  kind: "navigation",
@@ -2812,6 +2823,11 @@ function registerNavigationBridge(host, options) {
2812
2823
  [],
2813
2824
  () => {
2814
2825
  const snap = adapter.describe();
2826
+ const revision = adapter.getRevision?.();
2827
+ if (revision !== void 0) {
2828
+ snap.revision = revision;
2829
+ groundedRevision = revision;
2830
+ }
2815
2831
  const text = [
2816
2832
  `URL: ${snap.url}`,
2817
2833
  `Title: ${snap.title}`,
@@ -2836,6 +2852,8 @@ function registerNavigationBridge(host, options) {
2836
2852
  { handle: { type: "string" } },
2837
2853
  ["handle"],
2838
2854
  (args) => {
2855
+ const stale = staleIntent();
2856
+ if (stale) return stale;
2839
2857
  const handle = String(args.handle ?? "");
2840
2858
  const action = adapter.describe().actions.find((candidate) => candidate.handle === handle);
2841
2859
  if (!action) return errorResult(`No element for handle "${handle}"`);
@@ -2852,6 +2870,8 @@ function registerNavigationBridge(host, options) {
2852
2870
  { url: { type: "string", description: "Path like /packages or an absolute URL." } },
2853
2871
  ["url"],
2854
2872
  async (args) => {
2873
+ const stale = staleIntent();
2874
+ if (stale) return stale;
2855
2875
  const url = String(args.url ?? "");
2856
2876
  if (!url) return errorResult("url is required");
2857
2877
  const from = adapter.getLocation().url;
@@ -2878,6 +2898,8 @@ function registerNavigationBridge(host, options) {
2878
2898
  {},
2879
2899
  [],
2880
2900
  async () => {
2901
+ const stale = staleIntent();
2902
+ if (stale) return stale;
2881
2903
  if (!adapter.back) return errorResult("Host did not provide back navigation.");
2882
2904
  await adapter.back();
2883
2905
  return textResult("Went back");
@@ -2890,6 +2912,8 @@ function registerNavigationBridge(host, options) {
2890
2912
  {},
2891
2913
  [],
2892
2914
  async () => {
2915
+ const stale = staleIntent();
2916
+ if (stale) return stale;
2893
2917
  if (!adapter.forward) return errorResult("Host did not provide forward navigation.");
2894
2918
  await adapter.forward();
2895
2919
  return textResult("Went forward");
@@ -2906,6 +2930,8 @@ function registerNavigationBridge(host, options) {
2906
2930
  },
2907
2931
  [],
2908
2932
  (args) => {
2933
+ const stale = staleIntent();
2934
+ if (stale) return stale;
2909
2935
  const handle = typeof args.handle === "string" ? args.handle : void 0;
2910
2936
  adapter.scrollTo({
2911
2937
  handle,
@@ -2922,6 +2948,8 @@ function registerNavigationBridge(host, options) {
2922
2948
  { dy: { type: "number" } },
2923
2949
  ["dy"],
2924
2950
  (args) => {
2951
+ const stale = staleIntent();
2952
+ if (stale) return stale;
2925
2953
  adapter.scrollBy(Number(args.dy ?? 0));
2926
2954
  return textResult(`Scrolled by ${Number(args.dy ?? 0)}px`);
2927
2955
  },
@@ -2936,6 +2964,8 @@ function registerNavigationBridge(host, options) {
2936
2964
  },
2937
2965
  ["handle", "value"],
2938
2966
  (args) => {
2967
+ const stale = staleIntent();
2968
+ if (stale) return stale;
2939
2969
  const handle = String(args.handle ?? "");
2940
2970
  const res = adapter.setField(handle, args.value);
2941
2971
  if (!res.ok) return errorResult(res.error ?? `Could not set ${handle}`);
@@ -2949,6 +2979,8 @@ function registerNavigationBridge(host, options) {
2949
2979
  { handle: { type: "string" } },
2950
2980
  ["handle"],
2951
2981
  async (args) => {
2982
+ const stale = staleIntent();
2983
+ if (stale) return stale;
2952
2984
  const handle = String(args.handle ?? "");
2953
2985
  const action = adapter.describe().actions.find((a) => a.handle === handle);
2954
2986
  const rect = adapter.rectFor?.(handle) ?? void 0;
@@ -2968,6 +3000,8 @@ function registerNavigationBridge(host, options) {
2968
3000
  { handle: { type: "string" } },
2969
3001
  ["handle"],
2970
3002
  async (args) => {
3003
+ const stale = staleIntent();
3004
+ if (stale) return stale;
2971
3005
  const handle = String(args.handle ?? "");
2972
3006
  const rect = adapter.rectFor?.(handle) ?? void 0;
2973
3007
  if (pendingMode && adapter.confirm) {
@@ -4871,6 +4905,23 @@ function CoBrowsePresence({ session, connectUrl, shareBaseUrl, className }) {
4871
4905
  ] });
4872
4906
  }
4873
4907
  CoBrowsePresence.displayName = "CoBrowsePresence";
4908
+ var CURSOR_EDGE_INSET = 24;
4909
+ var FLUID_CURSOR_SPEED_PX_PER_SECOND = 1050;
4910
+ var MIN_GLIDE_MS = 240;
4911
+ var MAX_GLIDE_MS = 1200;
4912
+ function visibleCursorPoint(rect, viewport) {
4913
+ const x = rect.x + rect.width / 2;
4914
+ const y = rect.y + rect.height / 2;
4915
+ return {
4916
+ x: Math.min(Math.max(x, CURSOR_EDGE_INSET), Math.max(CURSOR_EDGE_INSET, viewport.width - CURSOR_EDGE_INSET)),
4917
+ y: Math.min(Math.max(y, CURSOR_EDGE_INSET), Math.max(CURSOR_EDGE_INSET, viewport.height - CURSOR_EDGE_INSET))
4918
+ };
4919
+ }
4920
+ function fluidGlideDuration(from, to) {
4921
+ if (!from) return 0;
4922
+ const distance = Math.hypot(to.x - from.x, to.y - from.y);
4923
+ return Math.round(Math.min(MAX_GLIDE_MS, Math.max(MIN_GLIDE_MS, distance / FLUID_CURSOR_SPEED_PX_PER_SECOND * 1e3)));
4924
+ }
4874
4925
  function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
4875
4926
  const { latest } = useAgentActivity(void 0, { capacity: 12 });
4876
4927
  const [cursor, setCursor] = react.useState(
@@ -4880,7 +4931,7 @@ function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
4880
4931
  react.useEffect(() => {
4881
4932
  if (!latest || (latest.source ?? "agent") === "user") return;
4882
4933
  if (latest.action === "agent_disconnected") {
4883
- setCursor(null);
4934
+ setCursor((prev) => prev ? { ...prev, status: "Standing by", glideMs: 0 } : prev);
4884
4935
  setPulse(null);
4885
4936
  return;
4886
4937
  }
@@ -4889,11 +4940,19 @@ function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
4889
4940
  const status = latest.target?.label ?? latest.action;
4890
4941
  const rect = latest.meta?.rect;
4891
4942
  if (rect) {
4892
- setCursor({ x: rect.x + rect.width / 2, y: rect.y + rect.height / 2, name, color, status });
4943
+ const target = visibleCursorPoint(rect, { width: window.innerWidth, height: window.innerHeight });
4944
+ const reduceMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
4945
+ setCursor((prev) => ({
4946
+ ...target,
4947
+ name,
4948
+ color,
4949
+ status,
4950
+ glideMs: reduceMotion ? 0 : fluidGlideDuration(prev, target)
4951
+ }));
4893
4952
  setPulse({ rect, color, key: latest.timestamp });
4894
4953
  } else {
4895
4954
  setCursor(
4896
- (prev) => prev ? { ...prev, status, name, color } : { x: window.innerWidth / 2, y: window.innerHeight / 2, name, color, status }
4955
+ (prev) => prev ? { ...prev, status, name, color } : { x: window.innerWidth / 2, y: window.innerHeight / 2, name, color, status, glideMs: 0 }
4897
4956
  );
4898
4957
  }
4899
4958
  }, [latest?.timestamp]);
@@ -4919,7 +4978,9 @@ function CoBrowseCursorLayer({ active = true, zIndex = 2147483e3 }) {
4919
4978
  name: cursor.name,
4920
4979
  color: cursor.color,
4921
4980
  status: cursor.status,
4922
- style: { transition: "left .35s cubic-bezier(.22,.61,.36,1), top .35s cubic-bezier(.22,.61,.36,1)" }
4981
+ style: {
4982
+ transition: cursor.glideMs ? `left ${cursor.glideMs}ms cubic-bezier(.22,.61,.36,1), top ${cursor.glideMs}ms cubic-bezier(.22,.61,.36,1)` : "none"
4983
+ }
4923
4984
  }
4924
4985
  )
4925
4986
  ] }),