@superblocksteam/vite-plugin-file-sync 2.0.161-next.0 → 2.0.162-next.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 (78) hide show
  1. package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts +2 -0
  2. package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
  3. package/dist/ai-service/agent/prompts/build-base-system-prompt.js +39 -27
  4. package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
  5. package/dist/ai-service/agent/tools/rbac/effective-permissions.d.ts +13 -0
  6. package/dist/ai-service/agent/tools/rbac/effective-permissions.d.ts.map +1 -0
  7. package/dist/ai-service/agent/tools/rbac/effective-permissions.js +49 -0
  8. package/dist/ai-service/agent/tools/rbac/effective-permissions.js.map +1 -0
  9. package/dist/ai-service/agent/tools.d.ts +5 -5
  10. package/dist/ai-service/agent/tools.d.ts.map +1 -1
  11. package/dist/ai-service/agent/tools.js +27 -9
  12. package/dist/ai-service/agent/tools.js.map +1 -1
  13. package/dist/ai-service/agent/tools2/access-control.d.ts +5 -0
  14. package/dist/ai-service/agent/tools2/access-control.d.ts.map +1 -1
  15. package/dist/ai-service/agent/tools2/access-control.js +16 -0
  16. package/dist/ai-service/agent/tools2/access-control.js.map +1 -1
  17. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.d.ts +2 -0
  18. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.d.ts.map +1 -1
  19. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js +6 -2
  20. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js.map +1 -1
  21. package/dist/ai-service/app-skills/helpers.d.ts +1 -1
  22. package/dist/ai-service/app-skills/helpers.d.ts.map +1 -1
  23. package/dist/ai-service/app-skills/helpers.js +2 -1
  24. package/dist/ai-service/app-skills/helpers.js.map +1 -1
  25. package/dist/ai-service/chat/chat-session-store.d.ts +2 -0
  26. package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
  27. package/dist/ai-service/chat/chat-session-store.js +2 -2
  28. package/dist/ai-service/chat/chat-session-store.js.map +1 -1
  29. package/dist/ai-service/checkpoint-screenshot.d.ts +9 -0
  30. package/dist/ai-service/checkpoint-screenshot.d.ts.map +1 -0
  31. package/dist/ai-service/checkpoint-screenshot.js +40 -0
  32. package/dist/ai-service/checkpoint-screenshot.js.map +1 -0
  33. package/dist/ai-service/index.d.ts +15 -9
  34. package/dist/ai-service/index.d.ts.map +1 -1
  35. package/dist/ai-service/index.js +141 -58
  36. package/dist/ai-service/index.js.map +1 -1
  37. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts +1 -1
  38. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts.map +1 -1
  39. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js +9 -7
  40. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js.map +1 -1
  41. package/dist/ai-service/state-machine/clark-fsm.d.ts +18 -9
  42. package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
  43. package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
  44. package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
  45. package/dist/ai-service/state-machine/handlers/agent-planning.js +68 -53
  46. package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
  47. package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
  48. package/dist/ai-service/state-machine/handlers/llm-generating.js +15 -2
  49. package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
  50. package/dist/ai-service/state-machine/helpers/peer.d.ts.map +1 -1
  51. package/dist/ai-service/state-machine/helpers/peer.js +21 -9
  52. package/dist/ai-service/state-machine/helpers/peer.js.map +1 -1
  53. package/dist/ai-service/state-machine/helpers/stable-peer.d.ts +20 -6
  54. package/dist/ai-service/state-machine/helpers/stable-peer.d.ts.map +1 -1
  55. package/dist/ai-service/state-machine/helpers/stable-peer.js +124 -30
  56. package/dist/ai-service/state-machine/helpers/stable-peer.js.map +1 -1
  57. package/dist/ai-service/state-machine/mocks.d.ts.map +1 -1
  58. package/dist/ai-service/state-machine/mocks.js +1 -0
  59. package/dist/ai-service/state-machine/mocks.js.map +1 -1
  60. package/dist/ai-service/types.d.ts +4 -0
  61. package/dist/ai-service/types.d.ts.map +1 -1
  62. package/dist/file-sync-vite-plugin.d.ts +5 -1
  63. package/dist/file-sync-vite-plugin.d.ts.map +1 -1
  64. package/dist/file-sync-vite-plugin.js +27 -11
  65. package/dist/file-sync-vite-plugin.js.map +1 -1
  66. package/dist/inject-index-vite-plugin.d.ts +15 -0
  67. package/dist/inject-index-vite-plugin.d.ts.map +1 -1
  68. package/dist/inject-index-vite-plugin.js +31 -0
  69. package/dist/inject-index-vite-plugin.js.map +1 -1
  70. package/dist/lock-service/index.d.ts +1 -0
  71. package/dist/lock-service/index.d.ts.map +1 -1
  72. package/dist/lock-service/index.js +14 -7
  73. package/dist/lock-service/index.js.map +1 -1
  74. package/dist/socket-manager.d.ts +12 -4
  75. package/dist/socket-manager.d.ts.map +1 -1
  76. package/dist/socket-manager.js +195 -39
  77. package/dist/socket-manager.js.map +1 -1
  78. package/package.json +9 -9
