@superblocksteam/vite-plugin-file-sync 2.0.167-next.0 → 2.0.168-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 (251) hide show
  1. package/dist/ai-service/agent/middleware.d.ts.map +1 -1
  2. package/dist/ai-service/agent/middleware.js +23 -0
  3. package/dist/ai-service/agent/middleware.js.map +1 -1
  4. package/dist/ai-service/agent/tool-message-utils.d.ts +1 -1
  5. package/dist/ai-service/agent/tool-message-utils.d.ts.map +1 -1
  6. package/dist/ai-service/agent/tool-message-utils.js.map +1 -1
  7. package/dist/ai-service/agent/tool-permission-delivery.d.ts +5 -4
  8. package/dist/ai-service/agent/tool-permission-delivery.d.ts.map +1 -1
  9. package/dist/ai-service/agent/tool-permission-delivery.js +22 -28
  10. package/dist/ai-service/agent/tool-permission-delivery.js.map +1 -1
  11. package/dist/ai-service/agent/tool-permission-outcome-metrics.d.ts +9 -0
  12. package/dist/ai-service/agent/tool-permission-outcome-metrics.d.ts.map +1 -0
  13. package/dist/ai-service/agent/tool-permission-outcome-metrics.js +23 -0
  14. package/dist/ai-service/agent/tool-permission-outcome-metrics.js.map +1 -0
  15. package/dist/ai-service/agent/tools/build-capture-screenshot.d.ts +44 -0
  16. package/dist/ai-service/agent/tools/build-capture-screenshot.d.ts.map +1 -1
  17. package/dist/ai-service/agent/tools/build-capture-screenshot.js +198 -18
  18. package/dist/ai-service/agent/tools/build-capture-screenshot.js.map +1 -1
  19. package/dist/ai-service/agent/tools/build-manage-checklist.d.ts +2 -0
  20. package/dist/ai-service/agent/tools/build-manage-checklist.d.ts.map +1 -1
  21. package/dist/ai-service/agent/tools/build-manage-checklist.js.map +1 -1
  22. package/dist/ai-service/agent/tools/build-reload-file.d.ts.map +1 -1
  23. package/dist/ai-service/agent/tools/build-reload-file.js +32 -19
  24. package/dist/ai-service/agent/tools/build-reload-file.js.map +1 -1
  25. package/dist/ai-service/agent/tools/get-security-findings.d.ts +45 -0
  26. package/dist/ai-service/agent/tools/get-security-findings.d.ts.map +1 -0
  27. package/dist/ai-service/agent/tools/get-security-findings.js +74 -0
  28. package/dist/ai-service/agent/tools/get-security-findings.js.map +1 -0
  29. package/dist/ai-service/agent/tools/integrations/integration-setup.d.ts +2 -2
  30. package/dist/ai-service/agent/tools/integrations/integration-setup.d.ts.map +1 -1
  31. package/dist/ai-service/agent/tools/integrations/integration-setup.js +24 -2
  32. package/dist/ai-service/agent/tools/integrations/integration-setup.js.map +1 -1
  33. package/dist/ai-service/agent/tools/report-security-findings.d.ts +20 -20
  34. package/dist/ai-service/agent/tools.d.ts.map +1 -1
  35. package/dist/ai-service/agent/tools.js +13 -2
  36. package/dist/ai-service/agent/tools.js.map +1 -1
  37. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.d.ts.map +1 -1
  38. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js +14 -0
  39. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js.map +1 -1
  40. package/dist/ai-service/agent/tools2/tools/git.d.ts +2 -1
  41. package/dist/ai-service/agent/tools2/tools/git.d.ts.map +1 -1
  42. package/dist/ai-service/agent/tools2/tools/git.js +1 -1
  43. package/dist/ai-service/agent/tools2/tools/git.js.map +1 -1
  44. package/dist/ai-service/agent/utils.d.ts.map +1 -1
  45. package/dist/ai-service/agent/utils.js +5 -0
  46. package/dist/ai-service/agent/utils.js.map +1 -1
  47. package/dist/ai-service/app-interface/app-package-manager.d.ts +43 -0
  48. package/dist/ai-service/app-interface/app-package-manager.d.ts.map +1 -0
  49. package/dist/ai-service/app-interface/app-package-manager.js +82 -0
  50. package/dist/ai-service/app-interface/app-package-manager.js.map +1 -0
  51. package/dist/ai-service/app-interface/constants.d.ts +2 -2
  52. package/dist/ai-service/app-interface/constants.d.ts.map +1 -1
  53. package/dist/ai-service/app-interface/constants.js +5 -0
  54. package/dist/ai-service/app-interface/constants.js.map +1 -1
  55. package/dist/ai-service/app-interface/file-system-interface.d.ts.map +1 -1
  56. package/dist/ai-service/app-interface/file-system-interface.js +0 -3
  57. package/dist/ai-service/app-interface/file-system-interface.js.map +1 -1
  58. package/dist/ai-service/app-interface/npm-error-parser.d.ts +1 -1
  59. package/dist/ai-service/app-interface/npm-error-parser.d.ts.map +1 -1
  60. package/dist/ai-service/app-interface/npm-error-parser.js +42 -8
  61. package/dist/ai-service/app-interface/npm-error-parser.js.map +1 -1
  62. package/dist/ai-service/app-interface/npm-registry.d.ts +30 -0
  63. package/dist/ai-service/app-interface/npm-registry.d.ts.map +1 -1
  64. package/dist/ai-service/app-interface/npm-registry.js +108 -1
  65. package/dist/ai-service/app-interface/npm-registry.js.map +1 -1
  66. package/dist/ai-service/app-interface/pnpm-lockfile.d.ts +28 -0
  67. package/dist/ai-service/app-interface/pnpm-lockfile.d.ts.map +1 -0
  68. package/dist/ai-service/app-interface/pnpm-lockfile.js +89 -0
  69. package/dist/ai-service/app-interface/pnpm-lockfile.js.map +1 -0
  70. package/dist/ai-service/app-interface/shell.d.ts +43 -7
  71. package/dist/ai-service/app-interface/shell.d.ts.map +1 -1
  72. package/dist/ai-service/app-interface/shell.js +252 -119
  73. package/dist/ai-service/app-interface/shell.js.map +1 -1
  74. package/dist/ai-service/app-interface/vulnerability-gate.d.ts +5 -0
  75. package/dist/ai-service/app-interface/vulnerability-gate.d.ts.map +1 -1
  76. package/dist/ai-service/app-interface/vulnerability-gate.js +3 -0
  77. package/dist/ai-service/app-interface/vulnerability-gate.js.map +1 -1
  78. package/dist/ai-service/app-skills/helpers.d.ts.map +1 -1
  79. package/dist/ai-service/app-skills/helpers.js +9 -9
  80. package/dist/ai-service/app-skills/helpers.js.map +1 -1
  81. package/dist/ai-service/chat/chat-session-store.d.ts +4 -1
  82. package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
  83. package/dist/ai-service/chat/chat-session-store.js +4 -0
  84. package/dist/ai-service/chat/chat-session-store.js.map +1 -1
  85. package/dist/ai-service/checklist/persisted-checklist-store.d.ts +66 -1
  86. package/dist/ai-service/checklist/persisted-checklist-store.d.ts.map +1 -1
  87. package/dist/ai-service/checklist/persisted-checklist-store.js +274 -4
  88. package/dist/ai-service/checklist/persisted-checklist-store.js.map +1 -1
  89. package/dist/ai-service/checklist/policy-remediation.d.ts +8 -0
  90. package/dist/ai-service/checklist/policy-remediation.d.ts.map +1 -0
  91. package/dist/ai-service/checklist/policy-remediation.js +46 -0
  92. package/dist/ai-service/checklist/policy-remediation.js.map +1 -0
  93. package/dist/ai-service/checklist/remediation-findings-client.d.ts +31 -0
  94. package/dist/ai-service/checklist/remediation-findings-client.d.ts.map +1 -0
  95. package/dist/ai-service/checklist/remediation-findings-client.js +117 -0
  96. package/dist/ai-service/checklist/remediation-findings-client.js.map +1 -0
  97. package/dist/ai-service/context-download.d.ts.map +1 -1
  98. package/dist/ai-service/context-download.js +76 -37
  99. package/dist/ai-service/context-download.js.map +1 -1
  100. package/dist/ai-service/dev-database-file-sync.d.ts +2 -1
  101. package/dist/ai-service/dev-database-file-sync.d.ts.map +1 -1
  102. package/dist/ai-service/dev-database-file-sync.js.map +1 -1
  103. package/dist/ai-service/index.d.ts +59 -9
  104. package/dist/ai-service/index.d.ts.map +1 -1
  105. package/dist/ai-service/index.js +1191 -479
  106. package/dist/ai-service/index.js.map +1 -1
  107. package/dist/ai-service/integrations/store.d.ts.map +1 -1
  108. package/dist/ai-service/integrations/store.js +11 -2
  109. package/dist/ai-service/integrations/store.js.map +1 -1
  110. package/dist/ai-service/llm/context-v2/adapter.d.ts +3 -0
  111. package/dist/ai-service/llm/context-v2/adapter.d.ts.map +1 -1
  112. package/dist/ai-service/llm/context-v2/adapter.js +6 -0
  113. package/dist/ai-service/llm/context-v2/adapter.js.map +1 -1
  114. package/dist/ai-service/llm/context-v2/context.d.ts +22 -1
  115. package/dist/ai-service/llm/context-v2/context.d.ts.map +1 -1
  116. package/dist/ai-service/llm/context-v2/context.js +185 -30
  117. package/dist/ai-service/llm/context-v2/context.js.map +1 -1
  118. package/dist/ai-service/llm/context-v2/conversation-context.d.ts +7 -0
  119. package/dist/ai-service/llm/context-v2/conversation-context.d.ts.map +1 -1
  120. package/dist/ai-service/llm/context-v2/manager.d.ts +11 -10
  121. package/dist/ai-service/llm/context-v2/manager.d.ts.map +1 -1
  122. package/dist/ai-service/llm/context-v2/manager.js +128 -85
  123. package/dist/ai-service/llm/context-v2/manager.js.map +1 -1
  124. package/dist/ai-service/llm/context-v2/storage/event-store.d.ts +73 -1
  125. package/dist/ai-service/llm/context-v2/storage/event-store.d.ts.map +1 -1
  126. package/dist/ai-service/llm/context-v2/storage/event-types.d.ts +152 -24
  127. package/dist/ai-service/llm/context-v2/storage/event-types.d.ts.map +1 -1
  128. package/dist/ai-service/llm/context-v2/storage/event-types.js +141 -0
  129. package/dist/ai-service/llm/context-v2/storage/event-types.js.map +1 -1
  130. package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.d.ts +20 -2
  131. package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.d.ts.map +1 -1
  132. package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.js +111 -49
  133. package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.js.map +1 -1
  134. package/dist/ai-service/llm/context-v2/storage/migration.d.ts.map +1 -1
  135. package/dist/ai-service/llm/context-v2/storage/migration.js +31 -4
  136. package/dist/ai-service/llm/context-v2/storage/migration.js.map +1 -1
  137. package/dist/ai-service/llm/context-v2/token-tracker.d.ts +34 -0
  138. package/dist/ai-service/llm/context-v2/token-tracker.d.ts.map +1 -1
  139. package/dist/ai-service/llm/context-v2/token-tracker.js +164 -0
  140. package/dist/ai-service/llm/context-v2/token-tracker.js.map +1 -1
  141. package/dist/ai-service/llm/context-v2/types.d.ts +5 -2
  142. package/dist/ai-service/llm/context-v2/types.d.ts.map +1 -1
  143. package/dist/ai-service/llm/context-v2/types.js +1 -1
  144. package/dist/ai-service/llm/context-v2/types.js.map +1 -1
  145. package/dist/ai-service/llm/stream/config.d.ts +7 -0
  146. package/dist/ai-service/llm/stream/config.d.ts.map +1 -1
  147. package/dist/ai-service/llm/stream/config.js +20 -2
  148. package/dist/ai-service/llm/stream/config.js.map +1 -1
  149. package/dist/ai-service/llm/stream/orchestrator.d.ts.map +1 -1
  150. package/dist/ai-service/llm/stream/orchestrator.js +30 -14
  151. package/dist/ai-service/llm/stream/orchestrator.js.map +1 -1
  152. package/dist/ai-service/llm/stream/session.d.ts +1 -1
  153. package/dist/ai-service/llm/stream/session.d.ts.map +1 -1
  154. package/dist/ai-service/llm/stream/session.js +1 -1
  155. package/dist/ai-service/llm/stream/session.js.map +1 -1
  156. package/dist/ai-service/security/secret-scanner-service.d.ts.map +1 -1
  157. package/dist/ai-service/security/secret-scanner-service.js +12 -2
  158. package/dist/ai-service/security/secret-scanner-service.js.map +1 -1
  159. package/dist/ai-service/security/secret-scanner.d.ts +1 -1
  160. package/dist/ai-service/security/secret-scanner.d.ts.map +1 -1
  161. package/dist/ai-service/security/secret-scanner.js +4 -1
  162. package/dist/ai-service/security/secret-scanner.js.map +1 -1
  163. package/dist/ai-service/security/vulnerability-triage.d.ts +2 -2
  164. package/dist/ai-service/state-machine/clark-fsm.d.ts +17 -1
  165. package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
  166. package/dist/ai-service/state-machine/clark-fsm.js +85 -8
  167. package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
  168. package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
  169. package/dist/ai-service/state-machine/handlers/agent-planning.js +97 -47
  170. package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
  171. package/dist/ai-service/state-machine/handlers/awaiting-user.d.ts.map +1 -1
  172. package/dist/ai-service/state-machine/handlers/awaiting-user.js +68 -50
  173. package/dist/ai-service/state-machine/handlers/awaiting-user.js.map +1 -1
  174. package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
  175. package/dist/ai-service/state-machine/handlers/llm-generating.js +91 -73
  176. package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
  177. package/dist/ai-service/state-machine/helpers/prompt-failure.d.ts +1 -1
  178. package/dist/ai-service/state-machine/helpers/prompt-failure.d.ts.map +1 -1
  179. package/dist/ai-service/state-machine/helpers/prompt-failure.js +1 -0
  180. package/dist/ai-service/state-machine/helpers/prompt-failure.js.map +1 -1
  181. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.d.ts +2 -1
  182. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.d.ts.map +1 -1
  183. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.js +1 -0
  184. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.js.map +1 -1
  185. package/dist/ai-service/state-machine/helpers/user-response-message.d.ts +4 -0
  186. package/dist/ai-service/state-machine/helpers/user-response-message.d.ts.map +1 -0
  187. package/dist/ai-service/state-machine/helpers/user-response-message.js +37 -0
  188. package/dist/ai-service/state-machine/helpers/user-response-message.js.map +1 -0
  189. package/dist/ai-service/template-renderer.d.ts.map +1 -1
  190. package/dist/ai-service/template-renderer.js +22 -9
  191. package/dist/ai-service/template-renderer.js.map +1 -1
  192. package/dist/ai-service/types.d.ts +2 -1
  193. package/dist/ai-service/types.d.ts.map +1 -1
  194. package/dist/ai-service/types.js.map +1 -1
  195. package/dist/components-manager.d.ts +9 -2
  196. package/dist/components-manager.d.ts.map +1 -1
  197. package/dist/components-manager.js +0 -4
  198. package/dist/components-manager.js.map +1 -1
  199. package/dist/file-sync-vite-plugin.d.ts.map +1 -1
  200. package/dist/file-sync-vite-plugin.js +2 -1
  201. package/dist/file-sync-vite-plugin.js.map +1 -1
  202. package/dist/file-system-helpers.d.ts +7 -0
  203. package/dist/file-system-helpers.d.ts.map +1 -1
  204. package/dist/file-system-helpers.js +9 -1
  205. package/dist/file-system-helpers.js.map +1 -1
  206. package/dist/file-system-manager.d.ts +12 -1
  207. package/dist/file-system-manager.d.ts.map +1 -1
  208. package/dist/file-system-manager.js +37 -22
  209. package/dist/file-system-manager.js.map +1 -1
  210. package/dist/index.d.ts +7 -0
  211. package/dist/index.d.ts.map +1 -1
  212. package/dist/index.js +7 -0
  213. package/dist/index.js.map +1 -1
  214. package/dist/lock-service/index.d.ts +20 -0
  215. package/dist/lock-service/index.d.ts.map +1 -1
  216. package/dist/lock-service/index.js +53 -5
  217. package/dist/lock-service/index.js.map +1 -1
  218. package/dist/migration/get-fullstack-template-dir.js +2 -2
  219. package/dist/migration/get-fullstack-template-dir.js.map +1 -1
  220. package/dist/migration/migration-routes.d.ts.map +1 -1
  221. package/dist/migration/migration-routes.js +8 -2
  222. package/dist/migration/migration-routes.js.map +1 -1
  223. package/dist/plugin-options.d.ts +1 -0
  224. package/dist/plugin-options.d.ts.map +1 -1
  225. package/dist/plugin-options.js.map +1 -1
  226. package/dist/router-parser.d.ts.map +1 -1
  227. package/dist/router-parser.js +336 -65
  228. package/dist/router-parser.js.map +1 -1
  229. package/dist/socket-manager.d.ts +25 -2
  230. package/dist/socket-manager.d.ts.map +1 -1
  231. package/dist/socket-manager.js +270 -55
  232. package/dist/socket-manager.js.map +1 -1
  233. package/dist/split-edit-after-clark.d.ts +14 -0
  234. package/dist/split-edit-after-clark.d.ts.map +1 -0
  235. package/dist/split-edit-after-clark.js +40 -0
  236. package/dist/split-edit-after-clark.js.map +1 -0
  237. package/dist/sync-service/index.d.ts +2 -1
  238. package/dist/sync-service/index.d.ts.map +1 -1
  239. package/dist/sync-service/index.js +5 -0
  240. package/dist/sync-service/index.js.map +1 -1
  241. package/dist/sync-service/list-dir.d.ts.map +1 -1
  242. package/dist/sync-service/list-dir.js +9 -4
  243. package/dist/sync-service/list-dir.js.map +1 -1
  244. package/dist/util/log-sanitizer.d.ts.map +1 -1
  245. package/dist/util/log-sanitizer.js +4 -0
  246. package/dist/util/log-sanitizer.js.map +1 -1
  247. package/dist/vite-module-reload.d.ts +34 -0
  248. package/dist/vite-module-reload.d.ts.map +1 -0
  249. package/dist/vite-module-reload.js +121 -0
  250. package/dist/vite-module-reload.js.map +1 -0
  251. package/package.json +9 -8
