xtctx 0.3.2 → 0.11.3

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 (308) hide show
  1. package/CHANGELOG.md +317 -122
  2. package/LICENSE +21 -21
  3. package/README.md +154 -121
  4. package/dist/src/cli/disconnect.d.ts +8 -0
  5. package/dist/src/cli/disconnect.d.ts.map +1 -0
  6. package/dist/src/cli/disconnect.js +42 -0
  7. package/dist/src/cli/disconnect.js.map +1 -0
  8. package/dist/src/cli/hook.d.ts +7 -0
  9. package/dist/src/cli/hook.d.ts.map +1 -0
  10. package/dist/src/cli/hook.js +32 -0
  11. package/dist/src/cli/hook.js.map +1 -0
  12. package/dist/src/cli/index.d.ts.map +1 -1
  13. package/dist/src/cli/index.js +86 -33
  14. package/dist/src/cli/index.js.map +1 -1
  15. package/dist/src/cli/setup.d.ts +8 -0
  16. package/dist/src/cli/setup.d.ts.map +1 -0
  17. package/dist/src/cli/setup.js +76 -0
  18. package/dist/src/cli/setup.js.map +1 -0
  19. package/dist/src/cli/status.d.ts +7 -0
  20. package/dist/src/cli/status.d.ts.map +1 -0
  21. package/dist/src/cli/status.js +133 -0
  22. package/dist/src/cli/status.js.map +1 -0
  23. package/dist/src/config/disconnect.d.ts +34 -0
  24. package/dist/src/config/disconnect.d.ts.map +1 -0
  25. package/dist/src/config/disconnect.js +386 -0
  26. package/dist/src/config/disconnect.js.map +1 -0
  27. package/dist/src/config/managed-block.d.ts +29 -0
  28. package/dist/src/config/managed-block.d.ts.map +1 -0
  29. package/dist/src/config/managed-block.js +67 -0
  30. package/dist/src/config/managed-block.js.map +1 -0
  31. package/dist/src/config/mcp-config.d.ts +93 -0
  32. package/dist/src/config/mcp-config.d.ts.map +1 -0
  33. package/dist/src/config/mcp-config.js +556 -0
  34. package/dist/src/config/mcp-config.js.map +1 -0
  35. package/dist/src/config/setup.d.ts +39 -0
  36. package/dist/src/config/setup.d.ts.map +1 -0
  37. package/dist/src/config/setup.js +379 -0
  38. package/dist/src/config/setup.js.map +1 -0
  39. package/dist/src/config/skills.d.ts +73 -0
  40. package/dist/src/config/skills.d.ts.map +1 -0
  41. package/dist/src/config/skills.js +427 -0
  42. package/dist/src/config/skills.js.map +1 -0
  43. package/dist/src/handoff/embeddings.d.ts +26 -0
  44. package/dist/src/handoff/embeddings.d.ts.map +1 -0
  45. package/dist/src/handoff/embeddings.js +132 -0
  46. package/dist/src/handoff/embeddings.js.map +1 -0
  47. package/dist/src/handoff/sqlite-index.d.ts +52 -0
  48. package/dist/src/handoff/sqlite-index.d.ts.map +1 -0
  49. package/dist/src/handoff/sqlite-index.js +905 -0
  50. package/dist/src/handoff/sqlite-index.js.map +1 -0
  51. package/dist/src/handoff/types.d.ts +63 -0
  52. package/dist/src/handoff/types.d.ts.map +1 -0
  53. package/dist/src/handoff/types.js +2 -0
  54. package/dist/src/handoff/types.js.map +1 -0
  55. package/dist/src/handoff/vector.d.ts +4 -0
  56. package/dist/src/handoff/vector.d.ts.map +1 -0
  57. package/dist/src/handoff/vector.js +31 -0
  58. package/dist/src/handoff/vector.js.map +1 -0
  59. package/dist/src/mcp/server.d.ts +3 -20
  60. package/dist/src/mcp/server.d.ts.map +1 -1
  61. package/dist/src/mcp/server.js +90 -205
  62. package/dist/src/mcp/server.js.map +1 -1
  63. package/dist/src/mcp/tools/continuity.d.ts +21 -0
  64. package/dist/src/mcp/tools/continuity.d.ts.map +1 -0
  65. package/dist/src/mcp/tools/continuity.js +55 -0
  66. package/dist/src/mcp/tools/continuity.js.map +1 -0
  67. package/dist/src/mcp/tools/manifest.d.ts +40 -0
  68. package/dist/src/mcp/tools/manifest.d.ts.map +1 -0
  69. package/dist/src/mcp/tools/manifest.js +105 -0
  70. package/dist/src/mcp/tools/manifest.js.map +1 -0
  71. package/dist/src/mcp/tools/sessions.d.ts +18 -31
  72. package/dist/src/mcp/tools/sessions.d.ts.map +1 -1
  73. package/dist/src/mcp/tools/sessions.js +109 -16
  74. package/dist/src/mcp/tools/sessions.js.map +1 -1
  75. package/dist/src/runtime/services.d.ts +11 -38
  76. package/dist/src/runtime/services.d.ts.map +1 -1
  77. package/dist/src/runtime/services.js +61 -200
  78. package/dist/src/runtime/services.js.map +1 -1
  79. package/dist/src/scrapers/antigravity.d.ts +52 -0
  80. package/dist/src/scrapers/antigravity.d.ts.map +1 -0
  81. package/dist/src/scrapers/antigravity.js +869 -0
  82. package/dist/src/scrapers/antigravity.js.map +1 -0
  83. package/dist/src/scrapers/base.d.ts +53 -1
  84. package/dist/src/scrapers/base.d.ts.map +1 -1
  85. package/dist/src/scrapers/base.js +93 -4
  86. package/dist/src/scrapers/base.js.map +1 -1
  87. package/dist/src/scrapers/claude-code.d.ts +37 -6
  88. package/dist/src/scrapers/claude-code.d.ts.map +1 -1
  89. package/dist/src/scrapers/claude-code.js +214 -40
  90. package/dist/src/scrapers/claude-code.js.map +1 -1
  91. package/dist/src/scrapers/codex.d.ts +36 -6
  92. package/dist/src/scrapers/codex.d.ts.map +1 -1
  93. package/dist/src/scrapers/codex.js +283 -60
  94. package/dist/src/scrapers/codex.js.map +1 -1
  95. package/dist/src/scrapers/copilot-cli.d.ts +34 -0
  96. package/dist/src/scrapers/copilot-cli.d.ts.map +1 -0
  97. package/dist/src/scrapers/copilot-cli.js +342 -0
  98. package/dist/src/scrapers/copilot-cli.js.map +1 -0
  99. package/dist/src/scrapers/copilot.d.ts +41 -8
  100. package/dist/src/scrapers/copilot.d.ts.map +1 -1
  101. package/dist/src/scrapers/copilot.js +256 -118
  102. package/dist/src/scrapers/copilot.js.map +1 -1
  103. package/dist/src/scrapers/cursor.d.ts +24 -9
  104. package/dist/src/scrapers/cursor.d.ts.map +1 -1
  105. package/dist/src/scrapers/cursor.js +259 -152
  106. package/dist/src/scrapers/cursor.js.map +1 -1
  107. package/dist/src/scrapers/index.d.ts +23 -0
  108. package/dist/src/scrapers/index.d.ts.map +1 -0
  109. package/dist/src/scrapers/index.js +22 -0
  110. package/dist/src/scrapers/index.js.map +1 -0
  111. package/dist/src/scrapers/opencode.d.ts +37 -0
  112. package/dist/src/scrapers/opencode.d.ts.map +1 -0
  113. package/dist/src/scrapers/opencode.js +277 -0
  114. package/dist/src/scrapers/opencode.js.map +1 -0
  115. package/dist/src/scrapers/registry.d.ts +1 -0
  116. package/dist/src/scrapers/registry.d.ts.map +1 -1
  117. package/dist/src/scrapers/registry.js +3 -0
  118. package/dist/src/scrapers/registry.js.map +1 -1
  119. package/dist/src/tools/sources.d.ts +28 -0
  120. package/dist/src/tools/sources.d.ts.map +1 -0
  121. package/dist/src/tools/sources.js +149 -0
  122. package/dist/src/tools/sources.js.map +1 -0
  123. package/dist/src/types/config.d.ts +24 -37
  124. package/dist/src/types/config.d.ts.map +1 -1
  125. package/dist/src/types/index.d.ts +0 -2
  126. package/dist/src/types/index.d.ts.map +1 -1
  127. package/dist/src/types/index.js +0 -2
  128. package/dist/src/types/index.js.map +1 -1
  129. package/dist/src/types/scraper.d.ts +34 -5
  130. package/dist/src/types/scraper.d.ts.map +1 -1
  131. package/dist/src/utils/atomic-file.d.ts +8 -0
  132. package/dist/src/utils/atomic-file.d.ts.map +1 -0
  133. package/dist/src/utils/atomic-file.js +15 -0
  134. package/dist/src/utils/atomic-file.js.map +1 -0
  135. package/dist/src/utils/errors.d.ts +7 -0
  136. package/dist/src/utils/errors.d.ts.map +1 -0
  137. package/dist/src/utils/errors.js +16 -0
  138. package/dist/src/utils/errors.js.map +1 -0
  139. package/dist/src/utils/package-info.d.ts +11 -0
  140. package/dist/src/utils/package-info.d.ts.map +1 -0
  141. package/dist/src/utils/package-info.js +30 -0
  142. package/dist/src/utils/package-info.js.map +1 -0
  143. package/dist/src/utils/project-scope.d.ts +19 -0
  144. package/dist/src/utils/project-scope.d.ts.map +1 -0
  145. package/dist/src/utils/project-scope.js +61 -0
  146. package/dist/src/utils/project-scope.js.map +1 -0
  147. package/package.json +70 -66
  148. package/dist/src/api/routes/config.d.ts +0 -4
  149. package/dist/src/api/routes/config.d.ts.map +0 -1
  150. package/dist/src/api/routes/config.js +0 -61
  151. package/dist/src/api/routes/config.js.map +0 -1
  152. package/dist/src/api/routes/knowledge.d.ts +0 -4
  153. package/dist/src/api/routes/knowledge.d.ts.map +0 -1
  154. package/dist/src/api/routes/knowledge.js +0 -40
  155. package/dist/src/api/routes/knowledge.js.map +0 -1
  156. package/dist/src/api/routes/search.d.ts +0 -7
  157. package/dist/src/api/routes/search.d.ts.map +0 -1
  158. package/dist/src/api/routes/search.js +0 -108
  159. package/dist/src/api/routes/search.js.map +0 -1
  160. package/dist/src/api/routes/sources.d.ts +0 -12
  161. package/dist/src/api/routes/sources.d.ts.map +0 -1
  162. package/dist/src/api/routes/sources.js +0 -70
  163. package/dist/src/api/routes/sources.js.map +0 -1
  164. package/dist/src/api/server.d.ts +0 -26
  165. package/dist/src/api/server.d.ts.map +0 -1
  166. package/dist/src/api/server.js +0 -312
  167. package/dist/src/api/server.js.map +0 -1
  168. package/dist/src/cli/ingest.d.ts +0 -6
  169. package/dist/src/cli/ingest.d.ts.map +0 -1
  170. package/dist/src/cli/ingest.js +0 -11
  171. package/dist/src/cli/ingest.js.map +0 -1
  172. package/dist/src/cli/init.d.ts +0 -6
  173. package/dist/src/cli/init.d.ts.map +0 -1
  174. package/dist/src/cli/init.js +0 -105
  175. package/dist/src/cli/init.js.map +0 -1
  176. package/dist/src/cli/serve.d.ts +0 -7
  177. package/dist/src/cli/serve.d.ts.map +0 -1
  178. package/dist/src/cli/serve.js +0 -100
  179. package/dist/src/cli/serve.js.map +0 -1
  180. package/dist/src/cli/sync.d.ts +0 -5
  181. package/dist/src/cli/sync.d.ts.map +0 -1
  182. package/dist/src/cli/sync.js +0 -13
  183. package/dist/src/cli/sync.js.map +0 -1
  184. package/dist/src/compaction/pipeline.d.ts +0 -19
  185. package/dist/src/compaction/pipeline.d.ts.map +0 -1
  186. package/dist/src/compaction/pipeline.js +0 -37
  187. package/dist/src/compaction/pipeline.js.map +0 -1
  188. package/dist/src/compaction/providers/cli.d.ts +0 -13
  189. package/dist/src/compaction/providers/cli.d.ts.map +0 -1
  190. package/dist/src/compaction/providers/cli.js +0 -60
  191. package/dist/src/compaction/providers/cli.js.map +0 -1
  192. package/dist/src/compaction/rule-based.d.ts +0 -8
  193. package/dist/src/compaction/rule-based.d.ts.map +0 -1
  194. package/dist/src/compaction/rule-based.js +0 -120
  195. package/dist/src/compaction/rule-based.js.map +0 -1
  196. package/dist/src/config/loader.d.ts +0 -12
  197. package/dist/src/config/loader.d.ts.map +0 -1
  198. package/dist/src/config/loader.js +0 -76
  199. package/dist/src/config/loader.js.map +0 -1
  200. package/dist/src/config/mappings/claude-code.d.ts +0 -3
  201. package/dist/src/config/mappings/claude-code.d.ts.map +0 -1
  202. package/dist/src/config/mappings/claude-code.js +0 -31
  203. package/dist/src/config/mappings/claude-code.js.map +0 -1
  204. package/dist/src/config/mappings/codex.d.ts +0 -3
  205. package/dist/src/config/mappings/codex.d.ts.map +0 -1
  206. package/dist/src/config/mappings/codex.js +0 -31
  207. package/dist/src/config/mappings/codex.js.map +0 -1
  208. package/dist/src/config/mappings/copilot.d.ts +0 -3
  209. package/dist/src/config/mappings/copilot.d.ts.map +0 -1
  210. package/dist/src/config/mappings/copilot.js +0 -33
  211. package/dist/src/config/mappings/copilot.js.map +0 -1
  212. package/dist/src/config/mappings/cursor.d.ts +0 -3
  213. package/dist/src/config/mappings/cursor.d.ts.map +0 -1
  214. package/dist/src/config/mappings/cursor.js +0 -29
  215. package/dist/src/config/mappings/cursor.js.map +0 -1
  216. package/dist/src/config/sync.d.ts +0 -13
  217. package/dist/src/config/sync.d.ts.map +0 -1
  218. package/dist/src/config/sync.js +0 -96
  219. package/dist/src/config/sync.js.map +0 -1
  220. package/dist/src/ingestion/coordinator.d.ts +0 -37
  221. package/dist/src/ingestion/coordinator.d.ts.map +0 -1
  222. package/dist/src/ingestion/coordinator.js +0 -127
  223. package/dist/src/ingestion/coordinator.js.map +0 -1
  224. package/dist/src/ingestion/daemon.d.ts +0 -17
  225. package/dist/src/ingestion/daemon.d.ts.map +0 -1
  226. package/dist/src/ingestion/daemon.js +0 -35
  227. package/dist/src/ingestion/daemon.js.map +0 -1
  228. package/dist/src/ingestion/watcher.d.ts +0 -19
  229. package/dist/src/ingestion/watcher.d.ts.map +0 -1
  230. package/dist/src/ingestion/watcher.js +0 -70
  231. package/dist/src/ingestion/watcher.js.map +0 -1
  232. package/dist/src/knowledge/autotag.d.ts +0 -3
  233. package/dist/src/knowledge/autotag.d.ts.map +0 -1
  234. package/dist/src/knowledge/autotag.js +0 -24
  235. package/dist/src/knowledge/autotag.js.map +0 -1
  236. package/dist/src/knowledge/dedup.d.ts +0 -7
  237. package/dist/src/knowledge/dedup.d.ts.map +0 -1
  238. package/dist/src/knowledge/dedup.js +0 -15
  239. package/dist/src/knowledge/dedup.js.map +0 -1
  240. package/dist/src/knowledge/repository.d.ts +0 -12
  241. package/dist/src/knowledge/repository.d.ts.map +0 -1
  242. package/dist/src/knowledge/repository.js +0 -74
  243. package/dist/src/knowledge/repository.js.map +0 -1
  244. package/dist/src/mcp/tools/config.d.ts +0 -36
  245. package/dist/src/mcp/tools/config.d.ts.map +0 -1
  246. package/dist/src/mcp/tools/config.js +0 -54
  247. package/dist/src/mcp/tools/config.js.map +0 -1
  248. package/dist/src/mcp/tools/knowledge.d.ts +0 -17
  249. package/dist/src/mcp/tools/knowledge.d.ts.map +0 -1
  250. package/dist/src/mcp/tools/knowledge.js +0 -46
  251. package/dist/src/mcp/tools/knowledge.js.map +0 -1
  252. package/dist/src/mcp/tools/search.d.ts +0 -22
  253. package/dist/src/mcp/tools/search.d.ts.map +0 -1
  254. package/dist/src/mcp/tools/search.js +0 -30
  255. package/dist/src/mcp/tools/search.js.map +0 -1
  256. package/dist/src/mcp/tools/write.d.ts +0 -38
  257. package/dist/src/mcp/tools/write.d.ts.map +0 -1
  258. package/dist/src/mcp/tools/write.js +0 -97
  259. package/dist/src/mcp/tools/write.js.map +0 -1
  260. package/dist/src/runtime/ingestion.d.ts +0 -17
  261. package/dist/src/runtime/ingestion.d.ts.map +0 -1
  262. package/dist/src/runtime/ingestion.js +0 -106
  263. package/dist/src/runtime/ingestion.js.map +0 -1
  264. package/dist/src/scrapers/gemini.d.ts +0 -17
  265. package/dist/src/scrapers/gemini.d.ts.map +0 -1
  266. package/dist/src/scrapers/gemini.js +0 -245
  267. package/dist/src/scrapers/gemini.js.map +0 -1
  268. package/dist/src/store/embeddings.d.ts +0 -9
  269. package/dist/src/store/embeddings.d.ts.map +0 -1
  270. package/dist/src/store/embeddings.js +0 -28
  271. package/dist/src/store/embeddings.js.map +0 -1
  272. package/dist/src/store/lance.d.ts +0 -25
  273. package/dist/src/store/lance.d.ts.map +0 -1
  274. package/dist/src/store/lance.js +0 -74
  275. package/dist/src/store/lance.js.map +0 -1
  276. package/dist/src/store/search.d.ts +0 -18
  277. package/dist/src/store/search.d.ts.map +0 -1
  278. package/dist/src/store/search.js +0 -55
  279. package/dist/src/store/search.js.map +0 -1
  280. package/dist/src/types/compaction.d.ts +0 -29
  281. package/dist/src/types/compaction.d.ts.map +0 -1
  282. package/dist/src/types/compaction.js +0 -2
  283. package/dist/src/types/compaction.js.map +0 -1
  284. package/dist/src/types/context.d.ts +0 -33
  285. package/dist/src/types/context.d.ts.map +0 -1
  286. package/dist/src/types/context.js +0 -18
  287. package/dist/src/types/context.js.map +0 -1
  288. package/dist/src/utils/retry.d.ts +0 -11
  289. package/dist/src/utils/retry.d.ts.map +0 -1
  290. package/dist/src/utils/retry.js +0 -46
  291. package/dist/src/utils/retry.js.map +0 -1
  292. package/dist/src/utils/shutdown.d.ts +0 -25
  293. package/dist/src/utils/shutdown.d.ts.map +0 -1
  294. package/dist/src/utils/shutdown.js +0 -56
  295. package/dist/src/utils/shutdown.js.map +0 -1
  296. package/dist/web/apple-touch-icon.png +0 -0
  297. package/dist/web/assets/index-Nuadr5ox.css +0 -1
  298. package/dist/web/assets/index-aA0u8eXA.js +0 -3504
  299. package/dist/web/assets/primeicons-C6QP2o4f.woff2 +0 -0
  300. package/dist/web/assets/primeicons-DMOk5skT.eot +0 -0
  301. package/dist/web/assets/primeicons-Dr5RGzOO.svg +0 -345
  302. package/dist/web/assets/primeicons-MpK4pl85.ttf +0 -0
  303. package/dist/web/assets/primeicons-WjwUDZjB.woff +0 -0
  304. package/dist/web/favicon.ico +0 -0
  305. package/dist/web/favicon.svg +0 -21
  306. package/dist/web/index.html +0 -21
  307. package/dist/web/site.webmanifest +0 -19
  308. package/dist/web/vite.svg +0 -1
