@songsid/agend 2.2.0-beta.1 → 2.2.0-beta.11

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 (401) hide show
  1. package/README.md +1 -1
  2. package/README.zh-TW.md +1 -1
  3. package/dist/agent-cli-instructions.md +16 -9
  4. package/dist/agent-cli.js +15 -7
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.js +24 -9
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-whats-new.d.ts +47 -0
  9. package/dist/agent-whats-new.js +147 -0
  10. package/dist/agent-whats-new.js.map +1 -0
  11. package/dist/auth-api.d.ts +5 -3
  12. package/dist/auth-api.js +19 -3
  13. package/dist/auth-api.js.map +1 -1
  14. package/dist/backend/antigravity.d.ts +8 -0
  15. package/dist/backend/antigravity.js +32 -9
  16. package/dist/backend/antigravity.js.map +1 -1
  17. package/dist/backend/claude-carry.d.ts +22 -0
  18. package/dist/backend/claude-carry.js +170 -0
  19. package/dist/backend/claude-carry.js.map +1 -0
  20. package/dist/backend/claude-code.d.ts +65 -2
  21. package/dist/backend/claude-code.js +226 -33
  22. package/dist/backend/claude-code.js.map +1 -1
  23. package/dist/backend/codex-metadata.d.ts +33 -0
  24. package/dist/backend/codex-metadata.js +122 -18
  25. package/dist/backend/codex-metadata.js.map +1 -1
  26. package/dist/backend/codex.d.ts +3 -0
  27. package/dist/backend/codex.js +17 -1
  28. package/dist/backend/codex.js.map +1 -1
  29. package/dist/backend/credential-profile.d.ts +40 -3
  30. package/dist/backend/credential-profile.js +113 -5
  31. package/dist/backend/credential-profile.js.map +1 -1
  32. package/dist/backend/effort-metadata.d.ts +37 -1
  33. package/dist/backend/effort-metadata.js +86 -15
  34. package/dist/backend/effort-metadata.js.map +1 -1
  35. package/dist/backend/grok.d.ts +4 -0
  36. package/dist/backend/grok.js +14 -0
  37. package/dist/backend/grok.js.map +1 -1
  38. package/dist/backend/kiro-engine-ledger.d.ts +5 -0
  39. package/dist/backend/kiro-engine-ledger.js +20 -0
  40. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  41. package/dist/backend/kiro-identity.d.ts +6 -0
  42. package/dist/backend/kiro-identity.js +19 -5
  43. package/dist/backend/kiro-identity.js.map +1 -1
  44. package/dist/backend/kiro.d.ts +39 -3
  45. package/dist/backend/kiro.js +143 -19
  46. package/dist/backend/kiro.js.map +1 -1
  47. package/dist/backend/launch-effort.d.ts +61 -0
  48. package/dist/backend/launch-effort.js +170 -0
  49. package/dist/backend/launch-effort.js.map +1 -0
  50. package/dist/backend/muse.d.ts +84 -0
  51. package/dist/backend/muse.js +449 -53
  52. package/dist/backend/muse.js.map +1 -1
  53. package/dist/backend/types.d.ts +47 -4
  54. package/dist/backend/types.js +12 -1
  55. package/dist/backend/types.js.map +1 -1
  56. package/dist/backend-command.d.ts +91 -0
  57. package/dist/backend-command.js +122 -0
  58. package/dist/backend-command.js.map +1 -0
  59. package/dist/background-work-ended.d.ts +18 -0
  60. package/dist/background-work-ended.js +30 -0
  61. package/dist/background-work-ended.js.map +1 -0
  62. package/dist/cache-service.d.ts +2 -0
  63. package/dist/cache-service.js +2 -1
  64. package/dist/cache-service.js.map +1 -1
  65. package/dist/channel/adapters/discord.d.ts +36 -1
  66. package/dist/channel/adapters/discord.js +126 -4
  67. package/dist/channel/adapters/discord.js.map +1 -1
  68. package/dist/channel/adapters/telegram.d.ts +42 -2
  69. package/dist/channel/adapters/telegram.js +142 -23
  70. package/dist/channel/adapters/telegram.js.map +1 -1
  71. package/dist/channel/mcp-server.js +9 -1
  72. package/dist/channel/mcp-server.js.map +1 -1
  73. package/dist/channel/mcp-tools.d.ts +11 -0
  74. package/dist/channel/mcp-tools.js +26 -13
  75. package/dist/channel/mcp-tools.js.map +1 -1
  76. package/dist/channel/message-edit-errors.d.ts +5 -0
  77. package/dist/channel/message-edit-errors.js +24 -0
  78. package/dist/channel/message-edit-errors.js.map +1 -0
  79. package/dist/channel/telegram-markdown.d.ts +6 -0
  80. package/dist/channel/telegram-markdown.js +104 -0
  81. package/dist/channel/telegram-markdown.js.map +1 -0
  82. package/dist/channel/tool-router.d.ts +2 -0
  83. package/dist/channel/tool-router.js +10 -4
  84. package/dist/channel/tool-router.js.map +1 -1
  85. package/dist/channel/types.d.ts +55 -3
  86. package/dist/cli.js +167 -39
  87. package/dist/cli.js.map +1 -1
  88. package/dist/command-table.d.ts +2 -2
  89. package/dist/command-table.js +6 -3
  90. package/dist/command-table.js.map +1 -1
  91. package/dist/config-validator.js +59 -0
  92. package/dist/config-validator.js.map +1 -1
  93. package/dist/cross-instance-envelope.d.ts +15 -2
  94. package/dist/cross-instance-envelope.js +29 -5
  95. package/dist/cross-instance-envelope.js.map +1 -1
  96. package/dist/daemon.d.ts +156 -1
  97. package/dist/daemon.js +773 -89
  98. package/dist/daemon.js.map +1 -1
  99. package/dist/delivery-outbox.d.ts +70 -8
  100. package/dist/delivery-outbox.js +315 -24
  101. package/dist/delivery-outbox.js.map +1 -1
  102. package/dist/delivery-reconciliation.d.ts +18 -1
  103. package/dist/delivery-reconciliation.js +67 -2
  104. package/dist/delivery-reconciliation.js.map +1 -1
  105. package/dist/fleet-context.d.ts +10 -1
  106. package/dist/fleet-manager.d.ts +404 -13
  107. package/dist/fleet-manager.js +2039 -210
  108. package/dist/fleet-manager.js.map +1 -1
  109. package/dist/general-knowledge/skills/backend-providers/SKILL.md +1 -1
  110. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +92 -21
  111. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +27 -3
  112. package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +8 -8
  113. package/dist/general-knowledge/skills/fleet-config/SKILL.md +5 -5
  114. package/dist/general-knowledge/skills/fleet-health/SKILL.md +4 -4
  115. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +13 -13
  116. package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +3 -2
  117. package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +6 -0
  118. package/dist/general-knowledge/skills/model-discovery/SKILL.md +12 -9
  119. package/dist/general-knowledge/skills/multi-channel/SKILL.md +8 -2
  120. package/dist/general-knowledge/skills/scheduling/SKILL.md +5 -3
  121. package/dist/general-knowledge/skills/session-management/SKILL.md +6 -5
  122. package/dist/general-knowledge/skills/tui-effort/SKILL.md +2 -2
  123. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +8 -3
  124. package/dist/general-knowledge/steering/core-rules.md +8 -7
  125. package/dist/host-memory.d.ts +10 -0
  126. package/dist/host-memory.js +33 -1
  127. package/dist/host-memory.js.map +1 -1
  128. package/dist/human-delivery-receipts.d.ts +83 -0
  129. package/dist/human-delivery-receipts.js +194 -0
  130. package/dist/human-delivery-receipts.js.map +1 -0
  131. package/dist/instance-avatar.d.ts +36 -0
  132. package/dist/instance-avatar.js +57 -0
  133. package/dist/instance-avatar.js.map +1 -0
  134. package/dist/instance-config-impact.js +6 -0
  135. package/dist/instance-config-impact.js.map +1 -1
  136. package/dist/instance-lifecycle.d.ts +29 -6
  137. package/dist/instance-lifecycle.js +79 -28
  138. package/dist/instance-lifecycle.js.map +1 -1
  139. package/dist/instructions.d.ts +3 -1
  140. package/dist/instructions.js +11 -8
  141. package/dist/instructions.js.map +1 -1
  142. package/dist/kiro-log-gate.d.ts +42 -0
  143. package/dist/kiro-log-gate.js +39 -0
  144. package/dist/kiro-log-gate.js.map +1 -0
  145. package/dist/locale.js +226 -44
  146. package/dist/locale.js.map +1 -1
  147. package/dist/log-service.d.ts +99 -0
  148. package/dist/log-service.js +243 -0
  149. package/dist/log-service.js.map +1 -0
  150. package/dist/log-steps.d.ts +169 -0
  151. package/dist/log-steps.js +858 -0
  152. package/dist/log-steps.js.map +1 -0
  153. package/dist/login-controller.d.ts +20 -3
  154. package/dist/login-controller.js +71 -19
  155. package/dist/login-controller.js.map +1 -1
  156. package/dist/login-flows.d.ts +5 -2
  157. package/dist/login-flows.js +5 -4
  158. package/dist/login-flows.js.map +1 -1
  159. package/dist/memory-pressure.d.ts +13 -1
  160. package/dist/memory-pressure.js +50 -10
  161. package/dist/memory-pressure.js.map +1 -1
  162. package/dist/needs-you-hub.js +2 -0
  163. package/dist/needs-you-hub.js.map +1 -1
  164. package/dist/needs-you-live.d.ts +6 -0
  165. package/dist/needs-you-live.js +23 -3
  166. package/dist/needs-you-live.js.map +1 -1
  167. package/dist/next-steps.js +1 -1
  168. package/dist/next-steps.js.map +1 -1
  169. package/dist/outbound-handlers.d.ts +100 -0
  170. package/dist/outbound-handlers.js +284 -97
  171. package/dist/outbound-handlers.js.map +1 -1
  172. package/dist/outbound-schemas.d.ts +38 -15
  173. package/dist/outbound-schemas.js +36 -8
  174. package/dist/outbound-schemas.js.map +1 -1
  175. package/dist/public-link-progress.d.ts +1 -1
  176. package/dist/public-link-progress.js +8 -2
  177. package/dist/public-link-progress.js.map +1 -1
  178. package/dist/public-preview.d.ts +101 -0
  179. package/dist/public-preview.js +236 -0
  180. package/dist/public-preview.js.map +1 -0
  181. package/dist/public-web-gateway.js +14 -4
  182. package/dist/public-web-gateway.js.map +1 -1
  183. package/dist/public-web-link.d.ts +24 -1
  184. package/dist/public-web-link.js +63 -9
  185. package/dist/public-web-link.js.map +1 -1
  186. package/dist/reply-buttons-controller.d.ts +89 -5
  187. package/dist/reply-buttons-controller.js +273 -8
  188. package/dist/reply-buttons-controller.js.map +1 -1
  189. package/dist/reply-buttons.d.ts +48 -3
  190. package/dist/reply-buttons.js +105 -10
  191. package/dist/reply-buttons.js.map +1 -1
  192. package/dist/reply-polls.d.ts +85 -0
  193. package/dist/reply-polls.js +154 -0
  194. package/dist/reply-polls.js.map +1 -0
  195. package/dist/restart-guard.d.ts +6 -3
  196. package/dist/restart-guard.js +6 -3
  197. package/dist/restart-guard.js.map +1 -1
  198. package/dist/restart-progress.d.ts +33 -0
  199. package/dist/restart-progress.js +30 -0
  200. package/dist/restart-progress.js.map +1 -1
  201. package/dist/service-activation.d.ts +15 -7
  202. package/dist/service-activation.js +46 -29
  203. package/dist/service-activation.js.map +1 -1
  204. package/dist/service-installer.d.ts +31 -0
  205. package/dist/service-installer.js +41 -2
  206. package/dist/service-installer.js.map +1 -1
  207. package/dist/settings-api.js +85 -25
  208. package/dist/settings-api.js.map +1 -1
  209. package/dist/settings-change.js +72 -1
  210. package/dist/settings-change.js.map +1 -1
  211. package/dist/settings-effect.js +28 -4
  212. package/dist/settings-effect.js.map +1 -1
  213. package/dist/settings-http-confirmation.js +7 -0
  214. package/dist/settings-http-confirmation.js.map +1 -1
  215. package/dist/setup-form.d.ts +1 -1
  216. package/dist/setup-form.js +1 -1
  217. package/dist/spawn-gate.js +5 -1
  218. package/dist/spawn-gate.js.map +1 -1
  219. package/dist/spawn-hold-reply.d.ts +12 -0
  220. package/dist/spawn-hold-reply.js +39 -0
  221. package/dist/spawn-hold-reply.js.map +1 -0
  222. package/dist/status-emojis.d.ts +10 -3
  223. package/dist/status-emojis.js +11 -2
  224. package/dist/status-emojis.js.map +1 -1
  225. package/dist/systemd-peer-bus.d.ts +95 -0
  226. package/dist/systemd-peer-bus.js +430 -0
  227. package/dist/systemd-peer-bus.js.map +1 -0
  228. package/dist/systemd-runtime.d.ts +27 -1
  229. package/dist/systemd-runtime.js +69 -7
  230. package/dist/systemd-runtime.js.map +1 -1
  231. package/dist/tips.js +4 -4
  232. package/dist/tips.js.map +1 -1
  233. package/dist/tool-permissions.js +3 -3
  234. package/dist/tool-permissions.js.map +1 -1
  235. package/dist/tool-progress.js +3 -2
  236. package/dist/tool-progress.js.map +1 -1
  237. package/dist/topic-commands.d.ts +42 -2
  238. package/dist/topic-commands.js +101 -11
  239. package/dist/topic-commands.js.map +1 -1
  240. package/dist/transcript-delta-reader.d.ts +3 -3
  241. package/dist/transcript-delta-reader.js +8 -8
  242. package/dist/transcript-delta-reader.js.map +1 -1
  243. package/dist/transcript-jsonl.d.ts +80 -3
  244. package/dist/transcript-jsonl.js +147 -10
  245. package/dist/transcript-jsonl.js.map +1 -1
  246. package/dist/transcript-monitor.d.ts +19 -0
  247. package/dist/transcript-monitor.js +87 -15
  248. package/dist/transcript-monitor.js.map +1 -1
  249. package/dist/transcript-sources.d.ts +78 -0
  250. package/dist/transcript-sources.js +375 -64
  251. package/dist/transcript-sources.js.map +1 -1
  252. package/dist/transcript-turns.d.ts +15 -2
  253. package/dist/transcript-turns.js +15 -2
  254. package/dist/transcript-turns.js.map +1 -1
  255. package/dist/tunnel/cloudflared.d.ts +7 -15
  256. package/dist/tunnel/cloudflared.js +36 -1
  257. package/dist/tunnel/cloudflared.js.map +1 -1
  258. package/dist/tunnel/purpose-lane.d.ts +5 -1
  259. package/dist/tunnel/purpose-lane.js +14 -7
  260. package/dist/tunnel/purpose-lane.js.map +1 -1
  261. package/dist/tunnel/types.d.ts +5 -1
  262. package/dist/tunnel/types.js.map +1 -1
  263. package/dist/turn-resume.d.ts +2 -0
  264. package/dist/turn-resume.js +1 -1
  265. package/dist/turn-resume.js.map +1 -1
  266. package/dist/types.d.ts +25 -0
  267. package/dist/ui/chat-commands.js +1 -0
  268. package/dist/ui/chat-emojis.js +33 -0
  269. package/dist/ui/chat-mentions.js +63 -0
  270. package/dist/ui/chat-render.js +61 -4
  271. package/dist/ui/chat-store.js +37 -3
  272. package/dist/ui/chat-strings.js +40 -20
  273. package/dist/ui/chat-thread.js +111 -12
  274. package/dist/ui/chat-time.js +80 -0
  275. package/dist/ui/panel-chat.js +41 -10
  276. package/dist/ui/panel-details.js +2 -0
  277. package/dist/ui/panel-fleet.js +199 -57
  278. package/dist/ui/panel-log.js +428 -0
  279. package/dist/ui/panel-needs.js +6 -2
  280. package/dist/ui/panel-settings.js +184 -32
  281. package/dist/ui/preview-panel.js +55 -15
  282. package/dist/ui/preview-status.js +28 -0
  283. package/dist/ui/preview.js +44 -3
  284. package/dist/ui/settings-dialogs.js +7 -2
  285. package/dist/ui/settings-model.js +2 -1
  286. package/dist/ui/settings-strings.js +56 -4
  287. package/dist/ui/settings-token.js +1 -1
  288. package/dist/ui/shared/app-i18n.js +43 -17
  289. package/dist/ui/shared/app-prefs.js +250 -0
  290. package/dist/ui/shared/app-route.js +7 -2
  291. package/dist/ui/shared/app-shell.js +165 -8
  292. package/dist/ui/shared/app-store.js +5 -1
  293. package/dist/ui/shared/app-stream.js +26 -5
  294. package/dist/ui/shared/app.css +163 -22
  295. package/dist/ui/shared/app.js +11 -1
  296. package/dist/ui/shared/fonts/README.md +1 -1
  297. package/dist/ui/shared/header-tools.js +55 -5
  298. package/dist/ui/shared/instance-nav.js +241 -45
  299. package/dist/ui/shared/instance-switch.js +4 -4
  300. package/dist/ui/shared/panel-view.js +6 -0
  301. package/dist/ui/shared/signin.js +7 -7
  302. package/dist/ui/shared/theme.js +39 -7
  303. package/dist/ui/shared/tokens.css +1 -1
  304. package/dist/ui/shared/ui-icons.js +1 -0
  305. package/dist/ui/shared/usage-dialog.js +23 -5
  306. package/dist/ui/shared/view-strings.js +32 -4
  307. package/dist/ui/signin.html +1 -1
  308. package/dist/unrecognised-prompt.d.ts +23 -0
  309. package/dist/unrecognised-prompt.js +74 -0
  310. package/dist/unrecognised-prompt.js.map +1 -0
  311. package/dist/update-check.d.ts +31 -0
  312. package/dist/update-check.js +47 -0
  313. package/dist/update-check.js.map +1 -1
  314. package/dist/update-install.d.ts +28 -2
  315. package/dist/update-install.js +22 -4
  316. package/dist/update-install.js.map +1 -1
  317. package/dist/update-launch.d.ts +46 -1
  318. package/dist/update-launch.js +146 -3
  319. package/dist/update-launch.js.map +1 -1
  320. package/dist/update-marker.d.ts +51 -0
  321. package/dist/update-marker.js +61 -0
  322. package/dist/update-marker.js.map +1 -1
  323. package/dist/update-preflight.d.ts +100 -0
  324. package/dist/update-preflight.js +264 -0
  325. package/dist/update-preflight.js.map +1 -0
  326. package/dist/updater-caller.d.ts +31 -0
  327. package/dist/updater-caller.js +109 -0
  328. package/dist/updater-caller.js.map +1 -0
  329. package/dist/usage/i18n-keys.d.ts +1 -1
  330. package/dist/usage/i18n-keys.js +1 -1
  331. package/dist/usage/i18n-keys.js.map +1 -1
  332. package/dist/usage/providers.d.ts +34 -5
  333. package/dist/usage/providers.js +160 -27
  334. package/dist/usage/providers.js.map +1 -1
  335. package/dist/usage/statusline-usage.d.ts +6 -2
  336. package/dist/usage/statusline-usage.js +10 -4
  337. package/dist/usage/statusline-usage.js.map +1 -1
  338. package/dist/view-api.d.ts +1 -1
  339. package/dist/view-api.js +38 -8
  340. package/dist/view-api.js.map +1 -1
  341. package/dist/wake-coordinator.d.ts +11 -0
  342. package/dist/wake-coordinator.js +38 -4
  343. package/dist/wake-coordinator.js.map +1 -1
  344. package/dist/web-api.d.ts +89 -4
  345. package/dist/web-api.js +551 -45
  346. package/dist/web-api.js.map +1 -1
  347. package/dist/web-auth.d.ts +2 -2
  348. package/dist/web-auth.js +9 -4
  349. package/dist/web-auth.js.map +1 -1
  350. package/dist/web-chat-history.d.ts +12 -1
  351. package/dist/web-chat-history.js +29 -7
  352. package/dist/web-chat-history.js.map +1 -1
  353. package/dist/web-commands.d.ts +27 -4
  354. package/dist/web-commands.js +19 -4
  355. package/dist/web-commands.js.map +1 -1
  356. package/dist/web-host-guard.d.ts +3 -11
  357. package/dist/web-host-guard.js +3 -12
  358. package/dist/web-host-guard.js.map +1 -1
  359. package/dist/web-images.d.ts +47 -0
  360. package/dist/web-images.js +169 -0
  361. package/dist/web-images.js.map +1 -0
  362. package/dist/web-login.d.ts +14 -0
  363. package/dist/web-login.js +4 -2
  364. package/dist/web-login.js.map +1 -1
  365. package/dist/web-mentions.d.ts +44 -0
  366. package/dist/web-mentions.js +75 -0
  367. package/dist/web-mentions.js.map +1 -0
  368. package/dist/web-prefs.d.ts +81 -0
  369. package/dist/web-prefs.js +297 -0
  370. package/dist/web-prefs.js.map +1 -0
  371. package/dist/web-preview.d.ts +14 -1
  372. package/dist/web-preview.js +35 -13
  373. package/dist/web-preview.js.map +1 -1
  374. package/dist/web-profile.d.ts +32 -0
  375. package/dist/web-profile.js +132 -0
  376. package/dist/web-profile.js.map +1 -0
  377. package/dist/web-reply-files.d.ts +45 -0
  378. package/dist/web-reply-files.js +223 -0
  379. package/dist/web-reply-files.js.map +1 -0
  380. package/dist/web-schedules.d.ts +12 -0
  381. package/dist/web-schedules.js +34 -0
  382. package/dist/web-schedules.js.map +1 -0
  383. package/dist/web-server-drain.d.ts +10 -0
  384. package/dist/web-server-drain.js +50 -0
  385. package/dist/web-server-drain.js.map +1 -0
  386. package/dist/web-session.d.ts +50 -1
  387. package/dist/web-session.js +63 -6
  388. package/dist/web-session.js.map +1 -1
  389. package/dist/web-settings-patch.d.ts +23 -0
  390. package/dist/web-settings-patch.js +77 -0
  391. package/dist/web-settings-patch.js.map +1 -0
  392. package/dist/web-shell-routes.d.ts +5 -1
  393. package/dist/web-shell-routes.js +10 -1
  394. package/dist/web-shell-routes.js.map +1 -1
  395. package/dist/web-upload.d.ts +17 -4
  396. package/dist/web-upload.js +64 -13
  397. package/dist/web-upload.js.map +1 -1
  398. package/dist/workflow-templates/default.md +1 -1
  399. package/package.json +1 -1
  400. package/templates/launchd.plist.ejs +3 -1
  401. package/templates/systemd.service.ejs +2 -1
