@adecore/agents 0.0.1 → 0.17.0-beta.2

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 (348) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +48 -0
  3. package/dist/async.d.ts +2 -0
  4. package/dist/async.js +31 -0
  5. package/dist/chat/attachment-store.d.ts +10 -0
  6. package/dist/chat/attachment-store.js +93 -0
  7. package/dist/chat/backend.d.ts +214 -0
  8. package/dist/chat/backend.js +1 -0
  9. package/dist/chat/background-work.d.ts +8 -0
  10. package/dist/chat/background-work.js +33 -0
  11. package/dist/chat/bookmark-store.d.ts +25 -0
  12. package/dist/chat/bookmark-store.js +141 -0
  13. package/dist/chat/chat-core.d.ts +311 -0
  14. package/dist/chat/chat-core.js +1025 -0
  15. package/dist/chat/chat-log.d.ts +33 -0
  16. package/dist/chat/chat-log.js +163 -0
  17. package/dist/chat/chat-process.d.ts +46 -0
  18. package/dist/chat/chat-process.js +190 -0
  19. package/dist/chat/chat-session.d.ts +209 -0
  20. package/dist/chat/chat-session.js +1511 -0
  21. package/dist/chat/chat-store.d.ts +37 -0
  22. package/dist/chat/chat-store.js +202 -0
  23. package/dist/chat/chat-title.d.ts +10 -0
  24. package/dist/chat/chat-title.js +74 -0
  25. package/dist/chat/claude-backend.d.ts +35 -0
  26. package/dist/chat/claude-backend.js +214 -0
  27. package/dist/chat/claude-protocol.d.ts +41 -0
  28. package/dist/chat/claude-protocol.js +642 -0
  29. package/dist/chat/claude-title.d.ts +11 -0
  30. package/dist/chat/claude-title.js +97 -0
  31. package/dist/chat/claude-transcript.d.ts +28 -0
  32. package/dist/chat/claude-transcript.js +211 -0
  33. package/dist/chat/codex-backend.d.ts +51 -0
  34. package/dist/chat/codex-backend.js +364 -0
  35. package/dist/chat/codex-protocol.d.ts +56 -0
  36. package/dist/chat/codex-protocol.js +767 -0
  37. package/dist/chat/codex-thread.d.ts +32 -0
  38. package/dist/chat/codex-thread.js +131 -0
  39. package/dist/chat/codex-transport.d.ts +42 -0
  40. package/dist/chat/codex-transport.js +188 -0
  41. package/dist/chat/composer-preferences.d.ts +15 -0
  42. package/dist/chat/composer-preferences.js +46 -0
  43. package/dist/chat/context-breakdown.d.ts +2 -0
  44. package/dist/chat/context-breakdown.js +74 -0
  45. package/dist/chat/delta-coalescer.d.ts +12 -0
  46. package/dist/chat/delta-coalescer.js +55 -0
  47. package/dist/chat/errors.d.ts +4 -0
  48. package/dist/chat/errors.js +4 -0
  49. package/dist/chat/fake-claude.d.ts +3 -0
  50. package/dist/chat/fake-claude.js +982 -0
  51. package/dist/chat/fake-cli.d.ts +25 -0
  52. package/dist/chat/fake-cli.js +157 -0
  53. package/dist/chat/fake-codex.d.ts +10 -0
  54. package/dist/chat/fake-codex.js +502 -0
  55. package/dist/chat/fixtures/claude-2.1.285-auto-fallback.json +24 -0
  56. package/dist/chat/fixtures/claude-2.1.285-native-children.json +6600 -0
  57. package/dist/chat/fixtures/claude-2.1.285-resume-direct-compact.json +202 -0
  58. package/dist/chat/fixtures/claude-2.1.285-resume-task-notification.json +57 -0
  59. package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.jsonl +10 -0
  60. package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.meta.json +1 -0
  61. package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.jsonl +2 -0
  62. package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.meta.json +1 -0
  63. package/dist/chat/fixtures/codex-0.159.2-native-approval.json +119 -0
  64. package/dist/chat/fixtures/codex-0.159.2-native-file-approval.json +136 -0
  65. package/dist/chat/fixtures/codex-0.159.2-native-interacted.json +220 -0
  66. package/dist/chat/fixtures/codex-0.159.2-usage-windows.json +74 -0
  67. package/dist/chat/input.d.ts +19 -0
  68. package/dist/chat/input.js +71 -0
  69. package/dist/chat/limit-resume.d.ts +11 -0
  70. package/dist/chat/limit-resume.js +68 -0
  71. package/dist/chat/projector.d.ts +60 -0
  72. package/dist/chat/projector.js +860 -0
  73. package/dist/chat/request-summary.d.ts +3 -0
  74. package/dist/chat/request-summary.js +143 -0
  75. package/dist/chat/subagent-projection.d.ts +8 -0
  76. package/dist/chat/subagent-projection.js +40 -0
  77. package/dist/chat/subagent-reader.d.ts +66 -0
  78. package/dist/chat/subagent-reader.js +396 -0
  79. package/dist/chat/subagent-settlement.d.ts +6 -0
  80. package/dist/chat/subagent-settlement.js +103 -0
  81. package/dist/chat/thread.d.ts +30 -0
  82. package/dist/chat/thread.js +147 -0
  83. package/dist/chat/wake-chat.d.ts +20 -0
  84. package/dist/chat/wake-chat.js +27 -0
  85. package/dist/client-sinks.d.ts +9 -0
  86. package/dist/client-sinks.js +34 -0
  87. package/dist/coded-error.d.ts +5 -0
  88. package/dist/coded-error.js +17 -0
  89. package/dist/context/argv.d.ts +11 -0
  90. package/dist/context/argv.js +58 -0
  91. package/dist/context/refusal.d.ts +9 -0
  92. package/dist/context/refusal.js +38 -0
  93. package/dist/context/verb.d.ts +85 -0
  94. package/dist/context/verb.js +233 -0
  95. package/dist/error-text.d.ts +5 -0
  96. package/dist/error-text.js +24 -0
  97. package/dist/events.d.ts +12 -0
  98. package/dist/events.js +1 -0
  99. package/dist/fs.d.ts +9 -0
  100. package/dist/fs.js +107 -0
  101. package/dist/host/agent-host.d.ts +28 -0
  102. package/dist/host/agent-host.js +105 -0
  103. package/dist/host/environment.d.ts +7 -0
  104. package/dist/host/environment.js +22 -0
  105. package/dist/host/handlers.d.ts +30 -0
  106. package/dist/host/handlers.js +111 -0
  107. package/dist/host/memory-port.d.ts +2 -0
  108. package/dist/host/memory-port.js +25 -0
  109. package/dist/host/wiring.d.ts +40 -0
  110. package/dist/host/wiring.js +120 -0
  111. package/dist/lineage.d.ts +44 -0
  112. package/dist/lineage.js +134 -0
  113. package/dist/messages/deliver-message.d.ts +21 -0
  114. package/dist/messages/deliver-message.js +42 -0
  115. package/dist/messages/deliver-notice.d.ts +25 -0
  116. package/dist/messages/deliver-notice.js +75 -0
  117. package/dist/messages/notice-notes.d.ts +15 -0
  118. package/dist/messages/notice-notes.js +26 -0
  119. package/dist/messages/notice-store.d.ts +30 -0
  120. package/dist/messages/notice-store.js +120 -0
  121. package/dist/modes.d.ts +11 -0
  122. package/dist/modes.js +28 -0
  123. package/dist/outbox/manual-clock.d.ts +10 -0
  124. package/dist/outbox/manual-clock.js +25 -0
  125. package/dist/outbox/outbox-worker.d.ts +44 -0
  126. package/dist/outbox/outbox-worker.js +173 -0
  127. package/dist/outbox/outbox.d.ts +37 -0
  128. package/dist/outbox/outbox.js +76 -0
  129. package/dist/providers/accounts/accounts.d.ts +20 -0
  130. package/dist/providers/accounts/accounts.js +158 -0
  131. package/dist/providers/accounts/launch.d.ts +17 -0
  132. package/dist/providers/accounts/launch.js +30 -0
  133. package/dist/providers/accounts/service.d.ts +87 -0
  134. package/dist/providers/accounts/service.js +538 -0
  135. package/dist/providers/accounts/shadow-home.d.ts +8 -0
  136. package/dist/providers/accounts/shadow-home.js +70 -0
  137. package/dist/providers/accounts/status.d.ts +12 -0
  138. package/dist/providers/accounts/status.js +85 -0
  139. package/dist/providers/accounts/store.d.ts +14 -0
  140. package/dist/providers/accounts/store.js +112 -0
  141. package/dist/providers/accounts/test-accounts.d.ts +12 -0
  142. package/dist/providers/accounts/test-accounts.js +38 -0
  143. package/dist/providers/accounts/variables.d.ts +27 -0
  144. package/dist/providers/accounts/variables.js +94 -0
  145. package/dist/providers/catalog.d.ts +17 -0
  146. package/dist/providers/catalog.js +76 -0
  147. package/dist/providers/claude-models.json +114 -0
  148. package/dist/providers/claude-provider.d.ts +4 -0
  149. package/dist/providers/claude-provider.js +29 -0
  150. package/dist/providers/claude.d.ts +15 -0
  151. package/dist/providers/claude.js +105 -0
  152. package/dist/providers/codex-models.json +135 -0
  153. package/dist/providers/codex-provider.d.ts +4 -0
  154. package/dist/providers/codex-provider.js +27 -0
  155. package/dist/providers/codex-rules.d.ts +9 -0
  156. package/dist/providers/codex-rules.js +35 -0
  157. package/dist/providers/codex.d.ts +14 -0
  158. package/dist/providers/codex.js +52 -0
  159. package/dist/providers/detect.d.ts +6 -0
  160. package/dist/providers/detect.js +30 -0
  161. package/dist/providers/provider.d.ts +25 -0
  162. package/dist/providers/provider.js +1 -0
  163. package/dist/providers/registry.d.ts +28 -0
  164. package/dist/providers/registry.js +118 -0
  165. package/dist/record-directory.d.ts +27 -0
  166. package/dist/record-directory.js +114 -0
  167. package/dist/run-process.d.ts +13 -0
  168. package/dist/run-process.js +42 -0
  169. package/dist/serializer.d.ts +9 -0
  170. package/dist/serializer.js +34 -0
  171. package/dist/skills.d.ts +21 -0
  172. package/dist/skills.js +174 -0
  173. package/dist/tasks/background-limit.d.ts +16 -0
  174. package/dist/tasks/background-limit.js +55 -0
  175. package/dist/tasks/end-children.d.ts +34 -0
  176. package/dist/tasks/end-children.js +53 -0
  177. package/dist/tasks/give-task.d.ts +14 -0
  178. package/dist/tasks/give-task.js +33 -0
  179. package/dist/tasks/task-coordinator.d.ts +52 -0
  180. package/dist/tasks/task-coordinator.js +323 -0
  181. package/dist/tasks/task-store.d.ts +49 -0
  182. package/dist/tasks/task-store.js +197 -0
  183. package/dist/tasks/task-work.d.ts +47 -0
  184. package/dist/tasks/task-work.js +28 -0
  185. package/dist/tasks/waiting-child.d.ts +51 -0
  186. package/dist/tasks/waiting-child.js +143 -0
  187. package/dist/tasks/wake-parent.d.ts +19 -0
  188. package/dist/tasks/wake-parent.js +109 -0
  189. package/dist/tasks/wiring.d.ts +51 -0
  190. package/dist/tasks/wiring.js +151 -0
  191. package/dist/title-file.d.ts +2 -0
  192. package/dist/title-file.js +55 -0
  193. package/dist/usage/accounts.d.ts +15 -0
  194. package/dist/usage/accounts.js +64 -0
  195. package/dist/usage/aggregate.d.ts +12 -0
  196. package/dist/usage/aggregate.js +154 -0
  197. package/dist/usage/exchange.d.ts +16 -0
  198. package/dist/usage/exchange.js +84 -0
  199. package/dist/usage/index-file.d.ts +15 -0
  200. package/dist/usage/index-file.js +123 -0
  201. package/dist/usage/limits/monitor.d.ts +50 -0
  202. package/dist/usage/limits/monitor.js +179 -0
  203. package/dist/usage/limits/normalize.d.ts +21 -0
  204. package/dist/usage/limits/normalize.js +160 -0
  205. package/dist/usage/limits/probe.d.ts +9 -0
  206. package/dist/usage/limits/probe.js +113 -0
  207. package/dist/usage/prices-snapshot.json +1071 -0
  208. package/dist/usage/pricing.d.ts +34 -0
  209. package/dist/usage/pricing.js +186 -0
  210. package/dist/usage/projects.d.ts +17 -0
  211. package/dist/usage/projects.js +49 -0
  212. package/dist/usage/readers/claude.d.ts +3 -0
  213. package/dist/usage/readers/claude.js +59 -0
  214. package/dist/usage/readers/codex.d.ts +23 -0
  215. package/dist/usage/readers/codex.js +155 -0
  216. package/dist/usage/record.d.ts +15 -0
  217. package/dist/usage/record.js +39 -0
  218. package/dist/usage/roots.d.ts +12 -0
  219. package/dist/usage/roots.js +29 -0
  220. package/dist/usage/scanner.d.ts +30 -0
  221. package/dist/usage/scanner.js +234 -0
  222. package/dist/usage/usage-service.d.ts +35 -0
  223. package/dist/usage/usage-service.js +130 -0
  224. package/dist/watch-seam.d.ts +29 -0
  225. package/dist/watch-seam.js +78 -0
  226. package/dist/watch-test-helpers.d.ts +28 -0
  227. package/dist/watch-test-helpers.js +86 -0
  228. package/package.json +42 -3
  229. package/src/async.ts +40 -0
  230. package/src/chat/attachment-store.ts +108 -0
  231. package/src/chat/backend.ts +199 -0
  232. package/src/chat/background-work.ts +42 -0
  233. package/src/chat/bookmark-store.ts +166 -0
  234. package/src/chat/chat-core.ts +1228 -0
  235. package/src/chat/chat-log.ts +187 -0
  236. package/src/chat/chat-process.ts +237 -0
  237. package/src/chat/chat-session.ts +1723 -0
  238. package/src/chat/chat-store.ts +261 -0
  239. package/src/chat/chat-title.ts +90 -0
  240. package/src/chat/claude-backend.ts +235 -0
  241. package/src/chat/claude-protocol.ts +684 -0
  242. package/src/chat/claude-title.ts +106 -0
  243. package/src/chat/claude-transcript.ts +233 -0
  244. package/src/chat/codex-backend.ts +397 -0
  245. package/src/chat/codex-protocol.ts +872 -0
  246. package/src/chat/codex-thread.ts +162 -0
  247. package/src/chat/codex-transport.ts +224 -0
  248. package/src/chat/composer-preferences.ts +67 -0
  249. package/src/chat/context-breakdown.ts +80 -0
  250. package/src/chat/delta-coalescer.ts +62 -0
  251. package/src/chat/errors.ts +25 -0
  252. package/src/chat/fake-claude.ts +1006 -0
  253. package/src/chat/fake-cli.ts +199 -0
  254. package/src/chat/fake-codex.ts +534 -0
  255. package/src/chat/fixtures/claude-2.1.285-auto-fallback.json +24 -0
  256. package/src/chat/fixtures/claude-2.1.285-native-children.json +6600 -0
  257. package/src/chat/fixtures/claude-2.1.285-resume-direct-compact.json +202 -0
  258. package/src/chat/fixtures/claude-2.1.285-resume-task-notification.json +57 -0
  259. package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.jsonl +10 -0
  260. package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.meta.json +1 -0
  261. package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.jsonl +2 -0
  262. package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.meta.json +1 -0
  263. package/src/chat/fixtures/codex-0.159.2-native-approval.json +119 -0
  264. package/src/chat/fixtures/codex-0.159.2-native-file-approval.json +136 -0
  265. package/src/chat/fixtures/codex-0.159.2-native-interacted.json +220 -0
  266. package/src/chat/fixtures/codex-0.159.2-usage-windows.json +74 -0
  267. package/src/chat/input.ts +89 -0
  268. package/src/chat/limit-resume.ts +74 -0
  269. package/src/chat/projector.ts +969 -0
  270. package/src/chat/request-summary.ts +168 -0
  271. package/src/chat/subagent-projection.ts +45 -0
  272. package/src/chat/subagent-reader.ts +469 -0
  273. package/src/chat/subagent-settlement.ts +111 -0
  274. package/src/chat/thread.ts +167 -0
  275. package/src/chat/wake-chat.ts +45 -0
  276. package/src/client-sinks.ts +39 -0
  277. package/src/coded-error.ts +18 -0
  278. package/src/context/argv.ts +61 -0
  279. package/src/context/refusal.ts +50 -0
  280. package/src/context/verb.ts +392 -0
  281. package/src/error-text.ts +28 -0
  282. package/src/events.ts +9 -0
  283. package/src/fs.ts +118 -0
  284. package/src/host/agent-host.ts +124 -0
  285. package/src/host/environment.ts +31 -0
  286. package/src/host/handlers.ts +142 -0
  287. package/src/host/memory-port.ts +27 -0
  288. package/src/host/wiring.ts +164 -0
  289. package/src/lineage.ts +159 -0
  290. package/src/messages/deliver-message.ts +61 -0
  291. package/src/messages/deliver-notice.ts +120 -0
  292. package/src/messages/notice-notes.ts +34 -0
  293. package/src/messages/notice-store.ts +142 -0
  294. package/src/modes.ts +38 -0
  295. package/src/outbox/manual-clock.ts +31 -0
  296. package/src/outbox/outbox-worker.ts +214 -0
  297. package/src/outbox/outbox.ts +106 -0
  298. package/src/providers/accounts/accounts.ts +192 -0
  299. package/src/providers/accounts/launch.ts +55 -0
  300. package/src/providers/accounts/service.ts +632 -0
  301. package/src/providers/accounts/shadow-home.ts +85 -0
  302. package/src/providers/accounts/status.ts +106 -0
  303. package/src/providers/accounts/store.ts +158 -0
  304. package/src/providers/accounts/test-accounts.ts +48 -0
  305. package/src/providers/accounts/variables.ts +132 -0
  306. package/src/providers/catalog.ts +90 -0
  307. package/src/providers/claude-models.json +114 -0
  308. package/src/providers/claude-provider.ts +32 -0
  309. package/src/providers/claude.ts +123 -0
  310. package/src/providers/codex-models.json +135 -0
  311. package/src/providers/codex-provider.ts +30 -0
  312. package/src/providers/codex-rules.ts +54 -0
  313. package/src/providers/codex.ts +69 -0
  314. package/src/providers/detect.ts +40 -0
  315. package/src/providers/provider.ts +46 -0
  316. package/src/providers/registry.ts +150 -0
  317. package/src/record-directory.ts +136 -0
  318. package/src/run-process.ts +61 -0
  319. package/src/serializer.ts +41 -0
  320. package/src/skills.ts +199 -0
  321. package/src/tasks/background-limit.ts +72 -0
  322. package/src/tasks/end-children.ts +100 -0
  323. package/src/tasks/give-task.ts +52 -0
  324. package/src/tasks/task-coordinator.ts +384 -0
  325. package/src/tasks/task-store.ts +236 -0
  326. package/src/tasks/task-work.ts +62 -0
  327. package/src/tasks/waiting-child.ts +203 -0
  328. package/src/tasks/wake-parent.ts +142 -0
  329. package/src/tasks/wiring.ts +231 -0
  330. package/src/title-file.ts +57 -0
  331. package/src/usage/accounts.ts +86 -0
  332. package/src/usage/aggregate.ts +220 -0
  333. package/src/usage/exchange.ts +93 -0
  334. package/src/usage/index-file.ts +178 -0
  335. package/src/usage/limits/monitor.ts +239 -0
  336. package/src/usage/limits/normalize.ts +188 -0
  337. package/src/usage/limits/probe.ts +120 -0
  338. package/src/usage/prices-snapshot.json +1071 -0
  339. package/src/usage/pricing.ts +236 -0
  340. package/src/usage/projects.ts +65 -0
  341. package/src/usage/readers/claude.ts +60 -0
  342. package/src/usage/readers/codex.ts +196 -0
  343. package/src/usage/record.ts +60 -0
  344. package/src/usage/roots.ts +39 -0
  345. package/src/usage/scanner.ts +265 -0
  346. package/src/usage/usage-service.ts +161 -0
  347. package/src/watch-seam.ts +111 -0
  348. package/src/watch-test-helpers.ts +102 -0