@@ -0,0 +1,869 @@
1
+ import { execFile } from "node:child_process";
2
+ import { readFile, readdir, stat } from "node:fs/promises";
3
+ import { request as httpsRequest } from "node:https";
4
+ import { promisify } from "node:util";
5
+ import { basename, extname, join } from "node:path";
6
+ import { AbstractScraper, estimateTokens, toDate } from "./base.js";
7
+ const execFileAsync = promisify(execFile);
8
+ const LANGUAGE_SERVER_SERVICE = "exa.language_server_pb.LanguageServerService";
9
+ export class AntigravityScraper extends AbstractScraper {
10
+ antigravityRoot;
11
+ projectRoot;
12
+ runtimeClient;
13
+ tool = "antigravity";
14
+ constructor(antigravityRoot, stateDir, projectRoot, runtimeClient = new AntigravityLanguageServerClient()) {
15
+ super(stateDir);
16
+ this.antigravityRoot = antigravityRoot;
17
+ this.projectRoot = projectRoot;
18
+ this.runtimeClient = runtimeClient;
19
+ }
20
+ async detect() {
21
+ try {
22
+ const target = await stat(this.antigravityRoot);
23
+ if (!target.isDirectory()) {
24
+ return false;
25
+ }
26
+ // Deliberately not mcp_config.json: xtctx's own setup writes that file,
27
+ // so treating it as evidence made a diagnostic report its own side
28
+ // effect as an installed tool. Only Antigravity's own state counts.
29
+ return (await pathIsDirectory(join(this.antigravityRoot, "brain"))) ||
30
+ (await pathIsDirectory(join(this.antigravityRoot, "conversations")));
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ getStorePaths() {
37
+ return [this.antigravityRoot];
38
+ }
39
+ async *scrape(since) {
40
+ const state = await this.getLastScrapedPosition();
41
+ const cutoff = since ?? state.lastTimestamp;
42
+ yield* this.readArtifacts(cutoff);
43
+ }
44
+ async *fullSync() {
45
+ yield* this.readArtifacts(new Date(0));
46
+ }
47
+ parseRaw(raw) {
48
+ const value = raw;
49
+ const content = toStringValue(value.content) ?? "";
50
+ return {
51
+ tool: "antigravity",
52
+ sessionId: toStringValue(value.sessionId) ?? "unknown",
53
+ timestamp: toDate(value.timestamp),
54
+ role: normalizeRole(toStringValue(value.role)),
55
+ content,
56
+ metadata: {
57
+ messageIndex: toMessageIndex(value.messageIndex),
58
+ tokenEstimate: estimateTokens(content),
59
+ referencedFiles: toStringArray(value.referencedFiles),
60
+ artifactType: toStringValue(value.artifactType),
61
+ artifactName: toStringValue(value.artifactName),
62
+ summary: toStringValue(value.summary),
63
+ sourcePath: toStringValue(value.sourcePath),
64
+ toolName: toStringValue(value.toolName),
65
+ model: toStringValue(value.model),
66
+ },
67
+ };
68
+ }
69
+ async *readArtifacts(since) {
70
+ const runtimeChunks = await this.readRuntimeChunks(since);
71
+ if (runtimeChunks.length > 0) {
72
+ for (const chunk of runtimeChunks) {
73
+ yield chunk;
74
+ }
75
+ return;
76
+ }
77
+ const brainDir = join(this.antigravityRoot, "brain");
78
+ const sessionDirs = await listDirectories(brainDir);
79
+ for (const sessionDir of sessionDirs) {
80
+ const sessionId = basename(sessionDir);
81
+ const artifacts = await this.readSessionArtifacts(sessionDir, sessionId);
82
+ artifacts.sort((left, right) => {
83
+ const time = left.timestamp.getTime() - right.timestamp.getTime();
84
+ return time === 0 ? left.sourcePath.localeCompare(right.sourcePath) : time;
85
+ });
86
+ for (const [messageIndex, artifact] of artifacts.entries()) {
87
+ if (artifact.timestamp <= since) {
88
+ continue;
89
+ }
90
+ yield this.parseRaw({
91
+ sessionId: artifact.sessionId,
92
+ timestamp: artifact.timestamp,
93
+ messageIndex,
94
+ content: formatArtifactContent(artifact),
95
+ referencedFiles: artifact.referencedFiles,
96
+ artifactType: artifact.artifactType,
97
+ artifactName: artifact.artifactName,
98
+ summary: artifact.summary,
99
+ sourcePath: artifact.sourcePath,
100
+ });
101
+ }
102
+ }
103
+ }
104
+ async readSessionArtifacts(sessionDir, sessionId) {
105
+ const names = await listFileNames(sessionDir);
106
+ const artifacts = [];
107
+ for (const name of names) {
108
+ if (!isReadableArtifactName(name)) {
109
+ continue;
110
+ }
111
+ const sourcePath = join(sessionDir, name);
112
+ const body = await readTextIfExists(sourcePath);
113
+ if (!body?.trim()) {
114
+ continue;
115
+ }
116
+ const metadata = await readArtifactMetadata(`${sourcePath}.metadata.json`);
117
+ const timestamp = await artifactTimestamp(sourcePath, metadata);
118
+ const candidate = {
119
+ sessionId,
120
+ sourcePath,
121
+ artifactName: name,
122
+ artifactType: metadata.artifactType,
123
+ summary: metadata.summary,
124
+ timestamp,
125
+ body,
126
+ referencedFiles: extractReferencedFiles(body),
127
+ };
128
+ if (this.projectRoot && !artifactMatchesProject(candidate, this.projectRoot)) {
129
+ continue;
130
+ }
131
+ artifacts.push(candidate);
132
+ }
133
+ return artifacts;
134
+ }
135
+ async readRuntimeChunks(since) {
136
+ const conversations = await this.safeListRuntimeConversations();
137
+ const chunks = [];
138
+ for (const conversation of conversations) {
139
+ if (this.projectRoot && !runtimeConversationMatchesProject(conversation, this.projectRoot)) {
140
+ continue;
141
+ }
142
+ const sortedMessages = conversation.messages
143
+ .filter((message) => message.content.trim().length > 0)
144
+ .sort((left, right) => {
145
+ const time = left.timestamp.getTime() - right.timestamp.getTime();
146
+ return time === 0 ? left.content.localeCompare(right.content) : time;
147
+ });
148
+ for (const [messageIndex, message] of sortedMessages.entries()) {
149
+ if (message.timestamp <= since) {
150
+ continue;
151
+ }
152
+ chunks.push(this.parseRaw({
153
+ sessionId: conversation.sessionId,
154
+ timestamp: message.timestamp,
155
+ messageIndex,
156
+ role: message.role,
157
+ content: message.content,
158
+ referencedFiles: message.referencedFiles,
159
+ sourcePath: message.sourcePath ?? `antigravity-ls:${conversation.sessionId}`,
160
+ artifactType: "ANTIGRAVITY_LANGUAGE_SERVER_TRANSCRIPT",
161
+ artifactName: message.stepType,
162
+ summary: conversation.title,
163
+ toolName: message.toolName,
164
+ model: message.model,
165
+ }));
166
+ }
167
+ }
168
+ return chunks;
169
+ }
170
+ async safeListRuntimeConversations() {
171
+ try {
172
+ return await this.runtimeClient.listConversations(join(this.antigravityRoot, "conversations"));
173
+ }
174
+ catch {
175
+ return [];
176
+ }
177
+ }
178
+ }
179
+ class AntigravityLanguageServerClient {
180
+ async listConversations(conversationsDir) {
181
+ const endpoints = await discoverLanguageServerEndpoints();
182
+ if (endpoints.length === 0) {
183
+ return [];
184
+ }
185
+ const bySession = new Map();
186
+ for (const endpoint of endpoints) {
187
+ const response = await callLanguageServer(endpoint, "GetAllCascadeTrajectories", {}, 5_000);
188
+ const summaries = isRecord(response?.trajectorySummaries)
189
+ ? response.trajectorySummaries
190
+ : {};
191
+ for (const [sessionId, summary] of Object.entries(summaries)) {
192
+ if (!isRecord(summary) || bySession.has(sessionId)) {
193
+ continue;
194
+ }
195
+ bySession.set(sessionId, { endpoint, summary });
196
+ }
197
+ }
198
+ const defaultEndpoint = endpoints[0];
199
+ for (const sessionId of await listConversationFileIds(conversationsDir)) {
200
+ if (!bySession.has(sessionId)) {
201
+ bySession.set(sessionId, {
202
+ endpoint: defaultEndpoint,
203
+ summary: {
204
+ summary: `[on-disk] ${sessionId.slice(0, 8)}`,
205
+ stepCount: 1000,
206
+ },
207
+ });
208
+ }
209
+ }
210
+ const conversations = [];
211
+ for (const [sessionId, entry] of bySession.entries()) {
212
+ const stepCount = toPositiveInteger(entry.summary.stepCount) ?? 1000;
213
+ const response = await callLanguageServer(entry.endpoint, "GetCascadeTrajectorySteps", { cascadeId: sessionId, startIndex: 0, endIndex: stepCount + 10 }, 30_000);
214
+ const steps = Array.isArray(response?.steps)
215
+ ? response.steps
216
+ : Array.isArray(response?.messages)
217
+ ? response.messages
218
+ : [];
219
+ const messages = parseAntigravityRuntimeSteps(sessionId, steps, entry.summary);
220
+ if (messages.length === 0) {
221
+ continue;
222
+ }
223
+ conversations.push({
224
+ sessionId,
225
+ title: toStringValue(entry.summary.summary),
226
+ createdAt: toDate(entry.summary.createdTime),
227
+ workspaces: extractWorkspaceUris(entry.summary),
228
+ messages,
229
+ });
230
+ }
231
+ return conversations;
232
+ }
233
+ }
234
+ export function parseAntigravityRuntimeSteps(sessionId, steps, summary) {
235
+ const fallbackTimestamp = toDate(summary.createdTime);
236
+ const messages = [];
237
+ for (const step of steps) {
238
+ if (!isRecord(step)) {
239
+ continue;
240
+ }
241
+ const stepType = toStringValue(step.type) ?? "";
242
+ const metadata = isRecord(step.metadata) ? step.metadata : {};
243
+ const timestamp = validDate(toDate(metadata.createdAt)) ?? validDate(fallbackTimestamp) ?? new Date(0);
244
+ const message = parseRuntimeStep(sessionId, step, stepType, timestamp);
245
+ if (message) {
246
+ messages.push(message);
247
+ }
248
+ }
249
+ return messages;
250
+ }
251
+ function parseRuntimeStep(sessionId, step, stepType, timestamp) {
252
+ if (stepType === "CORTEX_STEP_TYPE_USER_INPUT") {
253
+ return parseUserInputStep(sessionId, step, stepType, timestamp);
254
+ }
255
+ if (stepType === "CORTEX_STEP_TYPE_PLANNER_RESPONSE") {
256
+ return parsePlannerResponseStep(sessionId, step, stepType, timestamp);
257
+ }
258
+ if (stepType === "CORTEX_STEP_TYPE_CODE_ACTION") {
259
+ return parseCodeActionStep(sessionId, step, stepType, timestamp);
260
+ }
261
+ if (stepType === "CORTEX_STEP_TYPE_RUN_COMMAND") {
262
+ return parseRunCommandStep(sessionId, step, stepType, timestamp);
263
+ }
264
+ if (stepType === "CORTEX_STEP_TYPE_VIEW_FILE") {
265
+ return parsePathToolStep(sessionId, step, stepType, timestamp, "view_file", "viewFile", [
266
+ "absolutePathUri",
267
+ "filePath",
268
+ "path",
269
+ ]);
270
+ }
271
+ if (stepType === "CORTEX_STEP_TYPE_FIND") {
272
+ return parseSimpleToolStep(sessionId, step, stepType, timestamp, "find", "find", ["query"]);
273
+ }
274
+ if (stepType === "CORTEX_STEP_TYPE_LIST_DIRECTORY") {
275
+ return parsePathToolStep(sessionId, step, stepType, timestamp, "list_dir", "listDirectory", [
276
+ "directoryPath",
277
+ "path",
278
+ ]);
279
+ }
280
+ if (stepType === "CORTEX_STEP_TYPE_SEARCH_WEB") {
281
+ return parseSimpleToolStep(sessionId, step, stepType, timestamp, "search_web", "searchWeb", [
282
+ "query",
283
+ "summary",
284
+ ]);
285
+ }
286
+ if (stepType === "CORTEX_STEP_TYPE_READ_URL_CONTENT") {
287
+ return parseSimpleToolStep(sessionId, step, stepType, timestamp, "read_url", "readUrlContent", ["url"]);
288
+ }
289
+ if (stepType === "CORTEX_STEP_TYPE_COMMAND_STATUS") {
290
+ return runtimeMessage(sessionId, timestamp, "tool", "[Check Command Status]", [], {
291
+ stepType,
292
+ toolName: "command_status",
293
+ });
294
+ }
295
+ return null;
296
+ }
297
+ function parseUserInputStep(sessionId, step, stepType, timestamp) {
298
+ const userInput = isRecord(step.userInput) ? step.userInput : {};
299
+ const content = toStringValue(userInput.userResponse);
300
+ if (!content) {
301
+ return null;
302
+ }
303
+ const activeUserState = isRecord(userInput.activeUserState) ? userInput.activeUserState : {};
304
+ const activeDocument = isRecord(activeUserState.activeDocument)
305
+ ? activeUserState.activeDocument
306
+ : {};
307
+ const activeFile = toStringValue(activeDocument.absoluteUri);
308
+ const references = [...extractReferencedFiles(content), ...(activeFile ? [decodeFileUrl(activeFile)] : [])];
309
+ return runtimeMessage(sessionId, timestamp, "user", content, references, {
310
+ stepType,
311
+ sourcePath: activeFile,
312
+ });
313
+ }
314
+ function parsePlannerResponseStep(sessionId, step, stepType, timestamp) {
315
+ const plannerResponse = isRecord(step.plannerResponse) ? step.plannerResponse : {};
316
+ const content = toStringValue(plannerResponse.modifiedResponse) ?? toStringValue(plannerResponse.response);
317
+ if (!content) {
318
+ return null;
319
+ }
320
+ const metadata = isRecord(step.metadata) ? step.metadata : {};
321
+ return runtimeMessage(sessionId, timestamp, "assistant", content, extractReferencedFiles(content), {
322
+ stepType,
323
+ model: toStringValue(metadata.generatorModel),
324
+ });
325
+ }
326
+ function parseCodeActionStep(sessionId, step, stepType, timestamp) {
327
+ const codeAction = isRecord(step.codeAction) ? step.codeAction : {};
328
+ const description = toStringValue(codeAction.description);
329
+ const actionResult = isRecord(codeAction.actionResult) ? codeAction.actionResult : {};
330
+ const edit = isRecord(actionResult.edit) ? actionResult.edit : {};
331
+ const actionSpec = isRecord(codeAction.actionSpec) ? codeAction.actionSpec : {};
332
+ const createFile = isRecord(actionSpec.createFile) ? actionSpec.createFile : {};
333
+ const filePath = toStringValue(edit.absoluteUri) ?? toStringValue(createFile.path);
334
+ const diff = normalizeDiff(edit.diff);
335
+ const artifactMetadata = isRecord(codeAction.artifactMetadata)
336
+ ? codeAction.artifactMetadata
337
+ : {};
338
+ const sections = [
339
+ filePath ? `[Code Edit] ${filePath}` : "[Code Edit]",
340
+ description,
341
+ toStringValue(artifactMetadata.summary),
342
+ diff ? `Diff:\n${diff}` : undefined,
343
+ ].filter((line) => Boolean(line));
344
+ const content = sections.join("\n");
345
+ if (!content.trim()) {
346
+ return null;
347
+ }
348
+ return runtimeMessage(sessionId, timestamp, "tool", content, referencesFromValues([content, filePath]), {
349
+ stepType,
350
+ toolName: "code_edit",
351
+ sourcePath: filePath,
352
+ });
353
+ }
354
+ function parseRunCommandStep(sessionId, step, stepType, timestamp) {
355
+ const runCommand = isRecord(step.runCommand) ? step.runCommand : {};
356
+ const command = toStringValue(runCommand.commandLine) ?? toStringValue(runCommand.command);
357
+ if (!command) {
358
+ return null;
359
+ }
360
+ const combinedOutput = isRecord(runCommand.combinedOutput) ? runCommand.combinedOutput : {};
361
+ const sections = [
362
+ `[Command] ${command}`,
363
+ toStringValue(runCommand.cwd) ? `cwd: ${toStringValue(runCommand.cwd)}` : undefined,
364
+ runCommand.exitCode !== undefined ? `exit_code: ${String(runCommand.exitCode)}` : undefined,
365
+ toStringValue(combinedOutput.full) ? `Output:\n${toStringValue(combinedOutput.full)}` : undefined,
366
+ ].filter((line) => Boolean(line));
367
+ return runtimeMessage(sessionId, timestamp, "tool", sections.join("\n"), referencesFromValues(sections), {
368
+ stepType,
369
+ toolName: "run_command",
370
+ sourcePath: toStringValue(runCommand.cwd),
371
+ });
372
+ }
373
+ function parsePathToolStep(sessionId, step, stepType, timestamp, toolName, objectKey, fields) {
374
+ const payload = isRecord(step[objectKey]) ? step[objectKey] : {};
375
+ const value = firstStringField(payload, fields);
376
+ if (!value) {
377
+ return null;
378
+ }
379
+ return runtimeMessage(sessionId, timestamp, "tool", value, referencesFromValues([value]), {
380
+ stepType,
381
+ toolName,
382
+ sourcePath: value,
383
+ });
384
+ }
385
+ function parseSimpleToolStep(sessionId, step, stepType, timestamp, toolName, objectKey, fields) {
386
+ const payload = isRecord(step[objectKey]) ? step[objectKey] : {};
387
+ const values = fields.map((field) => toStringValue(payload[field])).filter((value) => Boolean(value));
388
+ if (values.length === 0) {
389
+ return null;
390
+ }
391
+ const content = values.join("\n");
392
+ return runtimeMessage(sessionId, timestamp, "tool", content, referencesFromValues(values), {
393
+ stepType,
394
+ toolName,
395
+ });
396
+ }
397
+ function runtimeMessage(sessionId, timestamp, role, content, referencedFiles, metadata) {
398
+ return {
399
+ sessionId,
400
+ timestamp,
401
+ role,
402
+ content,
403
+ referencedFiles: [...new Set(referencedFiles.filter((value) => value.trim().length > 0))],
404
+ stepType: metadata.stepType,
405
+ toolName: metadata.toolName,
406
+ sourcePath: metadata.sourcePath,
407
+ model: metadata.model,
408
+ };
409
+ }
410
+ async function discoverLanguageServerEndpoints() {
411
+ const processes = await discoverLanguageServerProcesses();
412
+ const endpoints = [];
413
+ const seenPorts = new Set();
414
+ for (const processInfo of processes) {
415
+ const ports = await findListeningPorts(processInfo.pid);
416
+ for (const port of ports) {
417
+ if (seenPorts.has(port)) {
418
+ continue;
419
+ }
420
+ const response = await callLanguageServer({ port, csrf: processInfo.csrf, pid: processInfo.pid }, "GetAllCascadeTrajectories", {}, 5_000);
421
+ if (response !== null) {
422
+ endpoints.push({ port, csrf: processInfo.csrf, pid: processInfo.pid });
423
+ seenPorts.add(port);
424
+ break;
425
+ }
426
+ }
427
+ }
428
+ return endpoints;
429
+ }
430
+ async function discoverLanguageServerProcesses() {
431
+ return process.platform === "win32"
432
+ ? discoverWindowsLanguageServerProcesses()
433
+ : discoverPosixLanguageServerProcesses();
434
+ }
435
+ async function discoverWindowsLanguageServerProcesses() {
436
+ try {
437
+ const script = [
438
+ "Get-CimInstance Win32_Process",
439
+ "Where-Object { $_.Name -like 'language_server*' }",
440
+ "Select-Object ProcessId, CommandLine",
441
+ "ConvertTo-Json -Compress",
442
+ ].join(" | ");
443
+ const { stdout } = await execFileAsync("powershell", ["-NoProfile", "-Command", script], {
444
+ timeout: 15_000,
445
+ });
446
+ return parseProcessJson(String(stdout));
447
+ }
448
+ catch {
449
+ return [];
450
+ }
451
+ }
452
+ async function discoverPosixLanguageServerProcesses() {
453
+ if (process.platform === "darwin") {
454
+ return discoverMacLanguageServerProcesses();
455
+ }
456
+ try {
457
+ const { stdout } = await execFileAsync("pgrep", ["-f", "language_server"], {
458
+ timeout: 5_000,
459
+ });
460
+ const pids = String(stdout)
461
+ .split(/\r?\n/)
462
+ .map((line) => Number(line.trim()))
463
+ .filter((pid) => Number.isFinite(pid) && pid > 0);
464
+ const processes = [];
465
+ for (const pid of pids) {
466
+ try {
467
+ const cmdline = await readFile(`/proc/${pid}/cmdline`, "utf8");
468
+ const commandLine = cmdline.replace(/\0/g, " ");
469
+ processes.push({ pid, csrf: extractCsrfToken(commandLine) });
470
+ }
471
+ catch {
472
+ // Ignore read failures for exited processes
473
+ }
474
+ }
475
+ return processes;
476
+ }
477
+ catch {
478
+ return [];
479
+ }
480
+ }
481
+ async function discoverMacLanguageServerProcesses() {
482
+ try {
483
+ const { stdout } = await execFileAsync("pgrep", ["-f", "language_server"], {
484
+ timeout: 5_000,
485
+ });
486
+ const processes = [];
487
+ for (const pidText of String(stdout).split(/\r?\n/)) {
488
+ const pid = Number(pidText.trim());
489
+ if (!Number.isFinite(pid)) {
490
+ continue;
491
+ }
492
+ try {
493
+ const { stdout: commandLine } = await execFileAsync("ps", ["-p", String(pid), "-o", "args="], {
494
+ timeout: 5_000,
495
+ });
496
+ processes.push({ pid, csrf: extractCsrfToken(String(commandLine)) });
497
+ }
498
+ catch {
499
+ // Ignore processes that exit between pgrep and ps.
500
+ }
501
+ }
502
+ return processes;
503
+ }
504
+ catch {
505
+ return [];
506
+ }
507
+ }
508
+ function parseProcessJson(raw) {
509
+ if (!raw.trim()) {
510
+ return [];
511
+ }
512
+ try {
513
+ const parsed = JSON.parse(raw);
514
+ const rows = Array.isArray(parsed) ? parsed : [parsed];
515
+ return rows
516
+ .filter(isRecord)
517
+ .map((row) => {
518
+ const pid = Number(row.ProcessId);
519
+ const commandLine = toStringValue(row.CommandLine) ?? "";
520
+ return Number.isFinite(pid)
521
+ ? { pid, csrf: extractCsrfToken(commandLine) }
522
+ : null;
523
+ })
524
+ .filter((value) => value !== null);
525
+ }
526
+ catch {
527
+ return [];
528
+ }
529
+ }
530
+ function extractCsrfToken(commandLine) {
531
+ return commandLine.match(/--csrf_token\s+(\S+)/)?.[1] ?? "";
532
+ }
533
+ async function findListeningPorts(pid) {
534
+ return process.platform === "win32" ? findWindowsListeningPorts(pid) : findPosixListeningPorts(pid);
535
+ }
536
+ /**
537
+ * Extract the ports a specific PID is listening on from `netstat -ano`.
538
+ *
539
+ * The PID is the last whitespace-separated column. Matching it with
540
+ * `endsWith` treats PID 2140 as a match for PID 140, which would attribute
541
+ * an unrelated process's port to the language server — and the CSRF token
542
+ * is POSTed to whatever answers there. The column is compared exactly.
543
+ */
544
+ export function parseWindowsListeningPorts(netstatOutput, pid) {
545
+ return netstatOutput
546
+ .split(/\r?\n/)
547
+ .filter((line) => {
548
+ const columns = line.trim().split(/\s+/);
549
+ return columns.includes("LISTENING") && columns[columns.length - 1] === String(pid);
550
+ })
551
+ .map((line) => line.match(/\s(?:127\.0\.0\.1|0\.0\.0\.0|\[::1\]):(\d+)\s/)?.[1])
552
+ .filter((value) => value !== undefined)
553
+ .map((value) => Number(value))
554
+ .filter((value) => Number.isFinite(value));
555
+ }
556
+ /** Extract listening ports from `lsof -Pan -p <pid> -iTCP -sTCP:LISTEN`. */
557
+ export function parsePosixListeningPorts(lsofOutput) {
558
+ return lsofOutput
559
+ .split(/\r?\n/)
560
+ .map((line) => line.match(/:(\d+)\s+\(LISTEN\)/)?.[1])
561
+ .filter((value) => value !== undefined)
562
+ .map((value) => Number(value))
563
+ .filter((value) => Number.isFinite(value));
564
+ }
565
+ async function findWindowsListeningPorts(pid) {
566
+ try {
567
+ const { stdout } = await execFileAsync("netstat", ["-ano"], { timeout: 10_000 });
568
+ return parseWindowsListeningPorts(String(stdout), pid);
569
+ }
570
+ catch {
571
+ return [];
572
+ }
573
+ }
574
+ async function findPosixListeningPorts(pid) {
575
+ try {
576
+ const { stdout } = await execFileAsync("lsof", ["-Pan", "-p", String(pid), "-iTCP", "-sTCP:LISTEN"], { timeout: 10_000 });
577
+ return parsePosixListeningPorts(String(stdout));
578
+ }
579
+ catch {
580
+ return [];
581
+ }
582
+ }
583
+ async function callLanguageServer(endpoint, method, payload, timeoutMs) {
584
+ return new Promise((resolve) => {
585
+ const body = JSON.stringify(payload);
586
+ const req = httpsRequest({
587
+ // Loopback only, and the language server presents a self-signed
588
+ // certificate there is no CA to validate against — so verification is
589
+ // off by necessity, not convenience. The control that matters is
590
+ // sending the CSRF token to the *right* process, which is why the
591
+ // PID column is matched exactly (see parseWindowsListeningPorts).
592
+ hostname: "127.0.0.1",
593
+ port: endpoint.port,
594
+ path: `/${LANGUAGE_SERVER_SERVICE}/${method}`,
595
+ method: "POST",
596
+ rejectUnauthorized: false,
597
+ timeout: timeoutMs,
598
+ headers: {
599
+ "Content-Type": "application/json",
600
+ "Content-Length": Buffer.byteLength(body),
601
+ "Connect-Protocol-Version": "1",
602
+ "X-Codeium-Csrf-Token": endpoint.csrf,
603
+ },
604
+ }, (res) => {
605
+ const chunks = [];
606
+ res.on("data", (chunk) => chunks.push(chunk));
607
+ res.on("end", () => {
608
+ if (res.statusCode !== 200) {
609
+ resolve(null);
610
+ return;
611
+ }
612
+ try {
613
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf-8"));
614
+ resolve(isRecord(parsed) ? parsed : null);
615
+ }
616
+ catch {
617
+ resolve(null);
618
+ }
619
+ });
620
+ });
621
+ req.on("timeout", () => {
622
+ req.destroy();
623
+ resolve(null);
624
+ });
625
+ req.on("error", () => resolve(null));
626
+ req.write(body);
627
+ req.end();
628
+ });
629
+ }
630
+ async function listConversationFileIds(conversationsDir) {
631
+ const names = await listFileNames(conversationsDir);
632
+ return names
633
+ .filter((name) => name.endsWith(".pb"))
634
+ .map((name) => basename(name, ".pb"))
635
+ .filter((name) => name.length > 0);
636
+ }
637
+ function formatArtifactContent(artifact) {
638
+ const header = [
639
+ `Antigravity artifact: ${artifact.artifactName}`,
640
+ `Source: ${artifact.sourcePath}`,
641
+ artifact.artifactType ? `Type: ${artifact.artifactType}` : undefined,
642
+ artifact.summary ? `Summary: ${artifact.summary}` : undefined,
643
+ ].filter((line) => Boolean(line));
644
+ return `${header.join("\n")}\n\n${artifact.body.trim()}`;
645
+ }
646
+ function artifactMatchesProject(artifact, projectRoot) {
647
+ const text = normalizeSearchText([
648
+ artifact.sourcePath,
649
+ artifact.summary ?? "",
650
+ artifact.body,
651
+ ...artifact.referencedFiles,
652
+ ].join("\n"));
653
+ const root = normalizeSearchText(projectRoot);
654
+ const projectName = normalizeSearchText(basename(projectRoot));
655
+ return text.includes(root) || text.includes(`/playground/${projectName}/`) ||
656
+ text.endsWith(`/playground/${projectName}`);
657
+ }
658
+ function runtimeConversationMatchesProject(conversation, projectRoot) {
659
+ if (conversation.workspaces.some((workspace) => textMentionsProject(workspace, projectRoot))) {
660
+ return true;
661
+ }
662
+ // Path evidence only. A previous fallback attributed a conversation when
663
+ // the project's directory name appeared as a word anywhere in the title or
664
+ // message text, which handed another project's private transcript to this
665
+ // one whenever it mentioned that word — any project called `core`, `docs`,
666
+ // or `client` collected most of the machine. A conversation Antigravity
667
+ // gives us no path for is not attributable, so it is excluded.
668
+ return conversation.messages.some((message) => textMentionsProject([
669
+ message.content,
670
+ message.sourcePath ?? "",
671
+ ...message.referencedFiles,
672
+ ].join("\n"), projectRoot));
673
+ }
674
+ function textMentionsProject(value, projectRoot) {
675
+ const text = normalizeSearchText(value);
676
+ const root = normalizeSearchText(projectRoot);
677
+ const projectName = normalizeSearchText(basename(projectRoot));
678
+ return text.includes(root) || text.includes(`/playground/${projectName}/`) ||
679
+ text.endsWith(`/playground/${projectName}`);
680
+ }
681
+ function extractWorkspaceUris(summary) {
682
+ const direct = extractWorkspaceUrisFromValue(summary.workspaces);
683
+ if (direct.length > 0) {
684
+ return direct;
685
+ }
686
+ const metadata = isRecord(summary.trajectoryMetadata) ? summary.trajectoryMetadata : {};
687
+ return extractWorkspaceUrisFromValue(metadata.workspaces);
688
+ }
689
+ function extractWorkspaceUrisFromValue(value) {
690
+ if (!Array.isArray(value)) {
691
+ return [];
692
+ }
693
+ return value
694
+ .filter(isRecord)
695
+ .map((workspace) => toStringValue(workspace.workspaceFolderAbsoluteUri))
696
+ .filter((uri) => uri !== undefined);
697
+ }
698
+ function extractReferencedFiles(content) {
699
+ const matches = content.match(/file:\/\/\/[^\s)\]>"]+/g) ?? [];
700
+ return [...new Set(matches.map(decodeFileUrl).filter((value) => value.length > 0))];
701
+ }
702
+ function referencesFromValues(values) {
703
+ return values.flatMap((value) => value ? extractReferencedFiles(value) : []);
704
+ }
705
+ function normalizeDiff(value) {
706
+ if (typeof value === "string") {
707
+ return value.trim().length > 0 ? value : undefined;
708
+ }
709
+ if (!isRecord(value)) {
710
+ return undefined;
711
+ }
712
+ const unifiedDiff = isRecord(value.unifiedDiff) ? value.unifiedDiff : {};
713
+ if (!Array.isArray(unifiedDiff.lines)) {
714
+ return JSON.stringify(value);
715
+ }
716
+ const lines = unifiedDiff.lines
717
+ .filter(isRecord)
718
+ .map((line) => `${diffLinePrefix(toStringValue(line.type))}${toStringValue(line.text) ?? ""}`);
719
+ return lines.length > 0 ? lines.join("\n") : undefined;
720
+ }
721
+ function diffLinePrefix(type) {
722
+ switch (type) {
723
+ case "UNIFIED_DIFF_LINE_TYPE_INSERT":
724
+ return "+";
725
+ case "UNIFIED_DIFF_LINE_TYPE_DELETE":
726
+ return "-";
727
+ default:
728
+ return " ";
729
+ }
730
+ }
731
+ function decodeFileUrl(value) {
732
+ try {
733
+ return decodeURIComponent(value.replace(/^file:\/\/\//, ""));
734
+ }
735
+ catch {
736
+ return value.replace(/^file:\/\/\//, "");
737
+ }
738
+ }
739
+ function firstStringField(record, fields) {
740
+ for (const field of fields) {
741
+ const value = toStringValue(record[field]);
742
+ if (value) {
743
+ return value;
744
+ }
745
+ }
746
+ return undefined;
747
+ }
748
+ function isReadableArtifactName(name) {
749
+ const extension = extname(name).toLowerCase();
750
+ return (extension === ".md" || extension === ".txt") &&
751
+ !name.endsWith(".metadata.json") &&
752
+ !name.includes(".resolved");
753
+ }
754
+ async function artifactTimestamp(sourcePath, metadata) {
755
+ const fromMetadata = toDate(metadata.updatedAt);
756
+ if (fromMetadata.getTime() > 0) {
757
+ return fromMetadata;
758
+ }
759
+ try {
760
+ return (await stat(sourcePath)).mtime;
761
+ }
762
+ catch {
763
+ return new Date(0);
764
+ }
765
+ }
766
+ async function readArtifactMetadata(path) {
767
+ const raw = await readTextIfExists(path);
768
+ if (!raw) {
769
+ return {};
770
+ }
771
+ try {
772
+ const parsed = JSON.parse(raw);
773
+ return {
774
+ artifactType: toStringValue(parsed.artifactType),
775
+ summary: toStringValue(parsed.summary),
776
+ updatedAt: toStringValue(parsed.updatedAt),
777
+ version: toStringValue(parsed.version),
778
+ };
779
+ }
780
+ catch {
781
+ return {};
782
+ }
783
+ }
784
+ async function listDirectories(path) {
785
+ try {
786
+ const entries = await readdir(path, { withFileTypes: true });
787
+ return entries
788
+ .filter((entry) => entry.isDirectory())
789
+ .map((entry) => join(path, entry.name))
790
+ .sort();
791
+ }
792
+ catch {
793
+ return [];
794
+ }
795
+ }
796
+ async function listFileNames(path) {
797
+ try {
798
+ const entries = await readdir(path, { withFileTypes: true });
799
+ return entries.filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
800
+ }
801
+ catch {
802
+ return [];
803
+ }
804
+ }
805
+ async function pathIsDirectory(path) {
806
+ try {
807
+ return (await stat(path)).isDirectory();
808
+ }
809
+ catch {
810
+ return false;
811
+ }
812
+ }
813
+ async function readTextIfExists(path) {
814
+ try {
815
+ return await readFile(path, "utf-8");
816
+ }
817
+ catch {
818
+ return null;
819
+ }
820
+ }
821
+ function normalizeSearchText(value) {
822
+ return safeDecode(value).replace(/\\/g, "/").replace(/\/+/g, "/").replace(/\/$/g, "").toLowerCase();
823
+ }
824
+ function safeDecode(value) {
825
+ try {
826
+ return decodeURIComponent(value);
827
+ }
828
+ catch {
829
+ return value;
830
+ }
831
+ }
832
+ function validDate(value) {
833
+ return value.getTime() > 0 && !Number.isNaN(value.getTime()) ? value : undefined;
834
+ }
835
+ function normalizeRole(value) {
836
+ switch (value) {
837
+ case "user":
838
+ case "assistant":
839
+ case "system":
840
+ case "tool":
841
+ return value;
842
+ default:
843
+ return "assistant";
844
+ }
845
+ }
846
+ function toStringValue(value) {
847
+ return typeof value === "string" ? value : undefined;
848
+ }
849
+ function toPositiveInteger(value) {
850
+ const parsed = Number(value);
851
+ return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : undefined;
852
+ }
853
+ function toStringArray(value) {
854
+ if (!Array.isArray(value)) {
855
+ return [];
856
+ }
857
+ return value.filter((item) => typeof item === "string");
858
+ }
859
+ function toMessageIndex(value) {
860
+ const parsed = Number(value);
861
+ if (Number.isFinite(parsed) && parsed >= 0) {
862
+ return Math.floor(parsed);
863
+ }
864
+ return 0;
865
+ }
866
+ function isRecord(value) {
867
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
868
+ }
869
+ //# sourceMappingURL=antigravity.js.map