@threadbase-sh/streamer 1.63.0 → 1.65.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 (300) hide show
  1. package/dist/agent/agent-client.d.ts +14 -0
  2. package/dist/agent/agent-client.d.ts.map +1 -0
  3. package/dist/agent/agent-config.d.ts +24 -0
  4. package/dist/agent/agent-config.d.ts.map +1 -0
  5. package/dist/agent/conversation-writer.d.ts +13 -0
  6. package/dist/agent/conversation-writer.d.ts.map +1 -0
  7. package/dist/agent/dedupe.d.ts +6 -0
  8. package/dist/agent/dedupe.d.ts.map +1 -0
  9. package/dist/agent/errors.d.ts +23 -0
  10. package/dist/agent/errors.d.ts.map +1 -0
  11. package/dist/agent/handle-send-agent-input.d.ts +21 -0
  12. package/dist/agent/handle-send-agent-input.d.ts.map +1 -0
  13. package/dist/agent/handle-start-agent-session.d.ts +21 -0
  14. package/dist/agent/handle-start-agent-session.d.ts.map +1 -0
  15. package/dist/agent/history-mapper.d.ts +4 -0
  16. package/dist/agent/history-mapper.d.ts.map +1 -0
  17. package/dist/agent/payload-guard.d.ts +29 -0
  18. package/dist/agent/payload-guard.d.ts.map +1 -0
  19. package/dist/api/app.d.ts +28 -0
  20. package/dist/api/app.d.ts.map +1 -0
  21. package/dist/api/handlers/conversations.handlers.d.ts +69 -0
  22. package/dist/api/handlers/conversations.handlers.d.ts.map +1 -0
  23. package/dist/api/handlers/http-helpers.d.ts +50 -0
  24. package/dist/api/handlers/http-helpers.d.ts.map +1 -0
  25. package/dist/api/handlers/sessions.handlers.d.ts +236 -0
  26. package/dist/api/handlers/sessions.handlers.d.ts.map +1 -0
  27. package/dist/api/middleware/auth.middleware.d.ts +5 -0
  28. package/dist/api/middleware/auth.middleware.d.ts.map +1 -0
  29. package/dist/api/middleware/cors.middleware.d.ts +5 -0
  30. package/dist/api/middleware/cors.middleware.d.ts.map +1 -0
  31. package/dist/api/middleware/error.middleware.d.ts +4 -0
  32. package/dist/api/middleware/error.middleware.d.ts.map +1 -0
  33. package/dist/api/routes/backup.routes.d.ts +5 -0
  34. package/dist/api/routes/backup.routes.d.ts.map +1 -0
  35. package/dist/api/routes/browse.routes.d.ts +5 -0
  36. package/dist/api/routes/browse.routes.d.ts.map +1 -0
  37. package/dist/api/routes/cacheAlert.routes.d.ts +5 -0
  38. package/dist/api/routes/cacheAlert.routes.d.ts.map +1 -0
  39. package/dist/api/routes/config.routes.d.ts +5 -0
  40. package/dist/api/routes/config.routes.d.ts.map +1 -0
  41. package/dist/api/routes/conversations.routes.d.ts +5 -0
  42. package/dist/api/routes/conversations.routes.d.ts.map +1 -0
  43. package/dist/api/routes/devices.routes.d.ts +17 -0
  44. package/dist/api/routes/devices.routes.d.ts.map +1 -0
  45. package/dist/api/routes/diagnostics.routes.d.ts +5 -0
  46. package/dist/api/routes/diagnostics.routes.d.ts.map +1 -0
  47. package/dist/api/routes/health.routes.d.ts +5 -0
  48. package/dist/api/routes/health.routes.d.ts.map +1 -0
  49. package/dist/api/routes/logs.routes.d.ts +3 -0
  50. package/dist/api/routes/logs.routes.d.ts.map +1 -0
  51. package/dist/api/routes/misc.routes.d.ts +77 -0
  52. package/dist/api/routes/misc.routes.d.ts.map +1 -0
  53. package/dist/api/routes/pair.routes.d.ts +5 -0
  54. package/dist/api/routes/pair.routes.d.ts.map +1 -0
  55. package/dist/api/routes/progress.routes.d.ts +39 -0
  56. package/dist/api/routes/progress.routes.d.ts.map +1 -0
  57. package/dist/api/routes/projects.routes.d.ts +5 -0
  58. package/dist/api/routes/projects.routes.d.ts.map +1 -0
  59. package/dist/api/routes/providers.routes.d.ts +19 -0
  60. package/dist/api/routes/providers.routes.d.ts.map +1 -0
  61. package/dist/api/routes/scanner.routes.d.ts +5 -0
  62. package/dist/api/routes/scanner.routes.d.ts.map +1 -0
  63. package/dist/api/routes/sessions.routes.d.ts +6 -0
  64. package/dist/api/routes/sessions.routes.d.ts.map +1 -0
  65. package/dist/api/routes/ws.routes.d.ts +7 -0
  66. package/dist/api/routes/ws.routes.d.ts.map +1 -0
  67. package/dist/api/types/api-deps.d.ts +114 -0
  68. package/dist/api/types/api-deps.d.ts.map +1 -0
  69. package/dist/auth.d.ts +81 -0
  70. package/dist/auth.d.ts.map +1 -0
  71. package/dist/browse.d.ts +17 -0
  72. package/dist/browse.d.ts.map +1 -0
  73. package/dist/claude-flags.d.ts +76 -0
  74. package/dist/claude-flags.d.ts.map +1 -0
  75. package/dist/cli.cjs +121 -7
  76. package/dist/cli.cjs.map +1 -1
  77. package/dist/codex-pty-runner.d.ts +90 -0
  78. package/dist/codex-pty-runner.d.ts.map +1 -0
  79. package/dist/config/update-config.d.ts +13 -0
  80. package/dist/config/update-config.d.ts.map +1 -0
  81. package/dist/conversation-cache.d.ts +336 -0
  82. package/dist/conversation-cache.d.ts.map +1 -0
  83. package/dist/db/config.d.ts +11 -0
  84. package/dist/db/config.d.ts.map +1 -0
  85. package/dist/db/index.d.ts +5 -0
  86. package/dist/db/index.d.ts.map +1 -0
  87. package/dist/db/migrations.d.ts +3 -0
  88. package/dist/db/migrations.d.ts.map +1 -0
  89. package/dist/db/pool.d.ts +5 -0
  90. package/dist/db/pool.d.ts.map +1 -0
  91. package/dist/db/query-timing.d.ts +52 -0
  92. package/dist/db/query-timing.d.ts.map +1 -0
  93. package/dist/db/repositories/cacheMetadata.repository.d.ts +12 -0
  94. package/dist/db/repositories/cacheMetadata.repository.d.ts.map +1 -0
  95. package/dist/db/repositories/conversations.repository.d.ts +27 -0
  96. package/dist/db/repositories/conversations.repository.d.ts.map +1 -0
  97. package/dist/db/repositories/devices.repository.d.ts +137 -0
  98. package/dist/db/repositories/devices.repository.d.ts.map +1 -0
  99. package/dist/db/repositories/managed-sessions.repository.d.ts +127 -0
  100. package/dist/db/repositories/managed-sessions.repository.d.ts.map +1 -0
  101. package/dist/db/repositories/projects.repository.d.ts +28 -0
  102. package/dist/db/repositories/projects.repository.d.ts.map +1 -0
  103. package/dist/db/repositories/push.repository.d.ts +186 -0
  104. package/dist/db/repositories/push.repository.d.ts.map +1 -0
  105. package/dist/db/repositories/sessions.repository.d.ts +16 -0
  106. package/dist/db/repositories/sessions.repository.d.ts.map +1 -0
  107. package/dist/db/runtime-store.d.ts +80 -0
  108. package/dist/db/runtime-store.d.ts.map +1 -0
  109. package/dist/db/sqlite-migrate.d.ts +13 -0
  110. package/dist/db/sqlite-migrate.d.ts.map +1 -0
  111. package/dist/db/upload-records.d.ts +11 -0
  112. package/dist/db/upload-records.d.ts.map +1 -0
  113. package/dist/e2ee/noise.d.ts +261 -0
  114. package/dist/e2ee/noise.d.ts.map +1 -0
  115. package/dist/e2ee/pair-payload.d.ts +60 -0
  116. package/dist/e2ee/pair-payload.d.ts.map +1 -0
  117. package/dist/e2ee/pair-request.d.ts +24 -0
  118. package/dist/e2ee/pair-request.d.ts.map +1 -0
  119. package/dist/external-tails.d.ts +91 -0
  120. package/dist/external-tails.d.ts.map +1 -0
  121. package/dist/feature-flags.d.ts +212 -0
  122. package/dist/feature-flags.d.ts.map +1 -0
  123. package/dist/handlers/handleListProjects.d.ts +3 -0
  124. package/dist/handlers/handleListProjects.d.ts.map +1 -0
  125. package/dist/index.cjs +101 -2
  126. package/dist/index.cjs.map +1 -1
  127. package/dist/index.d.ts +18 -2571
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +101 -2
  130. package/dist/index.js.map +1 -1
  131. package/dist/lifecycle/constants.d.ts +21 -0
  132. package/dist/lifecycle/constants.d.ts.map +1 -0
  133. package/dist/lifecycle/process-liveness.d.ts +7 -0
  134. package/dist/lifecycle/process-liveness.d.ts.map +1 -0
  135. package/dist/live-session-manager.d.ts +59 -0
  136. package/dist/live-session-manager.d.ts.map +1 -0
  137. package/dist/logger.d.ts +14 -0
  138. package/dist/logger.d.ts.map +1 -0
  139. package/dist/pair-store.d.ts +60 -0
  140. package/dist/pair-store.d.ts.map +1 -0
  141. package/dist/platform.d.ts +25 -0
  142. package/dist/platform.d.ts.map +1 -0
  143. package/dist/process-discovery.d.ts +35 -0
  144. package/dist/process-discovery.d.ts.map +1 -0
  145. package/dist/providers.d.ts +7 -0
  146. package/dist/providers.d.ts.map +1 -0
  147. package/dist/pty-host/protocol.d.ts +270 -0
  148. package/dist/pty-host/protocol.d.ts.map +1 -0
  149. package/dist/pty-host/remote-session-runner.d.ts +133 -0
  150. package/dist/pty-host/remote-session-runner.d.ts.map +1 -0
  151. package/dist/pty-host/socket.d.ts +54 -0
  152. package/dist/pty-host/socket.d.ts.map +1 -0
  153. package/dist/pty-host/spawn-host.d.ts +30 -0
  154. package/dist/pty-host/spawn-host.d.ts.map +1 -0
  155. package/dist/pty-manager.d.ts +65 -0
  156. package/dist/pty-manager.d.ts.map +1 -0
  157. package/dist/pty-shared.d.ts +27 -0
  158. package/dist/pty-shared.d.ts.map +1 -0
  159. package/dist/scanner-manager.d.ts +126 -0
  160. package/dist/scanner-manager.d.ts.map +1 -0
  161. package/dist/schemas/cacheAlert.schema.d.ts +14 -0
  162. package/dist/schemas/cacheAlert.schema.d.ts.map +1 -0
  163. package/dist/schemas/claudeFlags.schema.d.ts +7 -0
  164. package/dist/schemas/claudeFlags.schema.d.ts.map +1 -0
  165. package/dist/schemas/project.schema.d.ts +18 -0
  166. package/dist/schemas/project.schema.d.ts.map +1 -0
  167. package/dist/schemas/updateConfig.schema.d.ts +19 -0
  168. package/dist/schemas/updateConfig.schema.d.ts.map +1 -0
  169. package/dist/seal.d.ts +14 -0
  170. package/dist/seal.d.ts.map +1 -0
  171. package/dist/server-identity.d.ts +43 -0
  172. package/dist/server-identity.d.ts.map +1 -0
  173. package/dist/server-wiring.d.ts +189 -0
  174. package/dist/server-wiring.d.ts.map +1 -0
  175. package/dist/server.d.ts +404 -0
  176. package/dist/server.d.ts.map +1 -0
  177. package/dist/services/backup/backup.d.ts +103 -0
  178. package/dist/services/backup/backup.d.ts.map +1 -0
  179. package/dist/services/cache/cacheMetadata.d.ts +8 -0
  180. package/dist/services/cache/cacheMetadata.d.ts.map +1 -0
  181. package/dist/services/cache-integrity/alertStore.d.ts +24 -0
  182. package/dist/services/cache-integrity/alertStore.d.ts.map +1 -0
  183. package/dist/services/cache-integrity/backup.d.ts +8 -0
  184. package/dist/services/cache-integrity/backup.d.ts.map +1 -0
  185. package/dist/services/cache-integrity/cacheIntegrityMonitor.d.ts +71 -0
  186. package/dist/services/cache-integrity/cacheIntegrityMonitor.d.ts.map +1 -0
  187. package/dist/services/conversations/conversationWatcher.d.ts +99 -0
  188. package/dist/services/conversations/conversationWatcher.d.ts.map +1 -0
  189. package/dist/services/conversations/findSearchTarget.d.ts +30 -0
  190. package/dist/services/conversations/findSearchTarget.d.ts.map +1 -0
  191. package/dist/services/conversations/isAgentConversation.d.ts +8 -0
  192. package/dist/services/conversations/isAgentConversation.d.ts.map +1 -0
  193. package/dist/services/conversations/pruneAgentConversations.d.ts +8 -0
  194. package/dist/services/conversations/pruneAgentConversations.d.ts.map +1 -0
  195. package/dist/services/conversations/refreshConversationCache.d.ts +27 -0
  196. package/dist/services/conversations/refreshConversationCache.d.ts.map +1 -0
  197. package/dist/services/conversations/shouldRefreshProjectsFromHdd.d.ts +33 -0
  198. package/dist/services/conversations/shouldRefreshProjectsFromHdd.d.ts.map +1 -0
  199. package/dist/services/diagnostics/diagnostics.d.ts +66 -0
  200. package/dist/services/diagnostics/diagnostics.d.ts.map +1 -0
  201. package/dist/services/projectChats/deriveProjectChatTitle.d.ts +15 -0
  202. package/dist/services/projectChats/deriveProjectChatTitle.d.ts.map +1 -0
  203. package/dist/services/projects/ensureProjectsForConversations.d.ts +15 -0
  204. package/dist/services/projects/ensureProjectsForConversations.d.ts.map +1 -0
  205. package/dist/services/providers/capabilities.d.ts +105 -0
  206. package/dist/services/providers/capabilities.d.ts.map +1 -0
  207. package/dist/services/providers/providerHealth.d.ts +72 -0
  208. package/dist/services/providers/providerHealth.d.ts.map +1 -0
  209. package/dist/services/push/apnsClient.d.ts +105 -0
  210. package/dist/services/push/apnsClient.d.ts.map +1 -0
  211. package/dist/services/push/expoPushSender.d.ts +37 -0
  212. package/dist/services/push/expoPushSender.d.ts.map +1 -0
  213. package/dist/services/push/liveActivityContentState.d.ts +60 -0
  214. package/dist/services/push/liveActivityContentState.d.ts.map +1 -0
  215. package/dist/services/push/liveActivityNotifier.d.ts +49 -0
  216. package/dist/services/push/liveActivityNotifier.d.ts.map +1 -0
  217. package/dist/services/push/liveActivityRenewal.d.ts +63 -0
  218. package/dist/services/push/liveActivityRenewal.d.ts.map +1 -0
  219. package/dist/services/push/liveActivitySender.d.ts +88 -0
  220. package/dist/services/push/liveActivitySender.d.ts.map +1 -0
  221. package/dist/services/push/waitingInputNotifier.d.ts +41 -0
  222. package/dist/services/push/waitingInputNotifier.d.ts.map +1 -0
  223. package/dist/services/questions/answersToKeystrokes.d.ts +8 -0
  224. package/dist/services/questions/answersToKeystrokes.d.ts.map +1 -0
  225. package/dist/services/questions/codexGateAnswers.d.ts +14 -0
  226. package/dist/services/questions/codexGateAnswers.d.ts.map +1 -0
  227. package/dist/services/questions/codexScreen.d.ts +74 -0
  228. package/dist/services/questions/codexScreen.d.ts.map +1 -0
  229. package/dist/services/questions/detectAskUserQuestion.d.ts +6 -0
  230. package/dist/services/questions/detectAskUserQuestion.d.ts.map +1 -0
  231. package/dist/services/questions/detectPermissionGate.d.ts +62 -0
  232. package/dist/services/questions/detectPermissionGate.d.ts.map +1 -0
  233. package/dist/services/questions/detectQuestionFromScreen.d.ts +30 -0
  234. package/dist/services/questions/detectQuestionFromScreen.d.ts.map +1 -0
  235. package/dist/services/questions/detectShellPrompt.d.ts +18 -0
  236. package/dist/services/questions/detectShellPrompt.d.ts.map +1 -0
  237. package/dist/services/questions/parseAgentPhase.d.ts +12 -0
  238. package/dist/services/questions/parseAgentPhase.d.ts.map +1 -0
  239. package/dist/services/questions/parseStatusLine.d.ts +19 -0
  240. package/dist/services/questions/parseStatusLine.d.ts.map +1 -0
  241. package/dist/services/questions/questionBroadcast.d.ts +18 -0
  242. package/dist/services/questions/questionBroadcast.d.ts.map +1 -0
  243. package/dist/services/questions/resolveAnswer.d.ts +16 -0
  244. package/dist/services/questions/resolveAnswer.d.ts.map +1 -0
  245. package/dist/services/search/searchQuery.d.ts +78 -0
  246. package/dist/services/search/searchQuery.d.ts.map +1 -0
  247. package/dist/services/security/capabilities.d.ts +50 -0
  248. package/dist/services/security/capabilities.d.ts.map +1 -0
  249. package/dist/services/sessions/autoResumeOnBoot.d.ts +28 -0
  250. package/dist/services/sessions/autoResumeOnBoot.d.ts.map +1 -0
  251. package/dist/services/sessions/codexRolloutOwner.d.ts +55 -0
  252. package/dist/services/sessions/codexRolloutOwner.d.ts.map +1 -0
  253. package/dist/services/sessions/conversationBusy.d.ts +32 -0
  254. package/dist/services/sessions/conversationBusy.d.ts.map +1 -0
  255. package/dist/services/sessions/idempotency.d.ts +67 -0
  256. package/dist/services/sessions/idempotency.d.ts.map +1 -0
  257. package/dist/services/sessions/reconcileSessions.d.ts +63 -0
  258. package/dist/services/sessions/reconcileSessions.d.ts.map +1 -0
  259. package/dist/services/sessions/rehydrateSessions.d.ts +68 -0
  260. package/dist/services/sessions/rehydrateSessions.d.ts.map +1 -0
  261. package/dist/services/sessions/resumeIdentity.d.ts +16 -0
  262. package/dist/services/sessions/resumeIdentity.d.ts.map +1 -0
  263. package/dist/session-registry-boot.d.ts +142 -0
  264. package/dist/session-registry-boot.d.ts.map +1 -0
  265. package/dist/session-store.d.ts +32 -0
  266. package/dist/session-store.d.ts.map +1 -0
  267. package/dist/session-watchers.d.ts +60 -0
  268. package/dist/session-watchers.d.ts.map +1 -0
  269. package/dist/types.d.ts +616 -0
  270. package/dist/types.d.ts.map +1 -0
  271. package/dist/uploads.d.ts +16 -0
  272. package/dist/uploads.d.ts.map +1 -0
  273. package/dist/utils/bootToken.d.ts +3 -0
  274. package/dist/utils/bootToken.d.ts.map +1 -0
  275. package/dist/utils/canonicalizeFilePath.d.ts +75 -0
  276. package/dist/utils/canonicalizeFilePath.d.ts.map +1 -0
  277. package/dist/utils/canonicalizeProjectPath.d.ts +13 -0
  278. package/dist/utils/canonicalizeProjectPath.d.ts.map +1 -0
  279. package/dist/utils/codexConversationLine.d.ts +38 -0
  280. package/dist/utils/codexConversationLine.d.ts.map +1 -0
  281. package/dist/utils/conversationEtag.d.ts +19 -0
  282. package/dist/utils/conversationEtag.d.ts.map +1 -0
  283. package/dist/utils/dates.d.ts +12 -0
  284. package/dist/utils/dates.d.ts.map +1 -0
  285. package/dist/utils/debounce.d.ts +15 -0
  286. package/dist/utils/debounce.d.ts.map +1 -0
  287. package/dist/utils/deriveSessionName.d.ts +10 -0
  288. package/dist/utils/deriveSessionName.d.ts.map +1 -0
  289. package/dist/utils/fileIdentity.d.ts +36 -0
  290. package/dist/utils/fileIdentity.d.ts.map +1 -0
  291. package/dist/utils/isScannedSnapshotStale.d.ts +16 -0
  292. package/dist/utils/isScannedSnapshotStale.d.ts.map +1 -0
  293. package/dist/utils/scanProgressThrottle.d.ts +12 -0
  294. package/dist/utils/scanProgressThrottle.d.ts.map +1 -0
  295. package/dist/version.d.ts +3 -0
  296. package/dist/version.d.ts.map +1 -0
  297. package/dist/ws-hub.d.ts +15 -0
  298. package/dist/ws-hub.d.ts.map +1 -0
  299. package/package.json +3 -4
  300. package/dist/index.d.cts +0 -2571