@@ -1,9 +1,9 @@
1
- import { randomUUID } from "node:crypto";
1
+ import { randomUUID, timingSafeEqual } from "node:crypto";
2
2
  import { cp, mkdir, mkdtemp, rm } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { metrics } from "@opentelemetry/api";
6
- import { WebSocketServer } from "ws";
6
+ import { WebSocket, WebSocketServer } from "ws";
7
7
  import { createISocketClient, DEFAULT_NON_GIT_BRANCH, MAX_SOCKET_PAYLOAD_BYTES, SocketErrorException, SUPERBLOCKS_LIVE_GIT_BRANCH, TracedEventEmitter, TracedSocket, } from "@superblocksteam/shared";
8
8
  import { uploadCheckpointScreenshot } from "./ai-service/checkpoint-screenshot.js";
9
9
  import { uploadContextToBucketeer } from "./ai-service/context-upload.js";
@@ -18,6 +18,7 @@ import { GitConflictError, isMissingGitRevisionError, } from "./git-service/inde
18
18
  import { doesRemoteLiveBranchExist, ensureLiveBranchCheckedOut, pushLiveBranchWithAutoRecreate, rollbackLastCommit, } from "./git-service/live-branch.js";
19
19
  import { ensureProviderWorkflowFiles } from "./git-service/provider-workflow-files.js";
