@phigon/observer 8.1.0 → 8.3.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 (309) hide show
  1. package/README.md +244 -18
  2. package/dist/bin/guardian-runner-darwin-amd64 +0 -0
  3. package/dist/bin/guardian-runner-darwin-arm64 +0 -0
  4. package/dist/bin/guardian-runner-linux-amd64 +0 -0
  5. package/dist/bin/guardian-runner-linux-arm64 +0 -0
  6. package/dist/bin/guardian-runner-windows-amd64.exe +0 -0
  7. package/dist/bin/guardian-runner-windows-arm64.exe +0 -0
  8. package/dist/src/abortable-delay.d.ts +10 -0
  9. package/dist/src/abortable-delay.d.ts.map +1 -0
  10. package/dist/src/abortable-delay.js +21 -0
  11. package/dist/src/abortable-delay.js.map +1 -0
  12. package/dist/src/action-policy.d.ts.map +1 -1
  13. package/dist/src/action-policy.js +4 -2
  14. package/dist/src/action-policy.js.map +1 -1
  15. package/dist/src/activity/adapters/claude.d.ts.map +1 -1
  16. package/dist/src/activity/adapters/claude.js +28 -2
  17. package/dist/src/activity/adapters/claude.js.map +1 -1
  18. package/dist/src/activity/adapters/cline-task.d.ts +7 -0
  19. package/dist/src/activity/adapters/cline-task.d.ts.map +1 -0
  20. package/dist/src/activity/adapters/cline-task.js +197 -0
  21. package/dist/src/activity/adapters/cline-task.js.map +1 -0
  22. package/dist/src/activity/adapters/cline.d.ts +3 -0
  23. package/dist/src/activity/adapters/cline.d.ts.map +1 -0
  24. package/dist/src/activity/adapters/cline.js +102 -0
  25. package/dist/src/activity/adapters/cline.js.map +1 -0
  26. package/dist/src/activity/adapters/codex.d.ts.map +1 -1
  27. package/dist/src/activity/adapters/codex.js +76 -0
  28. package/dist/src/activity/adapters/codex.js.map +1 -1
  29. package/dist/src/activity/adapters/copilot-chat.d.ts +3 -0
  30. package/dist/src/activity/adapters/copilot-chat.d.ts.map +1 -0
  31. package/dist/src/activity/adapters/copilot-chat.js +176 -0
  32. package/dist/src/activity/adapters/copilot-chat.js.map +1 -0
  33. package/dist/src/activity/adapters/copilot.d.ts +2 -1
  34. package/dist/src/activity/adapters/copilot.d.ts.map +1 -1
  35. package/dist/src/activity/adapters/copilot.js +173 -2
  36. package/dist/src/activity/adapters/copilot.js.map +1 -1
  37. package/dist/src/activity/adapters/cursor.d.ts +3 -0
  38. package/dist/src/activity/adapters/cursor.d.ts.map +1 -0
  39. package/dist/src/activity/adapters/cursor.js +86 -0
  40. package/dist/src/activity/adapters/cursor.js.map +1 -0
  41. package/dist/src/activity/adapters/factory-droid.d.ts +3 -0
  42. package/dist/src/activity/adapters/factory-droid.d.ts.map +1 -0
  43. package/dist/src/activity/adapters/factory-droid.js +126 -0
  44. package/dist/src/activity/adapters/factory-droid.js.map +1 -0
  45. package/dist/src/activity/adapters/gemini.d.ts +3 -0
  46. package/dist/src/activity/adapters/gemini.d.ts.map +1 -0
  47. package/dist/src/activity/adapters/gemini.js +177 -0
  48. package/dist/src/activity/adapters/gemini.js.map +1 -0
  49. package/dist/src/activity/adapters/goose.d.ts +3 -0
  50. package/dist/src/activity/adapters/goose.d.ts.map +1 -0
  51. package/dist/src/activity/adapters/goose.js +116 -0
  52. package/dist/src/activity/adapters/goose.js.map +1 -0
  53. package/dist/src/activity/adapters/kiro.d.ts +3 -0
  54. package/dist/src/activity/adapters/kiro.d.ts.map +1 -0
  55. package/dist/src/activity/adapters/kiro.js +47 -0
  56. package/dist/src/activity/adapters/kiro.js.map +1 -0
  57. package/dist/src/activity/adapters/openai-compatible.d.ts.map +1 -1
  58. package/dist/src/activity/adapters/openai-compatible.js +9 -1
  59. package/dist/src/activity/adapters/openai-compatible.js.map +1 -1
  60. package/dist/src/activity/adapters/opencode.d.ts.map +1 -1
  61. package/dist/src/activity/adapters/opencode.js +108 -1
  62. package/dist/src/activity/adapters/opencode.js.map +1 -1
  63. package/dist/src/activity/adapters/zed.d.ts +3 -0
  64. package/dist/src/activity/adapters/zed.d.ts.map +1 -0
  65. package/dist/src/activity/adapters/zed.js +134 -0
  66. package/dist/src/activity/adapters/zed.js.map +1 -0
  67. package/dist/src/activity/collector.d.ts +43 -1
  68. package/dist/src/activity/collector.d.ts.map +1 -1
  69. package/dist/src/activity/collector.js +490 -77
  70. package/dist/src/activity/collector.js.map +1 -1
  71. package/dist/src/activity/goose-sqlite-source.d.ts +33 -0
  72. package/dist/src/activity/goose-sqlite-source.d.ts.map +1 -0
  73. package/dist/src/activity/goose-sqlite-source.js +274 -0
  74. package/dist/src/activity/goose-sqlite-source.js.map +1 -0
  75. package/dist/src/activity/index.d.ts +3 -0
  76. package/dist/src/activity/index.d.ts.map +1 -1
  77. package/dist/src/activity/index.js +3 -0
  78. package/dist/src/activity/index.js.map +1 -1
  79. package/dist/src/activity/ingest.d.ts +9 -0
  80. package/dist/src/activity/ingest.d.ts.map +1 -1
  81. package/dist/src/activity/ingest.js +94 -4
  82. package/dist/src/activity/ingest.js.map +1 -1
  83. package/dist/src/activity/json-document-source.d.ts +151 -0
  84. package/dist/src/activity/json-document-source.d.ts.map +1 -0
  85. package/dist/src/activity/json-document-source.js +586 -0
  86. package/dist/src/activity/json-document-source.js.map +1 -0
  87. package/dist/src/activity/native-capabilities.d.ts.map +1 -1
  88. package/dist/src/activity/native-capabilities.js +8 -12
  89. package/dist/src/activity/native-capabilities.js.map +1 -1
  90. package/dist/src/activity/normalize.d.ts.map +1 -1
  91. package/dist/src/activity/normalize.js +64 -6
  92. package/dist/src/activity/normalize.js.map +1 -1
  93. package/dist/src/activity/opencode-message-tables.d.ts +87 -0
  94. package/dist/src/activity/opencode-message-tables.d.ts.map +1 -0
  95. package/dist/src/activity/opencode-message-tables.js +546 -0
  96. package/dist/src/activity/opencode-message-tables.js.map +1 -0
  97. package/dist/src/activity/opencode-sqlite-source.d.ts +7 -0
  98. package/dist/src/activity/opencode-sqlite-source.d.ts.map +1 -1
  99. package/dist/src/activity/opencode-sqlite-source.js +154 -63
  100. package/dist/src/activity/opencode-sqlite-source.js.map +1 -1
  101. package/dist/src/activity/source-reader.d.ts +60 -4
  102. package/dist/src/activity/source-reader.d.ts.map +1 -1
  103. package/dist/src/activity/source-reader.js +593 -75
  104. package/dist/src/activity/source-reader.js.map +1 -1
  105. package/dist/src/activity/sqlite-connections.d.ts +77 -0
  106. package/dist/src/activity/sqlite-connections.d.ts.map +1 -0
  107. package/dist/src/activity/sqlite-connections.js +220 -0
  108. package/dist/src/activity/sqlite-connections.js.map +1 -0
  109. package/dist/src/activity/zed-sqlite-source.d.ts +65 -0
  110. package/dist/src/activity/zed-sqlite-source.d.ts.map +1 -0
  111. package/dist/src/activity/zed-sqlite-source.js +547 -0
  112. package/dist/src/activity/zed-sqlite-source.js.map +1 -0
  113. package/dist/src/agent-action-executor.d.ts +18 -1
  114. package/dist/src/agent-action-executor.d.ts.map +1 -1
  115. package/dist/src/agent-action-executor.js +113 -58
  116. package/dist/src/agent-action-executor.js.map +1 -1
  117. package/dist/src/agentic-guardian.d.ts.map +1 -1
  118. package/dist/src/agentic-guardian.js +6 -5
  119. package/dist/src/agentic-guardian.js.map +1 -1
  120. package/dist/src/atomic-replace.d.ts +7 -0
  121. package/dist/src/atomic-replace.d.ts.map +1 -1
  122. package/dist/src/atomic-replace.js +28 -3
  123. package/dist/src/atomic-replace.js.map +1 -1
  124. package/dist/src/bridge-journal.d.ts +32 -0
  125. package/dist/src/bridge-journal.d.ts.map +1 -0
  126. package/dist/src/bridge-journal.js +113 -0
  127. package/dist/src/bridge-journal.js.map +1 -0
  128. package/dist/src/cli.d.ts +7 -1
  129. package/dist/src/cli.d.ts.map +1 -1
  130. package/dist/src/cli.js +534 -50
  131. package/dist/src/cli.js.map +1 -1
  132. package/dist/src/collection-notice.d.ts +34 -0
  133. package/dist/src/collection-notice.d.ts.map +1 -0
  134. package/dist/src/collection-notice.js +73 -0
  135. package/dist/src/collection-notice.js.map +1 -0
  136. package/dist/src/config.d.ts +39 -0
  137. package/dist/src/config.d.ts.map +1 -1
  138. package/dist/src/config.js +187 -36
  139. package/dist/src/config.js.map +1 -1
  140. package/dist/src/connection-config.d.ts.map +1 -1
  141. package/dist/src/connection-config.js +5 -0
  142. package/dist/src/connection-config.js.map +1 -1
  143. package/dist/src/content-redaction.d.ts +17 -1
  144. package/dist/src/content-redaction.d.ts.map +1 -1
  145. package/dist/src/content-redaction.js +51 -3
  146. package/dist/src/content-redaction.js.map +1 -1
  147. package/dist/src/credential-store.d.ts +20 -1
  148. package/dist/src/credential-store.d.ts.map +1 -1
  149. package/dist/src/credential-store.js +29 -4
  150. package/dist/src/credential-store.js.map +1 -1
  151. package/dist/src/custom-agent-registry.d.ts.map +1 -1
  152. package/dist/src/custom-agent-registry.js +2 -1
  153. package/dist/src/custom-agent-registry.js.map +1 -1
  154. package/dist/src/discovery/adapters/generic.d.ts.map +1 -1
  155. package/dist/src/discovery/adapters/generic.js +160 -2
  156. package/dist/src/discovery/adapters/generic.js.map +1 -1
  157. package/dist/src/discovery/engine.d.ts +7 -1
  158. package/dist/src/discovery/engine.d.ts.map +1 -1
  159. package/dist/src/discovery/engine.js +21 -16
  160. package/dist/src/discovery/engine.js.map +1 -1
  161. package/dist/src/discovery/known-agent.d.ts +7 -0
  162. package/dist/src/discovery/known-agent.d.ts.map +1 -1
  163. package/dist/src/discovery/known-agent.js +9 -4
  164. package/dist/src/discovery/known-agent.js.map +1 -1
  165. package/dist/src/discovery/system-evidence.d.ts +40 -1
  166. package/dist/src/discovery/system-evidence.d.ts.map +1 -1
  167. package/dist/src/discovery/system-evidence.js +189 -16
  168. package/dist/src/discovery/system-evidence.js.map +1 -1
  169. package/dist/src/doctor-check.d.ts +51 -0
  170. package/dist/src/doctor-check.d.ts.map +1 -0
  171. package/dist/src/doctor-check.js +174 -0
  172. package/dist/src/doctor-check.js.map +1 -0
  173. package/dist/src/enroll.d.ts +86 -0
  174. package/dist/src/enroll.d.ts.map +1 -0
  175. package/dist/src/enroll.js +245 -0
  176. package/dist/src/enroll.js.map +1 -0
  177. package/dist/src/entry.d.ts +3 -0
  178. package/dist/src/entry.d.ts.map +1 -0
  179. package/dist/src/entry.js +14 -0
  180. package/dist/src/entry.js.map +1 -0
  181. package/dist/src/event-queue.d.ts +13 -1
  182. package/dist/src/event-queue.d.ts.map +1 -1
  183. package/dist/src/event-queue.js +137 -42
  184. package/dist/src/event-queue.js.map +1 -1
  185. package/dist/src/event-spool.d.ts +61 -3
  186. package/dist/src/event-spool.d.ts.map +1 -1
  187. package/dist/src/event-spool.js +341 -44
  188. package/dist/src/event-spool.js.map +1 -1
  189. package/dist/src/generated/phigon/v1/identity_pb.d.ts +7 -0
  190. package/dist/src/generated/phigon/v1/identity_pb.d.ts.map +1 -1
  191. package/dist/src/generated/phigon/v1/identity_pb.js +1 -1
  192. package/dist/src/generated/phigon/v1/identity_pb.js.map +1 -1
  193. package/dist/src/guardian-containment.d.ts +2 -6
  194. package/dist/src/guardian-containment.d.ts.map +1 -1
  195. package/dist/src/guardian-containment.js +12 -63
  196. package/dist/src/guardian-containment.js.map +1 -1
  197. package/dist/src/guardian-decision-journal.d.ts.map +1 -1
  198. package/dist/src/guardian-decision-journal.js +4 -19
  199. package/dist/src/guardian-decision-journal.js.map +1 -1
  200. package/dist/src/guardian-managed-exec.d.ts.map +1 -1
  201. package/dist/src/guardian-managed-exec.js +6 -18
  202. package/dist/src/guardian-managed-exec.js.map +1 -1
  203. package/dist/src/guardian-model-bridge.d.ts.map +1 -1
  204. package/dist/src/guardian-model-bridge.js +2 -1
  205. package/dist/src/guardian-model-bridge.js.map +1 -1
  206. package/dist/src/guardian-native.d.ts +0 -1
  207. package/dist/src/guardian-native.d.ts.map +1 -1
  208. package/dist/src/guardian-native.js +0 -6
  209. package/dist/src/guardian-native.js.map +1 -1
  210. package/dist/src/guardian-process-monitor.d.ts +4 -0
  211. package/dist/src/guardian-process-monitor.d.ts.map +1 -1
  212. package/dist/src/guardian-process-monitor.js +82 -26
  213. package/dist/src/guardian-process-monitor.js.map +1 -1
  214. package/dist/src/health.d.ts +19 -1
  215. package/dist/src/health.d.ts.map +1 -1
  216. package/dist/src/health.js +22 -1
  217. package/dist/src/health.js.map +1 -1
  218. package/dist/src/historical-identity-store.d.ts +8 -1
  219. package/dist/src/historical-identity-store.d.ts.map +1 -1
  220. package/dist/src/historical-identity-store.js +28 -10
  221. package/dist/src/historical-identity-store.js.map +1 -1
  222. package/dist/src/index.d.ts +2 -0
  223. package/dist/src/index.d.ts.map +1 -1
  224. package/dist/src/index.js +2 -0
  225. package/dist/src/index.js.map +1 -1
  226. package/dist/src/installation.d.ts +2 -0
  227. package/dist/src/installation.d.ts.map +1 -1
  228. package/dist/src/installation.js +6 -3
  229. package/dist/src/installation.js.map +1 -1
  230. package/dist/src/instance-lock.d.ts +45 -0
  231. package/dist/src/instance-lock.d.ts.map +1 -0
  232. package/dist/src/instance-lock.js +243 -0
  233. package/dist/src/instance-lock.js.map +1 -0
  234. package/dist/src/machine-command-executor.d.ts +22 -7
  235. package/dist/src/machine-command-executor.d.ts.map +1 -1
  236. package/dist/src/machine-command-executor.js +80 -19
  237. package/dist/src/machine-command-executor.js.map +1 -1
  238. package/dist/src/node-version.d.ts +8 -0
  239. package/dist/src/node-version.d.ts.map +1 -0
  240. package/dist/src/node-version.js +17 -0
  241. package/dist/src/node-version.js.map +1 -0
  242. package/dist/src/observer-log.d.ts +50 -0
  243. package/dist/src/observer-log.d.ts.map +1 -0
  244. package/dist/src/observer-log.js +141 -0
  245. package/dist/src/observer-log.js.map +1 -0
  246. package/dist/src/pair.d.ts +68 -1
  247. package/dist/src/pair.d.ts.map +1 -1
  248. package/dist/src/pair.js +204 -65
  249. package/dist/src/pair.js.map +1 -1
  250. package/dist/src/private-directory.d.ts +10 -0
  251. package/dist/src/private-directory.d.ts.map +1 -0
  252. package/dist/src/private-directory.js +66 -0
  253. package/dist/src/private-directory.js.map +1 -0
  254. package/dist/src/process-tree.d.ts +73 -0
  255. package/dist/src/process-tree.d.ts.map +1 -0
  256. package/dist/src/process-tree.js +256 -0
  257. package/dist/src/process-tree.js.map +1 -0
  258. package/dist/src/proxy.d.ts +68 -0
  259. package/dist/src/proxy.d.ts.map +1 -0
  260. package/dist/src/proxy.js +209 -0
  261. package/dist/src/proxy.js.map +1 -0
  262. package/dist/src/purge.d.ts +17 -0
  263. package/dist/src/purge.d.ts.map +1 -0
  264. package/dist/src/purge.js +84 -0
  265. package/dist/src/purge.js.map +1 -0
  266. package/dist/src/remove-machine.d.ts +5 -1
  267. package/dist/src/remove-machine.d.ts.map +1 -1
  268. package/dist/src/remove-machine.js +28 -14
  269. package/dist/src/remove-machine.js.map +1 -1
  270. package/dist/src/server-authority.d.ts +29 -0
  271. package/dist/src/server-authority.d.ts.map +1 -0
  272. package/dist/src/server-authority.js +106 -0
  273. package/dist/src/server-authority.js.map +1 -0
  274. package/dist/src/server-clock.d.ts +32 -0
  275. package/dist/src/server-clock.d.ts.map +1 -0
  276. package/dist/src/server-clock.js +72 -0
  277. package/dist/src/server-clock.js.map +1 -0
  278. package/dist/src/service-command.d.ts +20 -0
  279. package/dist/src/service-command.d.ts.map +1 -0
  280. package/dist/src/service-command.js +136 -0
  281. package/dist/src/service-command.js.map +1 -0
  282. package/dist/src/service.d.ts +98 -0
  283. package/dist/src/service.d.ts.map +1 -0
  284. package/dist/src/service.js +472 -0
  285. package/dist/src/service.js.map +1 -0
  286. package/dist/src/stream-client.d.ts +21 -1
  287. package/dist/src/stream-client.d.ts.map +1 -1
  288. package/dist/src/stream-client.js +137 -24
  289. package/dist/src/stream-client.js.map +1 -1
  290. package/dist/src/stream-state.d.ts +2 -0
  291. package/dist/src/stream-state.d.ts.map +1 -1
  292. package/dist/src/stream-state.js +5 -2
  293. package/dist/src/stream-state.js.map +1 -1
  294. package/dist/src/system-inspection.d.ts +78 -0
  295. package/dist/src/system-inspection.d.ts.map +1 -0
  296. package/dist/src/system-inspection.js +281 -0
  297. package/dist/src/system-inspection.js.map +1 -0
  298. package/dist/src/update.d.ts.map +1 -1
  299. package/dist/src/update.js +9 -3
  300. package/dist/src/update.js.map +1 -1
  301. package/dist/src/user-notices.d.ts +64 -0
  302. package/dist/src/user-notices.d.ts.map +1 -0
  303. package/dist/src/user-notices.js +141 -0
  304. package/dist/src/user-notices.js.map +1 -0
  305. package/package.json +7 -7
  306. package/dist/src/windows-process.d.ts +0 -7
  307. package/dist/src/windows-process.d.ts.map +0 -1
  308. package/dist/src/windows-process.js +0 -18
  309. package/dist/src/windows-process.js.map +0 -1