@@ -0,0 +1,33 @@
1
+ import { type ChatEvent } from '@adecore/agent-contracts';
2
+ export interface ChatLogLine {
3
+ seq: number;
4
+ at: number;
5
+ event: ChatEvent;
6
+ }
7
+ export declare const COMPACT_ABOVE_BYTES: number;
8
+ export declare function parseLog(raw: string): ChatLogLine[];
9
+ export interface ChatLogState {
10
+ seq: number;
11
+ resetSeq: number;
12
+ lines: ChatLogLine[];
13
+ }
14
+ export declare class ChatLog {
15
+ private readonly path;
16
+ private lines;
17
+ private bytes;
18
+ private base;
19
+ private fd;
20
+ private seqValue;
21
+ private resetSeqValue;
22
+ private written;
23
+ constructor(path: string | null, state?: ChatLogState);
24
+ get seq(): number;
25
+ get resetSeq(): number;
26
+ get size(): number;
27
+ get onDisk(): boolean;
28
+ append(event: ChatEvent, at: number): number;
29
+ after(since: number): ChatEvent[] | null;
30
+ compact(seq: number): void;
31
+ close(): void;
32
+ private open;
33
+ }
@@ -0,0 +1,163 @@
1
+ import { closeSync, fstatSync, mkdirSync, openSync, readSync, rmSync, writeSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { ChatEventSchema } from '@adecore/agent-contracts';
4
+ import { z } from 'zod';
5
+ import { errorText } from '../error-text.js';
6
+ import { writeAtomicSync } from '../fs.js';
7
+ /* Past this the next write of the record folds the log into it. */
8
+ export const COMPACT_ABOVE_BYTES = 1024 * 1024;
9
+ const LineSchema = z.object({ seq: z.number().int().positive(), at: z.number(), event: ChatEventSchema });
10
+ /*
11
+ * The lines of a log as they were written. A line that does not parse is left out: a crash in the
12
+ * middle of an append leaves a torn last line, and what came before it is still good.
13
+ */
14
+ export function parseLog(raw) {
15
+ const lines = [];
16
+ for (const text of raw.split('\n')) {
17
+ if (text === '') {
18
+ continue;
19
+ }
20
+ let parsed;
21
+ try {
22
+ parsed = LineSchema.safeParse(JSON.parse(text));
23
+ }
24
+ catch {
25
+ continue;
26
+ }
27
+ if (parsed.success && parsed.data.seq > (lines.at(-1)?.seq ?? 0)) {
28
+ lines.push(parsed.data);
29
+ }
30
+ }
31
+ return lines;
32
+ }
33
+ /*
34
+ * Number and synchronously append each broadcast event so disk, memory and snapshot boundaries keep
35
+ * one order. Recent lines stay in memory for immediate `since` attachment.
36
+ */
37
+ export class ChatLog {
38
+ path;
39
+ lines;
40
+ bytes;
41
+ // Every event after this seq is in `lines`; anything at or before it only the snapshot still has.
42
+ base;
43
+ fd = null;
44
+ seqValue;
45
+ resetSeqValue;
46
+ // False once a line failed to reach the disk, until a fold writes the file again.
47
+ written = true;
48
+ constructor(path, state = { seq: 0, resetSeq: 0, lines: [] }) {
49
+ this.path = path;
50
+ this.lines = state.lines;
51
+ this.bytes = state.lines.reduce((sum, line) => sum + JSON.stringify(line).length + 1, 0);
52
+ this.seqValue = Math.max(state.seq, state.lines.at(-1)?.seq ?? 0);
53
+ this.resetSeqValue = state.resetSeq;
54
+ const first = state.lines[0];
55
+ // A log with a hole in front of it only answers from after the hole.
56
+ this.base = first === undefined ? this.seqValue : first.seq - 1;
57
+ for (let i = 1; i < state.lines.length; i++) {
58
+ if (state.lines[i].seq !== state.lines[i - 1].seq + 1) {
59
+ this.base = state.lines[i - 1].seq;
60
+ this.lines = state.lines.slice(i);
61
+ }
62
+ }
63
+ }
64
+ get seq() {
65
+ return this.seqValue;
66
+ }
67
+ get resetSeq() {
68
+ return this.resetSeqValue;
69
+ }
70
+ /* How much the log file holds, which is what decides whether writing a snapshot folds it in. */
71
+ get size() {
72
+ return this.bytes;
73
+ }
74
+ /* Whether the file holds every line since the last fold, which is what lets a snapshot wait. */
75
+ get onDisk() {
76
+ return this.written;
77
+ }
78
+ /* Numbers an event and writes it down; answers its seq. The seq holds even when the disk refused the line. */
79
+ append(event, at) {
80
+ this.seqValue += 1;
81
+ const line = { seq: this.seqValue, at, event };
82
+ if (event.type === 'reset') {
83
+ this.resetSeqValue = line.seq;
84
+ }
85
+ const text = `${JSON.stringify(line)}\n`;
86
+ this.lines.push(line);
87
+ this.bytes += text.length;
88
+ if (this.path !== null) {
89
+ try {
90
+ this.fd ??= this.open(this.path);
91
+ writeSync(this.fd, text);
92
+ }
93
+ catch (e) {
94
+ this.written = false;
95
+ // The event still reaches every client; only a restart before the next snapshot loses it.
96
+ console.error(`Appending to ${this.path} failed:`, errorText(e));
97
+ }
98
+ }
99
+ return line.seq;
100
+ }
101
+ /*
102
+ * What came after `since`, when all of it is still here: null for a seq from before a reset (the
103
+ * thread it built on is gone), from before the lines held (folded into the snapshot) or from a
104
+ * stream this chat never reached.
105
+ */
106
+ after(since) {
107
+ if (since < this.resetSeqValue || since < this.base || since > this.seqValue) {
108
+ return null;
109
+ }
110
+ return this.lines.filter((line) => line.seq > since).map((line) => line.event);
111
+ }
112
+ /*
113
+ * Folds the log into a snapshot that holds everything up to `seq`. What came after it (events
114
+ * broadcast while that snapshot was on its way to disk) is written again as the whole log, in
115
+ * one synchronous step, so no append can land between the two. A snapshot older than the last
116
+ * fold changes nothing.
117
+ */
118
+ compact(seq) {
119
+ if (seq <= this.base) {
120
+ return;
121
+ }
122
+ this.lines = this.lines.filter((line) => line.seq > seq);
123
+ this.base = seq;
124
+ const text = this.lines.map((line) => `${JSON.stringify(line)}\n`).join('');
125
+ this.bytes = text.length;
126
+ if (this.path === null) {
127
+ return;
128
+ }
129
+ this.close();
130
+ try {
131
+ if (text === '') {
132
+ rmSync(this.path, { force: true });
133
+ }
134
+ else {
135
+ writeAtomicSync(this.path, text);
136
+ }
137
+ this.written = true;
138
+ }
139
+ catch (e) {
140
+ console.error(`Compacting ${this.path} failed:`, errorText(e));
141
+ }
142
+ }
143
+ close() {
144
+ if (this.fd !== null) {
145
+ closeSync(this.fd);
146
+ this.fd = null;
147
+ }
148
+ }
149
+ /* A torn last line from a crash gets its newline first, so the next line does not run into it and get lost with it. */
150
+ open(path) {
151
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
152
+ const fd = openSync(path, 'a+', 0o600);
153
+ const { size } = fstatSync(fd);
154
+ if (size > 0) {
155
+ const last = Buffer.alloc(1);
156
+ readSync(fd, last, 0, 1, size - 1);
157
+ if (last[0] !== 0x0a) {
158
+ writeSync(fd, '\n');
159
+ }
160
+ }
161
+ return fd;
162
+ }
163
+ }
@@ -0,0 +1,46 @@
1
+ export interface ChatProcess {
2
+ readonly pid: number;
3
+ readonly stdin: {
4
+ write(chunk: string): unknown;
5
+ flush(): unknown;
6
+ end(): unknown;
7
+ };
8
+ readonly stdout: ReadableStream<Uint8Array>;
9
+ readonly stderr?: ReadableStream<Uint8Array>;
10
+ kill(signal: 'SIGTERM' | 'SIGKILL'): void;
11
+ }
12
+ export interface ChatSpawnOptions {
13
+ command: string[];
14
+ cwd: string;
15
+ env: Record<string, string>;
16
+ onExit(exitCode: number | null): void;
17
+ onError?(error: Error): void;
18
+ }
19
+ export type SpawnChatProcess = (options: ChatSpawnOptions) => ChatProcess;
20
+ export declare function spawnChatProcess(options: ChatSpawnOptions): ChatProcess;
21
+ export declare class StreamTail {
22
+ readonly done: Promise<void>;
23
+ private text;
24
+ constructor(stream: ReadableStream<Uint8Array>);
25
+ lines(): string | null;
26
+ private read;
27
+ }
28
+ export interface ChatChildOptions extends Omit<ChatSpawnOptions, 'onExit'> {
29
+ spawn?: SpawnChatProcess;
30
+ onExit(exitCode: number | null, stderr: string | null): void;
31
+ onError?(error: Error): void;
32
+ }
33
+ export declare class ChatChild {
34
+ readonly process: ChatProcess;
35
+ readonly exited: Promise<void>;
36
+ private readonly tail;
37
+ private hasExited;
38
+ private graceTimer;
39
+ private ending;
40
+ constructor(options: ChatChildOptions);
41
+ get pid(): number;
42
+ endAfterGrace(): void;
43
+ end(): Promise<void>;
44
+ private clearGrace;
45
+ private report;
46
+ }
@@ -0,0 +1,190 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { Readable } from 'node:stream';
3
+ // How long an exit waits for the rest of stdout; a process the CLI started may hold the pipe open.
4
+ const STDOUT_DRAIN_MS = 250;
5
+ export function spawnChatProcess(options) {
6
+ const [command, ...args] = options.command;
7
+ if (command === undefined) {
8
+ throw new Error('No command to start');
9
+ }
10
+ const child = spawn(command, args, {
11
+ cwd: options.cwd,
12
+ env: options.env,
13
+ stdio: ['pipe', 'pipe', 'pipe'],
14
+ // A group of its own, so ending the chat also ends what its agent started (a dev server, a watcher).
15
+ detached: true
16
+ });
17
+ // Node reports a missing executable as an event after the call; a start that failed says so on the spot.
18
+ child.on('error', () => undefined);
19
+ const pid = child.pid;
20
+ if (pid === undefined) {
21
+ throw new Error(`Could not start ${command}: no such executable`);
22
+ }
23
+ child.stdin.on('error', (error) => options.onError?.(error));
24
+ child.on('exit', (exitCode) => {
25
+ // The last lines may still be on their way through the pipe; they belong before the exit.
26
+ const report = () => {
27
+ clearTimeout(timer);
28
+ child.stdout.off('end', report);
29
+ setImmediate(() => options.onExit(exitCode));
30
+ };
31
+ const timer = setTimeout(report, STDOUT_DRAIN_MS);
32
+ if (child.stdout.readableEnded) {
33
+ report();
34
+ return;
35
+ }
36
+ child.stdout.once('end', report);
37
+ });
38
+ return {
39
+ pid,
40
+ stdin: {
41
+ write: (chunk) => child.stdin.write(chunk),
42
+ flush: () => undefined,
43
+ end: () => child.stdin.end()
44
+ },
45
+ stdout: Readable.toWeb(child.stdout),
46
+ stderr: Readable.toWeb(child.stderr),
47
+ kill: (signal) => {
48
+ try {
49
+ process.kill(-pid, signal);
50
+ }
51
+ catch {
52
+ // Nothing of the group is left to signal.
53
+ }
54
+ }
55
+ };
56
+ }
57
+ // After stdin closed, a CLI that is still around is not going to say more.
58
+ const EXIT_GRACE_MS = 3000;
59
+ // What a CLI and everything it started get between the SIGTERM and the SIGKILL.
60
+ const KILL_GRACE_MS = 2000;
61
+ // How long a crash note waits for the last of stderr; a process the CLI started may hold the pipe open.
62
+ const STDERR_DRAIN_MS = 250;
63
+ // A pipe nobody reads is buffered in memory for as long as the CLI lives, so stderr is read into this much and no more.
64
+ const STDERR_TAIL_CHARS = 8 * 1024;
65
+ // What of that tail goes into the note, which the log and every client carry.
66
+ const NOTE_TAIL_LINES = 10;
67
+ const NOTE_TAIL_CHARS = 2000;
68
+ /* The last few kilobytes a stream carried. */
69
+ export class StreamTail {
70
+ done;
71
+ text = '';
72
+ constructor(stream) {
73
+ this.done = this.read(stream);
74
+ }
75
+ /* The last lines, short enough for a note; null when nothing but whitespace came. */
76
+ lines() {
77
+ const lines = this.text
78
+ .split('\n')
79
+ .map((line) => line.trimEnd())
80
+ .filter((line) => line.trim() !== '')
81
+ .slice(-NOTE_TAIL_LINES);
82
+ const tail = lines.join('\n').slice(-NOTE_TAIL_CHARS);
83
+ return tail === '' ? null : tail;
84
+ }
85
+ async read(stream) {
86
+ const reader = stream.getReader();
87
+ const decoder = new TextDecoder();
88
+ try {
89
+ while (true) {
90
+ const { value, done } = await reader.read();
91
+ if (done) {
92
+ break;
93
+ }
94
+ this.text = (this.text + decoder.decode(value, { stream: true })).slice(-STDERR_TAIL_CHARS);
95
+ }
96
+ }
97
+ catch {
98
+ // The pipe went with the process; what came before it is still the tail.
99
+ }
100
+ }
101
+ }
102
+ /*
103
+ * A spawned CLI and how it ends. Every signal goes to its process group, SIGTERM first and SIGKILL
104
+ * after a grace, and only ever because a person stopped, removed or restarted something.
105
+ */
106
+ export class ChatChild {
107
+ process;
108
+ // Settles when the CLI itself exited, not when its report of that was delivered.
109
+ exited;
110
+ tail;
111
+ hasExited = false;
112
+ graceTimer = null;
113
+ ending = null;
114
+ constructor(options) {
115
+ let settle = () => undefined;
116
+ this.exited = new Promise((resolve) => {
117
+ settle = resolve;
118
+ });
119
+ const spawn = options.spawn ?? spawnChatProcess;
120
+ this.process = spawn({
121
+ command: options.command,
122
+ cwd: options.cwd,
123
+ env: options.env,
124
+ ...(options.onError ? { onError: options.onError } : {}),
125
+ onExit: (exitCode) => {
126
+ this.hasExited = true;
127
+ this.clearGrace();
128
+ settle();
129
+ void this.report(exitCode, options.onExit);
130
+ }
131
+ });
132
+ this.tail = this.process.stderr ? new StreamTail(this.process.stderr) : null;
133
+ }
134
+ get pid() {
135
+ return this.process.pid;
136
+ }
137
+ /* Ends the CLI once the grace is over and it has not left on its own; the caller closed its input. */
138
+ endAfterGrace() {
139
+ if (this.hasExited || this.graceTimer !== null || this.ending !== null) {
140
+ return;
141
+ }
142
+ this.graceTimer = setTimeout(() => {
143
+ this.graceTimer = null;
144
+ void this.end();
145
+ }, EXIT_GRACE_MS);
146
+ this.graceTimer.unref?.();
147
+ }
148
+ /* SIGTERM to the group now and SIGKILL after the grace; settles once the CLI exited or was sent the SIGKILL. */
149
+ end() {
150
+ this.clearGrace();
151
+ if (this.hasExited) {
152
+ return Promise.resolve();
153
+ }
154
+ if (this.ending === null) {
155
+ this.process.kill('SIGTERM');
156
+ this.ending = new Promise((resolve) => {
157
+ // Sent even when the CLI left in time: what it started may still be shutting down, or ignoring the SIGTERM.
158
+ const timer = setTimeout(() => {
159
+ this.process.kill('SIGKILL');
160
+ resolve();
161
+ }, KILL_GRACE_MS);
162
+ timer.unref?.();
163
+ void this.exited.then(resolve);
164
+ });
165
+ }
166
+ return this.ending;
167
+ }
168
+ clearGrace() {
169
+ if (this.graceTimer !== null) {
170
+ clearTimeout(this.graceTimer);
171
+ this.graceTimer = null;
172
+ }
173
+ }
174
+ async report(exitCode, onExit) {
175
+ if (exitCode === null || exitCode === 0 || this.tail === null) {
176
+ onExit(exitCode, null);
177
+ return;
178
+ }
179
+ let release = () => undefined;
180
+ const timer = setTimeout(() => release(), STDERR_DRAIN_MS);
181
+ await Promise.race([
182
+ this.tail.done,
183
+ new Promise((resolve) => {
184
+ release = resolve;
185
+ })
186
+ ]);
187
+ clearTimeout(timer);
188
+ onExit(exitCode, this.tail.lines());
189
+ }
190
+ }
@@ -0,0 +1,209 @@
1
+ import type { ChatAttachment, ChatCheckpointDiff, ChatEvent, ChatInfo, ChatItem, ChatQueuedMessage, ChatSkill, ModelSelection, RuntimeMode } from '@adecore/agent-contracts';
2
+ import type { ChatProvider } from '../providers/provider.ts';
3
+ import type { LimitsUpdate } from '../usage/limits/normalize.ts';
4
+ import type { SpawnChatProcess } from './chat-process.ts';
5
+ import type { ChatTitleInput } from './chat-title.ts';
6
+ import type { SubagentSettlement } from './subagent-settlement.ts';
7
+ import { ChatThread } from './thread.ts';
8
+ import { type OutboxClock } from '../outbox/outbox-worker.ts';
9
+ export interface TurnCheckpoints {
10
+ take(cwd: string): Promise<string | null>;
11
+ diff(cwd: string, tree: string): Promise<ChatCheckpointDiff | null>;
12
+ settle(cwd: string, tree: string): Promise<{
13
+ diff: ChatCheckpointDiff;
14
+ after: string;
15
+ } | null>;
16
+ }
17
+ export interface PromptNotes {
18
+ next(): {
19
+ shown: string[];
20
+ heard: string[];
21
+ };
22
+ reset(): void;
23
+ }
24
+ export type ChatReferences = (ids: readonly string[] | undefined) => {
25
+ ids: string[];
26
+ note: string | null;
27
+ };
28
+ export interface ChatSessionOptions {
29
+ info: ChatInfo;
30
+ clock?: OutboxClock;
31
+ items?: ChatItem[];
32
+ preambles?: string[];
33
+ provider: ChatProvider;
34
+ command: string[];
35
+ env(account: string | undefined): Record<string, string>;
36
+ spawn?: SpawnChatProcess;
37
+ instructions?(): string | null;
38
+ resumeNote?(): string | null;
39
+ folders?(): readonly string[];
40
+ promptNotes?: PromptNotes;
41
+ references?: ChatReferences;
42
+ checkpoints?: TurnCheckpoints;
43
+ emit(event: ChatEvent): void;
44
+ onLimits?(update: LimitsUpdate): void;
45
+ persist(): void;
46
+ save?(): Promise<void>;
47
+ persistSoon(): void;
48
+ readTitle?(agentSessionId: string): Promise<string | null>;
49
+ nameThread?(input: ChatTitleInput): Promise<string | null>;
50
+ subagentSettlement?(toolUseId: string): Promise<SubagentSettlement | null>;
51
+ limitResume?: LimitResumeHooks;
52
+ }
53
+ export interface LimitResumeHooks {
54
+ allowed(): boolean;
55
+ now(): number;
56
+ owe(turnId: string, at: number): Promise<void>;
57
+ lapse(): Promise<void>;
58
+ owed(): boolean;
59
+ }
60
+ export interface ChatSendExtras {
61
+ mentions?: string[];
62
+ skills?: string[];
63
+ chats?: string[];
64
+ attachments?: ChatAttachment[];
65
+ }
66
+ export interface ResumeWords {
67
+ prompt: string;
68
+ note: string;
69
+ preamble: string | null;
70
+ }
71
+ export declare class ChatSession {
72
+ readonly thread: ChatThread;
73
+ private readonly options;
74
+ private readonly projector;
75
+ private backend;
76
+ private starting;
77
+ private pendingStart;
78
+ private sendAfterStop;
79
+ private sendAfterReplacement;
80
+ private readonly workflowWarnings;
81
+ private generation;
82
+ private awaitingBackgroundReport;
83
+ private launchedSelection;
84
+ private launchedAccount;
85
+ private launchedRuntimeMode;
86
+ private launchedFolders;
87
+ private turnReady;
88
+ private staleResults;
89
+ private spokenTurnId;
90
+ private titleTimer;
91
+ private naming;
92
+ private frozen;
93
+ private readonly orphans;
94
+ private pendingPreambles;
95
+ private submission;
96
+ constructor(options: ChatSessionOptions);
97
+ get id(): string;
98
+ get info(): ChatInfo;
99
+ get running(): boolean;
100
+ get preambles(): readonly string[];
101
+ get pid(): number | null;
102
+ get busy(): boolean;
103
+ configure(patch: {
104
+ selection?: ModelSelection;
105
+ runtimeMode?: RuntimeMode;
106
+ resumeAtReset?: boolean;
107
+ }): ChatInfo;
108
+ setAccount(account: string | undefined): ChatInfo;
109
+ send(text: string, extras?: ChatSendExtras): {
110
+ queued: boolean;
111
+ turnId: string;
112
+ };
113
+ unqueue(messageId: string): ChatQueuedMessage | null;
114
+ sendNow(messageId: string): boolean;
115
+ private get queue();
116
+ private setQueue;
117
+ private drainQueue;
118
+ private dispatch;
119
+ compact(): void;
120
+ cancel(advanceQueue?: boolean): void;
121
+ stopTask(taskId: string): void;
122
+ approve(requestId: string, decision: 'allow' | 'allow-always' | 'deny', message?: string): boolean;
123
+ answer(requestId: string, answers: Record<string, string>): boolean;
124
+ dismiss(itemId: string): boolean;
125
+ clear(force: boolean): void;
126
+ settleStored(selection: ModelSelection, resume: ResumeDecision): void;
127
+ resume(turnId: string, attempt: number, words: ResumeWords): Promise<void>;
128
+ abandon(turnId: string, reason: string | null): void;
129
+ wake(wake: {
130
+ text: string;
131
+ label: string;
132
+ note?: string;
133
+ taskIds: string[];
134
+ summaryFor?: string;
135
+ messageFrom?: string[];
136
+ }): string | null;
137
+ resumeSettingChanged(): void;
138
+ takeUpAfterLimit(turnId: string): 'opened' | 'wait' | 'lapsed';
139
+ get replacementWouldWait(): boolean;
140
+ private wentOn;
141
+ private resumeAllowed;
142
+ private oweResume;
143
+ settleOwedResume(): void;
144
+ private turnOpened;
145
+ continuedElsewhere(note: string): void;
146
+ lapseResume(): void;
147
+ deliverNote(delivery: {
148
+ noteId: string;
149
+ note: string;
150
+ from?: string;
151
+ preamble: string;
152
+ }): boolean;
153
+ upsertItems(items: readonly ChatItem[]): void;
154
+ addNote(level: 'info' | 'warning' | 'error', text: string): void;
155
+ end(reason: string): void;
156
+ freeze(): void;
157
+ dispose(): Promise<void>;
158
+ turnDiff(turnId: string): Promise<ChatCheckpointDiff | null>;
159
+ listThreadItems(params: {
160
+ threadId: string;
161
+ cursor?: string;
162
+ limit: number;
163
+ sortDirection: 'asc' | 'desc';
164
+ }): Promise<unknown> | null;
165
+ settleOrphanedSubagents(): Promise<void>;
166
+ private recordLostBackground;
167
+ markSubagentStopped(toolUseId: string): void;
168
+ markSubagentsStopped(): void;
169
+ private markStopped;
170
+ recheckOrphan(toolUseId: string): Promise<void>;
171
+ noteSubagentNative(toolUseId: string, native: {
172
+ agentId?: string;
173
+ threadId?: string;
174
+ }): void;
175
+ skills(scan: () => Promise<ChatSkill[]>): Promise<ChatSkill[]>;
176
+ private openTurn;
177
+ private settleAgentTurn;
178
+ private checkpoint;
179
+ private settleCheckpoint;
180
+ private contextNote;
181
+ private submit;
182
+ private acceptPrompt;
183
+ private run;
184
+ private watchStart;
185
+ private clearStartWarning;
186
+ private requiresReplacement;
187
+ private hasNativeWork;
188
+ private processInUse;
189
+ private replacementWaits;
190
+ private ensureBackend;
191
+ private awaitsResume;
192
+ private receive;
193
+ private pauseQueue;
194
+ private clearWorkflowWarnings;
195
+ private watchWorkflows;
196
+ private reportsCurrentWindow;
197
+ private refreshTitle;
198
+ private applyTitle;
199
+ private nameThread;
200
+ private runningBackgroundRows;
201
+ private settleFromTranscripts;
202
+ private settleRows;
203
+ private emit;
204
+ private delegatingChange;
205
+ }
206
+ export interface ResumeDecision {
207
+ resumeTurnId: string | null;
208
+ reason: string | null;
209
+ }