@@ -1,6 +1,6 @@
1
1
  import type http from "node:http";
2
2
  import type { Tracer } from "@opentelemetry/api";
3
- import type { EditorClientMethods, LibraryClientMethods } from "@superblocksteam/library-shared/types";
3
+ import type { EditorClientMethods, EditorHasDefaultStructure, GatewayClientMethods, LibraryClientMethods } from "@superblocksteam/library-shared/types";
4
4
  import { TracedEventEmitter } from "@superblocksteam/shared";
5
5
  import type { ISocketClient } from "@superblocksteam/shared";
6
6
  import type { AiService } from "./ai-service/index.js";
@@ -42,7 +42,7 @@ type SocketManagerEvents = {
42
42
  PeerConnectionLiveness
43
43
  ];
44
44
  "connect:gateway": [
45
- ISocketClient<EditorClientMethods>,
45
+ ISocketClient<GatewayClientMethods>,
46
46
  string,
47
47
  PeerConnectionLiveness
48
48
  ];
@@ -65,6 +65,10 @@ declare class SocketManager extends TracedEventEmitter<SocketManagerEvents> {
65
65
  * {@link getSurvivingEditorClient} which peerId to prefer among leftovers.
66
66
  */
67
67
  private readonly editorPeerIdsBySocket;
68
+ private readonly authenticatedEditorSockets;
69
+ private readonly authenticatedGatewaySockets;
70
+ private editorStructureReport;
71
+ private readonly editorStructureReportByGateway;
68
72
  private peerIds;
69
73
  /**
70
74
  * Editor peers whose presence is registered with the lock service, and the
@@ -111,6 +115,8 @@ declare class SocketManager extends TracedEventEmitter<SocketManagerEvents> {
111
115
  superblocksBaseUrl?: string;
112
116
  maxPayloadBytes?: number;
113
117
  }): void;
118
+ reportEditorHasDefaultStructure(hasDefaultStructure: EditorHasDefaultStructure): void;
119
+ private sendEditorStructureReport;
114
120
  callEditorClients(fn: (socket: ISocketClient<EditorClientMethods>) => Promise<void>): void;
115
121
  /**
116
122
  * Both registries, for the signals a headless turn needs as much as a browser
@@ -144,14 +150,16 @@ declare class SocketManager extends TracedEventEmitter<SocketManagerEvents> {
144
150
  callLibraryClients(fn: (socket: ISocketClient<LibraryClientMethods>) => Promise<void>): void;
145
151
  getAnyLibraryClient(): ISocketClient<LibraryClientMethods> | undefined;
146
152
  getEditorClients(): Iterable<ISocketClient<EditorClientMethods>>;
153
+ private getAuthenticatedEditorPeers;
154
+ private getAuthenticatedGatewayPeers;
147
155
  /**
148
156
  * Attached headless gateway peers. Separate from {@link getEditorClients} so
149
157
  * a broadcast meant for browser tabs never reaches a gateway, and the
150
158
  * gateway's own lifecycle can be observed without editor semantics.
151
159
  */
152
- getGatewayClients(): Iterable<ISocketClient<EditorClientMethods>>;
160
+ getGatewayClients(): Iterable<ISocketClient<GatewayClientMethods>>;
153
161
  /** The attached gateway peer with this peer id, if it is still connected. */
154
- getGatewayClient(peerId: string): ISocketClient<EditorClientMethods> | undefined;
162
+ getGatewayClient(peerId: string): ISocketClient<GatewayClientMethods> | undefined;
155
163
  closeAllClients(): void;
156
164
  dispose(): void;
157
165
  }