@@ -1,27 +1,47 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { readdir, stat, unlink } from "node:fs/promises";
2
3
  import { join } from "node:path";
3
4
  import { create, toBinary } from "@bufbuild/protobuf";
4
5
  import { timestampFromDate } from "@bufbuild/protobuf/wkt";
6
+ import { abortableDelay } from "../abortable-delay.js";
5
7
  import { ActivityKind, AgentKind, AgentState, } from "../generated/phigon/v1/common_pb.js";
6
8
  import { AgentIdentitySchema, } from "../generated/phigon/v1/identity_pb.js";
7
9
  import { mergeInventoryWithHistoricalIdentities } from "../historical-identity-store.js";
8
10
  import { EventEnvelopeSchema, } from "../generated/phigon/v1/telemetry_pb.js";
9
11
  import { claudeActivityAdapter } from "./adapters/claude.js";
12
+ import { clineExtensionActivityAdapter, clineTaskFirstMessageField, kiloCodeActivityAdapter, rooCodeActivityAdapter, } from "./adapters/cline-task.js";
13
+ import { clineActivityAdapter } from "./adapters/cline.js";
10
14
  import { codexActivityAdapter } from "./adapters/codex.js";
11
15
  import { copilotActivityAdapter } from "./adapters/copilot.js";