@@ -0,0 +1,32 @@
1
+ import type { DiscoveredProcess } from "../../types";
2
+ export declare const RESUME_BUSY_WINDOW_MS = 120000;
3
+ export declare function resolveResumeBusyWindowMs(env?: NodeJS.ProcessEnv): number;
4
+ export type BusySignal = "jsonl_mtime" | "process_argv" | "process_cwd" | "file_handle";
5
+ export interface ConversationBusyInput {
6
+ conversationId: string;
7
+ projectPath: string | null;
8
+ jsonlPath: string | null;
9
+ discovered: DiscoveredProcess[];
10
+ now?: number;
11
+ windowMs?: number;
12
+ platform?: NodeJS.Platform;
13
+ /**
14
+ * When this streamer's own PTY for this conversation last went idle (ms epoch),
15
+ * or null/undefined if it never owned one in this process's lifetime.
16
+ *
17
+ * Without this the probe cannot tell our own echo from a stranger: the normal
18
+ * background → hold_session → foreground → resume flow releases the PTY (so the
19
+ * hasSession early-return no longer applies) while leaving a JSONL we wrote
20
+ * seconds ago — which would 409 the single most common resume in the product.
21
+ * File activity at or before this timestamp is attributed to us, not a collision.
22
+ */
23
+ selfPtyEndedAt?: number | null;
24
+ }
25
+ export interface ConversationBusyResult {
26
+ busy: boolean;
27
+ detectedBy: BusySignal[];
28
+ lastActivityMs: number | null;
29
+ likelyOwner: "external" | "unknown";
30
+ }
31
+ export declare function conversationBusy(input: ConversationBusyInput): ConversationBusyResult;
32
+ //# sourceMappingURL=conversationBusy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversationBusy.d.ts","sourceRoot":"","sources":["../../../src/services/sessions/conversationBusy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOrD,eAAO,MAAM,qBAAqB,SAAU,CAAC;AAI7C,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAKtF;AAMD,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,aAAa,CAAC;AAOxF,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,UAAU,EAAE,CAAC;IAGzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,UAAU,GAAG,SAAS,CAAC;CACrC;AAMD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAmDrF"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Idempotency for session input (C4).
3
+ *
4
+ * `POST /api/sessions/:id/input` had no duplicate protection. A retry — a flaky
5
+ * network, a user double-tap, a client that resends on timeout — submitted the
6
+ * same prompt to the agent twice, and nothing downstream could tell the second
7
+ * submission from a deliberate repeat of the same words.
8
+ *
9
+ * The rate limiter does not help: 500 requests/minute is aimed at floods, and a
10
+ * genuine retry is well inside it.
11
+ *
12
+ * Clients send `idempotencyKey` with a write. A repeat of a key we have already
13
+ * accepted replays the original outcome instead of re-submitting.
14
+ *
15
+ * Scoped per session, because a key is only meaningful against the conversation
16
+ * it was minted for — the same key against a different session is a different
17
+ * write, and treating it as a duplicate would silently drop a real prompt.
18
+ */
19
+ /** How long a key is remembered. Long enough to cover a retry, not a session. */
20
+ export declare const IDEMPOTENCY_TTL_MS: number;
21
+ /**
22
+ * Cap on remembered keys per session. Bounds memory on a long conversation; the
23
+ * oldest entries are evicted first, so a key older than the cap behaves as if it
24
+ * had expired — the request is treated as new.
25
+ */
26
+ export declare const IDEMPOTENCY_MAX_KEYS = 200;
27
+ export interface IdempotentResult {
28
+ status: number;
29
+ body: unknown;
30
+ }
31
+ /**
32
+ * Per-session record of recently accepted idempotency keys.
33
+ *
34
+ * Deliberately in-memory: this guards against retries seconds apart, and a
35
+ * streamer restart already ends the PTY those retries would target (see the
36
+ * durable-session-runtime ADR). Persisting it would imply a durability the
37
+ * surrounding runtime does not have.
38
+ */
39
+ export declare class IdempotencyStore {
40
+ private ttlMs;
41
+ private maxKeys;
42
+ private bySession;
43
+ constructor(ttlMs?: number, maxKeys?: number);
44
+ /**
45
+ * Previously recorded result for this key, or null if the key is new,
46
+ * expired, or evicted. A miss always means "treat as a fresh request" —
47
+ * failing open, because dropping a real prompt is far worse than allowing a
48
+ * rare duplicate.
49
+ */
50
+ get(sessionId: string, key: string, now?: number): IdempotentResult | null;
51
+ /** Record the outcome of an accepted write so a retry can replay it. */
52
+ set(sessionId: string, key: string, result: IdempotentResult, now?: number): void;
53
+ /** Drop everything for a session whose PTY is gone. */
54
+ clear(sessionId: string): void;
55
+ /** Test/diagnostic helper: how many keys are currently held for a session. */
56
+ size(sessionId: string): number;
57
+ }
58
+ /**
59
+ * Extract and validate an idempotency key from a request body.
60
+ *
61
+ * Returns `undefined` when absent (the field is optional, so existing clients
62
+ * keep working) and throws on a present-but-invalid value rather than ignoring
63
+ * it — a client that sends a malformed key believes it has retry protection,
64
+ * and silently proceeding without it would be worse than a clear rejection.
65
+ */
66
+ export declare function readIdempotencyKey(body: Record<string, unknown>): string | undefined;
67
+ //# sourceMappingURL=idempotency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../../src/services/sessions/idempotency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,QAAiB,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAQD;;;;;;;GAOG;AACH,qBAAa,gBAAgB;IAIzB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,OAAO;IAJjB,OAAO,CAAC,SAAS,CAA8B;IAE/C,YACU,KAAK,GAAE,MAA2B,EAClC,OAAO,GAAE,MAA6B,EAC5C;IAEJ;;;;;OAKG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,gBAAgB,GAAG,IAAI,CAcrF;IAED,wEAAwE;IACxE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI,CAM5F;IAED,uDAAuD;IACvD,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED,8EAA8E;IAC9E,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9B;CACF;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAOpF"}
@@ -0,0 +1,63 @@
1
+ import type { ManagedSessionRow, SessionLifecycle } from "../../db/repositories/managed-sessions.repository";
2
+ /**
3
+ * Boot reconciliation (C1 Phase 3a).
4
+ * See docs/architecture/2026-07-24-durable-session-runtime.md.
5
+ *
6
+ * On startup the registry holds rows from previous runs. Some of those
7
+ * processes are gone; some are still alive (the crash and dev-takeover paths
8
+ * exit without reaching ptyManager.dispose(), so agents genuinely outlive the
9
+ * streamer today — they are simply invisible when they do). This module decides
10
+ * which is which.
11
+ *
12
+ * Two rules make it safe:
13
+ *
14
+ * 1. **A stored status is never trusted over a live probe.** A SIGKILLed
15
+ * streamer never ran its exit writes, so a row can claim `running`
16
+ * indefinitely. Only the pid probe decides liveness.
17
+ *
18
+ * 2. **Liveness is never treated as identity.** Pids are recycled. A live pid
19
+ * whose command line does not carry the recorded token is reported
20
+ * `orphaned` and never signalled — that is the difference between a
21
+ * durability feature and one that kills an unrelated user process.
22
+ */
23
+ /**
24
+ * Reason attached to a row whose pid was never probed because it was recorded
25
+ * under a previous machine boot. Exported so the caller can recognise the
26
+ * abstention and log it, without matching on a free-text string that could
27
+ * drift away from the one produced here.
28
+ */
29
+ export declare const PRE_BOOT_REASON = "recorded before this machine boot";
30
+ /** Result for a single registry row. Pure data — nothing here acts. */
31
+ export interface ReconcileVerdict {
32
+ sessionId: string;
33
+ lifecycle: SessionLifecycle;
34
+ /** Why this verdict was reached, for logs and the diagnostics surface. */
35
+ reason: string;
36
+ }
37
+ export interface ReconcileProbe {
38
+ /** Whether a process currently exists at this pid. */
39
+ isPidAlive: (pid: number) => boolean;
40
+ /** Full command line for a live pid, or "" when it cannot be read. */
41
+ getProcessArgs: (pid: number) => Promise<string>;
42
+ /**
43
+ * Whether the provider's own history shows this session ended cleanly.
44
+ * Distinguishes `completed` from `failed` for a process that is gone.
45
+ */
46
+ endedCleanly?: (row: ManagedSessionRow) => boolean;
47
+ }
48
+ /**
49
+ * Classify one registry row against live process state.
50
+ *
51
+ * Exported separately from reconcileSessions so the decision table can be
52
+ * tested without a database. `currentBootToken` is passed in rather than read
53
+ * here for the same reason — this stays a pure function of its arguments.
54
+ * Omitted/null means the caller has no boot identity, which skips the pre-boot
55
+ * check entirely; the server always supplies one.
56
+ */
57
+ export declare function classifySession(row: ManagedSessionRow, probe: ReconcileProbe, currentInstanceId: string, currentBootToken?: string | null): Promise<ReconcileVerdict>;
58
+ /**
59
+ * Classify every non-terminal row. Returns verdicts only — the caller decides
60
+ * what to persist or broadcast. This function never signals a process.
61
+ */
62
+ export declare function reconcileSessions(rows: ManagedSessionRow[], probe: ReconcileProbe, currentInstanceId: string, currentBootToken?: string | null): Promise<ReconcileVerdict[]>;
63
+ //# sourceMappingURL=reconcileSessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcileSessions.d.ts","sourceRoot":"","sources":["../../../src/services/sessions/reconcileSessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,mDAAmD,CAAC;AAG3D;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,sCAAsC,CAAC;AAEnE,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,sDAAsD;IACtD,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACrC,sEAAsE;IACtE,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,CAAC;CACpD;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,cAAc,EACrB,iBAAiB,EAAE,MAAM,EACzB,gBAAgB,GAAE,MAAM,GAAG,IAAW,GACrC,OAAO,CAAC,gBAAgB,CAAC,CAoF3B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,iBAAiB,EAAE,EACzB,KAAK,EAAE,cAAc,EACrB,iBAAiB,EAAE,MAAM,EACzB,gBAAgB,GAAE,MAAM,GAAG,IAAW,GACrC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAI7B"}
@@ -0,0 +1,68 @@
1
+ import type { ManagedSessionRow } from "../../db/repositories/managed-sessions.repository";
2
+ import type { ManagedSession } from "../../types";
3
+ /**
4
+ * Boot rehydration (persistence plan Phase 1, gaps G1/G2/G8).
5
+ * See docs/plans/live-sessions-persistence-plan.md §4.
6
+ *
7
+ * `recordShutdownState()` stamps `completed_at` on every live session as the
8
+ * streamer stops, which takes the row out of the reconciler's probe set. So a
9
+ * *cleanly* restarted session is not classified as anything — it simply
10
+ * disappears from `GET /api/sessions`, because `SessionStore` starts empty and
11
+ * nothing ever seeds it from the registry.
12
+ *
13
+ * This module is the decision half of the fix: which rows come back, and what
14
+ * they look like when they do. Pure functions with no database import, mirroring
15
+ * how `classifySession` is split out of `reconcileSessions` — the decision table
16
+ * is the part worth testing, and it should not need a SQLite file to do it.
17
+ *
18
+ * The stub these produce holds no PTY and never reaches `LiveSessionManager`;
19
+ * it lives in `SessionStore` alone, so the idle reaper and the grace timer
20
+ * (both of which iterate `ptyManager.listSessions()`) cannot see it.
21
+ */
22
+ /** Most recovered sessions to seed in one boot. */
23
+ export declare const REHYDRATE_MAX = 25;
24
+ /** How far back a registry row may reach and still be offered as recoverable. */
25
+ export declare const REHYDRATE_WINDOW_MS: number;
26
+ export interface ShouldRehydrateOptions {
27
+ now: number;
28
+ /** Whether the row's project directory still exists on disk. */
29
+ projectExists: (projectPath: string) => boolean;
30
+ }
31
+ /**
32
+ * Why a registry row was not brought back, or `null` when it was.
33
+ *
34
+ * A stable code rather than a sentence: it is logged per row and surfaced by
35
+ * `GET /api/diagnostics/sessions`, where "my session did not come back" has to
36
+ * be answerable without reading the source.
37
+ */
38
+ export type RehydrateSkipReason = "codex_unbound" | "project_missing" | "too_old" | "agent_exited";
39
+ /**
40
+ * Should this registry row come back as a recovered session? `null` means yes.
41
+ *
42
+ * Three ways to answer no, all of them "resuming this would waste the user's
43
+ * tap":
44
+ *
45
+ * - the project directory is gone — `handleResume` would fail to spawn, and
46
+ * `classifyResumability` already encodes the same rule for conversations;
47
+ * - the row is older than the window — a week-old session belongs in the
48
+ * conversations list, not the live-session list;
49
+ * - the agent exited on its own without recording a failure — it finished, so
50
+ * there is nothing to recover. A row that recorded a `failureReason` is
51
+ * deliberately still offered: that one was cut short.
52
+ *
53
+ * Plus a fourth that is not a heuristic but an impossibility: a Codex row that
54
+ * never bound its rollout id has no id that can resume it at all (G6).
55
+ */
56
+ export declare function rehydrateSkipReason(row: ManagedSessionRow, opts: ShouldRehydrateOptions): RehydrateSkipReason | null;
57
+ /**
58
+ * Registry row → an idle, non-attached `ManagedSession` stub.
59
+ *
60
+ * `status` is `idle` rather than anything new: a fresh `SessionStatus` value
61
+ * would be rejected by `VALID_STATUSES` on `?status=` and dropped by
62
+ * `SessionStore.paginate`'s filter, making recovered sessions *vanish* from
63
+ * already-shipped mobile clients. The recovered-ness travels on `rehydrated`
64
+ * instead, which `managedToResponse` turns into the existing
65
+ * `ownership: "historical"` / `lifecycle: "resumable"` pair.
66
+ */
67
+ export declare function rowToStubSession(row: ManagedSessionRow): ManagedSession;
68
+ //# sourceMappingURL=rehydrateSessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehydrateSessions.d.ts","sourceRoot":"","sources":["../../../src/services/sessions/rehydrateSessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mDAAmD,CAAC;AAC3F,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,aAAa,CAAC;AAGhE;;;;;;;;;;;;;;;;;;GAkBG;AAEH,mDAAmD;AACnD,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,QAA0B,CAAC;AAS3D,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,aAAa,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;CACjD;AAED;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,iBAAiB,GAAG,SAAS,GAAG,cAAc,CAAC;AAEnG;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,iBAAiB,EACtB,IAAI,EAAE,sBAAsB,GAC3B,mBAAmB,GAAG,IAAI,CAQ5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,GAAG,cAAc,CAwCvE"}
@@ -0,0 +1,16 @@
1
+ import type { ManagedSessionRow } from "../../db/repositories/managed-sessions.repository";
2
+ /**
3
+ * Which id can actually resume a registry row (persistence plan Phase 3, gap G6).
4
+ *
5
+ * Claude's session id is always resumable as-is: it was passed to the CLI as
6
+ * `--session-id`, so the JSONL is named after it and `--resume <id>` finds it.
7
+ *
8
+ * Codex has no such flag. A fresh Codex session is keyed by a local placeholder
9
+ * UUID and its real rollout id arrives later, via `watchForCodexRollout`, as
10
+ * `bound_conversation_id`. `codex resume <placeholder>` fails, so the bound id
11
+ * is the only usable one — and a row that never bound has none at all, which is
12
+ * what the `null` here means. Callers must treat `null` as "not resumable",
13
+ * never as "resume by session_id".
14
+ */
15
+ export declare function resumeIdForRow(row: ManagedSessionRow): string | null;
16
+ //# sourceMappingURL=resumeIdentity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resumeIdentity.d.ts","sourceRoot":"","sources":["../../../src/services/sessions/resumeIdentity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mDAAmD,CAAC;AAG3F;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAGpE"}
@@ -0,0 +1,142 @@
1
+ import { type ManagedSessionRow, type ManagedSessionsRepository } from "./db/repositories/managed-sessions.repository";
2
+ import type { FeatureFlagValues } from "./feature-flags";
3
+ import type { LiveSessionManager } from "./live-session-manager";
4
+ import type { Logger } from "./logger";
5
+ import type { ResumeOutcome } from "./server";
6
+ import { type ReconcileVerdict } from "./services/sessions/reconcileSessions";
7
+ import type { SessionStore } from "./session-store";
8
+ import type { ManagedSession } from "./types";
9
+ /**
10
+ * Everything the boot-time registry lifecycle reads from the server. Thunks
11
+ * rather than values for anything bound after construction or replaceable at
12
+ * runtime (`managedSessionsRepo` opens during listen(); tests swap `log` and
13
+ * `resumeSession` on the server instance) — the same reason ScannerManagerDeps
14
+ * passes `cache: () => ConversationCache | null`.
15
+ */
16
+ export type SessionRegistryBootDeps = {
17
+ log: () => Logger;
18
+ ptyManager: () => LiveSessionManager;
19
+ sessionStore: () => SessionStore;
20
+ featureFlags: () => FeatureFlagValues;
21
+ autoResumeOnBoot: () => boolean;
22
+ managedSessionsRepo: () => ManagedSessionsRepository | null;
23
+ streamerInstanceId: string;
24
+ sessionVerdicts: Map<string, ReconcileVerdict>;
25
+ selfPtyEndedAt: Map<string, number>;
26
+ resumeSession: (opts: {
27
+ sessionId: string;
28
+ projectName?: string;
29
+ branch?: string;
30
+ }) => Promise<ResumeOutcome>;
31
+ watchConversationFile: (sessionId: string, historyId?: string) => Promise<void>;
32
+ broadcastSessionList: () => void;
33
+ resolveConversationTarget: (sessionId: string) => Promise<{
34
+ ok: false;
35
+ reason: string;
36
+ } | {
37
+ ok: true;
38
+ [key: string]: unknown;
39
+ }>;
40
+ };
41
+ /**
42
+ * The durable session registry's boot-and-shutdown lifecycle: classifying what
43
+ * previous runs left behind, seeding the session list from it, optionally
44
+ * resuming, pruning retired rows, and mirroring spawns and shutdown back into
45
+ * the registry.
46
+ *
47
+ * Extracted from StreamerServer so registry work stops editing the server file
48
+ * (see docs/plans/2026-07-12-server-ts-split.md, PR 6). State stays on the
49
+ * server: this class only reads it through `deps`.
50
+ */
51
+ export declare class SessionRegistryBoot {
52
+ private deps;
53
+ constructor(deps: SessionRegistryBootDeps);
54
+ private get log();
55
+ private get ptyManager();
56
+ private get sessionStore();
57
+ private get featureFlags();
58
+ private get autoResumeOnBoot();
59
+ private get managedSessionsRepo();
60
+ private get streamerInstanceId();
61
+ private get sessionVerdicts();
62
+ private get selfPtyEndedAt();
63
+ /**
64
+ * Classify sessions left behind by previous streamer runs (C1 Phase 3a).
65
+ *
66
+ * Agents already outlive the streamer today on the crash and dev-takeover
67
+ * paths, which exit without reaching ptyManager.dispose() — they are just
68
+ * invisible when they do, because nothing recorded that they existed. This
69
+ * turns those rows into an explicit verdict per session.
70
+ *
71
+ * Read-only with respect to processes: it probes and classifies, and never
72
+ * signals anything. `orphaned` is a report, not a cleanup trigger.
73
+ */
74
+ reconcilePreviousSessions(): Promise<ReconcileVerdict[]>;
75
+ /**
76
+ * Drop finished sessions the registry has held long enough (plan Phase 4).
77
+ *
78
+ * The registry is authoritative and never rebuilt from the cache, so nothing
79
+ * else would ever remove a row: without this it grows for the life of the
80
+ * install, and every boot pays for rows about sessions from months ago.
81
+ */
82
+ pruneTerminalSessions(): void;
83
+ /**
84
+ * Seed the session list with what previous runs left behind (persistence plan
85
+ * Phase 1, gaps G1/G2/G8).
86
+ *
87
+ * Reconciliation classifies rows and stops there; a verdict is overlaid onto a
88
+ * SessionResponse that already exists, and after a clean restart none does —
89
+ * `SessionStore` starts empty. So the user's session did not become
90
+ * `resumable`, it became *absent*. This is the half that puts it back.
91
+ *
92
+ * The seeded stubs hold no PTY and are never handed to `LiveSessionManager`,
93
+ * so `reapIdleSessions` and `startGraceTimer` — both of which iterate
94
+ * `ptyManager.listSessions()` — cannot observe them. A later resume calls
95
+ * `sessionStore.addManaged` with the real session, which overwrites the stub
96
+ * by id rather than duplicating it.
97
+ */
98
+ rehydratePreviousSessions(verdicts: ReconcileVerdict[]): ManagedSessionRow[];
99
+ /** Resume only the recent sessions the user explicitly allowed us to start at boot. */
100
+ autoResumePreviousSessions(rows: ManagedSessionRow[]): Promise<void>;
101
+ /**
102
+ * Pick a token guaranteed to appear in the spawned process's argv, for the
103
+ * reconciler's pid-reuse guard.
104
+ *
105
+ * Claude always passes the session id (`--resume <id>` or `--session-id
106
+ * <id>`), so it is both present and unique. Codex only does on *resume*
107
+ * (`codex resume <id>`); a fresh Codex spawn is `codex --cd <path>
108
+ * --no-alt-screen` with no id at all, because the rollout id does not exist
109
+ * until the CLI writes it. boundConversationId is what distinguishes the two:
110
+ * it is set once that rollout has been discovered.
111
+ */
112
+ spawnArgvToken(session: ManagedSession): string;
113
+ /** Restore registry-only metadata after the host mirror has been adopted. */
114
+ refreshHostedSessionsFromRegistry(): void;
115
+ /**
116
+ * Mirror a freshly-spawned session into the durable registry (C1 Phase 2).
117
+ *
118
+ * Called at each addManaged() site rather than inside SessionStore, because
119
+ * the store is a pure in-memory structure with no DB dependency and adding
120
+ * one would drag persistence into every unit test that touches it.
121
+ *
122
+ * Best-effort by design: a failed registry write must never break session
123
+ * start. Losing a row costs post-restart *visibility* for that session, which
124
+ * is strictly better than refusing to run the agent at all.
125
+ */
126
+ recordSessionSpawn(session: ManagedSession): void;
127
+ /**
128
+ * Stamp every live session as ended-by-shutdown before dispose() kills it.
129
+ *
130
+ * PTYManager.dispose() signals each child directly and fires no
131
+ * onStatusChange, so the registry would otherwise keep rows sitting at
132
+ * `running` forever and the next boot could not tell a deliberate restart
133
+ * from a crash. Recording `shutdown` as the status source makes that
134
+ * distinction explicit rather than inferred.
135
+ *
136
+ * Not a `completed_at` write for the agent's own work — the agent did not
137
+ * finish, we stopped it — but the session is genuinely terminal, so it must
138
+ * leave the reconciler's probe set.
139
+ */
140
+ recordShutdownState(): void;
141
+ }
142
+ //# sourceMappingURL=session-registry-boot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-registry-boot.d.ts","sourceRoot":"","sources":["../src/session-registry-boot.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAE/B,MAAM,+CAA+C,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAQ9C,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,uCAAuC,CAAC;AAO/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,kBAAkB,CAAC;IACrC,YAAY,EAAE,MAAM,YAAY,CAAC;IACjC,YAAY,EAAE,MAAM,iBAAiB,CAAC;IACtC,gBAAgB,EAAE,MAAM,OAAO,CAAC;IAChC,mBAAmB,EAAE,MAAM,yBAAyB,GAAG,IAAI,CAAC;IAC5D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC/C,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,aAAa,EAAE,CAAC,IAAI,EAAE;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7B,qBAAqB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,oBAAoB,EAAE,MAAM,IAAI,CAAC;IAKjC,yBAAyB,EAAE,CACzB,SAAS,EAAE,MAAM,KACd,OAAO,CAAC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;CACpF,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,mBAAmB;IAClB,OAAO,CAAC,IAAI;IAAxB,YAAoB,IAAI,EAAE,uBAAuB,EAAI;IAErD,OAAO,KAAK,GAAG,GAEd;IAED,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,gBAAgB,GAE3B;IAED,OAAO,KAAK,mBAAmB,GAE9B;IAED,OAAO,KAAK,kBAAkB,GAE7B;IAED,OAAO,KAAK,eAAe,GAE1B;IAED,OAAO,KAAK,cAAc,GAEzB;IAED;;;;;;;;;;OAUG;IACG,yBAAyB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAgE7D;IAED;;;;;;OAMG;IACH,qBAAqB,IAAI,IAAI,CAiB5B;IAED;;;;;;;;;;;;;;OAcG;IACH,yBAAyB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,iBAAiB,EAAE,CAyE3E;IAED,uFAAuF;IACjF,0BAA0B,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqIzE;IAED;;;;;;;;;;OAUG;IACH,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAG9C;IAED,6EAA6E;IAC7E,iCAAiC,IAAI,IAAI,CAkBxC;IAED;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAqChD;IAED;;;;;;;;;;;;OAYG;IACH,mBAAmB,IAAI,IAAI,CA+B1B;CACF"}
@@ -0,0 +1,32 @@
1
+ import type { DiscoveredProcess, ManagedSession, SessionCursor, SessionListPage, SessionListQuery, SessionResponse } from "./types";
2
+ export declare class SessionStore {
3
+ private managed;
4
+ private discovered;
5
+ addManaged(session: ManagedSession): void;
6
+ /**
7
+ * Multi-agent mode only. Attach a dedupe LRU to a session record. Idempotent —
8
+ * calling twice keeps the existing LRU (and its contents).
9
+ */
10
+ initAgentSession(sessionId: string, dedupeCapacity: number): void;
11
+ updateManaged(sessionId: string, updates: Partial<ManagedSession>): ManagedSession | null;
12
+ removeManaged(sessionId: string): boolean;
13
+ /**
14
+ * The **live** stored record — mutating it mutates the store. Paired with
15
+ * `get()`, which hands back a throwaway response copy. Prefer
16
+ * `updateManaged()` for writes; this is for readers that need the internal
17
+ * shape (Date fields, `rehydrated`, …) rather than the wire shape.
18
+ */
19
+ getManaged(sessionId: string): ManagedSession | null;
20
+ setDiscovered(processes: DiscoveredProcess[]): void;
21
+ /**
22
+ * The **live** stored records — mutating an element mutates the store. Paired
23
+ * with `list()`, which hands back throwaway response copies.
24
+ */
25
+ listManaged(): ManagedSession[];
26
+ list(ptyAttachedIds: Set<string>): readonly Readonly<SessionResponse>[];
27
+ get(sessionId: string, ptyAttachedIds: Set<string>): Readonly<SessionResponse> | null;
28
+ paginate(ptyAttachedIds: Set<string>, query: SessionListQuery): SessionListPage;
29
+ }
30
+ export declare function encodeCursor(c: SessionCursor): string;
31
+ export declare function decodeCursor(s: string): SessionCursor;
32
+ //# sourceMappingURL=session-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,eAAe,EAGhB,MAAM,SAAS,CAAC;AAGjB,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,UAAU,CAAwC;IAE1D,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAExC;IAED;;;OAGG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAKhE;IAED,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,GAAG,IAAI,CAexF;IAED,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAExC;IAED;;;;;OAKG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAEnD;IAED,aAAa,CAAC,SAAS,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAKlD;IAED;;;OAGG;IACH,WAAW,IAAI,cAAc,EAAE,CAE9B;IAUD,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,QAAQ,CAAC,eAAe,CAAC,EAAE,CAiBtE;IAKD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,GAAG,IAAI,CASpF;IAOD,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,eAAe,CAsB9E;CACF;AAGD,wBAAgB,YAAY,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAErD;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,aAAa,CAgBrD"}
@@ -0,0 +1,60 @@
1
+ import type { Conversation } from "@threadbase-sh/scanner";
2
+ import type { ConversationCache } from "./conversation-cache";
3
+ import type { CacheMetadataRepository } from "./db/repositories/cacheMetadata.repository";
4
+ import type { ConversationsRepository } from "./db/repositories/conversations.repository";
5
+ import type { ManagedSessionsRepository } from "./db/repositories/managed-sessions.repository";
6
+ import type { ProjectsRepository } from "./db/repositories/projects.repository";
7
+ import type { SessionsRepository } from "./db/repositories/sessions.repository";
8
+ import type { LiveSessionManager } from "./live-session-manager";
9
+ import type { ScannerManager } from "./scanner-manager";
10
+ import type { ConversationWatcher } from "./services/conversations/conversationWatcher";
11
+ import type { SessionStore } from "./session-store";
12
+ import type { WSHub } from "./ws-hub";
13
+ /**
14
+ * Everything SessionWatchers reads from the server. Collaborators constructed
15
+ * once in the server constructor are passed by reference; the ones opened
16
+ * during listen() (and rebound by the integrity monitor's reset-and-rescan) are
17
+ * thunks, for the same reason ApiDeps passes `cache: () => ConversationCache | null`.
18
+ *
19
+ * `broadcastConversationLines` / `findConversationByUuid` / `ptyAttachedIds`
20
+ * stay late-bound calls back into the server rather than moved code: they are
21
+ * server methods with their own dependencies (and tests spy on them).
22
+ */
23
+ export type SessionWatchersDeps = {
24
+ ptyManager: LiveSessionManager;
25
+ sessionStore: SessionStore;
26
+ wsHub: WSHub;
27
+ fileWatcher: ConversationWatcher;
28
+ /** sessionId → JSONL filePath. Owned by the server; mutated in place here. */
29
+ sessionFileMap: Map<string, string>;
30
+ scannerManager: ScannerManager;
31
+ codexRoots: string[];
32
+ cache: () => ConversationCache | null;
33
+ projectsRepo: () => ProjectsRepository | null;
34
+ conversationsRepo: () => ConversationsRepository | null;
35
+ sessionsRepo: () => SessionsRepository | null;
36
+ cacheMetadataRepo: () => CacheMetadataRepository | null;
37
+ managedSessionsRepo: () => ManagedSessionsRepository | null;
38
+ findConversationByUuid: (uuid: string) => Promise<Conversation | null>;
39
+ broadcastConversationLines: (sessionId: string, lines: string[]) => void;
40
+ ptyAttachedIds: () => Set<string>;
41
+ };
42
+ /**
43
+ * Binds a live session to the transcript file its provider writes: finds the
44
+ * JSONL (Claude) or rollout (Codex), starts the tail watcher, replays whatever
45
+ * was written before the watcher attached, and links the session to its project.
46
+ *
47
+ * Extracted from StreamerServer so watcher work stops editing the server file
48
+ * (see docs/plans/2026-07-12-server-ts-split.md, PR 5).
49
+ */
50
+ export declare class SessionWatchers {
51
+ private deps;
52
+ private log;
53
+ constructor(deps: SessionWatchersDeps);
54
+ linkSessionToProject(sessionId: string, projectPath: string, filePath: string): void;
55
+ watchConversationFile(sessionId: string, historyId?: string): Promise<void>;
56
+ readFirstLineSessionId(filePath: string): string | null;
57
+ watchForJsonl(sessionId: string, projectPath: string): void;
58
+ watchForCodexRollout(sessionId: string, projectPath: string): void;
59
+ }
60
+ //# sourceMappingURL=session-watchers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-watchers.d.ts","sourceRoot":"","sources":["../src/session-watchers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAI3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAC1F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAC1F,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,+CAA+C,CAAC;AAC/F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEtC;;;;;;;;;GASG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,mBAAmB,CAAC;IACjC,8EAA8E;IAC9E,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,iBAAiB,GAAG,IAAI,CAAC;IACtC,YAAY,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAC;IAC9C,iBAAiB,EAAE,MAAM,uBAAuB,GAAG,IAAI,CAAC;IACxD,YAAY,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAC;IAC9C,iBAAiB,EAAE,MAAM,uBAAuB,GAAG,IAAI,CAAC;IACxD,mBAAmB,EAAE,MAAM,yBAAyB,GAAG,IAAI,CAAC;IAC5D,sBAAsB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACvE,0BAA0B,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACzE,cAAc,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,eAAe;IAGd,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,GAAG,CAAuB;IAElC,YAAoB,IAAI,EAAE,mBAAmB,EAAI;IAIjD,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAuCnF;IAOK,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,SAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAUnF;IAMD,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWtD;IAKD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAmG1D;IAUD,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CA+JjE;CACF"}