@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,32 @@
1
+ import type { ChatItem } from '@adecore/agent-contracts';
2
+ import type { SpawnChatProcess } from './chat-process.ts';
3
+ import { type CodexClientInfo } from './codex-transport.ts';
4
+ export interface ThreadItemsParams {
5
+ threadId: string;
6
+ cursor?: string;
7
+ limit: number;
8
+ sortDirection: 'asc' | 'desc';
9
+ }
10
+ export declare function parseThreadItemsPage(result: unknown): {
11
+ entries: Record<string, unknown>[];
12
+ nextCursor: string | null;
13
+ };
14
+ export declare function projectCodexItems(entriesOldestFirst: Record<string, unknown>[], now: number): ChatItem[];
15
+ export interface CodexProcessSpec {
16
+ command: string[];
17
+ cwd: string;
18
+ env: Record<string, string>;
19
+ spawn?: SpawnChatProcess;
20
+ client?: CodexClientInfo;
21
+ }
22
+ export declare function listThreadItemsOnce(spec: CodexProcessSpec, params: ThreadItemsParams): Promise<unknown>;
23
+ export interface ThreadForkParams {
24
+ threadId: string;
25
+ at: {
26
+ turnId: string;
27
+ } | {
28
+ turns: number;
29
+ } | null;
30
+ options: Record<string, unknown>;
31
+ }
32
+ export declare function forkThreadOnce(spec: CodexProcessSpec, params: ThreadForkParams): Promise<string>;
@@ -0,0 +1,131 @@
1
+ import { CodexProtocol } from './codex-protocol.js';
2
+ import { CodexTransport, DEFAULT_CODEX_CLIENT } from './codex-transport.js';
3
+ import { ChatError } from './errors.js';
4
+ import { readingThread, settledReading } from './subagent-projection.js';
5
+ import { errorText } from '../error-text.js';
6
+ function isRecord(value) {
7
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
8
+ }
9
+ /* One `thread/items/list` answer as far as a reader needs it: the entries newest first, and the cursor to older ones. */
10
+ export function parseThreadItemsPage(result) {
11
+ const data = isRecord(result) && Array.isArray(result.data) ? result.data.filter(isRecord) : [];
12
+ const nextCursor = isRecord(result) && typeof result.nextCursor === 'string' && result.nextCursor !== '' ? result.nextCursor : null;
13
+ return { entries: data, nextCursor };
14
+ }
15
+ function userText(content) {
16
+ return Array.isArray(content)
17
+ ? content
18
+ .filter(isRecord)
19
+ .map((block) => (typeof block.text === 'string' ? block.text : ''))
20
+ .filter((text) => text !== '')
21
+ .join('\n')
22
+ : '';
23
+ }
24
+ /*
25
+ * Items of a Codex thread, oldest first, through the mapping the live stream uses. Every entry is
26
+ * projected on its own: consecutive reasoning would otherwise become one thinking item whose id
27
+ * depends on where a page happened to start, and a client merging two reads by id would show that
28
+ * thought twice.
29
+ */
30
+ export function projectCodexItems(entriesOldestFirst, now) {
31
+ const { thread, projector } = readingThread('Codex', () => now);
32
+ const protocol = new CodexProtocol(0);
33
+ for (const entry of entriesOldestFirst) {
34
+ const item = isRecord(entry.item) ? entry.item : null;
35
+ if (!item || typeof item.id !== 'string') {
36
+ continue;
37
+ }
38
+ projector.reset();
39
+ if (item.type === 'userMessage') {
40
+ const text = userText(item.content);
41
+ if (text !== '') {
42
+ thread.upsert({ id: `user-${item.id}`, kind: 'user', createdAt: now, turnId: null, text });
43
+ }
44
+ continue;
45
+ }
46
+ for (const event of protocol.handle({ method: 'item/completed', params: { item } })) {
47
+ projector.project(0, event);
48
+ }
49
+ }
50
+ return thread.list().map(settledReading);
51
+ }
52
+ /* An app-server started for the questions `work` asks and ended after them, for a chat whose own process may not run. */
53
+ async function withAppServer(spec, work) {
54
+ const transport = new CodexTransport({
55
+ command: spec.command,
56
+ cwd: spec.cwd,
57
+ env: spec.env,
58
+ ...(spec.spawn ? { spawn: spec.spawn } : {}),
59
+ onFrame: () => undefined,
60
+ onExit: () => undefined
61
+ });
62
+ try {
63
+ await transport.request('initialize', {
64
+ clientInfo: spec.client ?? DEFAULT_CODEX_CLIENT,
65
+ capabilities: { experimentalApi: true, requestAttestation: false }
66
+ });
67
+ transport.notify('initialized', {});
68
+ return await work(transport);
69
+ }
70
+ finally {
71
+ transport.end();
72
+ transport.kill('SIGTERM');
73
+ }
74
+ }
75
+ /*
76
+ * A page asked of an app-server started for this one question, for a chat whose own process is not
77
+ * running. The app-server reads a thread it never loaded from disk (checked against codex-cli 0.154.0),
78
+ * so nothing has to be resumed first.
79
+ */
80
+ export function listThreadItemsOnce(spec, params) {
81
+ return withAppServer(spec, (transport) => transport.request('thread/items/list', params));
82
+ }
83
+ /* The id of the `turns`-th turn of a thread, oldest first, or null when it has fewer. */
84
+ async function nthTurnId(transport, threadId, turns) {
85
+ let seen = 0;
86
+ let cursor = null;
87
+ do {
88
+ const page = await transport.request('thread/turns/list', { threadId, limit: 100, sortDirection: 'asc', ...(cursor === null ? {} : { cursor }) });
89
+ const data = isRecord(page) && Array.isArray(page.data) ? page.data.filter(isRecord) : [];
90
+ for (const turn of data) {
91
+ if (++seen === turns) {
92
+ return typeof turn.id === 'string' ? turn.id : null;
93
+ }
94
+ }
95
+ cursor = isRecord(page) && typeof page.nextCursor === 'string' && page.nextCursor !== '' ? page.nextCursor : null;
96
+ } while (cursor !== null);
97
+ return null;
98
+ }
99
+ /*
100
+ * A new thread with the turns of `threadId` up to the one named, through `thread/fork`, and its id.
101
+ * Asked of a process of its own even while the chat's backend holds the thread: measured against
102
+ * codex-cli 0.154.0, with the original answering on in its own process afterwards.
103
+ */
104
+ export function forkThreadOnce(spec, params) {
105
+ return withAppServer(spec, async (transport) => {
106
+ let lastTurnId = null;
107
+ if (params.at !== null) {
108
+ lastTurnId = 'turnId' in params.at ? params.at.turnId : await nthTurnId(transport, params.threadId, params.at.turns);
109
+ if (lastTurnId === null) {
110
+ throw new ChatError('turn-not-found', 'Codex keeps fewer turns in this thread than the chat shows, so the turn could not be found');
111
+ }
112
+ }
113
+ let result;
114
+ try {
115
+ result = await transport.request('thread/fork', {
116
+ threadId: params.threadId,
117
+ excludeTurns: true,
118
+ ...params.options,
119
+ ...(lastTurnId === null ? {} : { lastTurnId })
120
+ });
121
+ }
122
+ catch (error) {
123
+ throw new ChatError('fork-failed', errorText(error));
124
+ }
125
+ const thread = isRecord(result) && isRecord(result.thread) ? result.thread : null;
126
+ if (thread === null || typeof thread.id !== 'string' || thread.id === '') {
127
+ throw new ChatError('fork-failed', 'Codex answered the fork without a thread');
128
+ }
129
+ return thread.id;
130
+ });
131
+ }
@@ -0,0 +1,42 @@
1
+ import { type SpawnChatProcess } from './chat-process.ts';
2
+ export type CodexFrame = Record<string, unknown>;
3
+ export interface CodexClientInfo {
4
+ name: string;
5
+ title: string;
6
+ version: string;
7
+ }
8
+ export declare const DEFAULT_CODEX_CLIENT: CodexClientInfo;
9
+ interface CodexTransportOptions {
10
+ command: string[];
11
+ cwd: string;
12
+ env: Record<string, string>;
13
+ spawn?: SpawnChatProcess;
14
+ onFrame(frame: CodexFrame): void;
15
+ onError?(error: Error): void;
16
+ onExit(exitCode: number | null, stderr: string | null): void;
17
+ }
18
+ export declare class CodexTransport {
19
+ private readonly child;
20
+ private readonly options;
21
+ private readonly pending;
22
+ private nextId;
23
+ private closed;
24
+ constructor(options: CodexTransportOptions);
25
+ get pid(): number;
26
+ get alive(): boolean;
27
+ request(method: string, params: unknown): Promise<unknown>;
28
+ notify(method: string, params: unknown): void;
29
+ respond(id: number | string, result: unknown): void;
30
+ respondError(id: number | string, code: number, message: string): void;
31
+ end(): void;
32
+ kill(signal?: 'SIGTERM' | 'SIGKILL'): void;
33
+ stop(): void;
34
+ dispose(): Promise<void>;
35
+ private write;
36
+ private failed;
37
+ private readLine;
38
+ private readLines;
39
+ private handleLine;
40
+ private handleExit;
41
+ }
42
+ export {};
@@ -0,0 +1,188 @@
1
+ import { errorText } from '../error-text.js';
2
+ import { ChatChild } from './chat-process.js';
3
+ export const DEFAULT_CODEX_CLIENT = { name: 'agents', title: 'Agents', version: '0.1.0' };
4
+ class CodexRpcError extends Error {
5
+ code;
6
+ constructor(method, error) {
7
+ const detail = typeof error === 'object' && error !== null ? error : {};
8
+ super(`${method} failed: ${typeof detail.message === 'string' ? detail.message : JSON.stringify(error)}`);
9
+ this.name = 'CodexRpcError';
10
+ this.code = typeof detail.code === 'number' ? detail.code : null;
11
+ }
12
+ }
13
+ /*
14
+ * One `codex app-server` process and the JSON-RPC framing on its stdio: newline-delimited JSON,
15
+ * a request carries an id and gets a result or an error back under the same id, a notification
16
+ * has no id. The app-server also sends requests of its own (approvals), answered with `respond`.
17
+ */
18
+ export class CodexTransport {
19
+ child;
20
+ options;
21
+ pending = new Map();
22
+ nextId = 1;
23
+ closed = false;
24
+ constructor(options) {
25
+ this.options = options;
26
+ this.child = new ChatChild({
27
+ command: options.command,
28
+ cwd: options.cwd,
29
+ env: options.env,
30
+ ...(options.spawn ? { spawn: options.spawn } : {}),
31
+ onError: (error) => this.failed(error),
32
+ onExit: (exitCode, stderr) => this.handleExit(exitCode, stderr)
33
+ });
34
+ void this.readLines();
35
+ }
36
+ get pid() {
37
+ return this.child.pid;
38
+ }
39
+ get alive() {
40
+ return !this.closed;
41
+ }
42
+ request(method, params) {
43
+ return new Promise((resolve, reject) => {
44
+ if (this.closed) {
45
+ reject(new Error(`${method} failed: Codex is not running`));
46
+ return;
47
+ }
48
+ const id = this.nextId++;
49
+ this.pending.set(id, { resolve, reject, method });
50
+ try {
51
+ this.write({ id, method, params });
52
+ }
53
+ catch (error) {
54
+ this.pending.delete(id);
55
+ reject(error);
56
+ }
57
+ });
58
+ }
59
+ notify(method, params) {
60
+ this.write({ method, params });
61
+ }
62
+ respond(id, result) {
63
+ this.write({ id, result });
64
+ }
65
+ respondError(id, code, message) {
66
+ this.write({ id, error: { code, message } });
67
+ }
68
+ /* Closes stdin; the app-server ends on its own once it has nothing more to say. */
69
+ end() {
70
+ try {
71
+ this.child.process.stdin.end();
72
+ }
73
+ catch {
74
+ // Already closed by the other side.
75
+ }
76
+ }
77
+ kill(signal = 'SIGTERM') {
78
+ this.child.process.kill(signal);
79
+ }
80
+ stop() {
81
+ this.end();
82
+ this.child.endAfterGrace();
83
+ }
84
+ dispose() {
85
+ return this.child.end();
86
+ }
87
+ write(frame) {
88
+ if (this.closed) {
89
+ throw new Error('Codex stdin is closed');
90
+ }
91
+ this.child.process.stdin.write(`${JSON.stringify(frame)}\n`);
92
+ this.child.process.stdin.flush();
93
+ }
94
+ failed(error) {
95
+ const failure = new Error(`Codex transport failed: ${errorText(error)}`);
96
+ for (const settle of this.pending.values()) {
97
+ settle.reject(failure);
98
+ }
99
+ this.pending.clear();
100
+ try {
101
+ this.options.onError?.(failure);
102
+ }
103
+ catch (reportError) {
104
+ console.error('Reporting a Codex transport failure failed:', errorText(reportError));
105
+ }
106
+ }
107
+ readLine(line) {
108
+ try {
109
+ this.handleLine(line);
110
+ }
111
+ catch (error) {
112
+ this.failed(error);
113
+ }
114
+ }
115
+ async readLines() {
116
+ const reader = this.child.process.stdout.getReader();
117
+ const decoder = new TextDecoder();
118
+ let buffered = '';
119
+ try {
120
+ while (true) {
121
+ const { value, done } = await reader.read();
122
+ if (done) {
123
+ break;
124
+ }
125
+ buffered += decoder.decode(value, { stream: true });
126
+ let newline = buffered.indexOf('\n');
127
+ while (newline >= 0) {
128
+ this.readLine(buffered.slice(0, newline));
129
+ buffered = buffered.slice(newline + 1);
130
+ newline = buffered.indexOf('\n');
131
+ }
132
+ }
133
+ if (buffered.trim() !== '') {
134
+ this.readLine(buffered);
135
+ }
136
+ }
137
+ catch (error) {
138
+ if (!this.closed) {
139
+ this.failed(error);
140
+ }
141
+ }
142
+ finally {
143
+ reader.releaseLock();
144
+ }
145
+ }
146
+ handleLine(line) {
147
+ if (this.closed || line.trim() === '') {
148
+ return;
149
+ }
150
+ let frame;
151
+ try {
152
+ frame = JSON.parse(line);
153
+ }
154
+ catch {
155
+ return;
156
+ }
157
+ if (typeof frame !== 'object' || frame === null || Array.isArray(frame)) {
158
+ return;
159
+ }
160
+ const message = frame;
161
+ if (typeof message.method === 'string') {
162
+ this.options.onFrame(message);
163
+ return;
164
+ }
165
+ const settle = typeof message.id === 'number' ? this.pending.get(message.id) : undefined;
166
+ if (!settle) {
167
+ return;
168
+ }
169
+ this.pending.delete(message.id);
170
+ if (message.error !== undefined) {
171
+ settle.reject(new CodexRpcError(settle.method, message.error));
172
+ }
173
+ else {
174
+ settle.resolve(message.result);
175
+ }
176
+ }
177
+ handleExit(exitCode, stderr) {
178
+ if (this.closed) {
179
+ return;
180
+ }
181
+ this.closed = true;
182
+ for (const settle of this.pending.values()) {
183
+ settle.reject(new Error(`${settle.method} failed: Codex exited`));
184
+ }
185
+ this.pending.clear();
186
+ this.options.onExit(exitCode, stderr);
187
+ }
188
+ }
@@ -0,0 +1,15 @@
1
+ import type { AgentKind, ChatPreferencesPayload, ModelSelection, RuntimeMode } from '@adecore/agent-contracts';
2
+ export interface ComposerPreference {
3
+ runtimeMode?: RuntimeMode;
4
+ selection?: ModelSelection;
5
+ account?: string;
6
+ }
7
+ export declare class ComposerPreferences {
8
+ private readonly byClient;
9
+ private told;
10
+ set(clientId: string, preference: ChatPreferencesPayload): void;
11
+ forget(clientId: string): void;
12
+ terminalMode(): RuntimeMode | undefined;
13
+ for(provider: AgentKind): ComposerPreference;
14
+ private newest;
15
+ }
@@ -0,0 +1,46 @@
1
+ /*
2
+ * What each connected client says a new chat starts with, for the chats the host starts with no
3
+ * client mounting them. Held per socket like the approval switch: a machine never remembers a
4
+ * person's pick after that person's client is gone, so with nobody connected a chat gets the
5
+ * host's own defaults.
6
+ */
7
+ export class ComposerPreferences {
8
+ byClient = new Map();
9
+ told = 0;
10
+ set(clientId, preference) {
11
+ this.told += 1;
12
+ this.byClient.set(clientId, { preference, told: this.told });
13
+ }
14
+ forget(clientId) {
15
+ this.byClient.delete(clientId);
16
+ }
17
+ /* The mode the newest pick gives a terminal agent; undefined with no client or none said. */
18
+ terminalMode() {
19
+ return this.newest()?.preference.terminalRuntimeMode;
20
+ }
21
+ /* The newest pick among the connected clients, narrowed to this provider; empty with none. */
22
+ for(provider) {
23
+ const newest = this.newest();
24
+ if (newest === null) {
25
+ return {};
26
+ }
27
+ const { runtimeMode, selections, accounts } = newest.preference;
28
+ const selection = selections?.[provider];
29
+ const account = accounts?.[provider];
30
+ return { ...(runtimeMode ? { runtimeMode } : {}), ...(selection ? { selection } : {}), ...(account ? { account } : {}) };
31
+ }
32
+ newest() {
33
+ let newest = null;
34
+ for (const held of this.byClient.values()) {
35
+ if (newest === null || isNewer(held, newest)) {
36
+ newest = held;
37
+ }
38
+ }
39
+ return newest;
40
+ }
41
+ }
42
+ function isNewer(held, than) {
43
+ const changedAt = held.preference.changedAt ?? 0;
44
+ const otherChangedAt = than.preference.changedAt ?? 0;
45
+ return changedAt === otherChangedAt ? held.told > than.told : changedAt > otherChangedAt;
46
+ }
@@ -0,0 +1,2 @@
1
+ import { type ChatContextBreakdown, type ChatItem } from '@adecore/agent-contracts';
2
+ export declare function estimateContextBreakdown(items: readonly ChatItem[], contextTokens: number): ChatContextBreakdown;
@@ -0,0 +1,74 @@
1
+ import { attachmentImageMime } from '@adecore/agent-contracts';
2
+ // A rough rate for prose and code alike; scaling to the CLI's own total absorbs the error.
3
+ const CHARS_PER_TOKEN = 4;
4
+ // What a model is charged for a picture at the size a CLI sends it, whatever its bytes on disk.
5
+ const IMAGE_TOKENS = 1_600;
6
+ // Tools whose result is a file's contents. An MCP tool is named `<server>/<tool>`, so only the last part counts.
7
+ const READ_TOOLS = new Set(['Read', 'NotebookRead', 'View', 'read_file', 'read_text_file', 'read_multiple_files']);
8
+ function isReadTool(name) {
9
+ return READ_TOOLS.has(name.slice(name.lastIndexOf('/') + 1));
10
+ }
11
+ function textTokens(text) {
12
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
13
+ }
14
+ function inputTokens(input) {
15
+ if (input === undefined || input === null) {
16
+ return 0;
17
+ }
18
+ return textTokens(typeof input === 'string' ? input : JSON.stringify(input));
19
+ }
20
+ /*
21
+ * What the context of the last request is made of, estimated from the thread and scaled to the
22
+ * total the CLI reported. A subagent's own work never reached the main context, only its prompt and
23
+ * its report did, and nothing before the last compaction survived it. Thinking is left out: a CLI
24
+ * does not carry it into the next request.
25
+ */
26
+ export function estimateContextBreakdown(items, contextTokens) {
27
+ let toolOutput = 0;
28
+ let filesRead = 0;
29
+ let conversation = 0;
30
+ const start = items.findLastIndex((item) => item.kind === 'compaction') + 1;
31
+ for (const item of items.slice(start)) {
32
+ switch (item.kind) {
33
+ case 'user':
34
+ conversation += textTokens(item.text);
35
+ filesRead += (item.attachments ?? []).filter((attachment) => attachmentImageMime(attachment) !== null).length * IMAGE_TOKENS;
36
+ break;
37
+ case 'assistant':
38
+ if (!item.parentToolUseId) {
39
+ conversation += textTokens(item.text);
40
+ }
41
+ break;
42
+ case 'tool': {
43
+ if (item.parentToolUseId !== null) {
44
+ break;
45
+ }
46
+ const tokens = inputTokens(item.input) + textTokens(item.output ?? item.progress?.output ?? '');
47
+ if (isReadTool(item.name)) {
48
+ filesRead += tokens;
49
+ }
50
+ else {
51
+ toolOutput += tokens;
52
+ }
53
+ break;
54
+ }
55
+ case 'subagent':
56
+ // A row the host drew for work it delegated stands for that work; the call that made it is a tool row of its own.
57
+ if (item.origin !== 'ruimte') {
58
+ toolOutput += textTokens(item.prompt ?? '') + textTokens(item.result ?? '');
59
+ }
60
+ break;
61
+ default:
62
+ break;
63
+ }
64
+ }
65
+ const total = Math.max(0, Math.round(contextTokens));
66
+ const estimated = toolOutput + filesRead + conversation;
67
+ const scale = estimated > total ? total / estimated : 1;
68
+ const parts = {
69
+ toolOutput: Math.floor(toolOutput * scale),
70
+ filesRead: Math.floor(filesRead * scale),
71
+ conversation: Math.floor(conversation * scale)
72
+ };
73
+ return { ...parts, system: Math.max(0, total - parts.toolOutput - parts.filesRead - parts.conversation) };
74
+ }
@@ -0,0 +1,12 @@
1
+ import type { ChatEvent } from '@adecore/agent-contracts';
2
+ export declare const DELTA_TICK_MS = 16;
3
+ export declare class DeltaCoalescer {
4
+ private pending;
5
+ private timer;
6
+ private readonly send;
7
+ private readonly tickMs;
8
+ constructor(send: (event: ChatEvent) => void, tickMs?: number);
9
+ push(event: ChatEvent): void;
10
+ flush(): void;
11
+ dispose(): void;
12
+ }
@@ -0,0 +1,55 @@
1
+ /* The same rhythm as terminal output: a frame at 60 Hz. */
2
+ export const DELTA_TICK_MS = 16;
3
+ /*
4
+ * Holds the deltas of one chat back for a frame and sends the run on one item as a single delta. A
5
+ * CLI writes tens of deltas a second and every one was a frame to every client and a state update
6
+ * there, while the reveal on the client only moves once per frame anyway. Any other event sends
7
+ * what is held first, so the order a client sees is the order the thread changed in.
8
+ */
9
+ export class DeltaCoalescer {
10
+ pending = null;
11
+ timer = null;
12
+ send;
13
+ tickMs;
14
+ constructor(send, tickMs = DELTA_TICK_MS) {
15
+ this.send = send;
16
+ this.tickMs = tickMs;
17
+ }
18
+ push(event) {
19
+ if (event.type !== 'delta') {
20
+ this.flush();
21
+ this.send(event);
22
+ return;
23
+ }
24
+ if (this.pending !== null && this.pending.itemId === event.itemId) {
25
+ this.pending.text += event.text;
26
+ return;
27
+ }
28
+ this.flush();
29
+ this.pending = { itemId: event.itemId, text: event.text };
30
+ this.timer = setTimeout(() => this.flush(), this.tickMs);
31
+ // A frame of text waiting must never be the reason the process stays up.
32
+ this.timer.unref?.();
33
+ }
34
+ /* Sends what is held now, before a client attaches or detaches or the tick runs out. */
35
+ flush() {
36
+ if (this.timer !== null) {
37
+ clearTimeout(this.timer);
38
+ this.timer = null;
39
+ }
40
+ const pending = this.pending;
41
+ if (pending === null) {
42
+ return;
43
+ }
44
+ this.pending = null;
45
+ this.send({ type: 'delta', itemId: pending.itemId, text: pending.text });
46
+ }
47
+ /* Drops what is held without sending it, for a chat that is gone. */
48
+ dispose() {
49
+ if (this.timer !== null) {
50
+ clearTimeout(this.timer);
51
+ this.timer = null;
52
+ }
53
+ this.pending = null;
54
+ }
55
+ }
@@ -0,0 +1,4 @@
1
+ import { CodedError } from '../coded-error.ts';
2
+ export type ChatErrorCode = 'history-expired' | 'chat-not-found' | 'chat-unreadable' | 'chat-busy' | 'request-not-found' | 'chat-unsupported' | 'invalid-attachments' | 'subagent-not-found' | 'task-not-found' | 'transcript-missing' | 'transcript-format' | 'fork-failed' | 'turn-not-found' | 'item-not-found' | 'bookmark-not-found' | 'too-many-bookmarks';
3
+ export declare class ChatError<TCode extends string = ChatErrorCode> extends CodedError<TCode> {
4
+ }
@@ -0,0 +1,4 @@
1
+ import { CodedError } from '../coded-error.js';
2
+ /* A host that refuses more than a chat does widens the codes with its own. */
3
+ export class ChatError extends CodedError {
4
+ }
@@ -0,0 +1,3 @@
1
+ import { type FakeCli, type FakeIo, type FakeProgram } from './fake-cli.ts';
2
+ export declare function fakeClaude(io: FakeIo): FakeProgram;
3
+ export declare function claudeStoppingOnResume(sessionIds: ReadonlySet<string>, how: 'missing' | 'exit'): FakeCli;