16
+ import { copilotChatActivityAdapter } from "./adapters/copilot-chat.js";
17
+ import { cursorActivityAdapter } from "./adapters/cursor.js";
18
+ import { factoryDroidActivityAdapter } from "./adapters/factory-droid.js";
19
+ import { geminiActivityAdapter } from "./adapters/gemini.js";
20
+ import { gooseActivityAdapter } from "./adapters/goose.js";
12
21
  import { hermesActivityAdapter } from "./adapters/hermes.js";
22
+ import { kiroActivityAdapter } from "./adapters/kiro.js";
13
23
  import { openClawActivityAdapter } from "./adapters/openclaw.js";
14
24
  import { openCodeActivityAdapter } from "./adapters/opencode.js";
25
+ import { zedActivityAdapter } from "./adapters/zed.js";
15
26
  import { createOpenAICompatibleAdapter } from "./adapters/openai-compatible.js";
16
27
  import { activityBridgeAgents } from "./bridges.js";
17
28
  import { genericActivityAdapter } from "./adapters/generic.js";
18
29
  import { LocalPolicyEngine } from "./policy.js";
19
30
  import { vendorDraft } from "./normalize.js";
20
31
  import { objectValue, recordObservedAt, stringField, } from "./record.js";
21
- import { JSONLSourceReader, } from "./source-reader.js";
32
+ import { JSONLSourceReader, fileReadThrough, } from "./source-reader.js";
33
+ import { bridgeWritingSuffix, ingestedBridgeFile } from "./ingest.js";
34
+ import { closedJournalSegments } from "../bridge-journal.js";
22
35
  import { processKeyCapability } from "../discovery/process-identity.js";
