@agimon-ai/po-author 0.0.0-alpha.52 → 0.0.0-alpha.53

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.
@@ -177,6 +177,7 @@ const documentProfiles = (profiles) => profiles.map((profile) => ({
177
177
  }));
178
178
  var AuthorBrowserBridge = class {
179
179
  #host;
180
+ #reconcileTabs;
180
181
  #canvases = /* @__PURE__ */ new Map();
181
182
  #parked = /* @__PURE__ */ new Map();
182
183
  #opening = /* @__PURE__ */ new Map();
@@ -184,8 +185,9 @@ var AuthorBrowserBridge = class {
184
185
  #generation = 0;
185
186
  #disposed = false;
186
187
  #releaseConnected;
187
- constructor(host) {
188
+ constructor(host, reconcileTabs) {
188
189
  this.#host = host;
190
+ this.#reconcileTabs = reconcileTabs;
189
191
  this.#releaseConnected = host.onHubConnected(() => {
190
192
  for (const canvas of this.#canvases.values()) {
191
193
  canvas.ownerToken = void 0;
@@ -304,6 +306,21 @@ var AuthorBrowserBridge = class {
304
306
  this.#dropLive(canvas);
305
307
  }
306
308
  }
309
+ /** Follows the server's open canvases: lets go of any it no longer has, such as one evicted for a newer one. */
310
+ canvases(sessionId, canvases) {
311
+ this.#reconcileTabs?.(this.#host, sessionId, canvases);
312
+ const open = new Set(canvases.map((canvas) => canvas.alias));
313
+ const gone = /* @__PURE__ */ new Set();
314
+ for (const entry of [
315
+ ...this.#opening.values(),
316
+ ...this.#parked.values(),
317
+ ...this.#canvases.values()
318
+ ]) {
319
+ const parked = "parked" in entry ? entry.parked : entry;
320
+ if (parked.sessionId === sessionId && !open.has(parked.alias)) gone.add(parked.alias);
321
+ }
322
+ for (const alias of gone) this.drop(sessionId, alias);
323
+ }
307
324
  apply(sessionId, message) {
308
325
  if (!this.#agentEnabled(sessionId) || message.alias === void 0) return;
309
326
  const canvas = this.#canvases.get(canvasKey(sessionId, message.alias));
@@ -510,13 +527,18 @@ var AuthorBrowserBridge = class {
510
527
  canvas.generation = ++this.#generation;
511
528
  }
512
529
  };
530
+ /** Key of the bridge that serves every session when the plugin is not mounted per session. */
531
+ const GLOBAL_BRIDGE = "global";
513
532
  const authorBridgeRuntimes = new Store(/* @__PURE__ */ new Map());
514
- const bridgeFor = (sessionId) => authorBridgeRuntimes.state.get(sessionId) ?? authorBridgeRuntimes.state.get("global");
515
- function startAuthorBrowserBridge(runtime) {
516
- const key = runtime.mount?.scope === "session" ? runtime.mount.sessionId : "global";
533
+ /** The server's last open-canvas snapshot per session, for a bridge that starts after it arrived. */
534
+ const latestCanvases = new Store(/* @__PURE__ */ new Map());
535
+ const bridgeFor = (sessionId) => authorBridgeRuntimes.state.get(sessionId) ?? authorBridgeRuntimes.state.get(GLOBAL_BRIDGE);
536
+ function startAuthorBrowserBridge(runtime, reconcileTabs) {
537
+ const key = runtime.mount?.scope === "session" ? runtime.mount.sessionId : GLOBAL_BRIDGE;
517
538
  authorBridgeRuntimes.state.get(key)?.dispose();
518
- const next = new AuthorBrowserBridge(runtime);
539
+ const next = new AuthorBrowserBridge(runtime, reconcileTabs);
519
540
  authorBridgeRuntimes.setState((state) => new Map(state).set(key, next));
541
+ for (const [sessionId, canvases] of latestCanvases.state) if (key === GLOBAL_BRIDGE || key === sessionId) next.canvases(sessionId, canvases);
520
542
  return () => {
521
543
  if (authorBridgeRuntimes.state.get(key) !== next) return;
522
544
  next.dispose();
@@ -531,9 +553,17 @@ async function openAuthorCanvas(sessionId, alias, profiles) {
531
553
  await bridgeFor(sessionId)?.open(sessionId, alias, profiles);
532
554
  }
533
555
  function applyAuthorHubMessage(sessionId, message) {
534
- bridgeFor(sessionId)?.apply(sessionId, message);
556
+ if (message.kind === "canvases") {
557
+ latestCanvases.setState((state) => new Map(state).set(sessionId, message.canvases));
558
+ bridgeFor(sessionId)?.canvases(sessionId, message.canvases);
559
+ } else bridgeFor(sessionId)?.apply(sessionId, message);
535
560
  }
536
561
  function dropAuthorViewportSession(sessionId, alias) {
562
+ if (alias === void 0 && latestCanvases.state.has(sessionId)) latestCanvases.setState((state) => {
563
+ const remaining = new Map(state);
564
+ remaining.delete(sessionId);
565
+ return remaining;
566
+ });
537
567
  bridgeFor(sessionId)?.drop(sessionId, alias);
538
568
  }
539
569
  /** Why the agent cannot use this canvas here, such as another device holding it; undefined when it can. */
@@ -561,6 +591,7 @@ function parseAuthorHubMessage(input) {
561
591
  if ((input.activation === "active" || input.activation === "inactive") && Number.isSafeInteger(input.capabilityCount) && input.capabilityCount >= 0) return input;
562
592
  if (typeof input.kind !== "string") return null;
563
593
  if (input.kind === "rejected") return typeof input.reason === "string" ? input : null;
594
+ if (input.kind === "canvases") return Array.isArray(input.canvases) && input.canvases.every((entry) => isRecord(entry) && typeof entry.alias === "string" && typeof entry.path === "string") ? input : null;
564
595
  if (!Number.isSafeInteger(input.generation) || typeof input.ownerToken !== "string") return null;
565
596
  if (input.kind === "accepted") return typeof input.leaseMs === "number" && (input.catalogToken === void 0 || typeof input.catalogToken === "string") ? input : null;
566
597
  if ((input.kind === "request" || input.kind === "cancel") && typeof input.catalogToken === "string" && typeof input.requestId === "string") {