20
20
  import { getSyncCountsAgainstDefaultBranch } from "./git-service/status-sync.js";
21
+ import { SplitEditAfterClark } from "./split-edit-after-clark.js";
21
22
  import { getErrorMeta, getLogger } from "./util/logger.js";
22
23
  import { OperationQueue } from "./util/operation-queue.js";
23
24
  const SB_WORKTREE_SNAPSHOTS_DIR = join(tmpdir(), "sb-worktree-snapshots");
@@ -42,6 +43,55 @@ function extractCallerUserId(peerAuthorization) {
42
43
  }
43
44
  const EDITOR_PROTOCOL = "editor";
44
45
  const GATEWAY_PROTOCOL = "gateway";
46
+ const AI_SERVICE_PROTOCOL = "ai-service";
47
+ const INTERNAL_METHODS = new Set([
48
+ "editor.internalApplyDraftChanges",
49
+ "editor.internalCreateDraftMarkerOnly",
50
+ "editor.internalHasDraftChanges",
51
+ "editor.internalGetBranchName",
52
+ "editor.internalUploadDirectory",
53
+ "editor.internalSyncCurrentDirectoryWithDraftsNow",
54
+ "editor.internalGetDraftDiff",
55
+ "editor.internalGetDraftChanges",
56
+ "editor.internalAcquireTurnLock",
57
+ "editor.internalReleaseTurnLock",
58
+ ]);
59
+ const AI_SERVICE_METHODS = new Set([
60
+ ...INTERNAL_METHODS,
61
+ "editor.aiAcceptDraft",
62
+ "editor.aiRejectDraft",
63
+ "editor.aiCancel",
64
+ "editor.aiClearDraftState",
65
+ ]);
66
+ function decodeDraftBinary(value) {
67
+ const content = Buffer.from(value, "base64");
68
+ if (content.toString("base64") !== value) {
69
+ throw new SocketErrorException(400, "Invalid draft binary content");
70
+ }
71
+ return content;
72
+ }
73
+ function uploadAcceptedDraftContext(aiService, applicationId, commitId, branchName, peerAuthorization) {
74
+ void (async () => {
75
+ const contextId = aiService.getContextId();
76
+ if (!contextId)
77
+ return;
78
+ const jwt = aiService.getClarkJwt() || peerAuthorization?.replace("Bearer ", "");
79
+ if (!jwt)
80
+ return;
81
+ await uploadContextToBucketeer({
82
+ applicationId,
83
+ commitId,
84
+ branchName,
85
+ appRootDirPath: aiService.appRootDirPath,
86
+ useServerUpload: aiService.isAiFileUploadEnabled,
87
+ superblocksBaseUrl: aiService.superblocksBaseUrl,
88
+ logger: getLogger(),
89
+ jwt,
90
+ });
91
+ })().catch((error) => {
92
+ getLogger().warn("[aiAcceptDraft] Failed to upload context to bucketeer (non-blocking)", getErrorMeta(error));
93
+ });
94
+ }
45
95
  /**
46
96
  * Subprotocols the dev server answers with. The library runtime offers none at
47
97
  * all and connects on that basis; there is no `library` protocol to negotiate.
@@ -52,6 +102,7 @@ const GATEWAY_PROTOCOL = "gateway";
52
102
  const NEGOTIABLE_PROTOCOLS = [
53
103
  EDITOR_PROTOCOL,
54
104
  GATEWAY_PROTOCOL,
105
+ AI_SERVICE_PROTOCOL,
55
106
  ];
56
107
  function selectProtocol(offered) {
57
108
  for (const protocol of offered) {
@@ -81,6 +132,9 @@ export function classifyConnectionKind(offeredProtocols) {
81
132
  if (selected === EDITOR_PROTOCOL) {
82
133
  return "editor";
83
134
  }
135
+ if (selected === AI_SERVICE_PROTOCOL) {
136
+ return "ai-service";
137
+ }
84
138
  return "unsupported";
85
139
  }
86
140
  /**
@@ -132,6 +186,9 @@ export function disposeSocketManager() {
132
186
  _socketManager?.dispose();
133
187
  _socketManager = undefined;
134
188
  }
189
+ export async function closeAllSocketClients() {
190
+ await _socketManager?.closeAllClientsGracefully();
191
+ }
135
192
  class SocketManager extends TracedEventEmitter {
136
193
  peerLiveness = new PeerLiveness();
137
194
  socketLiveness = new WeakMap();
@@ -174,10 +231,13 @@ class SocketManager extends TracedEventEmitter {
174
231
  super(tracer);
175
232
  }
176
233
  socketServer;
177
- startSocketServer({ server, fileSyncManager, syncService, lockService, aiService, gitService, gitBootstrapAuthFailed, gitBootstrapFailed, activateGitService, refreshGitCredentials, checkAuthorization, path, tracer, gitOperationLock, superblocksBaseUrl, maxPayloadBytes = MAX_SOCKET_PAYLOAD_BYTES, }) {
234
+ startSocketServer({ server, applicationId, internalToken, fileSyncManager, syncService, draftClient, lockService, aiService, gitService, gitBootstrapAuthFailed, gitBootstrapFailed, activateGitService, refreshGitCredentials, checkAuthorization, onAuthenticated, path, tracer, gitOperationLock, superblocksBaseUrl, maxPayloadBytes = MAX_SOCKET_PAYLOAD_BYTES, }) {
178
235
  if (this.socketServer) {
179
236
  throw new Error("Socket server already started");
180
237
  }
238
+ const splitEditAfterClark = syncService && !aiService
239
+ ? new SplitEditAfterClark(syncService)
240
+ : undefined;
181
241
  const wss = new WebSocketServer({
182
242
  path,
183
243
  noServer: true,
@@ -224,8 +284,9 @@ class SocketManager extends TracedEventEmitter {
224
284
  return;
225
285
  }
226
286
  try {
227
- await lockService?.relinquishLockForTurn(() => headlessLockOwners.some((owner) => !owner.handbackFailed &&
228
- (!ownedTurns.includes(owner) || owner.peerId !== peerId)));
287
+ const stillNeeded = () => headlessLockOwners.some((owner) => !owner.handbackFailed &&
288
+ (!ownedTurns.includes(owner) || owner.peerId !== peerId));
289
+ await lockService?.relinquishLockForTurn(stillNeeded);
229
290
  }
230
291
  catch (error) {
231
292
  await headlessLockOperationQueue.enqueue(async () => {
@@ -281,8 +342,9 @@ class SocketManager extends TracedEventEmitter {
281
342
  return true;
282
343
  }
283
344
  try {
284
- await lockService?.relinquishLockForTurn(() => headlessLockOwners.some((owner) => !owner.handbackFailed &&
285
- (owner !== ownedTurn || owner.peerId !== peerId)));
345
+ const stillNeeded = () => headlessLockOwners.some((owner) => !owner.handbackFailed &&
346
+ (owner !== ownedTurn || owner.peerId !== peerId));
347
+ await lockService?.relinquishLockForTurn(stillNeeded);
286
348
  }
287
349
  catch (error) {
288
350
  await headlessLockOperationQueue.enqueue(async () => {
@@ -411,7 +473,7 @@ class SocketManager extends TracedEventEmitter {
411
473
  // because the dev server's own auto-commit pushes to the same remote with
412
474
  // the same credential and has to be able to report a rejection too.
413
475
  clearGitAuthFailed();
414
- const getActiveGitService = async () => {
476
+ const getActiveGitService = async (options) => {
415
477
  if (disconnectInProgress) {
416
478
  return undefined;
417
479
  }
@@ -448,6 +510,11 @@ class SocketManager extends TracedEventEmitter {
448
510
  aiService?.setRuntimeGitServices(activeGitService, syncService, gitOperationLock);
449
511
  return activeGitService;
450
512
  }
513
+ // No remote. A failed startup bootstrap resolves `undefined` too;
514
+ // `gitBootstrapFailed` is what keeps those apps on the retry loop.
515
+ if (!options?.retryWhenUnconfigured && !gitBootstrapFailed) {
516
+ return undefined;
517
+ }
451
518
  }
452
519
  catch (error) {
453
520
  getLogger().warn("[git] activation attempt failed", {
@@ -525,17 +592,24 @@ class SocketManager extends TracedEventEmitter {
525
592
  ws.close(1002, "Unsupported subprotocol");
526
593
  return;
527
594
  }
595
+ if (kind === "ai-service" &&
596
+ (req.headers.origin !== undefined ||
597
+ !["127.0.0.1", "::1", "::ffff:127.0.0.1"].includes(req.socket.remoteAddress ?? ""))) {
598
+ ws.close(1008, "Internal access denied");
599
+ return;
600
+ }
528
601
  const peerId = url.searchParams.get("peerId");
529
602
  if (!peerId) {
530
603
  getLogger().error("No peerId in upgrade request");
531
604
  ws.close(1008, "No peerId found");
532
605
  return;
533
606
  }
534
- if (url.searchParams.get("applicationId") !== syncService?.applicationId) {
607
+ const expectedApplicationId = applicationId ?? syncService?.applicationId;
608
+ if (url.searchParams.get("applicationId") !== expectedApplicationId) {
535
609
  getLogger().error("Application ID mismatch", {
536
610
  error: {
537
611
  kind: "ValidationError",
538
- message: `Expected application ID ${syncService?.applicationId}, received ${url.searchParams.get("applicationId")}`,
612
+ message: `Expected application ID ${expectedApplicationId}, received ${url.searchParams.get("applicationId")}`,
539
613
  },
540
614
  });
541
615
  ws.close(1008, "Application ID mismatch");
@@ -560,6 +634,19 @@ class SocketManager extends TracedEventEmitter {
560
634
  if (connectionLiveness) {
561
635
  ws.on("message", () => connectionLiveness.markInbound());
562
636
  }
637
+ const gatewayLivenessTimer = kind === "gateway" &&
638
+ !lockService &&
639
+ draftClient &&
640
+ connectionLiveness
641
+ ? setInterval(() => {
642
+ if (!connectionLiveness.observe().available &&
643
+ headlessLockOwners.some((owner) => owner.peerId === peerId)) {
644
+ dropHeadlessLockOwnersInBackground(peerId, "gateway liveness lapsed");
645
+ aiService?.endHeadlessTurn(peerId, connectionLiveness);
646
+ }
647
+ }, 30_000)
648
+ : undefined;
649
+ gatewayLivenessTimer?.unref();
563
650
  /**
564
651
  * When an editor authenticates after git was enabled, the lock may
565
652
  * still be on 'main'. This calls
@@ -578,10 +665,33 @@ class SocketManager extends TracedEventEmitter {
578
665
  ? ++nextEditorConnectionSequence
579
666
  : 0;
580
667
  const authMiddleware = async (params, ctx, _peer, next) => {
668
+ if (kind === "ai-service") {
669
+ const expected = internalToken?.();
670
+ const provided = ctx.peerAuthorization?.startsWith("Bearer ")
671
+ ? ctx.peerAuthorization.slice(7)
672
+ : undefined;
673
+ const expectedBytes = Buffer.from(expected ?? "");
674
+ const providedBytes = Buffer.from(provided ?? "");
675
+ const hasInternalToken = !!expected &&
676
+ !!provided &&
677
+ expectedBytes.length === providedBytes.length &&
678
+ timingSafeEqual(expectedBytes, providedBytes);
679
+ if (!AI_SERVICE_METHODS.has(ctx.method) ||
680
+ (!hasInternalToken &&
681
+ !(await checkAuthorization(ctx.peerAuthorization)).success)) {
682
+ throw new SocketErrorException(403, "Internal access denied");
683
+ }
684
+ return await next();
685
+ }
686
+ if (INTERNAL_METHODS.has(ctx.method)) {
687
+ throw new SocketErrorException(403, "Internal access denied");
688
+ }
581
689
  const result = await checkAuthorization(ctx.peerAuthorization);
582
690
  if (!result.success) {
583
691
  throw new SocketErrorException(result.status, result.error);
584
692
  }
693
+ if (ctx.peerAuthorization)
694
+ onAuthenticated?.(ctx.peerAuthorization);
585
695
  if (isEditor && this.editorSockets.has(socket)) {
586
696
  this.authenticatedEditorSockets.add(socket);
587
697
  }
@@ -722,6 +832,94 @@ class SocketManager extends TracedEventEmitter {
722
832
  const middlewares = [recordingMiddleware, authMiddleware].filter((m) => m != null);
723
833
  const socket = createISocketClient(new TracedSocket(ws, {
724
834
  editor: {
835
+ internalApplyDraftChanges: [
836
+ async ({ changes }) => {
837
+ if (!syncService) {
838
+ throw new Error("Sync service not available");
839
+ }
840
+ await syncService.applyDraftChanges(changes.map(({ filePath, content }) => ({
841
+ filePath,
842
+ ...(content && {
843
+ content: content.kind === "binary"
844
+ ? decodeDraftBinary(content.value)
845
+ : content.value,
846
+ }),
847
+ })));
848
+ },
849
+ ],
850
+ internalCreateDraftMarkerOnly: [
851
+ async ({ filePath }) => {
852
+ if (!syncService)
853
+ throw new Error("Sync service not available");
854
+ await syncService.createDraftMarkerOnly(filePath);
855
+ },
856
+ ],
857
+ internalHasDraftChanges: [
858
+ async () => {
859
+ if (!syncService)
860
+ throw new Error("Sync service not available");
861
+ return await syncService.hasLocalDraftChanges();
862
+ },
863
+ ],
864
+ internalGetBranchName: [
865
+ async () => {
866
+ if (!syncService)
867
+ throw new Error("Sync service not available");
868
+ return syncService.branchName;
869
+ },
870
+ ],
871
+ internalUploadDirectory: [
872
+ async ({ source }) => {
873
+ if (!syncService)
874
+ throw new Error("Sync service not available");
875
+ await syncService.uploadDirectory(source);
876
+ },
877
+ ],
878
+ internalSyncCurrentDirectoryWithDraftsNow: [
879
+ async ({ source }) => {
880
+ if (!syncService)
881
+ throw new Error("Sync service not available");
882
+ return await syncService.syncCurrentDirectoryWithDraftsNow(source);
883
+ },
884
+ ],
885
+ internalGetDraftDiff: [
886
+ async ({ paths }) => {
887
+ if (!syncService)
888
+ throw new Error("Sync service not available");
889
+ return await syncService.getDraftDiff(paths);
890
+ },
891
+ ],
892
+ internalGetDraftChanges: [
893
+ async () => {
894
+ if (!syncService)
895
+ throw new Error("Sync service not available");
896
+ return await syncService.getDraftChanges();
897
+ },
898
+ ],
899
+ internalAcquireTurnLock: [
900
+ async (request) => {
901
+ if (!lockService)
902
+ throw new Error("Lock service not available");
903
+ await lockService.acquireLockForTurn();
904
+ lockService.markGatewayAttached("ai-service");
905
+ if (request?.promptId) {
906
+ await splitEditAfterClark?.startTurn(request.promptId, request.chatSessionId);
907
+ }
908
+ },
909
+ ],
910
+ internalReleaseTurnLock: [
911
+ async (request) => {
912
+ if (!lockService)
913
+ throw new Error("Lock service not available");
914
+ if (request?.promptId) {
915
+ void splitEditAfterClark
916
+ ?.endTurn(request.promptId)
917
+ .catch((error) => getLogger().warn("Could not finish edit-after-Clark measurement", getErrorMeta(error)));
918
+ }
919
+ await lockService.relinquishLockForTurn();
920
+ lockService.markGatewayDetached("ai-service");
921
+ },
922
+ ],
725
923
  // Renames
726
924
  renameElement: [fileSyncManager.handleRenameElement],
727
925
  renameApi: [fileSyncManager.handleRenameApi],
@@ -740,12 +938,18 @@ class SocketManager extends TracedEventEmitter {
740
938
  // AI + draft handling
741
939
  aiGenerate: [
742
940
  async (request) => {
941
+ if (kind === "ai-service") {
942
+ throw new Error("Internal client cannot start AI generation");
943
+ }
743
944
  const inputSource = { kind, peerId };
744
945
  await aiService?.handleAiGenerate(request, inputSource);
745
946
  },
746
947
  ],
747
948
  aiGenerateWithQueue: [
748
949
  async (request) => {
950
+ if (kind === "ai-service") {
951
+ throw new Error("Internal client cannot start AI generation");
952
+ }
749
953
  if (!aiService) {
750
954
  throw new Error("AI service not available");
751
955
  }
@@ -835,12 +1039,30 @@ class SocketManager extends TracedEventEmitter {
835
1039
  ],
836
1040
  aiRejectDraft: [
837
1041
  async (request) => {
1042
+ if (draftClient) {
1043
+ await draftClient.rejectDraft(request);
1044
+ await aiService?.handleUserRejectedDraft(request.silently);
1045
+ return;
1046
+ }
838
1047
  await syncService?.discardLocalDraftChanges("ai:reject");
1048
+ splitEditAfterClark?.discardLastTurn();
839
1049
  await aiService?.handleUserRejectedDraft(request.silently);
840
1050
  },
841
1051
  ],
842
1052
  aiAcceptDraft: [
843
1053
  async (request, _context) => {
1054
+ if (draftClient) {
1055
+ const result = await draftClient.acceptDraft(request);
1056
+ await aiService?.handleUserAcceptedDraft(request.silently);
1057
+ const appId = applicationId ?? syncService?.applicationId;
1058
+ if (result.commitId && aiService && appId) {
1059
+ uploadAcceptedDraftContext(aiService, appId, result.commitId, result.branchName, _context.peerAuthorization);
1060
+ }
1061
+ return {
1062
+ commitId: result.commitId,
1063
+ ...(result.warning && { warning: result.warning }),
1064
+ };
1065
+ }
844
1066
  if (!syncService) {
845
1067
  throw new Error("Sync service not available");
846
1068
  }
@@ -887,47 +1109,8 @@ class SocketManager extends TracedEventEmitter {
887
1109
  });
888
1110
  }
889
1111
  if (result?.commitId) {
890
- // Upload context file to bucketeer asynchronously (fire-and-forget)
891
- if (aiService && syncService) {
892
- void (async () => {
893
- try {
894
- // Get contextId using the same pattern as other context operations
895
- // This gets userId from clark.context.jwt
896
- const contextId = aiService.getContextId();
897
- if (!contextId) {
898
- getLogger().warn("[aiAcceptDraft] No contextId available (JWT missing or no userId), skipping context upload");
899
- return;
900
- }
901
- // Get JWT from clark context (same source as userId)
902
- // We need to access it via a method or use peer authorization as fallback
903
- const jwt = aiService.getClarkJwt() ||
904
- _context.peerAuthorization?.replace("Bearer ", "");
905
- if (!jwt) {
906
- getLogger().warn("[aiAcceptDraft] No JWT found, skipping context upload");
907
- return;
908
- }
909
- await uploadContextToBucketeer({
910
- applicationId: syncService.applicationId,
911
- commitId: result.commitId,
912
- branchName: syncService.branchName,
913
- appRootDirPath: aiService.appRootDirPath,
914
- useServerUpload: aiService.isAiFileUploadEnabled,
915
- superblocksBaseUrl: aiService.superblocksBaseUrl,
916
- logger: getLogger(),
917
- jwt,
918
- });
919
- getLogger().info("[aiAcceptDraft] context upload completed", {
920
- applicationId: syncService.applicationId,
921
- branchName: syncService.branchName,
922
- commitId: result.commitId,
923
- contextUserId: contextId.userId,
924
- });
925
- }
926
- catch (error) {
927
- getLogger().warn("[aiAcceptDraft] Failed to upload context to bucketeer (non-blocking)", getErrorMeta(error));
928
- // Don't throw - this is fire-and-forget
929
- }
930
- })();
1112
+ if (aiService) {
1113
+ uploadAcceptedDraftContext(aiService, syncService.applicationId, result.commitId, syncService.branchName, _context.peerAuthorization);
931
1114
  }
932
1115
  return draftStateCleared === true
933
1116
  ? { commitId: result.commitId }
@@ -966,13 +1149,23 @@ class SocketManager extends TracedEventEmitter {
966
1149
  ],
967
1150
  aiClearDraftState: [
968
1151
  async () => {
969
- await syncService?.clearDraftsDirectory();
1152
+ if (draftClient) {
1153
+ await draftClient.clearDraftState();
1154
+ }
1155
+ else {
1156
+ await syncService?.clearDraftsDirectory();
1157
+ }
970
1158
  },
971
1159
  ],
972
1160
  aiCancel: [
973
1161
  async (request) => {
974
1162
  await aiService?.handleUserCanceled(request?.reason);
975
- await syncService?.reportDraftStateToServer(false);
1163
+ if (draftClient) {
1164
+ await draftClient.reportDraftStateCleared();
1165
+ }
1166
+ else {
1167
+ await syncService?.reportDraftStateToServer(false);
1168
+ }
976
1169
  },
977
1170
  ],
978
1171
  aiNotifyIntegrationSetupClosed: [
@@ -1039,8 +1232,10 @@ class SocketManager extends TracedEventEmitter {
1039
1232
  const messages = await aiService?.chatSessionStore.getMessages({
1040
1233
  force: request?.force,
1041
1234
  });
1235
+ aiService?.applyEditorContextOptions?.(request?.contextOptionsV2);
1042
1236
  return {
1043
1237
  messages: messages ?? [],
1238
+ ...aiService?.getChatStateSnapshot(),
1044
1239
  };
1045
1240
  },
1046
1241
  ],
@@ -1463,7 +1658,9 @@ class SocketManager extends TracedEventEmitter {
1463
1658
  // would be stuck with git permanently disabled.
1464
1659
  disconnectInProgress = false;
1465
1660
  clearGitAuthFailed();
1466
- const service = await getActiveGitService();
1661
+ const service = await getActiveGitService({
1662
+ retryWhenUnconfigured: true,
1663
+ });
1467
1664
  return { activated: !!service };
1468
1665
  }),
1469
1666
  ],
@@ -1802,12 +1999,13 @@ class SocketManager extends TracedEventEmitter {
1802
1999
  // Git activation is deliberately NOT started here — it runs from the
1803
2000
  // auth middleware, after presence. See `activateGitForAuthedEditor`.
1804
2001
  }
1805
- else {
2002
+ else if (kind === "library") {
1806
2003
  this.librarySockets.add(socket);
1807
2004
  this.emit("connect:library", socket);
1808
2005
  }
1809
2006
  existingSockets.add(socket);
1810
2007
  ws.on("close", (code, reason) => {
2008
+ clearInterval(gatewayLivenessTimer);
1811
2009
  connectionLiveness?.close();
1812
2010
  const peerSockets = this.peerIds.get(peerId);
1813
2011
  peerSockets?.delete(socket);
@@ -2133,6 +2331,23 @@ class SocketManager extends TracedEventEmitter {
2133
2331
  socket.close();
2134
2332
  }
2135
2333
  }
2334
+ async closeAllClientsGracefully() {
2335
+ await Promise.all(Array.from(this.socketServer?.clients ?? []).map((socket) => new Promise((resolve) => {
2336
+ if (socket.readyState === WebSocket.CLOSED) {
2337
+ resolve();
2338
+ return;
2339
+ }
2340
+ const timeout = setTimeout(() => {
2341
+ socket.terminate();
2342
+ resolve();
2343
+ }, 1_000);
2344
+ socket.once("close", () => {
2345
+ clearTimeout(timeout);
2346
+ resolve();
2347
+ });
2348
+ socket.close(1001, "Server shutting down");
2349
+ })));
2350
+ }
2136
2351
  dispose() {
2137
2352
  this._disposer();
2138
2353
  }