23
36
  const maxEnvelopeBytes = 1 << 20;
24
37
  const maxPendingCheckpoints = 256;
38
+ // Bridge files are cleaned up this often, and kept at most this long.
39
+ const bridgePruneIntervalMs = 5 * 60_000;
40
+ const bridgeFileRetentionMs = 7 * 86_400_000;
41
+ // A closed journal segment untouched this long has no writer left.
42
+ const closedSegmentQuietMs = 60_000;
43
+ // Saved live checkpoints remembered while their events await acknowledgement.
44
+ const retainedLiveCheckpoints = 8;
25
45
  export const historySyncQueueHeadroom = 1_024;
26
46
  const maxDraftsPerRecord = 32;
27
47
  const adapters = new Map([
@@ -37,8 +57,37 @@ const adapters = new Map([
37
57
  // Keep the flexible generic parser after the generated bridge adapters so
38
58
  // the generic bridge cannot accidentally replace it with a fixed label.
39
59
  ["generic", genericActivityAdapter],
60
+ // Deliberately replaces the generated OpenCode bridge parser: the native
61
+ // adapter reads both OpenCode's database events and the structured CLI
62
+ // records (step_start, tool_use, text, step_finish) that
63
+ // `ingest --agent opencode` receives, as docs/activity-collection.md says.
40
64
  ["opencode", openCodeActivityAdapter],
65
+ // Native recordings use their own adapter IDs so the OpenAI-compatible
66
+ // `ingest --agent` bridges for the same products keep their parser.
67
+ ["gemini-cli", geminiActivityAdapter],
68
+ ["cline-cli", clineActivityAdapter],
69
+ ["goose-session", gooseActivityAdapter],
70
+ ["cursor-transcript", cursorActivityAdapter],
71
+ ["copilot-chat", copilotChatActivityAdapter],
72
+ ["kiro-session", kiroActivityAdapter],
73
+ ["zed-thread", zedActivityAdapter],
74
+ ["factory-droid", factoryDroidActivityAdapter],
75
+ ["cline-task", clineExtensionActivityAdapter],
76
+ ["roo-code-task", rooCodeActivityAdapter],
77
+ ["kilo-code-task", kiloCodeActivityAdapter],
41
78
  ]);
79
+ /**
80
+ * `emit` refused an event. The reader was put back to where the scan
81
+ * started, so its records are read again once delivery has room.
82
+ */
83
+ class DeliveryRefusedError extends Error {
84
+ }
85
+ const sourceIdentityFailureKey = "agent identities";
86
+ /** The kind of an error, never its message, which may quote a record. */
87
+ function errorName(error) {
88
+ const name = error instanceof Error ? error.name : "";
89
+ return /^[A-Za-z][A-Za-z0-9]{0,63}$/u.test(name) ? name : "unknown error";
90
+ }
42
91
  export class ActivityCollector {
43
92
  #config;
44
93
  #credential;
@@ -52,6 +101,15 @@ export class ActivityCollector {
52
101
  #canCollect;
53
102
  #canSyncHistory;
54
103
  #onSourceIdentity;
104
+ #onDeliveryRefused;
105
+ #onSourceFailure;
106
+ #reportedFailures = new WeakSet();
107
+ // Failures in a row of record conversion, per adapter, and of saving
108
+ // source identities. The keys are a fixed, small set.
109
+ #collectionFailures = new Map();
110
+ #registeredSourcesFailing = false;
111
+ #deliveryRefused = false;
112
+ #refusedDeliveries = 0;
55
113
  #policy;
56
114
  #limiter;
57
115
  #agentsByKind = new Map();
@@ -65,6 +123,11 @@ export class ActivityCollector {
65
123
  #oversizedEvents = 0;
66
124
  #acknowledgedSequence;
67
125
  #durableDelivery;
126
+ // Saved live-reader checkpoints, oldest first, with the sequence that
127
+ // their events end at. Bridge files they read through can be deleted once
128
+ // the server acknowledges that sequence.
129
+ #committedLive = [];
130
+ #flushDelivery;
68
131
  #pendingCheckpoints = [];
69
132
  #checkpointSave = Promise.resolve();
70
133
  #sourceIdentitySaves = [];
@@ -84,6 +147,8 @@ export class ActivityCollector {
84
147
  this.#canCollect = options.canCollect ?? (() => true);
85
148
  this.#canSyncHistory = options.canSyncHistory ?? this.#canCollect;
86
149
  this.#onSourceIdentity = options.onSourceIdentity ?? (() => undefined);
150
+ this.#onDeliveryRefused = options.onDeliveryRefused ?? (() => undefined);
151
+ this.#onSourceFailure = options.onSourceFailure ?? (() => undefined);
87
152
  this.#policy = new LocalPolicyEngine(options.policy ?? options.config.policy, options.config.redactionPathPatterns);
88
153
  this.#limiter = new MinuteRateLimiter(options.config.activityRateLimitPerMinute);
89
154
  this.#lastSequence = options.initialSequence;
@@ -94,6 +159,7 @@ export class ActivityCollector {
94
159
  throw new Error("Initial activity acknowledgement is invalid.");
95
160
  }
96
161
  this.#durableDelivery = options.durableDelivery ?? false;
162
+ this.#flushDelivery = options.flushDelivery ?? (() => undefined);
97
163
  }
98
164
  updateInventory(inventory) {
99
165
  this.#agentsByKind.clear();
@@ -107,6 +173,9 @@ export class ActivityCollector {
107
173
  if (acknowledgedSequence < 0n || allocationSequence < 0n) {
108
174
  throw new Error("Activity sequence must not be negative.");
109
175
  }
176
+ // The server lost acknowledgements: no saved checkpoint is known to be
177
+ // delivered any more.
178
+ this.#committedLive = [];
110
179
  this.#acknowledgedSequence = acknowledgedSequence;
111
180
  this.#lastSequence =
112
181
  allocationSequence < acknowledgedSequence
@@ -140,22 +209,35 @@ export class ActivityCollector {
140
209
  throw new Error("History sync expired before all available records were uploaded.");
141
210
  }
142
211
  if (!this.#canSyncHistory()) {
143
- await abortableDelay(250, signal ?? new AbortController().signal);
212
+ await abortableDelay(250, signal);
144
213
  continue;
145
214
  }
146
215
  // Release the collection lock between bounded batches. A large archive
147
216
  // must not prevent the live reader from following current work.
148
- const pass = await this.#withScanLock(async () => {
149
- const before = this.#emittedEvents;
150
- const result = await this.#collectOnce(this.#historyReader, true);
151
- eventsUploaded += this.#emittedEvents - before;
152
- return result;
153
- });
217
+ const started = performance.now();
218
+ let pass;
219
+ try {
220
+ pass = await this.#withScanLock(async () => {
221
+ const before = this.#emittedEvents;
222
+ const result = await this.#collectOnce(this.#historyReader, true);
223
+ eventsUploaded += this.#emittedEvents - before;
224
+ return result;
225
+ });
226
+ }
227
+ catch (error) {
228
+ if (!(error instanceof DeliveryRefusedError))
229
+ throw error;
230
+ await abortableDelay(250, signal);
231
+ continue;
232
+ }
154
233
  recordsScanned += pass.sourceRecords;
155
234
  filesDiscovered = Math.max(filesDiscovered, pass.discoveredFiles);
156
235
  complete = pass.complete;
157
236
  if (!complete && pass.sourceRecords === 0) {
158
- await abortableDelay(100, signal ?? new AbortController().signal);
237
+ await abortableDelay(100, signal);
238
+ }
239
+ else if (!complete) {
240
+ await abortableDelay(historyRest(performance.now() - started), signal);
159
241
  }
160
242
  }
161
243
  return Object.freeze({
@@ -186,10 +268,12 @@ export class ActivityCollector {
186
268
  discoveredFiles: 0,
187
269
  };
188
270
  }
271
+ const start = reader.position();
189
272
  const scan = await reader.scan([
190
273
  ...this.#sources,
191
- ...await this.#additionalSources(),
274
+ ...await this.#registeredSources(),
192
275
  ], Math.max(1, Math.floor(allowance / maxDraftsPerRecord)));
276
+ this.#reportSourceFailures(reader);
193
277
  this.#sourceRecords += scan.metrics.records;
194
278
  this.#malformedRecords += scan.metrics.malformed;
195
279
  this.#oversizedRecords += scan.metrics.oversized;
@@ -198,61 +282,47 @@ export class ActivityCollector {
198
282
  const adapter = adapters.get(record.adapterId);
199
283
  if (adapter === undefined)
200
284
  continue;
201
- const sessionID = reportedSessionID(record.adapterId, record.value, record.sourceId);
202
- const drafts = adapter.parse(record.value, {
203
- sessionId: sessionID,
204
- observedAt: record.observedAt,
205
- policy: this.#policy,
206
- });
207
- const context = {
208
- sessionId: sessionID,
209
- observedAt: recordObservedAt(record.value, record.observedAt),
210
- policy: this.#policy,
211
- };
212
- const genericAgent = record.adapterId === "generic"
213
- ? reportedGenericAgentIdentity(record.value, record.sourceDisplayName)
214
- : undefined;
215
- const nativeDraft = vendorDraft(adapter.agentKind, context, genericAgent?.hint !== undefined
216
- ? genericAgent.hint
217
- : adapter.id, vendorType(record.value), record.value);
218
- const candidateDrafts = [nativeDraft, ...drafts.slice(0, 31)];
219
- const permittedDrafts = !historical && record.initialReplay === true &&
220
- this.#config.activityInitialReplayMode === "messages"
221
- ? candidateDrafts.filter((draft) => draft.payload.case === "activity" &&
222
- (draft.payload.value.kind === ActivityKind.PROMPT ||
223
- draft.payload.value.kind === ActivityKind.RESPONSE))
224
- : candidateDrafts;
285
+ let converted;
286
+ try {
287
+ converted = this.#convertRecord(record, adapter, historical, now);
288
+ }
289
+ catch (error) {
290
+ // A record this observer cannot convert is skipped like a malformed
291
+ // one: reading it again would fail the same way, and stopping
292
+ // would end collection from every source.
293
+ this.#malformedRecords++;
294
+ this.#reportCollectionFailure(record.adapterId, `one record could not be converted and was skipped (${errorName(error)})`);
295
+ continue;
296
+ }
297
+ this.#collectionFailures.delete(record.adapterId);
298
+ this.#oversizedEvents += converted.oversized;
225
299
  // A source record is atomic: never advance its cursor after dropping
226
300
  // part of its normalized activity. One record may use the final budget
227
301
  // plus a bounded burst; the next scan waits for replenishment.
228
302
  if (!historical)
229
- this.#limiter.consume(now, permittedDrafts.length);
230
- for (const [draftIndex, draft] of permittedDrafts.entries()) {
231
- const nextSequence = this.#lastSequence + 1n;
232
- const event = create(EventEnvelopeSchema, {
233
- tenantId: this.#credential.tenantId,
234
- machineId: this.#credential.machineId,
235
- agentId: this.#agentID(draft.agentKind, record.adapterId, record.sourceId, record.value, record.observedAt, genericAgent?.displayName),
236
- sessionId: draft.sessionId,
237
- sequence: nextSequence,
238
- observedAt: timestampFromDate(clampObservedAt(draft.observedAt, now)),
239
- schemaVersion: 1,
240
- privacyClass: draft.privacyClass,
241
- sourceEventId: createHash("sha256")
242
- .update(`${record.sourceRecordId}\u0000${draftIndex}\u0000${draft.payload.case}`)
243
- .digest("hex"),
244
- payload: draft.payload,
245
- });
246
- if (toBinary(EventEnvelopeSchema, event).byteLength >
247
- maxEnvelopeBytes) {
248
- this.#oversizedEvents++;
249
- continue;
303
+ this.#limiter.consume(now, converted.drafts);
304
+ for (const event of converted.events) {
305
+ try {
306
+ this.#emit(event);
250
307
  }
251
- this.#lastSequence = nextSequence;
252
- this.#emit(event);
308
+ catch (error) {
309
+ // The reader already moved past this scan's records. Put it back
310
+ // so none is skipped; the ones delivered before this event are
311
+ // sent again with the same source event IDs, which the server
312
+ // deduplicates. The sequence stays unused, leaving no gap.
313
+ reader.rewind(start);
314
+ this.#refusedDeliveries++;
315
+ if (!this.#deliveryRefused) {
316
+ this.#deliveryRefused = true;
317
+ this.#onDeliveryRefused(error);
318
+ }
319
+ throw new DeliveryRefusedError("Telemetry delivery refused an activity event.", { cause: error });
320
+ }
321
+ this.#lastSequence = event.sequence;
253
322
  this.#emittedEvents++;
254
323
  }
255
324
  }
325
+ this.#deliveryRefused = false;
256
326
  await this.#flushSourceIdentitySaves();
257
327
  const pending = {
258
328
  throughSequence: this.#lastSequence,
@@ -274,12 +344,163 @@ export class ActivityCollector {
274
344
  discoveredFiles: scan.metrics.discoveredFiles,
275
345
  };
276
346
  }
347
+ /**
348
+ * The events for one source record, numbered from the next sequence, and
349
+ * how many normalized drafts they came from. Throws when the record cannot
350
+ * be converted; nothing is emitted for it then.
351
+ */
352
+ #convertRecord(record, adapter, historical, now) {
353
+ const sessionID = reportedSessionID(record.adapterId, record.value, record.sourceId);
354
+ const drafts = adapter.parse(record.value, {
355
+ sessionId: sessionID,
356
+ observedAt: record.observedAt,
357
+ policy: this.#policy,
358
+ });
359
+ const context = {
360
+ sessionId: sessionID,
361
+ observedAt: recordObservedAt(record.value, record.observedAt),
362
+ policy: this.#policy,
363
+ };
364
+ const genericAgent = record.adapterId === "generic"
365
+ ? reportedGenericAgentIdentity(record.value, record.sourceDisplayName)
366
+ : undefined;
367
+ const nativeDraft = vendorDraft(adapter.agentKind, context, genericAgent?.hint !== undefined
368
+ ? genericAgent.hint
369
+ : adapter.id, vendorType(record.value), record.value);
370
+ const candidateDrafts = [nativeDraft, ...drafts.slice(0, 31)];
371
+ const permittedDrafts = !historical && record.initialReplay === true &&
372
+ this.#config.activityInitialReplayMode === "messages"
373
+ ? candidateDrafts.filter((draft) => draft.payload.case === "activity" &&
374
+ (draft.payload.value.kind === ActivityKind.PROMPT ||
375
+ draft.payload.value.kind === ActivityKind.RESPONSE))
376
+ : candidateDrafts;
377
+ const events = [];
378
+ let oversized = 0;
379
+ for (const [draftIndex, draft] of permittedDrafts.entries()) {
380
+ const event = create(EventEnvelopeSchema, {
381
+ tenantId: this.#credential.tenantId,
382
+ machineId: this.#credential.machineId,
383
+ agentId: this.#agentID(draft.agentKind, record.adapterId, record.sourceId, record.value, record.observedAt, genericAgent?.displayName),
384
+ sessionId: draft.sessionId,
385
+ sequence: this.#lastSequence + 1n + BigInt(events.length),
386
+ observedAt: timestampFromDate(clampObservedAt(draft.observedAt, now)),
387
+ schemaVersion: 1,
388
+ privacyClass: draft.privacyClass,
389
+ sourceEventId: createHash("sha256")
390
+ .update(`${record.sourceRecordId}\u0000${draftIndex}\u0000${draft.payload.case}`)
391
+ .digest("hex"),
392
+ payload: draft.payload,
393
+ });
394
+ if (toBinary(EventEnvelopeSchema, event).byteLength >
395
+ maxEnvelopeBytes) {
396
+ oversized++;
397
+ continue;
398
+ }
399
+ events.push(event);
400
+ }
401
+ return { events, drafts: permittedDrafts.length, oversized };
402
+ }
403
+ /**
404
+ * Says that collection work keeps failing, at the 1st, 2nd, 4th, 8th...
405
+ * failure in a row, so it stays visible without flooding the log. The
406
+ * message never includes activity content.
407
+ */
408
+ #reportCollectionFailure(key, message) {
409
+ const failures = (this.#collectionFailures.get(key) ?? 0) + 1;
410
+ this.#collectionFailures.set(key, failures);
411
+ if ((failures & (failures - 1)) !== 0)
412
+ return;
413
+ const now = this.#clock();
414
+ this.#onSourceFailure(Object.freeze({
415
+ adapterId: key,
416
+ failures,
417
+ message,
418
+ failedAt: now,
419
+ retryAt: now,
420
+ }));
421
+ }
277
422
  async run(signal) {
278
- while (!signal.aborted) {
279
- await this.scanOnce();
280
- await abortableDelay(this.#config.activityScanIntervalMs, signal);
423
+ let prunedAt = 0;
424
+ try {
425
+ while (!signal.aborted) {
426
+ try {
427
+ await this.scanOnce();
428
+ }
429
+ catch (error) {
430
+ // A full queue or spool waits for acknowledgements like any
431
+ // other backpressure; the refused records are read again.
432
+ if (!(error instanceof DeliveryRefusedError))
433
+ throw error;
434
+ }
435
+ if (this.#clock().getTime() - prunedAt >= bridgePruneIntervalMs) {
436
+ prunedAt = this.#clock().getTime();
437
+ // Cleanup only frees disk space; it retries on the next pass.
438
+ await this.pruneBridgeFiles().catch(() => undefined);
439
+ }
440
+ await abortableDelay(this.#config.activityScanIntervalMs, signal);
441
+ }
442
+ }
443
+ finally {
444
+ this.close();
445
+ }
446
+ }
447
+ /**
448
+ * Deletes bridge files this observer is done with: an `ingest` file or a
449
+ * closed journal segment whose records were all read under a checkpoint
450
+ * the server has acknowledged through, or any of them after seven days.
451
+ * A file an `ingest` run is still writing, and each journal's newest
452
+ * segment, are never deleted while in use.
453
+ */
454
+ async pruneBridgeFiles() {
455
+ const now = this.#clock().getTime();
456
+ const acknowledged = this.#acknowledgedSequence;
457
+ const settled = this.#committedLive.findLast((checkpoint) => checkpoint.throughSequence <= acknowledged);
458
+ for (const agent of activityBridgeAgents) {
459
+ const root = join(this.#config.dataDirectory, "bridges", agent.id);
460
+ let names;
461
+ try {
462
+ names = await readdir(root);
463
+ }
464
+ catch {
465
+ continue;
466
+ }
467
+ const present = new Set(names);
468
+ const closedSegments = new Set(closedJournalSegments(names));
469
+ for (const name of names) {
470
+ const ingested = ingestedBridgeFile.test(name);
471
+ if (!ingested && !closedSegments.has(name))
472
+ continue;
473
+ const path = join(root, name);
474
+ try {
475
+ const modifiedMs = (await stat(path)).mtimeMs;
476
+ const expired = now - modifiedMs >= bridgeFileRetentionMs;
477
+ const inUse = ingested
478
+ ? present.has(`${name}${bridgeWritingSuffix}`)
479
+ // A segment closes when its journal starts the next one; a
480
+ // writer that chose it just before may still be appending.
481
+ : now - modifiedMs < closedSegmentQuietMs;
482
+ if (expired ||
483
+ (!inUse && settled !== undefined &&
484
+ await fileReadThrough(path, settled.cursors))) {
485
+ await unlink(path);
486
+ if (ingested) {
487
+ await unlink(`${path}${bridgeWritingSuffix}`).catch(() => undefined);
488
+ }
489
+ }
490
+ }
491
+ catch {
492
+ // Gone, or locked by another program: retried on the next pass.
493
+ }
494
+ }
281
495
  }
282
496
  }
497
+ /**
498
+ * Release the database connections the live reader keeps open. A later
499
+ * scan opens them again.
500
+ */
501
+ close() {
502
+ this.#reader.close();
503
+ }
283
504
  metrics() {
284
505
  return Object.freeze({
285
506
  sourceRecords: this.#sourceRecords,
@@ -291,8 +512,53 @@ export class ActivityCollector {
291
512
  lastSequence: this.#lastSequence,
292
513
  pendingCheckpoints: this.#pendingCheckpoints.length,
293
514
  backpressuredScans: this.#backpressuredScans,
515
+ refusedDeliveries: this.#refusedDeliveries,
516
+ failingSources: this.#reader.sourceFailures().length,
294
517
  });
295
518
  }
519
+ /**
520
+ * Sources of registered custom agents. When their list cannot be read,
521
+ * this scan goes on with the built-in sources.
522
+ */
523
+ async #registeredSources() {
524
+ try {
525
+ const sources = await this.#additionalSources();
526
+ this.#registeredSourcesFailing = false;
527
+ return sources;
528
+ }
529
+ catch (error) {
530
+ if (!this.#registeredSourcesFailing) {
531
+ this.#registeredSourcesFailing = true;
532
+ const now = this.#clock();
533
+ this.#onSourceFailure(Object.freeze({
534
+ adapterId: "registered agents",
535
+ failures: 1,
536
+ message: (error instanceof Error ? error.message : String(error))
537
+ .replace(/\s+/gu, " ")
538
+ .slice(0, 200),
539
+ failedAt: now,
540
+ // Read again on the next scan.
541
+ retryAt: now,
542
+ }));
543
+ }
544
+ return [];
545
+ }
546
+ }
547
+ #reportSourceFailures(reader) {
548
+ for (const failure of reader.sourceFailures()) {
549
+ if (failure.failures !== 1 || this.#reportedFailures.has(failure))
550
+ continue;
551
+ this.#reportedFailures.add(failure);
552
+ this.#onSourceFailure(failure);
553
+ }
554
+ }
555
+ /** Sources and files skipped after a failure, by the live and history readers. */
556
+ sourceFailures() {
557
+ return [
558
+ ...this.#reader.sourceFailures(),
559
+ ...this.#historyReader.sourceFailures(),
560
+ ];
561
+ }
296
562
  sourceIdentities() {
297
563
  return [...this.#sourceIdentities.values()].sort((left, right) => left.agentId.localeCompare(right.agentId));
298
564
  }
@@ -306,15 +572,34 @@ export class ActivityCollector {
306
572
  this.#checkpointSave = this.#checkpointSave
307
573
  .catch(() => undefined)
308
574
  .then(async () => {
309
- const currentDurableThrough = this.#durableDelivery
310
- ? this.#lastSequence
311
- : this.#acknowledgedSequence;
575
+ let currentDurableThrough = this.#acknowledgedSequence;
576
+ if (this.#durableDelivery) {
577
+ const emittedThrough = this.#lastSequence;
578
+ try {
579
+ await this.#flushDelivery();
580
+ currentDurableThrough = emittedThrough;
581
+ }
582
+ catch {
583
+ // Not written: only checkpoints the server acknowledged move.
584
+ // The rest stay pending for the next save, and delivery refuses
585
+ // new events until the spool can be written again.
586
+ }
587
+ }
312
588
  const committed = new Set();
313
589
  for (const pending of eligible) {
314
590
  if (pending.throughSequence > currentDurableThrough)
315
591
  continue;
316
592
  await pending.reader.commit(pending.checkpoint);
317
593
  committed.add(pending);
594
+ if (pending.reader === this.#reader) {
595
+ this.#committedLive = [
596
+ ...this.#committedLive.slice(-(retainedLiveCheckpoints - 1)),
597
+ {
598
+ cursors: pending.checkpoint.cursors,
599
+ throughSequence: pending.throughSequence,
600
+ },
601
+ ];
602
+ }
318
603
  }
319
604
  this.#pendingCheckpoints = this.#pendingCheckpoints.filter((candidate) => !committed.has(candidate));
320
605
  });
@@ -366,21 +651,45 @@ export class ActivityCollector {
366
651
  capabilities: ["activity", "source-isolated", "unattributed"],
367
652
  });
368
653
  this.#sourceIdentities.set(agentID, identity);
654
+ // An identity that could not be saved is forgotten, so the source's
655
+ // next record saves it again.
656
+ const forget = (error) => {
657
+ if (this.#sourceIdentities.get(agentID) === identity) {
658
+ this.#sourceIdentities.delete(agentID);
659
+ }
660
+ throw error;
661
+ };
662
+ let save;
369
663
  try {
370
- this.#sourceIdentitySaves.push(Promise.resolve(this.#onSourceIdentity(identity)));
664
+ save = Promise.resolve(this.#onSourceIdentity(identity)).catch(forget);
371
665
  }
372
666
  catch (error) {
373
- this.#sourceIdentitySaves.push(Promise.reject(error));
667
+ save = Promise.reject(error).catch(forget);
374
668
  }
669
+ // Handled here too: a scan that stops early leaves the save for the
670
+ // next flush, and an unhandled rejection would end the process.
671
+ save.catch(() => undefined);
672
+ this.#sourceIdentitySaves.push(save);
375
673
  }
674
+ /**
675
+ * Waits for the identity saves this scan started. A failed save is
676
+ * reported and retried with a later record; it never stops collection,
677
+ * because the events already carry the agent ID.
678
+ */
376
679
  async #flushSourceIdentitySaves() {
377
680
  const pending = this.#sourceIdentitySaves.splice(0);
378
681
  if (pending.length === 0)
379
682
  return;
380
683
  const results = await Promise.allSettled(pending);
381
684
  const failure = results.find((result) => result.status === "rejected");
382
- if (failure !== undefined)
383
- throw failure.reason;
685
+ if (failure === undefined) {
686
+ this.#collectionFailures.delete(sourceIdentityFailureKey);
687
+ return;
688
+ }
689
+ const reason = failure.reason;
690
+ this.#reportCollectionFailure(sourceIdentityFailureKey, (reason instanceof Error ? reason.message : String(reason))
691
+ .replace(/\s+/gu, " ")
692
+ .slice(0, 200));
384
693
  }
