@dev.fast/whiteboard 0.0.0-stage → 0.2.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 (247) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -2
  3. package/THIRD_PARTY_NOTICES.md +93 -0
  4. package/dist/account-alias-DOHN1RSH.js +973 -0
  5. package/dist/agent-cli-ChOq0ZuO.js +117 -0
  6. package/dist/agent-cli-DzWfZr6G.js +2 -0
  7. package/dist/agent-client-CPQo7iTI.js +305 -0
  8. package/dist/authoring-tools-Cr6Kpjsx.js +234 -0
  9. package/dist/build-info.json +1 -0
  10. package/dist/cli-hVgHcdsv.js +47 -0
  11. package/dist/cli-runner-D8y2_luU.js +5095 -0
  12. package/dist/cli.d.ts +1 -0
  13. package/dist/cli.js +128 -0
  14. package/dist/client-DFoh50W3.js +950 -0
  15. package/dist/desktop-discovery-DBO-PM2V.js +164 -0
  16. package/dist/error-message-OtiDonty.js +6 -0
  17. package/dist/fs-utils-BMPLt0cr.js +22 -0
  18. package/dist/fuzzy-match-BoAmcyak.js +30 -0
  19. package/dist/headless-host-BJxf4dUU.js +94 -0
  20. package/dist/input-error-OLgB_h31.js +11 -0
  21. package/dist/local-data-CxLk75rx.js +4491 -0
  22. package/dist/mcp-QeF8vkdf.js +140 -0
  23. package/dist/package-paths-B6-zxvIO.js +47 -0
  24. package/dist/process-error-telemetry-v9e5h6D7.js +3952 -0
  25. package/dist/profile-4ry2f3AM.js +76 -0
  26. package/dist/profile-Bh5FIrmf.js +2 -0
  27. package/dist/request-origin-D_4QIdoN.js +811 -0
  28. package/dist/review-agent-traces-DNJFYzc8.js +3866 -0
  29. package/dist/review-home-paths-6zZH-1c9.js +2560 -0
  30. package/dist/review-telemetry-DEFnQsQ2.js +1195 -0
  31. package/dist/runtime-CKiWhGrf.js +52 -0
  32. package/dist/runtime.d.ts +9 -0
  33. package/dist/runtime.js +2 -0
  34. package/dist/s3-SzLpeCG6.js +345 -0
  35. package/dist/s3-config-BgxkSoOy.js +2 -0
  36. package/dist/s3-config-CAxhO9u_.js +676 -0
  37. package/dist/server/desktop-host.d.ts +4 -0
  38. package/dist/server/desktop-host.js +1702 -0
  39. package/dist/server-discovery-Dbzn3w6Z.js +66 -0
  40. package/dist/sharing/index.d.ts +1977 -0
  41. package/dist/sharing/index.js +2 -0
  42. package/dist/src-CGP5ytbV.js +283 -0
  43. package/dist/src-CmdiBL20.js +3676 -0
  44. package/dist/src-DnwdaQ2r.js +865 -0
  45. package/dist/src-X9phtB2j.js +68 -0
  46. package/dist/stored-document-migration-DHeMpLMb.js +2671 -0
  47. package/dist/tool-failure-C8zB73HV.js +98 -0
  48. package/dist/tutorial-trace-DrBvkaeL.js +43 -0
  49. package/instructions/authoring.md +36 -0
  50. package/instructions/file-lenses.md +13 -0
  51. package/instructions/scratchpad.md +23 -0
  52. package/instructions/trace-archaeology.md +113 -0
  53. package/onboarding.md +8 -0
  54. package/package.json +106 -3
  55. package/src/agent-selection.ts +100 -0
  56. package/src/agent-session-ref.ts +80 -0
  57. package/src/ask/agents.ts +351 -0
  58. package/src/ask/checkout-files.ts +47 -0
  59. package/src/ask/file-refs.ts +111 -0
  60. package/src/ask/pi-mcp.ts +154 -0
  61. package/src/ask/protocol.ts +244 -0
  62. package/src/ask/thread-state.ts +350 -0
  63. package/src/ask/thread.ts +1400 -0
  64. package/src/ask/threads.ts +219 -0
  65. package/src/ask/watch.ts +79 -0
  66. package/src/cli-install.ts +1041 -0
  67. package/src/cli-runner.ts +1461 -0
  68. package/src/cli-runtime-info.ts +35 -0
  69. package/src/cli.ts +222 -0
  70. package/src/connect-prompts.ts +248 -0
  71. package/src/cursor-deeplink.ts +11 -0
  72. package/src/desktop-discovery.ts +355 -0
  73. package/src/diff-selection-migration.ts +63 -0
  74. package/src/embedded-posthog-key.ts +6 -0
  75. package/src/error-telemetry.ts +247 -0
  76. package/src/evidence.ts +14 -0
  77. package/src/exception-telemetry.ts +126 -0
  78. package/src/fixtures/blocks/call_stack_diff.json +27 -0
  79. package/src/fixtures/blocks/callout.json +11 -0
  80. package/src/fixtures/blocks/code.json +9 -0
  81. package/src/fixtures/blocks/code_peek.json +8 -0
  82. package/src/fixtures/blocks/database_lens.json +51 -0
  83. package/src/fixtures/blocks/divider.json +1 -0
  84. package/src/fixtures/blocks/fixtures.ts +33 -0
  85. package/src/fixtures/blocks/flow_diagram.json +36 -0
  86. package/src/fixtures/blocks/ids.ts +8 -0
  87. package/src/fixtures/blocks/image.json +9 -0
  88. package/src/fixtures/blocks/markdown.json +7 -0
  89. package/src/fixtures/blocks/section.json +11 -0
  90. package/src/fixtures/blocks/sequence.json +31 -0
  91. package/src/fixtures/blocks/software_map.json +7 -0
  92. package/src/fixtures/blocks/trace_quote.json +9 -0
  93. package/src/fixtures/blocks/tutorial.json +51 -0
  94. package/src/fs-utils.ts +32 -0
  95. package/src/fuzzy-match.ts +94 -0
  96. package/src/install.ts +29 -0
  97. package/src/legacy-skills.ts +133 -0
  98. package/src/lens-selection.ts +230 -0
  99. package/src/markdown-latex-math.ts +230 -0
  100. package/src/markdown.ts +81 -0
  101. package/src/package-paths.ts +53 -0
  102. package/src/posthog-capture-client.ts +610 -0
  103. package/src/review-api/README.md +295 -0
  104. package/src/review-api/activity.ts +340 -0
  105. package/src/review-api/agent-cli.ts +219 -0
  106. package/src/review-api/agent-client.ts +188 -0
  107. package/src/review-api/anchor-quotes.ts +88 -0
  108. package/src/review-api/ask-history.ts +242 -0
  109. package/src/review-api/authoring-tools.ts +211 -0
  110. package/src/review-api/blocks/call_stack_diff.ts +75 -0
  111. package/src/review-api/blocks/callout.ts +24 -0
  112. package/src/review-api/blocks/code.ts +9 -0
  113. package/src/review-api/blocks/code_peek.ts +13 -0
  114. package/src/review-api/blocks/database_lens.ts +150 -0
  115. package/src/review-api/blocks/definition.ts +40 -0
  116. package/src/review-api/blocks/divider.ts +6 -0
  117. package/src/review-api/blocks/flow_diagram.ts +111 -0
  118. package/src/review-api/blocks/image.ts +10 -0
  119. package/src/review-api/blocks/index.ts +90 -0
  120. package/src/review-api/blocks/markdown.ts +17 -0
  121. package/src/review-api/blocks/section.ts +24 -0
  122. package/src/review-api/blocks/sequence.ts +56 -0
  123. package/src/review-api/blocks/software_map.ts +9 -0
  124. package/src/review-api/blocks/trace_quote.ts +10 -0
  125. package/src/review-api/blocks/tutorial.ts +39 -0
  126. package/src/review-api/checkout-fs.ts +14 -0
  127. package/src/review-api/client.ts +1 -0
  128. package/src/review-api/comparison-coverage.ts +304 -0
  129. package/src/review-api/component-reference.ts +30 -0
  130. package/src/review-api/diff-lenses.ts +175 -0
  131. package/src/review-api/document-headings.ts +51 -0
  132. package/src/review-api/document-text.ts +200 -0
  133. package/src/review-api/document.ts +906 -0
  134. package/src/review-api/file-lenses.ts +100 -0
  135. package/src/review-api/http.ts +1877 -0
  136. package/src/review-api/image-decode.ts +29 -0
  137. package/src/review-api/input-error.ts +9 -0
  138. package/src/review-api/instructions.ts +89 -0
  139. package/src/review-api/lens-alignment.ts +52 -0
  140. package/src/review-api/local-data.ts +1837 -0
  141. package/src/review-api/map-input.ts +154 -0
  142. package/src/review-api/mcp-client-agent.ts +32 -0
  143. package/src/review-api/mcp.ts +240 -0
  144. package/src/review-api/origin.ts +43 -0
  145. package/src/review-api/profile.ts +146 -0
  146. package/src/review-api/public-tools.ts +102 -0
  147. package/src/review-api/pull-request.ts +389 -0
  148. package/src/review-api/read-schemas.ts +85 -0
  149. package/src/review-api/recovery.ts +2 -0
  150. package/src/review-api/request-origin.ts +33 -0
  151. package/src/review-api/review-progress.ts +382 -0
  152. package/src/review-api/status-tool.ts +10 -0
  153. package/src/review-api/store-schema.ts +22 -0
  154. package/src/review-api/store.ts +1647 -0
  155. package/src/review-api/tool-failure.ts +64 -0
  156. package/src/review-api/trace-schema.ts +13 -0
  157. package/src/review-api/traces.ts +119 -0
  158. package/src/review-api/unsupported-files.integration.ts +91 -0
  159. package/src/review-api/workspaces.ts +692 -0
  160. package/src/review-api/worktree-source.ts +170 -0
  161. package/src/review-api/worktree-structural.integration.ts +320 -0
  162. package/src/review-app-launcher.ts +432 -0
  163. package/src/review-app-picker.ts +168 -0
  164. package/src/review-app.ts +134 -0
  165. package/src/review-bundled-tools.ts +249 -0
  166. package/src/review-checkout-paths.ts +37 -0
  167. package/src/review-diff-files.ts +89 -0
  168. package/src/review-head-checkout.ts +300 -0
  169. package/src/review-home-paths.ts +73 -0
  170. package/src/review-info.ts +59 -0
  171. package/src/review-instances.ts +113 -0
  172. package/src/review-logger.ts +183 -0
  173. package/src/review-preferences.ts +88 -0
  174. package/src/review-prepare.ts +291 -0
  175. package/src/review-stack.ts +112 -0
  176. package/src/review-telemetry.ts +1066 -0
  177. package/src/runtime.ts +74 -0
  178. package/src/server/account-alias.ts +30 -0
  179. package/src/server/bounded-stream.ts +34 -0
  180. package/src/server/bug-report.ts +301 -0
  181. package/src/server/client-error-budget.ts +45 -0
  182. package/src/server/crash-report.ts +273 -0
  183. package/src/server/desktop-host-shutdown.ts +64 -0
  184. package/src/server/desktop-host.ts +190 -0
  185. package/src/server/desktop-server.ts +703 -0
  186. package/src/server/diffr-config.ts +509 -0
  187. package/src/server/diffr-languages.ts +97 -0
  188. package/src/server/global-verb-relay.ts +190 -0
  189. package/src/server/headless-host.ts +149 -0
  190. package/src/server/hono-http.ts +162 -0
  191. package/src/server/http-json.ts +24 -0
  192. package/src/server/json-review-reporting.ts +174 -0
  193. package/src/server/process-error-telemetry.ts +174 -0
  194. package/src/server/review-api-parsers.ts +90 -0
  195. package/src/server/review-lifecycle-telemetry.ts +73 -0
  196. package/src/server/review-open-watchdog.ts +46 -0
  197. package/src/server/review-server-core.ts +290 -0
  198. package/src/server/structural-comparisons.ts +153 -0
  199. package/src/server/structural-diff.ts +186 -0
  200. package/src/server/tutorial-service.ts +241 -0
  201. package/src/server/ui-telemetry.ts +198 -0
  202. package/src/server-discovery.ts +95 -0
  203. package/src/session-markers.ts +132 -0
  204. package/src/sharing/auth.ts +41 -0
  205. package/src/sharing/cli.ts +83 -0
  206. package/src/sharing/client.ts +334 -0
  207. package/src/sharing/export.ts +195 -0
  208. package/src/sharing/host.ts +316 -0
  209. package/src/sharing/import.ts +787 -0
  210. package/src/sharing/index.ts +16 -0
  211. package/src/sharing/repository.ts +145 -0
  212. package/src/sharing/routes.ts +34 -0
  213. package/src/slug.ts +23 -0
  214. package/src/software-map-diff-counts.ts +517 -0
  215. package/src/software-map-model.ts +1147 -0
  216. package/src/software-map-topology-diff.ts +260 -0
  217. package/src/source.ts +92 -0
  218. package/src/startup-trace.ts +232 -0
  219. package/src/stored-document-migration.ts +151 -0
  220. package/src/telemetry-clean-text.ts +257 -0
  221. package/src/telemetry-config.ts +298 -0
  222. package/src/telemetry-debug-sink.ts +38 -0
  223. package/src/telemetry.ts +13 -0
  224. package/src/trace-cli.ts +156 -0
  225. package/src/trace-storage-cli.ts +509 -0
  226. package/src/tutorial-conversation.ts +18 -0
  227. package/src/ui-telemetry-events.ts +765 -0
  228. package/src/unified-diff.ts +71 -0
  229. package/src/viewed-coverage.ts +259 -0
  230. package/src/windows-cli.ts +125 -0
  231. package/tutorial/document.json +275 -0
  232. package/tutorial/runtime-manifest.json +12 -0
  233. package/tutorial/sample-service/package.json +9 -0
  234. package/tutorial/sample-service/src/api/checkout-api.ts +17 -0
  235. package/tutorial/sample-service/src/app.ts +26 -0
  236. package/tutorial/sample-service/src/database/schema.ts +10 -0
  237. package/tutorial/sample-service/src/fulfillment/fulfillment-queue.ts +15 -0
  238. package/tutorial/sample-service/src/fulfillment/fulfillment-worker.ts +24 -0
  239. package/tutorial/sample-service/src/inventory/inventory-service.ts +11 -0
  240. package/tutorial/sample-service/src/orders/order-service.ts +34 -0
  241. package/tutorial/sample-service/src/orders/order.ts +21 -0
  242. package/tutorial/sample-service/src/orders/orders-repository.ts +24 -0
  243. package/tutorial/sample-service/src/payments/payment-gateway.ts +12 -0
  244. package/tutorial/sample-service/src/shipping/shipping-gateway.ts +15 -0
  245. package/tutorial/sample-service/tsconfig.json +12 -0
  246. package/tutorial/software-map.json +139 -0
  247. package/tutorial/trace.json +20 -0