@@ -26,17 +26,125 @@ import { measureSyncWork } from "./sync-work-attribution.js";
26
26
  * - Every poll re-resolves which transcript is active. Pinning to the first
27
27
  * one found silently goes blind when the CLI starts a new session.
28
28
  */
29
- import { closeSync, existsSync, openSync, readSync, realpathSync } from "node:fs";
30
- import { readdir, readFile, stat } from "node:fs/promises";
29
+ import { existsSync, realpathSync } from "node:fs";
30
+ import { open as openFile, readdir, readFile, stat } from "node:fs/promises";
31
31
  import { homedir } from "node:os";
32
32
  import { join, resolve } from "node:path";
33
33
  import { sharedRolloutIndex } from "./rollout-index.js";
34
34
  import Database from "better-sqlite3";
35
35
  import { sharedKiroTranscriptLane } from "./kiro-transcript-lane.js";
36
36
  export { extractKiroAssistantStrings } from "./kiro-db-reader.js";
37
- import { lastLineBoundary, readNewLines } from "./transcript-jsonl.js";
37
+ import { LINE_MAX_BYTES, LONG_HEAD_BYTES, LOOKS_PER_POLL, READ_MAX_BYTES, lastLineBoundary, readNewLines, readWholeLine, resolveLongLines } from "./transcript-jsonl.js";
38
38
  import { museSessionCwd, readFileHeadSync } from "./backend/muse.js";