385
694
  }
386
695
  export function mergeInventoryWithSourceIdentities(inventory, sourceIdentities) {
@@ -557,6 +866,99 @@ function configuredSources(config) {
557
866
  roots: config.activitySourceDirectories.opencode,
558
867
  format: "opencode-sqlite",
559
868
  },
869
+ {
870
+ adapterId: "gemini-cli",
871
+ roots: config.activitySourceDirectories.gemini,
872
+ // <project>/chats/session-*.jsonl and subagent <parent>/<id>.jsonl.
873
+ pathPattern: "^[^/]+/chats/",
874
+ },
875
+ {
876
+ adapterId: "cline-cli",
877
+ roots: config.activitySourceDirectories.cline,
878
+ format: "json-messages",
879
+ // <session>/<session>.messages.json, rewritten in place per turn.
880
+ pathPattern: "^[^/]+/[^/]+\\.messages\\.json$",
881
+ messagesField: "messages",
882
+ },
883
+ {
884
+ adapterId: "goose-session",
885
+ roots: config.activitySourceDirectories.goose,
886
+ format: "goose-sqlite",
887
+ },
888
+ {
889
+ adapterId: "cursor-transcript",
890
+ roots: config.activitySourceDirectories.cursor,
891
+ format: "json-messages",
892
+ // <project>/agent-transcripts/<id>/<id>.jsonl and
893
+ // <id>/subagents/<sub>.jsonl. The final line often has no newline yet.
894
+ pathPattern: "^[^/]+/agent-transcripts/.+\\.jsonl$",
895
+ document: {
896
+ documentFormat: "jsonl",
897
+ directoryPattern: "^[^/]+(?:/agent-transcripts(?:/[^/]+(?:/subagents)?)?)?$",
898
+ },
899
+ },
900
+ // VS Code chat sessions: <workspace>/chatSessions/<id>.json documents
901
+ // and, with operation-log storage, <id>.jsonl logs of the same session.
902
+ ...["json", "jsonl"].map((extension) => ({
903
+ adapterId: "copilot-chat",
904
+ roots: config.activitySourceDirectories.copilotChat,
905
+ format: "json-messages",
906
+ pathPattern: `^(?:[^/]+/chatSessions/)?[^/]+\\.${extension}$`,
907
+ messagesField: "requests",
908
+ document: {
909
+ documentFormat: extension === "json"
910
+ ? "json"
911
+ : "mutation-log",
912
+ directoryPattern: "^[^/]+(?:/chatSessions)?$",
913
+ messageIdField: "requestId",
914
+ completeFields: ["result", "isCanceled", "modelState.completedAt"],
915
+ sessionIdField: "sessionId",
916
+ },
917
+ })),
918
+ {
919
+ adapterId: "kiro-session",
920
+ roots: config.activitySourceDirectories.kiro,
921
+ format: "json-messages",
922
+ // <workspace>/<session id>.json beside the <workspace>/sessions.json index.
923
+ pathPattern: "^[^/]+/(?!sessions[.]json$)[^/]+[.]json$",
924
+ messagesField: "history",
925
+ document: {
926
+ directoryPattern: "^[^/]+$",
927
+ messageIdField: "message.id",
928
+ sessionIdField: "sessionId",
929
+ },
930
+ },
931
+ {
932
+ adapterId: "zed-thread",
933
+ roots: config.activitySourceDirectories.zed,
934
+ format: "zed-sqlite",
935
+ },
936
+ {
937
+ adapterId: "factory-droid",
938
+ roots: config.activitySourceDirectories.droid,
939
+ // <project>/<session id>.jsonl, appended as the session runs, beside
940
+ // <session id>.settings.json.
941
+ pathPattern: "^(?:[^/]+/)?[^/]+\\.jsonl$",
942
+ },
943
+ // Cline, Roo Code and Kilo Code tasks: <task id>/ui_messages.json, the
944
+ // list of messages the extension shows, rewritten as the task runs.
945
+ ...[
946
+ ["cline-task", config.activitySourceDirectories.clineTasks],
947
+ ["roo-code-task", config.activitySourceDirectories.rooCode],
948
+ ["kilo-code-task", config.activitySourceDirectories.kiloCode],
949
+ ].map(([adapterId, roots]) => ({
950
+ adapterId,
951
+ roots,
952
+ format: "json-messages",
953
+ pathPattern: "^[^/]+/ui_messages[.]json$",
954
+ messagesField: "",
955
+ document: {
956
+ directoryPattern: "^[^/]+$",
957
+ messageIdField: "ts",
958
+ pendingField: "partial",
959
+ firstMessageField: clineTaskFirstMessageField,
960
+ },
961
+ })),
560
962
  {
561
963
  adapterId: "generic",
562
964
  roots: config.activitySourceDirectories.generic,
@@ -602,6 +1004,17 @@ function sourceIdentityDisplayName(adapterID, sourceID, sourceDisplayName) {
602
1004
  case "hermes": return "Hermes Agent";
603
1005
  case "openclaw": return "OpenClaw";
604
1006
  case "opencode": return "OpenCode";
1007
+ case "gemini-cli": return "Gemini CLI";
1008
+ case "cline-cli": return "Cline";
1009
+ case "goose-session": return "Goose";
1010
+ case "cursor-transcript": return "Cursor";
1011
+ case "copilot-chat": return "GitHub Copilot";
1012
+ case "kiro-session": return "Kiro";
1013
+ case "zed-thread": return "Zed";
1014
+ case "factory-droid": return "Factory Droid";
1015
+ case "cline-task": return "Cline";
1016
+ case "roo-code-task": return "Roo Code";
1017
+ case "kilo-code-task": return "Kilo Code";
605
1018
  default: return "Agent activity";
606
1019
  }
607
1020
  })();
@@ -692,15 +1105,15 @@ function clampObservedAt(value, now) {
692
1105
  return now;
693
1106
  return value;
694
1107
  }
695
- async function abortableDelay(milliseconds, signal) {
696
- if (signal.aborted)
697
- return;
698
- await new Promise((resolve) => {
699
- const timeout = setTimeout(resolve, milliseconds);
700
- signal.addEventListener("abort", () => {
701
- clearTimeout(timeout);
702
- resolve();
703
- }, { once: true });
704
- });
1108
+ /**
1109
+ * How long a history import rests after a batch that took `spentMs`. Resting
1110
+ * as long as the batch worked keeps a large first import near half a core,
1111
+ * and leaves the other half of the event loop to live collection; a full
1112
+ * catch-up once held a whole core for minutes. One second at most.
1113
+ */
1114
+ export function historyRest(spentMs) {
1115
+ if (!Number.isFinite(spentMs) || spentMs <= 0)
1116
+ return 0;
1117
+ return Math.min(1_000, Math.round(spentMs));
705
1118
  }
706
1119
  //# sourceMappingURL=collector.js.map