@@ -1 +1 @@
1
- {"version":3,"file":"socket-manager.d.ts","sourceRoot":"","sources":["../src/socket-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"socket-manager.d.ts","sourceRoot":"","sources":["../src/socket-manager.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { cp, mkdir, mkdtemp, rm } from "node:fs/promises";
2
3
  import { tmpdir } from "node:os";
3
4
  import { join } from "node:path";
4
5
  import { WebSocketServer } from "ws";
5
6
  import { createISocketClient, DEFAULT_NON_GIT_BRANCH, MAX_SOCKET_PAYLOAD_BYTES, SocketErrorException, SUPERBLOCKS_LIVE_GIT_BRANCH, TracedEventEmitter, TracedSocket, } from "@superblocksteam/shared";
7
+ import { uploadCheckpointScreenshot } from "./ai-service/checkpoint-screenshot.js";
6
8
  import { uploadContextToBucketeer } from "./ai-service/context-upload.js";
7
9
  import { createSocketRecordingMiddleware } from "./ai-service/recording/recorders/socket-recorder.js";
8
10
  import { parseJwt } from "./ai-service/util/parse-jwt.js";
@@ -136,6 +138,10 @@ class SocketManager extends TracedEventEmitter {
136
138
  * {@link getSurvivingEditorClient} which peerId to prefer among leftovers.
137
139
  */
138
140
  editorPeerIdsBySocket = new WeakMap();
141
+ authenticatedEditorSockets = new WeakSet();
142
+ authenticatedGatewaySockets = new WeakSet();
143
+ editorStructureReport;
144
+ editorStructureReportByGateway = new WeakMap();
139
145
  peerIds = new Map();
140
146
  /**
141
147
  * Editor peers whose presence is registered with the lock service, and the
@@ -182,6 +188,7 @@ class SocketManager extends TracedEventEmitter {
182
188
  this.socketServer = wss;
183
189
  let activeGitService = gitService;
184
190
  const headlessLockOwners = [];
191
+ const headlessLockHandbackQueue = new OperationQueue();
185
192
  const headlessLockOperationQueue = new OperationQueue();
186
193
  /**
187
194
  * Drop every turn this peer owned, and hand the lock back if that was the
@@ -194,28 +201,50 @@ class SocketManager extends TracedEventEmitter {
194
201
  * nothing. The AppBranchLock would then stay held for the rest of that
195
202
  * socket's life, with no turn behind it.
196
203
  *
197
- * Queued with the release path so the two cannot both decide they are
198
- * last.
204
+ * Owner decisions and handbacks use separate queues so generation waits
205
+ * serialize releases without keeping later headless prompts from starting.
199
206
  */
200
- const dropHeadlessLockOwners = async (peerId) => {
201
- await headlessLockOperationQueue.enqueue(async () => {
202
- const ownedAny = headlessLockOwners.some((owner) => owner.peerId === peerId);
203
- if (!ownedAny) {
204
- return;
207
+ const dropHeadlessLockOwnersNow = async (peerId) => {
208
+ const ownedTurns = await headlessLockOperationQueue.enqueue(async () => {
209
+ const owned = headlessLockOwners.filter((owner) => owner.peerId === peerId);
210
+ if (headlessLockOwners.some((owner) => !owner.handbackFailed && !owned.includes(owner))) {
211
+ for (let index = headlessLockOwners.length - 1; index >= 0; index--) {
212
+ const owner = headlessLockOwners[index];
213
+ if (owner && owned.includes(owner)) {
214
+ headlessLockOwners.splice(index, 1);
215
+ }
216
+ }
217
+ return [];
205
218
  }
219
+ return owned;
220
+ });
221
+ if (ownedTurns.length === 0) {
222
+ return;
223
+ }
224
+ try {
225
+ await lockService?.relinquishLockForTurn(() => headlessLockOwners.some((owner) => !owner.handbackFailed &&
226
+ (!ownedTurns.includes(owner) || owner.peerId !== peerId)));
227
+ }
228
+ catch (error) {
229
+ await headlessLockOperationQueue.enqueue(async () => {
230
+ for (const owner of ownedTurns) {
231
+ if (headlessLockOwners.includes(owner) && owner.peerId === peerId) {
232
+ owner.handbackFailed = true;
233
+ }
234
+ }
235
+ });
236
+ throw error;
237
+ }
238
+ await headlessLockOperationQueue.enqueue(async () => {
206
239
  for (let index = headlessLockOwners.length - 1; index >= 0; index--) {
207
- if (headlessLockOwners[index]?.peerId === peerId) {
240
+ const owner = headlessLockOwners[index];
241
+ if (owner && owner.peerId === peerId && ownedTurns.includes(owner)) {
208
242
  headlessLockOwners.splice(index, 1);
209
243
  }
210
244
  }
211
- if (headlessLockOwners.length > 0) {
212
- return;
213
- }
214
- // Re-read after the handback's generation wait: a replacement gateway
215
- // can claim a turn while the old one's write is still finishing.
216
- await lockService?.relinquishLockForTurn(() => headlessLockOwners.length > 0);
217
245
  });
218
246
  };
247
+ const dropHeadlessLockOwners = (peerId) => headlessLockHandbackQueue.enqueue(() => dropHeadlessLockOwnersNow(peerId));
219
248
  const dropHeadlessLockOwnersInBackground = (peerId, occasion) => {
220
249
  void dropHeadlessLockOwners(peerId).catch((err) => {
221
250
  getLogger().warn(`[socket-manager] failed to hand the turn lock back after ${occasion}: ${err instanceof Error ? err.message : String(err)}`);
@@ -225,29 +254,52 @@ class SocketManager extends TracedEventEmitter {
225
254
  dropHeadlessLockOwnersInBackground(peerId, "a gateway's liveness lapsed");
226
255
  aiService?.endHeadlessTurn(peerId);
227
256
  });
228
- const releaseHeadlessLock = async (peerId, turnId) => {
229
- return await headlessLockOperationQueue.enqueue(async () => {
257
+ const releaseHeadlessLockNow = async (peerId, turnId) => {
258
+ const ownedTurn = await headlessLockOperationQueue.enqueue(async () => {
230
259
  const ownerIndex = headlessLockOwners.findIndex((owner) => owner.peerId === peerId &&
231
260
  (turnId ? owner.turnId === turnId : owner.turnId === undefined));
232
261
  if (ownerIndex === -1) {
233
- return false;
262
+ return undefined;
234
263
  }
235
- const [owner] = headlessLockOwners.splice(ownerIndex, 1);
236
- if (headlessLockOwners.length > 0) {
237
- return true;
264
+ const owner = headlessLockOwners[ownerIndex];
265
+ if (!owner) {
266
+ return undefined;
238
267
  }
239
- try {
240
- await lockService?.relinquishLockForTurn(() => headlessLockOwners.length > 0);
268
+ if (headlessLockOwners.some((candidate) => candidate !== owner && !candidate.handbackFailed)) {
269
+ headlessLockOwners.splice(ownerIndex, 1);
241
270
  return true;
242
271
  }
243
- catch (error) {
244
- if (owner) {
245
- headlessLockOwners.splice(ownerIndex, 0, owner);
272
+ owner.handbackFailed = false;
273
+ return owner;
274
+ });
275
+ if (!ownedTurn) {
276
+ return false;
277
+ }
278
+ if (ownedTurn === true) {
279
+ return true;
280
+ }
281
+ try {
282
+ await lockService?.relinquishLockForTurn(() => headlessLockOwners.some((owner) => !owner.handbackFailed &&
283
+ (owner !== ownedTurn || owner.peerId !== peerId)));
284
+ }
285
+ catch (error) {
286
+ await headlessLockOperationQueue.enqueue(async () => {
287
+ if (headlessLockOwners.includes(ownedTurn)) {
288
+ ownedTurn.handbackFailed = true;
246
289
  }
247
- throw error;
290
+ });
291
+ throw error;
292
+ }
293
+ return await headlessLockOperationQueue.enqueue(async () => {
294
+ const ownerIndex = headlessLockOwners.findIndex((owner) => owner === ownedTurn && owner.peerId === peerId);
295
+ if (ownerIndex === -1) {
296
+ return false;
248
297
  }
298
+ headlessLockOwners.splice(ownerIndex, 1);
299
+ return true;
249
300
  });
250
301
  };
302
+ const releaseHeadlessLock = (peerId, turnId) => headlessLockHandbackQueue.enqueue(() => releaseHeadlessLockNow(peerId, turnId));
251
303
  // Provide sync context (branchName, lastCommitId) to the AI service
252
304
  // for remote context restore fallback from Bucketeer on fresh pods.
253
305
  if (aiService && syncService) {
@@ -256,6 +308,8 @@ class SocketManager extends TracedEventEmitter {
256
308
  getLastCommitId: () => syncService.lastCommitId,
257
309
  });
258
310
  }
311
+ aiService?.setAuthenticatedEditorPeersProvider?.(() => this.getAuthenticatedEditorPeers());
312
+ aiService?.setAuthenticatedGatewayPeersProvider?.(() => this.getAuthenticatedGatewayPeers());
259
313
  // Sticky guard. Set to true by `gitDisconnect` and deliberately NOT
260
314
  // cleared when it finishes: any callers of `getActiveGitService()` that
261
315
  // started before disconnect but resolve after it completes must still
@@ -438,6 +492,13 @@ class SocketManager extends TracedEventEmitter {
438
492
  if (!result.success) {
439
493
  throw new SocketErrorException(result.status, result.error);
440
494
  }
495
+ if (isEditor && this.editorSockets.has(socket)) {
496
+ this.authenticatedEditorSockets.add(socket);
497
+ }
498
+ if (kind === "gateway" && this.gatewaySockets.has(socket)) {
499
+ this.authenticatedGatewaySockets.add(socket);
500
+ this.sendEditorStructureReport(socket);
501
+ }
441
502
  if (isEditor &&
442
503
  userId &&
443
504
  !this.editorPresenceUserIdByPeerId.has(peerId) &&
@@ -535,7 +596,8 @@ class SocketManager extends TracedEventEmitter {
535
596
  // AI + draft handling
536
597
  aiGenerate: [
537
598
  async (request) => {
538
- await aiService?.handleAiGenerate(request);
599
+ const inputSource = { kind, peerId };
600
+ await aiService?.handleAiGenerate(request, inputSource);
539
601
  },
540
602
  ],
541
603
  aiGenerateWithQueue: [
@@ -547,10 +609,12 @@ class SocketManager extends TracedEventEmitter {
547
609
  // gateway peer that asked for the work, not to whichever
548
610
  // socket connected most recently. The prompt's own
549
611
  // `requestId` is the correlator that same caller presents to
550
- // reacquire this turn after losing its socket.
612
+ // reacquire this turn after losing its socket. Legacy
613
+ // callers that omit it receive a server-minted identity.
551
614
  const { queueMode, ...aiRequest } = request;
615
+ const inputSource = { kind, peerId };
552
616
  if (kind !== "gateway") {
553
- return await aiService.handleAiGenerateWithQueue(aiRequest, queueMode);
617
+ return await aiService.handleAiGenerateWithQueue(aiRequest, queueMode, inputSource);
554
618
  }
555
619
  // Write access is taken per turn, not held for the whole
556
620
  // session, so the app is unlocked between prompts and a
@@ -558,10 +622,16 @@ class SocketManager extends TracedEventEmitter {
558
622
  // awaited: a turn that cannot write must fail here carrying
559
623
  // the server's own reason — somebody is editing in the
560
624
  // browser — rather than bind routing and then unwind it.
561
- const requestedTurnId = typeof request.requestId === "string"
625
+ const requestedTurnId = typeof request.requestId === "string" &&
626
+ request.requestId.length > 0
562
627
  ? request.requestId
563
- : undefined;
628
+ : randomUUID();
629
+ const headlessRequest = {
630
+ ...aiRequest,
631
+ requestId: requestedTurnId,
632
+ };
564
633
  const lockOwner = {
634
+ handbackFailed: false,
565
635
  peerId,
566
636
  turnId: requestedTurnId,
567
637
  };
@@ -572,7 +642,7 @@ class SocketManager extends TracedEventEmitter {
572
642
  });
573
643
  let result;
574
644
  try {
575
- result = await aiService.handleAiGenerateWithQueue(aiRequest, queueMode);
645
+ result = await aiService.handleAiGenerateWithQueue(headlessRequest, queueMode, inputSource);
576
646
  const ownerIndex = headlessLockOwners.indexOf(lockOwner);
577
647
  for (let index = ownerIndex - 1; index >= 0; index--) {
578
648
  if (headlessLockOwners[index]?.peerId === peerId) {
@@ -650,9 +720,27 @@ class SocketManager extends TracedEventEmitter {
650
720
  commitId: result?.commitId,
651
721
  silently: request.silently,
652
722
  });
653
- const draftStateCleared = await draftStateReport;
723
+ const handleCleanupError = (error) => {
724
+ if (!result?.commitId) {
725
+ throw error;
726
+ }
727
+ return false;
728
+ };
729
+ let draftStateCleared = await draftStateReport.catch(handleCleanupError);
654
730
  if (draftStateCleared !== true) {
655
- throw new Error("Failed to clear server draft state");
731
+ draftStateCleared = await syncService
732
+ .reportDraftStateToServer(false)
733
+ .catch(handleCleanupError);
734
+ }
735
+ if (draftStateCleared !== true) {
736
+ if (!result?.commitId) {
737
+ throw new Error("Failed to clear server draft state");
738
+ }
739
+ getLogger().warn("[aiAcceptDraft] checkpoint saved but draft-state cleanup failed", {
740
+ applicationId: syncService.applicationId,
741
+ branchName: syncService.branchName,
742
+ commitId: result.commitId,
743
+ });
656
744
  }
657
745
  if (result?.commitId) {
658
746
  // Upload context file to bucketeer asynchronously (fire-and-forget)
@@ -697,14 +785,41 @@ class SocketManager extends TracedEventEmitter {
697
785
  }
698
786
  })();
699
787
  }
700
- return {
701
- commitId: result.commitId,
702
- };
788
+ return draftStateCleared === true
789
+ ? { commitId: result.commitId }
790
+ : {
791
+ commitId: result.commitId,
792
+ warning: {
793
+ code: "draft_state_cleanup_failed",
794
+ message: "Checkpoint saved, but the server draft state could not be cleared",
795
+ },
796
+ };
703
797
  }
704
798
  getLogger().info("[aiAcceptDraft] No new checkpoint created for ai:accept (no hash changes from last checkpoint)");
705
799
  return { commitId: "" };
706
800
  },
707
801
  ],
802
+ aiUploadCheckpointScreenshot: [
803
+ async (request, context) => {
804
+ if (!syncService) {
805
+ throw new Error("Sync service not available");
806
+ }
807
+ const jwt = aiService?.getClarkJwt() ||
808
+ context.peerAuthorization?.replace("Bearer ", "");
809
+ const resolvedBaseUrl = superblocksBaseUrl || aiService?.superblocksBaseUrl;
810
+ if (!jwt || !resolvedBaseUrl) {
811
+ throw new Error("Screenshot upload credentials are not available");
812
+ }
813
+ await uploadCheckpointScreenshot({
814
+ applicationId: syncService.applicationId,
815
+ branchName: syncService.branchName,
816
+ commitId: request.commitId,
817
+ jwt,
818
+ screenshotDataUrl: request.screenshotDataUrl,
819
+ superblocksBaseUrl: resolvedBaseUrl,
820
+ });
821
+ },
822
+ ],
708
823
  aiClearDraftState: [
709
824
  async () => {
710
825
  await syncService?.clearDraftsDirectory();
@@ -717,8 +832,8 @@ class SocketManager extends TracedEventEmitter {
717
832
  },
718
833
  ],
719
834
  aiNotifyIntegrationSetupClosed: [
720
- async () => {
721
- await aiService?.handleIntegrationSetupClosed();
835
+ async (request) => {
836
+ await aiService?.handleIntegrationSetupClosed(request);
722
837
  },
723
838
  ],
724
839
  aiInterpretPrompt: [
@@ -1568,6 +1683,33 @@ class SocketManager extends TracedEventEmitter {
1568
1683
  });
1569
1684
  });
1570
1685
  }
1686
+ reportEditorHasDefaultStructure(hasDefaultStructure) {
1687
+ if (hasDefaultStructure !== "yes" &&
1688
+ hasDefaultStructure !== "no" &&
1689
+ hasDefaultStructure !== "unknown") {
1690
+ throw new Error("Invalid editor structure report");
1691
+ }
1692
+ this.editorStructureReport = { hasDefaultStructure };
1693
+ for (const { peer } of this.getAuthenticatedGatewayPeers()) {
1694
+ this.sendEditorStructureReport(peer);
1695
+ }
1696
+ }
1697
+ sendEditorStructureReport(gateway) {
1698
+ const report = this.editorStructureReport;
1699
+ if (!report ||
1700
+ this.editorStructureReportByGateway.get(gateway) ===
1701
+ report.hasDefaultStructure) {
1702
+ return;
1703
+ }
1704
+ this.editorStructureReportByGateway.set(gateway, report.hasDefaultStructure);
1705
+ gateway.call.aiSetEditorHasDefaultStructure(report).catch((error) => {
1706
+ if (this.editorStructureReportByGateway.get(gateway) ===
1707
+ report.hasDefaultStructure) {
1708
+ this.editorStructureReportByGateway.delete(gateway);
1709
+ }
1710
+ errorHandler(error);
1711
+ });
1712
+ }
1571
1713
  callEditorClients(fn) {
1572
1714
  for (const socket of this.editorSockets.keys()) {
1573
1715
  fn(socket).catch(errorHandler);
@@ -1646,6 +1788,20 @@ class SocketManager extends TracedEventEmitter {
1646
1788
  getEditorClients() {
1647
1789
  return this.editorSockets.keys();
1648
1790
  }
1791
+ *getAuthenticatedEditorPeers() {
1792
+ for (const [peer, peerId] of this.editorSockets) {
1793
+ if (this.authenticatedEditorSockets.has(peer)) {
1794
+ yield { peer, peerId };
1795
+ }
1796
+ }
1797
+ }
1798
+ *getAuthenticatedGatewayPeers() {
1799
+ for (const [peer, peerId] of this.gatewaySockets) {
1800
+ if (this.authenticatedGatewaySockets.has(peer)) {
1801
+ yield { peer, peerId };
1802
+ }
1803
+ }
1804
+ }
1649
1805
  /**
1650
1806
  * Attached headless gateway peers. Separate from {@link getEditorClients} so
1651
1807
  * a broadcast meant for browser tabs never reaches a gateway, and the