39
39
  import { performance } from "node:perf_hooks";
40
+ /**
41
+ * #1641 R3: the complete records in [from, to) that `keep` accepts — `to` is a line boundary (a checkpoint's), so
42
+ * nothing is cut. #1218 P0: read in READ_MAX_BYTES chunks (the range a checkpoint skips can be many MB), and only kept
43
+ * lines are held. A line over LINE_MAX_BYTES is judged by its head: kept, it is read whole (≤ WHOLE_LINE_MAX_BYTES —
44
+ * a turn's record must not be lost to the limit, #1786 review); otherwise it is skipped without being decoded.
45
+ */
46
+ export async function readLineRange(path, from, to, keep = () => true) {
47
+ const fh = await openFile(path, "r");
48
+ const out = [];
49
+ const whole = async (offset, bytes, head) => {
50
+ if (!keep(head))
51
+ return;
52
+ const line = await readWholeLine(path, offset, bytes);
53
+ if (line !== null && line.trim() && keep(line))
54
+ out.push(line);
55
+ };
56
+ try {
57
+ let pos = from;
58
+ let long = null; // a long line that started in an earlier chunk
59
+ while (pos < to) {
60
+ const buffer = Buffer.alloc(Math.min(READ_MAX_BYTES, to - pos));
61
+ for (let filled = 0; filled < buffer.length;) {
62
+ const { bytesRead } = await fh.read(buffer, filled, buffer.length - filled, pos + filled);
63
+ if (bytesRead <= 0)
64
+ throw new Error(`readLineRange: no progress reading ${path} at ${pos + filled}`);
65
+ filled += bytesRead;
66
+ }
67
+ // Up to the last line end in this chunk (the range's end is one); a chunk with none is inside one long line.
68
+ const complete = buffer.lastIndexOf(0x0a) + 1;
69
+ if (complete === 0) {
70
+ long ??= { start: pos, head: buffer.toString("utf8", 0, Math.min(LONG_HEAD_BYTES, buffer.length)) };
71
+ pos += buffer.length;
72
+ continue;
73
+ }
74
+ let start = 0;
75
+ while (start < complete) {
76
+ const end = buffer.indexOf(0x0a, start);
77
+ if (long) {
78
+ await whole(long.start, pos + end - long.start, long.head);
79
+ long = null;
80
+ }
81
+ else if (end - start > LINE_MAX_BYTES) {
82
+ await whole(pos + start, end - start, buffer.toString("utf8", start, start + LONG_HEAD_BYTES));
83
+ }
84
+ else {
85
+ const line = buffer.toString("utf8", start, end);
86
+ if (line.trim() && keep(line))
87
+ out.push(line);
88
+ }
89
+ start = end + 1;
90
+ }
91
+ pos += complete;
92
+ }
93
+ return out;
94
+ }
95
+ finally {
96
+ await fh.close();
97
+ }
98
+ }
99
+ /** #1641 R2: how far back from the baseline a rollout is scanned for the last turn boundary (a turn writes a lot). */
100
+ export const ROLLOUT_TURN_SCAN_MAX_BYTES = 16 * 1024 * 1024;
101
+ const ROLLOUT_TURN_SCAN_CHUNK = 256 * 1024;
102
+ /**
103
+ * #1641 R2: the last turn boundary (task_started / task_complete / turn_aborted) a codex rollout holds before `end`,
104
+ * scanning back at most ROLLOUT_TURN_SCAN_MAX_BYTES: `none` when the whole prefix holds none (no turn ever started),
105
+ * null when the bound ran out first (unknown). Nothing else is read or replayed.
106
+ */
107
+ export async function lastRolloutTurnBoundary(path, end, maxBytes = ROLLOUT_TURN_SCAN_MAX_BYTES) {
108
+ const fh = await openFile(path, "r");
109
+ try {
110
+ const stop = Math.max(0, end - maxBytes);
111
+ let pos = end;
112
+ let carry = Buffer.alloc(0);
113
+ while (pos > stop) {
114
+ const from = Math.max(stop, pos - ROLLOUT_TURN_SCAN_CHUNK);
115
+ const chunk = Buffer.alloc(pos - from);
116
+ await fh.read(chunk, 0, chunk.length, from);
117
+ pos = from;
118
+ const buf = Buffer.concat([chunk, carry]);
119
+ const lines = buf.toString("utf8").split("\n");
120
+ // The first piece may be the tail of a line that starts in an earlier chunk (unless this is the scan's start).
121
+ carry = pos > stop ? Buffer.from(lines.shift() ?? "", "utf8") : Buffer.alloc(0);
122
+ for (let i = lines.length - 1; i >= 0; i--) {
123
+ const line = lines[i];
124
+ if (!/"(?:task_started|task_complete|turn_aborted)"/.test(line))
125
+ continue;
126
+ let entry;
127
+ try {
128
+ entry = JSON.parse(line);
129
+ }
130
+ catch {
131
+ continue;
132
+ }
133
+ const p = entry.payload;
134
+ if (entry.type !== "event_msg" || !p || typeof p.turn_id !== "string")
135
+ continue;
136
+ if (p.type === "task_started")
137
+ return { kind: "start", turnId: p.turn_id };
138
+ if (p.type === "task_complete" || p.type === "turn_aborted")
139
+ return { kind: "end", turnId: p.turn_id };
140
+ }
141
+ }
142
+ return stop === 0 ? { kind: "none" } : null;
143
+ }
144
+ finally {
145
+ await fh.close();
146
+ }
147
+ }
40
148
  const EMPTY = { toolUses: [], toolResults: [], assistantTexts: [] };
