@particle-academy/agent-integrations 0.32.0 → 0.34.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.
@@ -1,4 +1,4 @@
1
- import { ShareControls, AgentCursor, AgentActivityHighlight, AgentPanel } from './chunk-HSI5RVF4.js';
1
+ import { ShareControls, AgentCursor, AgentActivityHighlight, AgentPanel } from './chunk-SJTLTITO.js';
2
2
  import { createSessionDescriptor, attachSseRelay } from './chunk-N6RLSLOF.js';
3
3
  import { attachInProcess } from './chunk-AFUULW5E.js';
4
4
  import { registerWhiteboardBridge } from './chunk-2U5JB6ZA.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 = { ...DEFAULT_AGENT14, ...options.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 }));
@@ -4431,11 +4617,11 @@ function attachSseRelay(server, options) {
4431
4617
 
4432
4618
  // src/sharing/use-co-browse-session.ts
4433
4619
  init_registry();
4434
- var DEFAULT_AGENT15 = { id: "agent", name: "Agent", color: "#a855f7" };
4620
+ var DEFAULT_AGENT16 = { id: "agent", name: "Agent", color: "#a855f7" };
4435
4621
  var USER = { id: "human", name: "You" };
4436
4622
  function useCoBrowseSession(options) {
4437
4623
  const { adapter, extraBridges } = options;
4438
- const agent = { ...DEFAULT_AGENT15, ...options.agent ?? {} };
4624
+ const agent = { ...DEFAULT_AGENT16, ...options.agent ?? {} };
4439
4625
  const relayBaseUrl = options.relayBaseUrl ?? "/agent-relay";
4440
4626
  const serverRef = react.useRef(null);
4441
4627
  const relayRef = react.useRef(null);
@@ -4772,11 +4958,21 @@ function ShareControls({
4772
4958
  onStart,
4773
4959
  onStop,
4774
4960
  status,
4961
+ agentConnected = false,
4962
+ activity,
4775
4963
  shareBaseUrl,
4776
4964
  className,
4777
4965
  style
4778
4966
  }) {
4779
4967
  const [tab, setTab] = react.useState("prompt");
4968
+ const wasConnected = react.useRef(false);
4969
+ react.useEffect(() => {
4970
+ if (agentConnected && !wasConnected.current) {
4971
+ wasConnected.current = true;
4972
+ setTab("activity");
4973
+ }
4974
+ if (!agentConnected) wasConnected.current = false;
4975
+ }, [agentConnected]);
4780
4976
  if (!session) {
4781
4977
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["fai-share fai-share--idle", className ?? ""].filter(Boolean).join(" "), style, children: [
4782
4978
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "fai-share__start", onClick: onStart, children: "Start shared session" }),
@@ -4811,12 +5007,14 @@ function ShareControls({
4811
5007
  ] })
4812
5008
  ] }),
4813
5009
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fai-share__tabs", role: "tablist", children: [
5010
+ agentConnected && /* @__PURE__ */ jsxRuntime.jsx(TabButton, { tab: "activity", active: tab, setTab, children: "Activity" }),
4814
5011
  /* @__PURE__ */ jsxRuntime.jsx(TabButton, { tab: "prompt", active: tab, setTab, children: "Agent prompt" }),
4815
5012
  /* @__PURE__ */ jsxRuntime.jsx(TabButton, { tab: "url", active: tab, setTab, children: "URL" }),
4816
5013
  /* @__PURE__ */ jsxRuntime.jsx(TabButton, { tab: "json", active: tab, setTab, children: "JSON" }),
4817
5014
  /* @__PURE__ */ jsxRuntime.jsx(TabButton, { tab: "curl", active: tab, setTab, children: "cURL recipe" })
4818
5015
  ] }),
4819
5016
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fai-share__panel", children: [
5017
+ tab === "activity" && /* @__PURE__ */ jsxRuntime.jsx(ActivityLog, { activity }),
4820
5018
  tab === "prompt" && /* @__PURE__ */ jsxRuntime.jsx(
4821
5019
  CopyBox,
4822
5020
  {
@@ -4844,6 +5042,21 @@ function ShareControls({
4844
5042
  ] })
4845
5043
  ] });
4846
5044
  }
5045
+ function ActivityLog({ activity }) {
5046
+ const entries = (activity ?? []).slice().reverse();
5047
+ if (entries.length === 0) {
5048
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "fai-share__empty", children: "Connected. Nothing yet \u2014 actions appear here as the agent takes them." });
5049
+ }
5050
+ return /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "fai-share__activity", "aria-live": "polite", children: entries.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "fai-share__activity-row", "data-kind": entry.kind, children: [
5051
+ /* @__PURE__ */ jsxRuntime.jsx("time", { className: "fai-share__activity-time", dateTime: new Date(entry.at).toISOString(), children: new Date(entry.at).toLocaleTimeString(void 0, {
5052
+ hour: "2-digit",
5053
+ minute: "2-digit",
5054
+ second: "2-digit"
5055
+ }) }),
5056
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fai-share__activity-source", children: entry.source }),
5057
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fai-share__activity-text", children: entry.text })
5058
+ ] }, entry.id)) });
5059
+ }
4847
5060
  function TabButton({ tab, active, setTab, children }) {
4848
5061
  return /* @__PURE__ */ jsxRuntime.jsx(
4849
5062
  "button",
@@ -5627,6 +5840,7 @@ exports.MCPB_MIN_NODE = MCPB_MIN_NODE;
5627
5840
  exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
5628
5841
  exports.MemoryHumanPlusEventStore = MemoryHumanPlusEventStore;
5629
5842
  exports.MicroMcpServer = MicroMcpServer;
5843
+ exports.PASSKEY_BRIDGE_TOOLS = PASSKEY_BRIDGE_TOOLS;
5630
5844
  exports.RelayTransport = RelayTransport;
5631
5845
  exports.ScreensActivityBridge = ScreensActivityBridge;
5632
5846
  exports.ShareControls = ShareControls;
@@ -5668,6 +5882,7 @@ exports.registerFormBridge = registerFormBridge;
5668
5882
  exports.registerGitBridge = registerGitBridge;
5669
5883
  exports.registerMapBridge = registerMapBridge;
5670
5884
  exports.registerNavigationBridge = registerNavigationBridge;
5885
+ exports.registerPasskeyBridge = registerPasskeyBridge;
5671
5886
  exports.registerSceneBridge = registerSceneBridge;
5672
5887
  exports.registerScreensBridge = registerScreensBridge;
5673
5888
  exports.registerSheetsBridge = registerSheetsBridge;