@@ -0,0 +1,811 @@
1
+ import { a as jsonNumber, c as jsonString, i as jsonBoolean, n as isJsonObject } from "./src-X9phtB2j.js";
2
+ import { f as REVIEW_DIFF_LAYOUTS, p as REVIEW_THEME_CHOICES } from "./src-DnwdaQ2r.js";
3
+ import { z } from "zod";
4
+ //#region src/telemetry-clean-text.ts
5
+ const USER_DATA_REGEXES = [
6
+ {
7
+ label: "URL",
8
+ regex: /[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s]*/
9
+ },
10
+ {
11
+ label: "Google API Key",
12
+ regex: /AIza[A-Za-z0-9_\\\-]{35}/
13
+ },
14
+ {
15
+ label: "JWT",
16
+ regex: /eyJ[0eXAiOiJKV1Qi|hbGci|a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+/
17
+ },
18
+ {
19
+ label: "Slack Token",
20
+ regex: /xox[pbar]\-[A-Za-z0-9]/
21
+ },
22
+ {
23
+ label: "GitHub Token",
24
+ regex: /(gh[psuro]_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})/
25
+ },
26
+ {
27
+ label: "Generic Secret",
28
+ regex: /(key|token|sig|secret|signature|password|passwd|pwd|android:value)[^a-zA-Z0-9]/i
29
+ },
30
+ {
31
+ label: "CLI Credentials",
32
+ regex: /((login|psexec|(certutil|psexec)\.exe).{1,50}(\s-u(ser(name)?)?\s+.{3,100})?\s-(admin|user|vm|root)?p(ass(word)?)?\s+["']?[^$\-\/\s]|(^|[\s\r\n\\])net(\.exe)?.{1,5}(user\s+|share\s+\/user:| user -? secrets ? set) \s + [^ $\s \/])/
33
+ },
34
+ {
35
+ label: "Microsoft Entra ID",
36
+ regex: /eyJ(?:0eXAiOiJKV1Qi|hbGci|[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.)/
37
+ },
38
+ {
39
+ label: "Email",
40
+ regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/
41
+ }
42
+ ];
43
+ const REDACTED_PATH_MARKER = "<REDACTED: user-file-path>";
44
+ /**
45
+ * The path shape the cleaner replaces: one or more `segment/` runs, with an
46
+ * optional drive letter, UNC, or `file://` root. Upstream keeps this inline in
47
+ * `anonymizeFilePaths`; it is named here so the allowlist can assert that no
48
+ * path shape survived cleaning.
49
+ */
50
+ const FILE_PATH_PATTERN = /(file:\/\/)?([a-zA-Z]:(\\\\|\\|\/)|(\\\\|\\|\/))?([\w\-\._@]+(\\\\|\\|\/))+[\w\-\._@]*/;
51
+ /**
52
+ * True when the value still holds something path-shaped. Deliberately the same
53
+ * definition the cleaner uses: the point of re-testing is not a second opinion
54
+ * on what a path looks like, it is to catch the cleaner failing to apply its own
55
+ * rule — wrong patterns, wrong order, or an exception swallowed on the way.
56
+ */
57
+ function containsFilePath(value) {
58
+ return FILE_PATH_PATTERN.test(value);
59
+ }
60
+ /**
61
+ * True when the value still holds something the cleaner should have replaced.
62
+ * The event allowlist calls this as its own second check, so it must not depend
63
+ * on the cleaner having run.
64
+ */
65
+ function hasPossibleUserInfo(value) {
66
+ return USER_DATA_REGEXES.some((entry) => entry.regex.test(value));
67
+ }
68
+ /**
69
+ * Cleans a given stack of possible paths
70
+ * @param stack The stack to sanitize
71
+ * @param cleanupPatterns Cleanup patterns to remove from the stack
72
+ * @returns The cleaned stack
73
+ */
74
+ function anonymizeFilePaths(stack, cleanupPatterns) {
75
+ if (!stack || !stack.includes("/") && !stack.includes("\\")) return stack;
76
+ let updatedStack = stack;
77
+ const cleanUpIndexes = [];
78
+ for (const regexp of cleanupPatterns) while (true) {
79
+ const result = regexp.exec(stack);
80
+ if (!result) break;
81
+ cleanUpIndexes.push([result.index, regexp.lastIndex]);
82
+ }
83
+ const nodeModulesRegex = /(?:^|[\\\/])((node_modules|node_modules\.asar)[\\\/].*)$/;
84
+ const fileRegex = /(file:\/\/)?([a-zA-Z]:(\\\\|\\|\/)|(\\\\|\\|\/))?([\w\-\._@]+(\\\\|\\|\/))+[\w\-\._@]*/g;
85
+ let lastIndex = 0;
86
+ updatedStack = "";
87
+ while (true) {
88
+ const result = fileRegex.exec(stack);
89
+ if (!result) break;
90
+ if (!cleanUpIndexes.some(([start, end]) => result.index < end && start < fileRegex.lastIndex)) {
91
+ const nodeModulesMatch = nodeModulesRegex.exec(result[0]);
92
+ if (nodeModulesMatch) updatedStack += stack.substring(lastIndex, result.index) + `${REDACTED_PATH_MARKER}/` + nodeModulesMatch[1];
93
+ else updatedStack += stack.substring(lastIndex, result.index) + REDACTED_PATH_MARKER;
94
+ lastIndex = fileRegex.lastIndex;
95
+ }
96
+ }
97
+ if (lastIndex < stack.length) updatedStack += stack.substring(lastIndex);
98
+ return updatedStack;
99
+ }
100
+ /**
101
+ * Redacts a value if it contains commonly leaked PII.
102
+ * @param value The value returned (as-is) when no PII is detected
103
+ * @param probe The string actually matched against the PII heuristics. Defaults
104
+ * to `value`; callers may pass a value that includes a trailing delimiter (e.g. a
105
+ * newline) so that heuristics relying on a non-alphanumeric boundary match the
106
+ * same way they would against the original whole string.
107
+ * @returns A `<REDACTED: ...>` marker if the probe matched, otherwise `value`
108
+ */
109
+ function redactIfPossibleUserInfo(value, probe = value) {
110
+ for (const secretRegex of USER_DATA_REGEXES) if (secretRegex.regex.test(probe)) return `<REDACTED: ${secretRegex.label}>`;
111
+ return value;
112
+ }
113
+ /**
114
+ * Attempts to remove commonly leaked PII.
115
+ *
116
+ * When a match is found the check is applied per line so that a single suspicious
117
+ * frame (e.g. a stack frame containing a function name such as `getStorageKey`
118
+ * which matches the broad `Generic Secret` heuristic) only redacts that line —
119
+ * replacing it with a `<REDACTED: ...>` marker — instead of wiping the entire
120
+ * multi-line value such as a whole callstack.
121
+ * @param property The property whose offending lines will be replaced with a redaction marker if they contain user data
122
+ * @returns The new value for the property
123
+ */
124
+ function removePropertiesWithPossibleUserInfo(property) {
125
+ if (!property) return property;
126
+ if (!hasPossibleUserInfo(property)) return property;
127
+ if (!property.includes("\n")) return redactIfPossibleUserInfo(property);
128
+ const lines = property.split("\n");
129
+ for (let i = 0; i < lines.length; i++) {
130
+ const probe = i < lines.length - 1 ? lines[i] + "\n" : lines[i];
131
+ lines[i] = redactIfPossibleUserInfo(lines[i], probe);
132
+ }
133
+ return lines.join("\n");
134
+ }
135
+ /**
136
+ * The whole pipeline for one free-text value, in VS Code's order: replace
137
+ * path-shaped runs, delete the known local directories outright, then replace
138
+ * any line holding a secret or address shape.
139
+ */
140
+ function cleanTelemetryText(value, cleanupPatterns) {
141
+ let updated = value.replaceAll("%20", " ");
142
+ updated = anonymizeFilePaths(updated, cleanupPatterns);
143
+ for (const regexp of cleanupPatterns) updated = updated.replace(regexp, "");
144
+ return removePropertiesWithPossibleUserInfo(updated);
145
+ }
146
+ //#endregion
147
+ //#region src/ui-telemetry-events.ts
148
+ const PEEK_VIA = [
149
+ "prose_link",
150
+ "diagram",
151
+ "map",
152
+ "db_lens",
153
+ "call_stack_frame"
154
+ ];
155
+ const PEEK_ROOT_KIND = ["range"];
156
+ const LSP_FEATURE = [
157
+ "hover",
158
+ "goto_definition",
159
+ "peek_definition",
160
+ "goto_type_definition",
161
+ "goto_implementation",
162
+ "references",
163
+ "rename",
164
+ "format",
165
+ "code_action",
166
+ "symbol_search"
167
+ ];
168
+ const LSP_VIA = ["command", "mouse"];
169
+ const LSP_EDITOR_KIND = [
170
+ "files_tab",
171
+ "inline_peek",
172
+ "diff"
173
+ ];
174
+ const LSP_LANGUAGE = [
175
+ "typescript",
176
+ "javascript",
177
+ "python",
178
+ "go",
179
+ "rust",
180
+ "swift",
181
+ "csharp",
182
+ "json",
183
+ "css",
184
+ "html",
185
+ "markdown",
186
+ "yaml",
187
+ "toml",
188
+ "shell",
189
+ "sql",
190
+ "other"
191
+ ];
192
+ const LS_GROUP = [
193
+ "python",
194
+ "go",
195
+ "rust",
196
+ "swift",
197
+ "csharp"
198
+ ];
199
+ const EXTENSION_ID = [
200
+ "vscodevim.vim",
201
+ "tuttieee.emacs-mcx",
202
+ "ms-vscode.sublime-keybindings",
203
+ "ms-python.python",
204
+ "astral-sh.ty",
205
+ "charliermarsh.ruff",
206
+ "golang.go",
207
+ "rust-lang.rust-analyzer",
208
+ "swiftlang.swift-vscode",
209
+ "llvm-vs-code-extensions.lldb-dap",
210
+ "muhammad-sammy.csharp",
211
+ "ms-dotnettools.vscode-dotnet-runtime"
212
+ ];
213
+ const EXTENSION_TRIGGER = [
214
+ "user",
215
+ "auto_upgrade",
216
+ "startup_seed",
217
+ "keymap",
218
+ "rollback"
219
+ ];
220
+ const EXTENSION_INSTALL_PHASE = ["download", "install"];
221
+ const SETTING_NAME = [
222
+ "telemetry_enabled",
223
+ "keymap",
224
+ "software_map_enabled",
225
+ "scratchpad_enabled",
226
+ "diffr_config",
227
+ "structural_diff",
228
+ "theme",
229
+ "ctrl_tab",
230
+ "ready_notification",
231
+ "document_width"
232
+ ];
233
+ const REVIEW_OPENED_VIA = [
234
+ "home",
235
+ "cli",
236
+ "other"
237
+ ];
238
+ /** Where a review's source comes from: its target's kind, or the one scratchpad. */
239
+ const SESSION_SOURCE_KIND = [
240
+ "worktree",
241
+ "commits",
242
+ "scratchpad"
243
+ ];
244
+ /**
245
+ * The authoring agent a review session is attributed to. This is the one
246
+ * definition: `review-telemetry.ts`'s `ReviewSessionAgent` type imports and
247
+ * derives from it instead of redeclaring the list.
248
+ */
249
+ const SESSION_AGENT_KIND = [
250
+ "codex",
251
+ "claude",
252
+ "cursor",
253
+ "opencode",
254
+ "pi",
255
+ "omp",
256
+ "other"
257
+ ];
258
+ /**
259
+ * How a review session ends. `abnormal` is reported on the next launch for a
260
+ * session the previous process never closed. This is the one definition:
261
+ * `review-telemetry.ts`'s `ReviewSessionOutcome` type imports and derives from
262
+ * it instead of redeclaring the list.
263
+ */
264
+ const SESSION_OUTCOME = [
265
+ "closed",
266
+ "dismissed",
267
+ "deleted",
268
+ "app_quit",
269
+ "abnormal"
270
+ ];
271
+ const REVIEW_DISMISSED_VIA = ["review_topbar", "home"];
272
+ const REVIEW_RESTORED_VIA = ["home", "open"];
273
+ const MAP_LEVEL = [
274
+ "system",
275
+ "container",
276
+ "component",
277
+ "code"
278
+ ];
279
+ const SOURCE_TREE_OPENED_VIA = ["topbar", "home"];
280
+ /** Tab names the dwell beacon reports. Shared with its request parser. */
281
+ const REVIEW_TELEMETRY_TABS = [
282
+ "review",
283
+ "commits",
284
+ "map",
285
+ "files",
286
+ "trace"
287
+ ];
288
+ const COMMIT_DIFF_VIA = [
289
+ "row",
290
+ "file",
291
+ "footer"
292
+ ];
293
+ const CLIENT_ERROR_SOURCE = [
294
+ "window",
295
+ "worker",
296
+ "fetch",
297
+ "render",
298
+ "document",
299
+ "renderer_unexpected",
300
+ "main_unexpected",
301
+ "bootstrap",
302
+ "settings",
303
+ "server_unexpected"
304
+ ];
305
+ /** Which process died. "unknown" is a minidump the live listeners did not see. */
306
+ const CRASH_PROCESS = [
307
+ "renderer",
308
+ "gpu",
309
+ "utility",
310
+ "server",
311
+ "unknown"
312
+ ];
313
+ const CRASH_SOURCE = ["live", "minidump"];
314
+ /** Where a packaged macOS build runs from; Squirrel cannot update `volume` or `translocated`. */
315
+ const INSTALL_LOCATION = [
316
+ "applications",
317
+ "user_applications",
318
+ "volume",
319
+ "translocated",
320
+ "other"
321
+ ];
322
+ const DIFF_OPENED_VIA = [
323
+ "topbar",
324
+ "lens",
325
+ "locate"
326
+ ];
327
+ /** Which diff opened: a commit's, or a file diff drawn plain or structural. */
328
+ const DIFF_OPENED_KIND = [
329
+ "commit",
330
+ "file",
331
+ "structural"
332
+ ];
333
+ const DISCORD_VIA = [
334
+ "topbar",
335
+ "dialog",
336
+ "docs"
337
+ ];
338
+ const REVIEW_DELETED_VIA = ["home"];
339
+ const ERROR_PROCESS = [
340
+ "main",
341
+ "renderer",
342
+ "canvas",
343
+ "server"
344
+ ];
345
+ const UPDATE_FAILURE_PHASE = [
346
+ "check",
347
+ "download",
348
+ "install"
349
+ ];
350
+ const UPDATE_MESSAGE_SOURCE = [
351
+ "electron",
352
+ "request",
353
+ "shipit",
354
+ "fallback"
355
+ ];
356
+ /**
357
+ * Directories that exist only in the shipped Review bundle. A stack frame is
358
+ * reportable when — and only when — it has already been rewritten to start at
359
+ * one of these, which is what strips the user's home directory off the front.
360
+ * See error-telemetry.ts; this table re-checks the result independently.
361
+ */
362
+ const BUNDLE_FRAME_ROOTS = [
363
+ "vs/review/",
364
+ "vs/workbench/",
365
+ "vs/platform/",
366
+ "vs/base/",
367
+ "vs/code/",
368
+ "vs/editor/",
369
+ "assets/",
370
+ "review-runtime/"
371
+ ];
372
+ /**
373
+ * The Electron entry files, which sit beside those directories rather than
374
+ * inside one. A crash before the app can start lands here and nowhere else, so
375
+ * without this list the report that matters most carries no location at all.
376
+ * The list is closed for the same reason the directories are.
377
+ */
378
+ const BUNDLE_FRAME_ENTRY_FILES = [
379
+ "main.js",
380
+ "cli.js",
381
+ "bootstrap-cli.js",
382
+ "bootstrap-esm.js",
383
+ "bootstrap-fork.js",
384
+ "bootstrap-meta.js",
385
+ "bootstrap-node.js"
386
+ ];
387
+ /**
388
+ * One normalized stack frame: a bundle-relative path, then `:line:col`. The
389
+ * character class is deliberately narrow — anything outside it could carry user
390
+ * content — and there is no function name, because the file already identifies
391
+ * the frame and a minified name adds surface for nothing.
392
+ */
393
+ const escapeForPattern = (value) => value.replaceAll(".", "\\.").replaceAll("/", "\\/");
394
+ const BUNDLE_FRAME_PATTERN = new RegExp("^(?:" + [`(?:${BUNDLE_FRAME_ROOTS.map(escapeForPattern).join("|")})[A-Za-z0-9_./-]*`, `(?:${BUNDLE_FRAME_ENTRY_FILES.map(escapeForPattern).join("|")})`].join("|") + "):\\d{1,9}:\\d{1,9}$");
395
+ const MAX_BUNDLE_FRAMES = 10;
396
+ const MAX_BUNDLE_FRAMES_LENGTH = 1024;
397
+ const HASH_HEX_PATTERN = /^[0-9a-f]{16}$/;
398
+ const MAX_CLEANED_MESSAGE_LENGTH = 300;
399
+ /** A marker the cleaner writes, e.g. `<REDACTED: user-file-path>`. */
400
+ const REDACTION_MARKER_PATTERN = /<REDACTED: [A-Za-z][A-Za-z0-9 -]*>/g;
401
+ /**
402
+ * Header the canvas app attaches to review API requests so server-side events
403
+ * can share the UI session id. The CORS preflight allowlist in
404
+ * server/hono-http.ts must permit every header named here — the canvas runs on
405
+ * a vscode-file:// origin, so a header missing from that list fails the
406
+ * preflight and takes the whole request down with it.
407
+ */
408
+ const REVIEW_APP_SESSION_ID_HEADER = "x-review-app-session-id";
409
+ const UI_TELEMETRY_EVENTS = {
410
+ app_opened: {
411
+ event: "review_app_opened",
412
+ properties: {}
413
+ },
414
+ peek_opened: {
415
+ event: "review_peek_opened",
416
+ properties: { via: PEEK_VIA }
417
+ },
418
+ tour_started: {
419
+ event: "review_tour_started",
420
+ properties: { steps: "number" }
421
+ },
422
+ tour_completed: {
423
+ event: "review_tour_completed",
424
+ properties: { steps: "number" }
425
+ },
426
+ map_expanded: {
427
+ event: "review_map_expanded",
428
+ properties: { level: MAP_LEVEL }
429
+ },
430
+ commit_expanded: {
431
+ event: "review_commit_expanded",
432
+ properties: { expanded: "boolean" }
433
+ },
434
+ commit_diff_opened: {
435
+ event: "review_commit_diff_opened",
436
+ properties: { via: COMMIT_DIFF_VIA }
437
+ },
438
+ source_tree_opened: {
439
+ event: "review_source_tree_opened",
440
+ properties: { via: SOURCE_TREE_OPENED_VIA }
441
+ },
442
+ diff_layout_changed: {
443
+ event: "review_diff_layout_changed",
444
+ properties: { layout: REVIEW_DIFF_LAYOUTS }
445
+ },
446
+ review_dismissed: {
447
+ event: "review_review_dismissed",
448
+ properties: { via: REVIEW_DISMISSED_VIA }
449
+ },
450
+ review_restored: {
451
+ event: "review_review_restored",
452
+ properties: { via: REVIEW_RESTORED_VIA }
453
+ },
454
+ client_error: {
455
+ event: "review_client_error",
456
+ properties: {
457
+ error_source: CLIENT_ERROR_SOURCE,
458
+ error_process: ERROR_PROCESS,
459
+ error_name: "enum_free_short",
460
+ component: "enum_free_short",
461
+ message: "cleaned_message",
462
+ message_hash: "hash_hex",
463
+ frames: "bundle_frames"
464
+ }
465
+ },
466
+ update_started: {
467
+ event: "review_update_started",
468
+ properties: {
469
+ update_attempt_id: "opaque_id",
470
+ target_version: "release_version"
471
+ }
472
+ },
473
+ update_completed: {
474
+ event: "review_update_completed",
475
+ properties: {
476
+ update_attempt_id: "opaque_id",
477
+ target_version: "release_version",
478
+ duration_ms: "number"
479
+ }
480
+ },
481
+ update_failed: {
482
+ event: "review_update_failed",
483
+ properties: {
484
+ phase: UPDATE_FAILURE_PHASE,
485
+ message_source: UPDATE_MESSAGE_SOURCE,
486
+ update_attempt_id: "opaque_id",
487
+ target_version: "release_version",
488
+ duration_ms: "number",
489
+ error_name: "enum_free_short",
490
+ message: "cleaned_message",
491
+ message_hash: "hash_hex"
492
+ }
493
+ },
494
+ lsp_used: {
495
+ event: "review_lsp_used",
496
+ properties: {
497
+ feature: LSP_FEATURE,
498
+ via: LSP_VIA,
499
+ language: LSP_LANGUAGE,
500
+ editor_kind: LSP_EDITOR_KIND
501
+ }
502
+ },
503
+ ls_activated: {
504
+ event: "review_ls_activated",
505
+ properties: {
506
+ group: LS_GROUP,
507
+ ok: "boolean"
508
+ }
509
+ },
510
+ extension_installed: {
511
+ event: "review_extension_installed",
512
+ properties: {
513
+ extension_id: EXTENSION_ID,
514
+ trigger: EXTENSION_TRIGGER,
515
+ cached: "boolean",
516
+ duration_ms: "number"
517
+ }
518
+ },
519
+ extension_install_failed: {
520
+ event: "review_extension_install_failed",
521
+ properties: {
522
+ extension_id: EXTENSION_ID,
523
+ trigger: EXTENSION_TRIGGER,
524
+ phase: EXTENSION_INSTALL_PHASE
525
+ }
526
+ },
527
+ extension_enabled: {
528
+ event: "review_extension_enabled",
529
+ properties: {
530
+ extension_id: EXTENSION_ID,
531
+ trigger: EXTENSION_TRIGGER
532
+ }
533
+ },
534
+ extension_disabled: {
535
+ event: "review_extension_disabled",
536
+ properties: {
537
+ extension_id: EXTENSION_ID,
538
+ trigger: EXTENSION_TRIGGER
539
+ }
540
+ },
541
+ extension_uninstalled: {
542
+ event: "review_extension_uninstalled",
543
+ properties: {
544
+ extension_id: EXTENSION_ID,
545
+ trigger: EXTENSION_TRIGGER
546
+ }
547
+ },
548
+ tour_step_advanced: {
549
+ event: "review_tour_step_advanced",
550
+ properties: {
551
+ step: "number",
552
+ steps: "number"
553
+ }
554
+ },
555
+ tour_abandoned: {
556
+ event: "review_tour_abandoned",
557
+ properties: {
558
+ step: "number",
559
+ steps: "number"
560
+ }
561
+ },
562
+ bug_report_dialog_opened: {
563
+ event: "review_bug_report_dialog_opened",
564
+ properties: {}
565
+ },
566
+ bug_report_cancelled: {
567
+ event: "review_bug_report_cancelled",
568
+ properties: {}
569
+ },
570
+ bug_report_send_failed: {
571
+ event: "review_bug_report_send_failed",
572
+ properties: { error_name: "enum_free_short" }
573
+ },
574
+ setting_changed: {
575
+ event: "review_setting_changed",
576
+ properties: {
577
+ setting: SETTING_NAME,
578
+ enabled: "boolean",
579
+ value: REVIEW_THEME_CHOICES
580
+ }
581
+ },
582
+ review_opened: {
583
+ event: "review_review_opened",
584
+ properties: { via: REVIEW_OPENED_VIA }
585
+ },
586
+ session_started: {
587
+ event: "review_session_started",
588
+ properties: {
589
+ source_kind: SESSION_SOURCE_KIND,
590
+ agent_kind: SESSION_AGENT_KIND
591
+ }
592
+ },
593
+ session_ended: {
594
+ event: "review_session_ended",
595
+ properties: {
596
+ outcome: SESSION_OUTCOME,
597
+ duration_ms: "number"
598
+ }
599
+ },
600
+ review_presented: {
601
+ event: "review_review_presented",
602
+ properties: { load_ms: "number" }
603
+ },
604
+ home_empty_state_viewed: {
605
+ event: "review_home_empty_state_viewed",
606
+ properties: {}
607
+ },
608
+ crash: {
609
+ event: "review_crash",
610
+ properties: {
611
+ process: CRASH_PROCESS,
612
+ reason: "enum_free_short",
613
+ exit_code: "number",
614
+ uptime_ms: "number",
615
+ source: CRASH_SOURCE
616
+ }
617
+ },
618
+ hang_started: {
619
+ event: "review_hang_started",
620
+ properties: {}
621
+ },
622
+ hang_ended: {
623
+ event: "review_hang_ended",
624
+ properties: { duration_ms: "number" }
625
+ },
626
+ app_ready: {
627
+ event: "review_app_ready",
628
+ properties: {
629
+ duration_ms: "number",
630
+ install_location: INSTALL_LOCATION
631
+ }
632
+ },
633
+ error_burst: {
634
+ event: "review_error_burst",
635
+ properties: {
636
+ message_hash: "hash_hex",
637
+ suppressed: "number"
638
+ }
639
+ },
640
+ diff_opened: {
641
+ event: "review_diff_opened",
642
+ properties: {
643
+ kind: DIFF_OPENED_KIND,
644
+ via: DIFF_OPENED_VIA
645
+ }
646
+ },
647
+ scratchpad_opened: {
648
+ event: "review_scratchpad_opened",
649
+ properties: {}
650
+ },
651
+ discord_clicked: {
652
+ event: "review_discord_clicked",
653
+ properties: { via: DISCORD_VIA }
654
+ },
655
+ discord_dialog_shown: {
656
+ event: "review_discord_dialog_shown",
657
+ properties: {}
658
+ },
659
+ discord_dialog_dismissed: {
660
+ event: "review_discord_dialog_dismissed",
661
+ properties: {}
662
+ },
663
+ peek_resolved: {
664
+ event: "review_peek_resolved",
665
+ properties: { root_kind: PEEK_ROOT_KIND }
666
+ },
667
+ peek_resolve_failed: {
668
+ event: "review_peek_resolve_failed",
669
+ properties: { root_kind: PEEK_ROOT_KIND }
670
+ },
671
+ review_deleted: {
672
+ event: "review_review_deleted",
673
+ properties: { via: REVIEW_DELETED_VIA }
674
+ },
675
+ review_shared: {
676
+ event: "review_review_shared",
677
+ properties: {}
678
+ }
679
+ };
680
+ const MAX_FREE_STRING_LENGTH = 40;
681
+ const FREE_STRING_PATTERN = /^[A-Za-z0-9_$-]+$/;
682
+ const OPAQUE_ID_PATTERN = /^[A-Za-z0-9_-]{8,64}$/;
683
+ const RELEASE_VERSION_PATTERN = /^\d{1,10}\.\d{1,10}\.\d{1,10}(?:[-+][0-9A-Za-z.-]{1,40})?$/;
684
+ const COMMON_PROPERTIES = { app_session_id: "opaque_id" };
685
+ function isUiTelemetryEventName(name) {
686
+ return Object.hasOwn(UI_TELEMETRY_EVENTS, name);
687
+ }
688
+ /**
689
+ * Validate a raw UI event payload against the allowlist. Returns the PostHog
690
+ * event name plus only the sanctioned properties, or null when the event
691
+ * itself is unknown. Unknown keys and invalid values are silently dropped —
692
+ * never forwarded.
693
+ */
694
+ function sanitizeUiTelemetryEvent(input) {
695
+ const name = z.string().safeParse(input.name);
696
+ if (!name.success || !isUiTelemetryEventName(name.data)) return null;
697
+ const spec = UI_TELEMETRY_EVENTS[name.data];
698
+ const raw = isJsonObject(input.properties) ? input.properties : {};
699
+ const properties = {};
700
+ const propertySpecs = {
701
+ ...COMMON_PROPERTIES,
702
+ ...spec.properties
703
+ };
704
+ for (const [key, propSpec] of Object.entries(propertySpecs)) {
705
+ const value = raw[key];
706
+ if (value === void 0 || value === null) continue;
707
+ const text = jsonString(value);
708
+ if (propSpec === "number") {
709
+ const number = jsonNumber(value);
710
+ if (number !== void 0) properties[key] = number;
711
+ continue;
712
+ }
713
+ if (propSpec === "boolean") {
714
+ const boolean = jsonBoolean(value);
715
+ if (boolean !== void 0) properties[key] = boolean;
716
+ continue;
717
+ }
718
+ if (propSpec === "opaque_id") {
719
+ if (isValidReviewAppSessionId(text)) properties[key] = text;
720
+ continue;
721
+ }
722
+ if (propSpec === "release_version") {
723
+ if (text !== void 0 && RELEASE_VERSION_PATTERN.test(text)) properties[key] = text;
724
+ continue;
725
+ }
726
+ if (propSpec === "enum_free_short") {
727
+ if (text !== void 0 && text.length <= MAX_FREE_STRING_LENGTH && FREE_STRING_PATTERN.test(text)) properties[key] = text;
728
+ continue;
729
+ }
730
+ if (propSpec === "hash_hex") {
731
+ if (text !== void 0 && HASH_HEX_PATTERN.test(text)) properties[key] = text;
732
+ continue;
733
+ }
734
+ if (propSpec === "bundle_frames") {
735
+ const frames = sanitizeBundleFrames(text);
736
+ if (frames) properties[key] = frames;
737
+ continue;
738
+ }
739
+ if (propSpec === "cleaned_message") {
740
+ if (text !== void 0 && isReportableCleanedMessage(text)) properties[key] = text;
741
+ continue;
742
+ }
743
+ if (text !== void 0 && propSpec.includes(text)) properties[key] = text;
744
+ }
745
+ return {
746
+ event: spec.event,
747
+ properties
748
+ };
749
+ }
750
+ /**
751
+ * The independent second check on a cleaned error message.
752
+ *
753
+ * It never repairs the message. Either the cleaner finished the job or the
754
+ * message is dropped and only the digest goes, so a bug in the cleaner cannot
755
+ * by itself put a path on the wire.
756
+ *
757
+ * The path rule is the load-bearing one: the cleaner replaces every path-shaped
758
+ * run with a marker, so a surviving path shape means something got past it.
759
+ */
760
+ function isReportableCleanedMessage(value) {
761
+ if (value.length === 0 || value.length > MAX_CLEANED_MESSAGE_LENGTH) return false;
762
+ const remainder = value.replaceAll(REDACTION_MARKER_PATTERN, " ");
763
+ return !containsFilePath(remainder) && !hasPossibleUserInfo(remainder);
764
+ }
765
+ /**
766
+ * Keep only the frames that match BUNDLE_FRAME_PATTERN. Frames arrive already
767
+ * normalized; this is the independent second check, so it never repairs a frame
768
+ * — it drops it. Returns undefined when nothing survives.
769
+ */
770
+ function sanitizeBundleFrames(value) {
771
+ if (value === void 0) return void 0;
772
+ const kept = [];
773
+ let length = 0;
774
+ for (const frame of value.split("|")) {
775
+ if (!BUNDLE_FRAME_PATTERN.test(frame)) continue;
776
+ const next = length + frame.length + (kept.length > 0 ? 1 : 0);
777
+ if (next > MAX_BUNDLE_FRAMES_LENGTH) break;
778
+ kept.push(frame);
779
+ length = next;
780
+ if (kept.length >= MAX_BUNDLE_FRAMES) break;
781
+ }
782
+ return kept.length > 0 ? kept.join("|") : void 0;
783
+ }
784
+ function isValidReviewAppSessionId(value) {
785
+ return value !== void 0 && OPAQUE_ID_PATTERN.test(value);
786
+ }
787
+ //#endregion
788
+ //#region src/review-api/request-origin.ts
789
+ /**
790
+ * Headers the agent CLI sets so the server can say where a review came from.
791
+ * Both are telemetry only and parse to closed enums: anything else a caller
792
+ * sends becomes "other".
793
+ */
794
+ const REVIEW_VIA_HEADER = "x-review-via";
795
+ const REVIEW_AGENT_HEADER = "x-review-agent";
796
+ const viaSchema = z.enum([
797
+ "api",
798
+ "mcp",
799
+ "other"
800
+ ]).catch("other");
801
+ const agentSchema = z.enum(SESSION_AGENT_KIND).optional().catch("other");
802
+ function reviewRequestOrigin(headers) {
803
+ const via = viaSchema.parse(headers.get(REVIEW_VIA_HEADER));
804
+ const agentKind = agentSchema.parse(headers.get("x-review-agent") ?? void 0);
805
+ return agentKind ? {
806
+ via,
807
+ agentKind
808
+ } : { via };
809
+ }
810
+ //#endregion
811
+ export { REVIEW_APP_SESSION_ID_HEADER as a, sanitizeUiTelemetryEvent as c, BUNDLE_FRAME_PATTERN as i, cleanTelemetryText as l, REVIEW_VIA_HEADER as n, REVIEW_TELEMETRY_TABS as o, reviewRequestOrigin as r, isReportableCleanedMessage as s, REVIEW_AGENT_HEADER as t };