@adecore/agents 0.0.1 → 0.17.0-beta.1

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 +33 -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,26 @@
1
+ /*
2
+ * What another node left for a chat, taken from the queue in front of its next prompt so the model
3
+ * hears it once. Nothing of it is shown: a person read each message in the thread as it landed
4
+ * (`showNotices`), so repeating it above the turn says the same thing twice.
5
+ */
6
+ export class NoticeNotes {
7
+ store;
8
+ chatId;
9
+ words;
10
+ constructor(store, chatId, words) {
11
+ this.store = store;
12
+ this.chatId = chatId;
13
+ this.words = words;
14
+ }
15
+ next() {
16
+ return { shown: [], heard: this.store.take(this.chatId).map((notice) => this.words.heard(notice)) };
17
+ }
18
+ reset() { }
19
+ }
20
+ /*
21
+ * The lines a chat that just loaded owes a person for what landed while nobody held it, marked so a
22
+ * reload writes none again. The model still hears the messages themselves from the queue.
23
+ */
24
+ export async function unshownNotes(store, chatId, words) {
25
+ return (await store.show(chatId)).map((notice) => words.shown(notice));
26
+ }
@@ -0,0 +1,30 @@
1
+ import { z } from 'zod';
2
+ export declare const MAX_NOTICES = 10;
3
+ export declare const NOTICE_MAX_AGE_MS: number;
4
+ declare const NoticeSchema: z.ZodObject<{
5
+ projectId: z.ZodString;
6
+ targetId: z.ZodString;
7
+ from: z.ZodString;
8
+ fromTitle: z.ZodString;
9
+ text: z.ZodString;
10
+ createdAt: z.ZodNumber;
11
+ shown: z.ZodOptional<z.ZodBoolean>;
12
+ }, z.core.$strip>;
13
+ export type Notice = z.infer<typeof NoticeSchema>;
14
+ export declare class NoticeStore {
15
+ readonly dir: string;
16
+ private readonly queues;
17
+ private readonly now;
18
+ private lastDrop;
19
+ constructor(dataDir: string, now?: () => number);
20
+ load(): Promise<void>;
21
+ put(notice: Omit<Notice, 'createdAt'>): Promise<number>;
22
+ take(targetId: string): Notice[];
23
+ settled(): Promise<void>;
24
+ show(targetId: string): Promise<Notice[]>;
25
+ waiting(targetId: string): Notice[];
26
+ prune(projectId: string, ids: ReadonlySet<string>): Promise<void>;
27
+ private fresh;
28
+ private persist;
29
+ }
30
+ export {};
@@ -0,0 +1,120 @@
1
+ import { join } from 'node:path';
2
+ import { z } from 'zod';
3
+ import { errorText } from '../error-text.js';
4
+ import { RecordDirectory } from '../record-directory.js';
5
+ // Per receiver. Past this the oldest goes: an agent in a loop must not fill a queue nobody reads.
6
+ export const MAX_NOTICES = 10;
7
+ /*
8
+ * A message nobody picked up in this long is dropped rather than delivered. It is news about work
9
+ * happening now ("the build is green"), and a model reading it six hours late would act on
10
+ * something that has moved on. The cap alone would not do it: one message in a queue of one waits
11
+ * for ever.
12
+ */
13
+ export const NOTICE_MAX_AGE_MS = 6 * 60 * 60 * 1000;
14
+ const NoticeSchema = z.object({
15
+ projectId: z.string().min(1),
16
+ /* The node the message is for; also the file it waits in. */
17
+ targetId: z.string().min(1),
18
+ /* The node that sent it, by id, since that is what the receiver can act on. */
19
+ from: z.string().min(1),
20
+ fromTitle: z.string(),
21
+ text: z.string().min(1),
22
+ createdAt: z.number(),
23
+ /*
24
+ * Whether a person was shown this message already. Its own mark, beside the queue: the queue is
25
+ * emptied by the model reading the message, and the two readers must not take it from each
26
+ * other. Absent is unshown, so a message left by an older host is still shown once.
27
+ */
28
+ shown: z.boolean().optional()
29
+ });
30
+ /* A queue waits in the file of the node it is for, so the first message in it names that node. */
31
+ const FileSchema = z.object({ notices: z.array(NoticeSchema).min(1) });
32
+ /*
33
+ * The messages waiting for each node, held until that node's agent has a moment to hear them, under
34
+ * `<dataDir>/notices`. On disk because the receiver may well be a node nobody has mounted yet, and a
35
+ * host that restarts must not lose what was left for it. Delivered once, whichever channel gets there
36
+ * first.
37
+ */
38
+ export class NoticeStore {
39
+ dir;
40
+ queues;
41
+ now;
42
+ lastDrop = Promise.resolve();
43
+ constructor(dataDir, now = Date.now) {
44
+ this.dir = join(dataDir, 'notices');
45
+ this.now = now;
46
+ this.queues = new RecordDirectory({
47
+ dir: this.dir,
48
+ schema: FileSchema,
49
+ idOf: (file) => file.notices[0].targetId,
50
+ // What went stale while the host was down is not delivered; a queue of nothing else goes.
51
+ keep: (file) => {
52
+ const fresh = file.notices.filter((notice) => this.fresh(notice));
53
+ return fresh[0] === undefined ? null : { notices: fresh };
54
+ }
55
+ });
56
+ }
57
+ load() {
58
+ return this.queues.load();
59
+ }
60
+ /* Puts one message in a node's queue and answers how many now wait for it. */
61
+ async put(notice) {
62
+ const queue = [...this.waiting(notice.targetId), { ...notice, createdAt: this.now() }].slice(-MAX_NOTICES);
63
+ await this.persist(notice.targetId, queue);
64
+ return queue.length;
65
+ }
66
+ /*
67
+ * Everything waiting for a node, gone the moment it is asked for. Synchronous, because the
68
+ * callers are: a shell's first screen and a hook's answer are both written before anything can
69
+ * be awaited. The queue leaves memory first and the file follows, so two channels asking in the
70
+ * same tick cannot both be handed the same message.
71
+ */
72
+ take(targetId) {
73
+ const queue = this.waiting(targetId);
74
+ if (queue.length === 0) {
75
+ return [];
76
+ }
77
+ this.lastDrop = this.persist(targetId, []).catch((e) => console.error(`Dropping the messages of ${targetId} failed:`, errorText(e)));
78
+ return queue;
79
+ }
80
+ /* Resolves once the file the last `take` dropped is gone; `take` cannot hand that promise back itself. */
81
+ settled() {
82
+ return this.lastDrop;
83
+ }
84
+ /*
85
+ * What nobody showed a person yet, marked here so it is shown once. The messages stay in the
86
+ * queue: this is the second reader, and only the model's copy may be taken away.
87
+ */
88
+ async show(targetId) {
89
+ const queue = this.waiting(targetId);
90
+ const unshown = queue.filter((notice) => notice.shown !== true);
91
+ if (unshown.length === 0) {
92
+ return [];
93
+ }
94
+ await this.persist(targetId, queue.map((notice) => ({ ...notice, shown: true })));
95
+ return unshown;
96
+ }
97
+ /* What waits for a node, without taking it; the stale ones are already gone from the answer. */
98
+ waiting(targetId) {
99
+ const file = this.queues.get(targetId);
100
+ if (file === undefined) {
101
+ return [];
102
+ }
103
+ return file.notices.filter((notice) => this.fresh(notice));
104
+ }
105
+ /* Drops what a project was holding for ids it no longer has: the node was deleted before it read them. */
106
+ prune(projectId, ids) {
107
+ return this.queues.prune((file) => file.notices[0].projectId === projectId && !ids.has(file.notices[0].targetId));
108
+ }
109
+ fresh(notice) {
110
+ return this.now() - notice.createdAt < NOTICE_MAX_AGE_MS;
111
+ }
112
+ /* The queue of one node, emptied here and on disk both; an empty one leaves no file behind. */
113
+ async persist(targetId, queue) {
114
+ if (queue.length === 0) {
115
+ await this.queues.remove(targetId);
116
+ return;
117
+ }
118
+ await this.queues.write({ notices: queue });
119
+ }
120
+ }
@@ -0,0 +1,11 @@
1
+ import { type RuntimeMode } from '@adecore/agent-contracts';
2
+ import { z } from 'zod';
3
+ export declare const MODE_ORDER: ("auto" | "auto-accept-edits" | "full-access" | "supervised")[];
4
+ export declare function narrowerMode(mode: RuntimeMode, ceiling: RuntimeMode): RuntimeMode;
5
+ export declare function ceilingForOpening(opener: RuntimeMode, requested: RuntimeMode | undefined): RuntimeMode;
6
+ export declare const modeFlag: z.ZodOptional<z.ZodEnum<{
7
+ auto: "auto";
8
+ "auto-accept-edits": "auto-accept-edits";
9
+ "full-access": "full-access";
10
+ supervised: "supervised";
11
+ }>>;
package/dist/modes.js ADDED
@@ -0,0 +1,28 @@
1
+ import { RuntimeModeSchema } from '@adecore/agent-contracts';
2
+ import { z } from 'zod';
3
+ import { VerbRefusal } from './context/verb.js';
4
+ /*
5
+ * Every CLI's flags widen in this order (its permission flags and the Codex thread options), and where
6
+ * two modes share a flag clamping down grants the same, never more. So one order serves every CLI.
7
+ */
8
+ export const MODE_ORDER = RuntimeModeSchema.options;
9
+ function rankOf(mode) {
10
+ return MODE_ORDER.indexOf(mode);
11
+ }
12
+ export function narrowerMode(mode, ceiling) {
13
+ return rankOf(mode) <= rankOf(ceiling) ? mode : ceiling;
14
+ }
15
+ function modeLines(mine) {
16
+ return [`mode\tyou\t${mine}`, ...MODE_ORDER.filter((mode) => rankOf(mode) <= rankOf(mine)).map((mode) => `mode\t${mode}\tallowed`)];
17
+ }
18
+ /*
19
+ * The mode an agent the opener opens may get at most, refusing a `--mode` above it. The ceiling is
20
+ * the opener's own mode, so a chain of agents can only ever narrow what it may do, never widen it.
21
+ */
22
+ export function ceilingForOpening(opener, requested) {
23
+ if (requested !== undefined && rankOf(requested) > rankOf(opener)) {
24
+ throw new VerbRefusal('mode-above-parent', `You run in ${opener} and --mode ${requested} is wider; an agent you open runs in your mode or a narrower one`, modeLines(opener));
25
+ }
26
+ return opener;
27
+ }
28
+ export const modeFlag = z.enum(MODE_ORDER, { error: `--mode is one of ${MODE_ORDER.join(', ')}` }).optional();
@@ -0,0 +1,10 @@
1
+ import type { OutboxClock } from './outbox-worker.ts';
2
+ export declare class ManualClock implements OutboxClock {
3
+ private time;
4
+ private timers;
5
+ private nextId;
6
+ now(): number;
7
+ setTimeout(run: () => void, ms: number): unknown;
8
+ clearTimeout(handle: unknown): void;
9
+ advance(ms: number): void;
10
+ }
@@ -0,0 +1,25 @@
1
+ /* A clock a test moves by hand. A timer fires only inside `advance`, never on its own. */
2
+ export class ManualClock {
3
+ time = 1_000_000;
4
+ timers = [];
5
+ nextId = 1;
6
+ now() {
7
+ return this.time;
8
+ }
9
+ setTimeout(run, ms) {
10
+ const id = this.nextId++;
11
+ this.timers.push({ at: this.time + ms, run, id });
12
+ return id;
13
+ }
14
+ clearTimeout(handle) {
15
+ this.timers = this.timers.filter((timer) => timer.id !== handle);
16
+ }
17
+ advance(ms) {
18
+ this.time += ms;
19
+ const due = this.timers.filter((timer) => timer.at <= this.time);
20
+ this.timers = this.timers.filter((timer) => timer.at > this.time);
21
+ for (const timer of due) {
22
+ timer.run();
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,44 @@
1
+ import type { OutboxEntryOf, OutboxStore, OutboxWorkShape } from './outbox.ts';
2
+ export declare const RETRY_DELAYS_MS: readonly number[];
3
+ export interface OutboxClock {
4
+ now(): number;
5
+ setTimeout(run: () => void, ms: number): unknown;
6
+ clearTimeout(handle: unknown): void;
7
+ }
8
+ export declare const systemClock: OutboxClock;
9
+ export type OutboxOutcome = void | 'wait';
10
+ export type OutboxHandlers<Work extends OutboxWorkShape> = {
11
+ [K in Work['kind']]: (entry: Extract<OutboxEntryOf<Work>, {
12
+ kind: K;
13
+ }>) => Promise<OutboxOutcome>;
14
+ };
15
+ export interface OutboxWorkerOptions<Work extends OutboxWorkShape> {
16
+ store: OutboxStore<Work>;
17
+ handlers: OutboxHandlers<Work>;
18
+ clock?: OutboxClock;
19
+ onParked?: (entry: OutboxEntryOf<Work>, error: unknown) => void;
20
+ }
21
+ export declare class OutboxWorker<Work extends OutboxWorkShape> {
22
+ private readonly store;
23
+ private readonly handlers;
24
+ private readonly clock;
25
+ private readonly onParked;
26
+ private readonly running;
27
+ private readonly waiting;
28
+ private readonly wakes;
29
+ private timer;
30
+ private started;
31
+ private waiters;
32
+ constructor(options: OutboxWorkerOptions<Work>);
33
+ start(): void;
34
+ stop(): void;
35
+ enqueue(projectId: string, target: string, work: Work, notBefore?: number): Promise<void>;
36
+ wake(target: string): void;
37
+ settled(): Promise<void>;
38
+ private drain;
39
+ private run;
40
+ private failed;
41
+ private schedule;
42
+ private isSettled;
43
+ private notifySettled;
44
+ }
@@ -0,0 +1,173 @@
1
+ import { errorText } from '../error-text.js';
2
+ /* How long a failed entry waits before each next attempt; one more failure than there are delays parks it. */
3
+ export const RETRY_DELAYS_MS = [1_000, 5_000, 30_000];
4
+ export const systemClock = {
5
+ now: () => Date.now(),
6
+ setTimeout: (run, ms) => setTimeout(run, ms),
7
+ clearTimeout: (handle) => clearTimeout(handle)
8
+ };
9
+ /*
10
+ * Works the outbox off, oldest first and one entry at a time per target, so two pieces of work
11
+ * about one node never overlap while nodes beside it do not wait on each other. A handler is
12
+ * idempotent, since a restart between the work and the removal of its file runs it again.
13
+ */
14
+ export class OutboxWorker {
15
+ store;
16
+ handlers;
17
+ clock;
18
+ onParked;
19
+ running = new Set();
20
+ // Entries that said `wait`, until their target is woken; in memory only, so a restart looks again.
21
+ waiting = new Set();
22
+ // Bumped by every wake of a target, so a wake that lands while its entry is still deciding to wait is not lost.
23
+ wakes = new Map();
24
+ timer = null;
25
+ started = false;
26
+ waiters = [];
27
+ constructor(options) {
28
+ this.store = options.store;
29
+ this.handlers = options.handlers;
30
+ this.clock = options.clock ?? systemClock;
31
+ this.onParked = options.onParked ?? (() => undefined);
32
+ }
33
+ start() {
34
+ this.started = true;
35
+ this.drain();
36
+ }
37
+ stop() {
38
+ this.started = false;
39
+ if (this.timer !== null) {
40
+ this.clock.clearTimeout(this.timer);
41
+ this.timer = null;
42
+ }
43
+ }
44
+ /* Owes a piece of work and starts on it; resolves once the entry is on disk, not once it is done. */
45
+ async enqueue(projectId, target, work, notBefore) {
46
+ const now = this.clock.now();
47
+ await this.store.put(projectId, target, work, now, notBefore ?? now);
48
+ this.drain();
49
+ }
50
+ /*
51
+ * Something about this target changed (a chat ended its turn), so whatever waited on it looks again.
52
+ * Deferred, so it never runs inside the broadcast of the chat that said so.
53
+ */
54
+ wake(target) {
55
+ this.wakes.set(target, (this.wakes.get(target) ?? 0) + 1);
56
+ let woke = false;
57
+ for (const entry of this.store.list()) {
58
+ if (entry.target === target && this.waiting.delete(entry.id)) {
59
+ woke = true;
60
+ }
61
+ }
62
+ if (woke) {
63
+ queueMicrotask(() => this.drain());
64
+ }
65
+ }
66
+ /* Resolves once nothing is running and nothing is due; an entry waiting out a retry or a wake does not count. */
67
+ settled() {
68
+ if (this.isSettled()) {
69
+ return Promise.resolve();
70
+ }
71
+ return new Promise((resolve) => {
72
+ this.waiters.push(resolve);
73
+ });
74
+ }
75
+ drain() {
76
+ if (!this.started) {
77
+ this.notifySettled();
78
+ return;
79
+ }
80
+ const now = this.clock.now();
81
+ const busy = new Set(this.running);
82
+ let nextDue = null;
83
+ for (const entry of this.store.list()) {
84
+ // A waiting entry holds no lane. The resume of a chat must not queue behind a wake that waits for it.
85
+ if (busy.has(entry.target) || this.waiting.has(entry.id)) {
86
+ continue;
87
+ }
88
+ // Nor does work due at a later time, which nothing owed before it has to wait for.
89
+ if (entry.attempts === 0 && entry.notBefore > now) {
90
+ nextDue = nextDue === null ? entry.notBefore : Math.min(nextDue, entry.notBefore);
91
+ continue;
92
+ }
93
+ const lanes = this.store.lanesOf(entry);
94
+ // Oldest first per target. A younger entry never overtakes one that waits out a retry.
95
+ for (const lane of lanes) {
96
+ busy.add(lane);
97
+ }
98
+ // The other lanes only wait for work that is running; work that is merely owed there is what ending takes away.
99
+ if (lanes.some((lane) => lane !== entry.target && this.running.has(lane))) {
100
+ continue;
101
+ }
102
+ if (entry.notBefore > now) {
103
+ nextDue = nextDue === null ? entry.notBefore : Math.min(nextDue, entry.notBefore);
104
+ continue;
105
+ }
106
+ void this.run(entry);
107
+ }
108
+ this.schedule(nextDue, now);
109
+ this.notifySettled();
110
+ }
111
+ async run(entry) {
112
+ const lanes = this.store.lanesOf(entry);
113
+ for (const lane of lanes) {
114
+ this.running.add(lane);
115
+ }
116
+ const woken = this.wakes.get(entry.target) ?? 0;
117
+ try {
118
+ const outcome = await this.handlers[entry.kind](entry);
119
+ if (outcome === 'wait') {
120
+ if ((this.wakes.get(entry.target) ?? 0) === woken && this.store.has(entry.id)) {
121
+ this.waiting.add(entry.id);
122
+ }
123
+ return;
124
+ }
125
+ await this.store.remove(entry.id);
126
+ }
127
+ catch (e) {
128
+ await this.failed(entry, e).catch((error) => console.error(`The outbox could not record a failed ${entry.kind}:`, errorText(error)));
129
+ }
130
+ finally {
131
+ for (const lane of lanes) {
132
+ this.running.delete(lane);
133
+ }
134
+ this.drain();
135
+ }
136
+ }
137
+ async failed(entry, error) {
138
+ const delay = RETRY_DELAYS_MS[entry.attempts];
139
+ if (delay === undefined) {
140
+ await this.store.remove(entry.id);
141
+ console.error(`Gave up on ${entry.kind} for ${entry.target}:`, errorText(error));
142
+ this.onParked(entry, error);
143
+ return;
144
+ }
145
+ await this.store.update({ ...entry, attempts: entry.attempts + 1, notBefore: this.clock.now() + delay });
146
+ }
147
+ schedule(nextDue, now) {
148
+ if (this.timer !== null) {
149
+ this.clock.clearTimeout(this.timer);
150
+ this.timer = null;
151
+ }
152
+ if (nextDue !== null) {
153
+ this.timer = this.clock.setTimeout(() => {
154
+ this.timer = null;
155
+ this.drain();
156
+ }, Math.max(0, nextDue - now));
157
+ }
158
+ }
159
+ isSettled() {
160
+ const now = this.clock.now();
161
+ return this.running.size === 0 && (!this.started || !this.store.list().some((entry) => entry.notBefore <= now && !this.waiting.has(entry.id)));
162
+ }
163
+ notifySettled() {
164
+ if (!this.isSettled()) {
165
+ return;
166
+ }
167
+ const waiting = this.waiters;
168
+ this.waiters = [];
169
+ for (const resolve of waiting) {
170
+ resolve();
171
+ }
172
+ }
173
+ }
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ export interface OutboxWorkShape {
3
+ kind: string;
4
+ payload: unknown;
5
+ }
6
+ declare const OutboxEntryFieldsSchema: z.ZodObject<{
7
+ id: z.ZodString;
8
+ projectId: z.ZodString;
9
+ target: z.ZodString;
10
+ createdAt: z.ZodNumber;
11
+ attempts: z.ZodNumber;
12
+ notBefore: z.ZodNumber;
13
+ }, z.core.$strip>;
14
+ export type OutboxEntryFields = z.infer<typeof OutboxEntryFieldsSchema>;
15
+ export type OutboxEntryOf<Work extends OutboxWorkShape> = Work & OutboxEntryFields;
16
+ export interface OutboxStoreOptions<Work extends OutboxWorkShape> {
17
+ dataDir: string;
18
+ work: z.ZodType<Work>;
19
+ lanesOf?(entry: OutboxEntryOf<Work>): string[];
20
+ outlivesTarget?(entry: OutboxEntryOf<Work>): boolean;
21
+ }
22
+ export declare class OutboxStore<Work extends OutboxWorkShape> {
23
+ readonly dir: string;
24
+ private readonly entries;
25
+ private readonly lanes;
26
+ private readonly outlivesTarget;
27
+ constructor(options: OutboxStoreOptions<Work>);
28
+ load(): Promise<void>;
29
+ put(projectId: string, target: string, work: Work, now: number, notBefore?: number): Promise<OutboxEntryOf<Work>>;
30
+ update(entry: OutboxEntryOf<Work>): Promise<void>;
31
+ remove(id: string): Promise<void>;
32
+ list(): OutboxEntryOf<Work>[];
33
+ has(id: string): boolean;
34
+ lanesOf(entry: OutboxEntryOf<Work>): string[];
35
+ prune(projectId: string, ids: ReadonlySet<string>): Promise<void>;
36
+ }
37
+ export {};
@@ -0,0 +1,76 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { join } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { RecordDirectory } from '../record-directory.js';
5
+ const OutboxEntryFieldsSchema = z.object({
6
+ id: z.string().min(1),
7
+ projectId: z.string().min(1),
8
+ // The node the work is about. Entries for one target run one at a time, and it goes with the node.
9
+ target: z.string().min(1),
10
+ createdAt: z.number(),
11
+ attempts: z.number().int().nonnegative(),
12
+ notBefore: z.number()
13
+ });
14
+ /*
15
+ * Work a host still owes, one file per entry under `<dataDir>/outbox`, removed once it is done. On
16
+ * disk because the owing outlives the process: a node written a moment before a restart still has
17
+ * its agent started after it.
18
+ */
19
+ export class OutboxStore {
20
+ dir;
21
+ entries;
22
+ lanes;
23
+ outlivesTarget;
24
+ constructor(options) {
25
+ this.dir = join(options.dataDir, 'outbox');
26
+ this.entries = new RecordDirectory({
27
+ dir: this.dir,
28
+ schema: z.intersection(options.work, OutboxEntryFieldsSchema),
29
+ idOf: (entry) => entry.id
30
+ });
31
+ this.lanes = options.lanesOf ?? ((entry) => [entry.target]);
32
+ this.outlivesTarget = options.outlivesTarget ?? (() => false);
33
+ }
34
+ /* Reads what an earlier run of the host still owed. Call before the worker starts. */
35
+ load() {
36
+ return this.entries.load();
37
+ }
38
+ /* `notBefore` later than `now` is work due at a time, which holds no lane until then. */
39
+ async put(projectId, target, work, now, notBefore = now) {
40
+ const entry = {
41
+ ...work,
42
+ id: `${work.kind}-${randomBytes(6).toString('hex')}`,
43
+ projectId,
44
+ target,
45
+ createdAt: now,
46
+ attempts: 0,
47
+ notBefore
48
+ };
49
+ await this.entries.write(entry);
50
+ return entry;
51
+ }
52
+ /* The same entry with what its last attempt left behind. */
53
+ async update(entry) {
54
+ if (!this.entries.has(entry.id)) {
55
+ return;
56
+ }
57
+ await this.entries.write(entry);
58
+ }
59
+ remove(id) {
60
+ return this.entries.remove(id);
61
+ }
62
+ /* Oldest first, which is the order the work was owed in. */
63
+ list() {
64
+ return this.entries.all().sort((a, b) => a.createdAt - b.createdAt);
65
+ }
66
+ has(id) {
67
+ return this.entries.has(id);
68
+ }
69
+ lanesOf(entry) {
70
+ return this.lanes(entry);
71
+ }
72
+ /* Drops what this project owed for ids it no longer has, since nothing is started for a node that was deleted. */
73
+ prune(projectId, ids) {
74
+ return this.entries.prune((entry) => entry.projectId === projectId && !this.outlivesTarget(entry) && !ids.has(entry.target));
75
+ }
76
+ }
@@ -0,0 +1,20 @@
1
+ import { type AgentKind, type ProviderAccount, type ProviderAccountMap } from '@adecore/agent-contracts';
2
+ import type { ChatProvider } from '../provider.ts';
3
+ import { type AccountsHost, type ReservedVariables } from './variables.ts';
4
+ export type Env = Record<string, string | undefined>;
5
+ export declare function isKnownKind(kind: string): kind is AgentKind;
6
+ export declare function isDefaultAccount(id: string, account: Pick<ProviderAccount, 'kind'>): boolean;
7
+ export declare function expandHome(path: string, env: Env): string;
8
+ export declare function accountProblem(id: string, account: ProviderAccount, provider: ChatProvider, env: Env, reserved?: ReservedVariables): string | null;
9
+ export declare function folderVariablesOf(providerOf: (kind: AgentKind) => ChatProvider): string[];
10
+ export declare function withDefaults(accounts: ProviderAccountMap): ProviderAccountMap;
11
+ export declare function defaultFolder(provider: ChatProvider, env: Env): string;
12
+ export declare function accountFolder(id: string, account: ProviderAccount, provider: ChatProvider, env: Env): string;
13
+ export declare function transcriptFolder(id: string, account: ProviderAccount, provider: ChatProvider, env: Env): string;
14
+ export declare function accountEnv(id: string, account: ProviderAccount, provider: ChatProvider, baseEnv: Env, variables?: Record<string, string>): Env;
15
+ export interface NamedAccount {
16
+ id: string;
17
+ account: ProviderAccount;
18
+ }
19
+ export declare function canContinue(from: NamedAccount, to: NamedAccount, providerOf: (kind: AgentKind) => ChatProvider, env: Env): boolean;
20
+ export declare function readAccount(id: string, raw: unknown, providerOf: (kind: AgentKind) => ChatProvider, env: Env, host?: AccountsHost): unknown;