41
149
  function emptyEvents() {
42
150
  return { toolUses: [], toolResults: [], assistantTexts: [] };
@@ -51,7 +159,13 @@ export class CodexRolloutSource {
51
159
  workingDirectory;
52
160
  sessionsDir;
53
161
  currentFile = null;
162
+ /** #1218: the rollout followed now. */
163
+ currentPath() { return this.currentFile; }
54
164
  byteOffset = 0;
165
+ /** #1218 P0: the offset is inside a long line being skipped (readNewLines `skipping`). */
166
+ skipping = false;
167
+ /** #1218 P0: a long line being skipped that is to be read whole once its end is found. */
168
+ pendingLong = null;
55
169
  /** EOF snapshots taken when the monitor attaches; existing history is skipped. */
56
170
  initialOffsets = new Map();
57
171
  /** Files whose session_meta was read and did NOT match our cwd. */
@@ -71,29 +185,137 @@ export class CodexRolloutSource {
71
185
  this.index = index ?? sharedRolloutIndex(sessionsDir);
72
186
  this.snapshotExistingFiles();
73
187
  }
188
+ /** #1641: the last poll found this instance's rollout and read it (see TranscriptSource.following). */
189
+ lastPollFollowed = false;
190
+ /** #1641 R2: the turn open in the current file (its task_started read, no end yet), and whether that is known. */
191
+ openTurn = null;
192
+ turnKnown = false;
193
+ /** #1641 R2: boundaries decided at adoption (the old file's open turn ended, the new file's open turn carried in). */
194
+ pendingTurns = [];
195
+ following() {
196
+ return this.lastPollFollowed && this.currentFile !== null;
197
+ }
198
+ turnStateKnown() {
199
+ return this.turnKnown;
200
+ }
201
+ /** #1641 R3: turn records decided outside a poll (a checkpoint's adoption or skipped range), handed over now. */
202
+ takeTurns() {
203
+ return this.pendingTurns.splice(0);
204
+ }
74
205
  reset() {
206
+ this.lastPollFollowed = false;
207
+ this.openTurn = null;
208
+ this.turnKnown = false;
209
+ this.pendingTurns = [];
75
210
  this.currentFile = null;
76
211
  this.byteOffset = 0;
212
+ this.skipping = false;
213
+ this.pendingLong = null;
77
214
  this.openTurnId = "";
78
215
  this.rejected.clear();
79
216
  this.accepted.clear();
80
217
  this.snapshotExistingFiles();
81
218
  }
82
219
  async checkpoint() {
83
- const active = this.candidateFiles(true).find(file => this.fileBelongsToUs(file.path));
220
+ const active = await this.firstOwned(this.candidateFiles(true));
84
221
  if (!active)
85
222
  return null;
86
223
  try {
87
224
  // A line boundary, not the size: a record being written now is read once complete (#1250).
88
225
  const offset = await lastLineBoundary(active.path);
226
+ if (active.path !== this.currentFile)
227
+ await this.adopt(active.path, offset); // a switch, whoever notices it
228
+ else if (offset > this.byteOffset) {
229
+ // #1641 R3: the range this checkpoint skips is never polled — its turn boundaries (and user records) are taken
230
+ // now, so the turn state stays the file's. Tool and text records stay skipped, as before.
231
+ for (const line of await readLineRange(active.path, this.byteOffset, offset, (l) => l.includes('"event_msg"') || l.includes('"role":"user"'))) {
232
+ let entry;
233
+ try {
234
+ entry = JSON.parse(line);
235
+ }
236
+ catch {
237
+ continue;
238
+ }
239
+ this.turnRecord(entry, this.pendingTurns);
240
+ }
241
+ }
89
242
  this.currentFile = active.path;
90
243
  this.byteOffset = offset;
244
+ this.skipping = false;
245
+ this.pendingLong = null;
91
246
  return { path: active.path, offset, sessionId: active.path };
92
247
  }
93
248
  catch {
94
249
  return null;
95
250
  }
96
251
  }
252
+ /**
253
+ * Follow `path` from `offset` (#1641 R2). A baseline past an existing file's start skips its history — but not the
254
+ * state of its current turn: the last boundary before the baseline says whether a turn is running now (carried in as
255
+ * its start; no tool, user or text record is replayed), and none within the scan bound leaves it unknown until one is
256
+ * read. A turn left open in the file being replaced belongs to that file, no longer followed: its busy evidence is
257
+ * withdrawn (retracted, not ended — nothing says how it ended).
258
+ */
259
+ async adopt(path, offset) {
260
+ let baseline = null;
261
+ let known = offset === 0; // read from its start: every boundary will be read
262
+ if (offset > 0) {
263
+ try {
264
+ baseline = await lastRolloutTurnBoundary(path, offset);
265
+ known = baseline !== null; // `none`: the whole prefix holds no turn — known closed
266
+ }
267
+ catch {
268
+ known = false;
269
+ }
270
+ }
271
+ if (this.openTurn)
272
+ this.pendingTurns.push({ kind: "retract", turnId: this.openTurn });
273
+ this.openTurn = baseline?.kind === "start" ? baseline.turnId : null;
274
+ if (this.openTurn)
275
+ this.pendingTurns.push({ kind: "start", turnId: this.openTurn });
276
+ this.turnKnown = known;
277
+ this.currentFile = path;
278
+ this.byteOffset = offset;
279
+ this.skipping = false;
280
+ this.pendingLong = null;
281
+ this.openTurnId = this.openTurn ?? ""; // a steer after the baseline belongs to the carried-in turn
282
+ }
283
+ /**
284
+ * A turn record (a boundary, or a user message) into `turns`, keeping the open turn current; true when `entry` was
285
+ * one (or another event_msg), so the caller looks no further. #1510: codex 0.162 writes one task_started per turn and
286
+ * ends it with task_complete (last_agent_message null after a provider error) or turn_aborted; a steer lands inside
287
+ * the open turn as another user message.
288
+ */
289
+ turnRecord(entry, turns) {
290
+ const p = entry.payload;
291
+ if (!p)
292
+ return false;
293
+ if (entry.type === "event_msg") {
294
+ if (p.type === "task_started" && typeof p.turn_id === "string") {
295
+ this.openTurnId = p.turn_id;
296
+ this.openTurn = p.turn_id;
297
+ this.turnKnown = true;
298
+ turns.push({ kind: "start", turnId: p.turn_id });
299
+ }
300
+ else if ((p.type === "task_complete" || p.type === "turn_aborted") && typeof p.turn_id === "string") {
301
+ const end = p.type === "turn_aborted" ? "aborted" : p.last_agent_message == null ? "error" : "complete";
302
+ if (this.openTurn === p.turn_id)
303
+ this.openTurn = null;
304
+ this.turnKnown = true;
305
+ turns.push({ kind: "end", turnId: p.turn_id, end });
306
+ }
307
+ return true;
308
+ }
309
+ if (entry.type === "response_item" && p.type === "message" && p.role === "user") {
310
+ const text = (p.content ?? [])
311
+ .map(block => typeof block.text === "string" ? block.text : "").join("\n");
312
+ const at = Date.parse(String(entry.timestamp ?? ""));
313
+ if (text.trim() && Number.isFinite(at))
314
+ turns.push({ kind: "user", turnId: this.openTurnId, text, at });
315
+ return true;
316
+ }
317
+ return false;
318
+ }
97
319
  snapshotExistingFiles() {
98
320
  this.initialOffsets = new Map(this.candidateFiles(true).map(file => [file.path, file.size]));
99
321
  }
@@ -111,23 +333,31 @@ export class CodexRolloutSource {
111
333
  candidateFiles(fresh = false) {
112
334
  return this.index.list(fresh);
113
335
  }
114
- fileBelongsToUs(path) {
336
+ /** The first of `files` that is ours (in order), or undefined. Verdicts are cached, so a known file costs no read. */
337
+ async firstOwned(files) {
338
+ for (const file of files)
339
+ if (await this.fileBelongsToUs(file.path))
340
+ return file;
341
+ return undefined;
342
+ }
343
+ async fileBelongsToUs(path) {
115
344
  if (this.rejected.has(path))
116
345
  return false;
117
346
  if (this.accepted.has(path))
118
347
  return true;
119
348
  try {
120
349
  // session_meta is the first line. It can carry long instructions, so
121
- // give it headroom — but never read the whole rollout.
122
- const fd = openSync(path, "r");
350
+ // give it headroom — but never read the whole rollout. #1218 P0: read asynchronously, off the fleet loop's
351
+ // critical path (it was a synchronous 64 KiB read per new rollout).
352
+ const fh = await openFile(path, "r");
123
353
  let head;
124
354
  try {
125
355
  const buf = Buffer.alloc(65536);
126
- const bytes = readSync(fd, buf, 0, buf.length, 0);
127
- head = buf.toString("utf-8", 0, bytes);
356
+ const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
357
+ head = buf.toString("utf-8", 0, bytesRead);
128
358
  }
129
359
  finally {
130
- closeSync(fd);
360
+ await fh.close();
131
361
  }
132
362
  const firstLine = head.split("\n")[0];
133
363
  const meta = JSON.parse(firstLine);
@@ -148,9 +378,13 @@ export class CodexRolloutSource {
148
378
  // Re-resolve every poll: a restarted codex writes a NEW rollout, and
149
379
  // staying pinned to the old one goes silently blind (#528 trap 1).
150
380
  const candidates = this.candidateFiles();
151
- const active = candidates.find(c => this.fileBelongsToUs(c.path));
152
- if (!active)
381
+ const active = await this.firstOwned(candidates);
382
+ // Cleared only by a real loss (none found, an adoption or read that failed) — never for the length of a poll's
383
+ // await, when a capture reading following() would drop the veto mid-turn.
384
+ if (!active) {
385
+ this.lastPollFollowed = false;
153
386
  return EMPTY;
387
+ }
154
388
  if (active.path !== this.currentFile) {
155
389
  // Existing rollout: continue at the EOF captured when the source was
156
390
  // created. New rollout: read from the start. Using current mtime here is
@@ -165,18 +399,43 @@ export class CodexRolloutSource {
165
399
  offset = await lastLineBoundary(active.path, captured);
166
400
  }
167
401
  catch {
402
+ this.lastPollFollowed = false;
168
403
  return EMPTY;
169
404
  }
170
405
  }
171
- this.currentFile = active.path;
172
- this.byteOffset = offset;
173
- this.openTurnId = "";
406
+ await this.adopt(active.path, offset);
407
+ }
408
+ // #1218 P0: bounded looks (READ_MAX_BYTES each), a few per poll, each awaited (a sampled rollout was 322 MB).
409
+ const records = [];
410
+ for (let look = 0; look < LOOKS_PER_POLL; look++) {
411
+ let read;
412
+ try {
413
+ read = await readNewLines(active.path, this.byteOffset, { skipping: this.skipping });
414
+ }
415
+ catch (err) {
416
+ this.lastPollFollowed = false;
417
+ throw err;
418
+ }
419
+ this.skipping = read.skipping;
420
+ const moved = read.newOffset !== this.byteOffset;
421
+ this.byteOffset = read.newOffset;
422
+ // #1786 review: a long turn record (task_complete with a long final reply, a pasted prompt) is read whole — a
423
+ // turn's end must never be lost to the line limit. Long outputs stay head-only.
424
+ const resolved = await resolveLongLines(active.path, read, this.pendingLong, codexWholeLine);
425
+ this.pendingLong = resolved.pending;
426
+ records.push(...resolved.records);
427
+ if (!moved || !read.more)
428
+ break;
174
429
  }
175
- const { lines, newOffset } = await readNewLines(this.currentFile, this.byteOffset);
176
- this.byteOffset = newOffset;
430
+ this.lastPollFollowed = true;
177
431
  const events = emptyEvents();
178
- const turns = [];
179
- for (const line of lines) {
432
+ const turns = this.pendingTurns.splice(0);
433
+ for (const record of records) {
434
+ if ("long" in record) {
435
+ codexLongHead(record.long.head, events);
436
+ continue;
437
+ }
438
+ const line = record.line;
180
439
  let entry;
181
440
  try {
182
441
  entry = JSON.parse(line);
@@ -187,29 +446,10 @@ export class CodexRolloutSource {
187
446
  const p = entry.payload;
188
447
  if (!p)
189
448
  continue;
190
- // #1510: codex 0.162 writes one task_started per turn and ends it with task_complete (last_agent_message null
191
- // after a provider error) or turn_aborted; a steer lands inside the open turn as another user message.
192
- if (entry.type === "event_msg") {
193
- if (p.type === "task_started" && typeof p.turn_id === "string") {
194
- this.openTurnId = p.turn_id;
195
- turns.push({ kind: "start", turnId: p.turn_id });
196
- }
197
- else if ((p.type === "task_complete" || p.type === "turn_aborted") && typeof p.turn_id === "string") {
198
- const end = p.type === "turn_aborted" ? "aborted" : p.last_agent_message == null ? "error" : "complete";
199
- turns.push({ kind: "end", turnId: p.turn_id, end });
200
- }
449
+ if (this.turnRecord(entry, turns))
201
450
  continue;
202
- }
203
451
  if (entry.type !== "response_item")
204
452
  continue;
205
- if (p.type === "message" && p.role === "user") {
206
- const text = (p.content ?? [])
207
- .map(block => typeof block.text === "string" ? block.text : "").join("\n");
208
- const at = Date.parse(String(entry.timestamp ?? ""));
209
- if (text.trim() && Number.isFinite(at))
210
- turns.push({ kind: "user", turnId: this.openTurnId, text, at });
211
- continue;
212
- }
213
453
  if (p.type === "function_call") {
214
454
  let input = p.arguments;
215
455
  if (typeof input === "string") {
@@ -244,6 +484,25 @@ export class CodexRolloutSource {
244
484
  return events;
245
485
  }
246
486
  }
487
+ /** #1786 review: the long rollout lines read whole — turn records (event_msg) and messages (a pasted prompt, a long reply). */
488
+ function codexWholeLine(head) {
489
+ return head.includes('"type":"event_msg"') || (head.includes('"type":"response_item"') && /"payload":\{"type":"message"/.test(head));
490
+ }
491
+ /**
492
+ * #1218 P0: a rollout line too long to parse (an output of MBs, a huge call input) — what its head still says, so the
493
+ * bubble keeps the step: a call by its name, or an output. Turn records and messages are read whole (codexWholeLine).
494
+ */
495
+ function codexLongHead(head, events) {
496
+ if (!head.includes('"type":"response_item"'))
497
+ return;
498
+ const type = /"payload":\{[^{}]{0,400}?"type":"(function_call|custom_tool_call|function_call_output|custom_tool_call_output)"/.exec(head)?.[1];
499
+ if (type === "function_call" || type === "custom_tool_call") {
500
+ const name = /"name":"([^"]{1,200})"/.exec(head)?.[1];
501
+ events.toolUses.push({ name: name ?? "unknown", input: undefined });
502
+ }
503
+ else if (type)
504
+ events.toolResults.push({ name: "unknown" });
505
+ }
247
506
  /* -------------------------------------------------------------------- muse */
248
507
  /**
249
508
  * #1510: muse's turn boundaries, from its session logs (`<root>/<YYYY>/<MM>/<DD>/<id>/session.jsonl`, recorded on
@@ -277,6 +536,9 @@ export class MuseSessionSource {
277
536
  baselineComplete = false;
278
537
  rejected = new Set();
279
538
  sizes = new Map();
539
+ /** #1218 P0: per log, whether its offset is inside a long line being skipped, and a long turn record held for its end. */
540
+ skipping = new Map();
541
+ pendingLong = new Map();
280
542
  listedAt = Number.NEGATIVE_INFINITY;
281
543
  baselined = false;
282
544
  /** intent_id → its text and time, until it materializes (bounded). */
@@ -293,6 +555,8 @@ export class MuseSessionSource {
293
555
  this.rejected.clear();
294
556
  this.sizes.clear();
295
557
  this.intents.clear();
558
+ this.skipping.clear();
559
+ this.pendingLong.clear();
296
560
  this.listedAt = Number.NEGATIVE_INFINITY;
297
561
  this.baselined = false;
298
562
  this.baselineComplete = false;
@@ -352,22 +616,45 @@ export class MuseSessionSource {
352
616
  continue;
353
617
  }
354
618
  }
355
- let read;
356
- try {
357
- read = await readNewLines(path, from);
358
- }
359
- catch {
360
- continue;
361
- }
362
- this.files.set(path, read.newOffset);
363
- this.sizes.set(path, size);
364
- // One record at a time: a record of an unexpected shape is skipped, never the batch it came in.
365
- for (const line of read.lines) {
619
+ // #1218 P0 (#1786 review): bounded looks, as the other sources; the size is remembered only once the log has been
620
+ // read to it, so the rest of a delta over the look budget is read next poll, not when the log grows again.
621
+ let caughtUp = false;
622
+ for (let look = 0; look < LOOKS_PER_POLL; look++) {
623
+ let read;
366
624
  try {
367
- this.parse(line, turns);
625
+ read = await readNewLines(path, from, { skipping: this.skipping.get(path) ?? false });
626
+ }
627
+ catch {
628
+ break;
629
+ }
630
+ this.skipping.set(path, read.skipping);
631
+ const moved = read.newOffset !== from;
632
+ from = read.newOffset;
633
+ this.files.set(path, from);
634
+ // A long turn record (a pasted prompt in user_intent.accepted) is read whole, bounded.
635
+ const resolved = await resolveLongLines(path, read, this.pendingLong.get(path) ?? null, (head) => head.includes('"payload_type":"runtime.'));
636
+ if (resolved.pending)
637
+ this.pendingLong.set(path, resolved.pending);
638
+ else
639
+ this.pendingLong.delete(path);
640
+ // One record at a time: a record of an unexpected shape is skipped, never the batch it came in.
641
+ for (const record of resolved.records) {
642
+ if (!("line" in record))
643
+ continue;
644
+ try {
645
+ this.parse(record.line, turns);
646
+ }
647
+ catch { /* skipped */ }
368
648
  }
369
- catch { /* skipped */ }
649
+ if (!read.more) {
650
+ caughtUp = !read.skipping;
651
+ break;
652
+ }
653
+ if (!moved)
654
+ break;
370
655
  }
656
+ if (caughtUp)
657
+ this.sizes.set(path, size);
371
658
  }
372
659
  const events = emptyEvents();
373
660
  if (turns.length)
@@ -564,6 +851,10 @@ export class KiroSessionSource {
564
851
  lane;
565
852
  currentFile = null;
566
853
  byteOffset = 0;
854
+ /** #1218 P0: the offset is inside a long line being skipped (readNewLines `skipping`). */
855
+ skipping = false;
856
+ /** #1218 P0: a long line being skipped that is to be read whole once its end is found. */
857
+ pendingLong = null;
567
858
  createdAt;
568
859
  generation = 0;
569
860
  cursor;
@@ -597,6 +888,8 @@ export class KiroSessionSource {
597
888
  this.close();
598
889
  this.currentFile = null;
599
890
  this.byteOffset = 0;
891
+ this.skipping = false;
892
+ this.pendingLong = null;
600
893
  this.cursor = undefined;
601
894
  this.needsBaseline = true;
602
895
  void this.initialize();
@@ -734,6 +1027,8 @@ export class KiroSessionSource {
734
1027
  if (active.createdAtMs >= this.createdAt) {
735
1028
  this.currentFile = active.jsonlPath;
736
1029
  this.byteOffset = 0; // our own fresh session — observable from the start
1030
+ this.skipping = false;
1031
+ this.pendingLong = null;
737
1032
  }
738
1033
  else {
739
1034
  // An older session is attached at its last line boundary. If that cannot be read now, nothing is adopted
@@ -749,23 +1044,39 @@ export class KiroSessionSource {
749
1044
  return EMPTY;
750
1045
  this.currentFile = active.jsonlPath;
751
1046
  this.byteOffset = offset;
1047
+ this.skipping = false;
1048
+ this.pendingLong = null;
752
1049
  return EMPTY;
753
1050
  }
754
1051
  }
755
- const { lines, newOffset } = await readNewLines(this.currentFile, this.byteOffset);
756
- if (generation !== this.generation)
757
- return EMPTY;
758
- this.byteOffset = newOffset;
1052
+ // #1218 P0: bounded looks, as the other JSONL sources. Kiro's nesting needs the whole record: a long line is read
1053
+ // whole up to WHOLE_LINE_MAX_BYTES (as every line was before), and skipped beyond it.
759
1054
  const events = emptyEvents();
760
- for (const line of lines) {
761
- let entry;
762
- try {
763
- entry = JSON.parse(line);
764
- }
765
- catch {
766
- continue;
1055
+ for (let look = 0; look < LOOKS_PER_POLL; look++) {
1056
+ const read = await readNewLines(this.currentFile, this.byteOffset, { skipping: this.skipping });
1057
+ if (generation !== this.generation)
1058
+ return EMPTY;
1059
+ this.skipping = read.skipping;
1060
+ const moved = read.newOffset !== this.byteOffset;
1061
+ this.byteOffset = read.newOffset;
1062
+ const resolved = await resolveLongLines(this.currentFile, read, this.pendingLong, () => true);
1063
+ if (generation !== this.generation)
1064
+ return EMPTY;
1065
+ this.pendingLong = resolved.pending;
1066
+ for (const record of resolved.records) {
1067
+ if (!("line" in record))
1068
+ continue;
1069
+ let entry;
1070
+ try {
1071
+ entry = JSON.parse(record.line);
1072
+ }
1073
+ catch {
1074
+ continue;
1075
+ }
1076
+ collectKiroEvents(entry, events);
767
1077
  }
768
- collectKiroEvents(entry, events);
1078
+ if (!moved || !read.more)
1079
+ break;
769
1080
  }
770
1081
  return events;
771
1082
  }