@leuduan/lagent 0.0.0-stage → 0.1.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 (327) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +144 -2
  3. package/dist/chunks/2O7ZGEIT.js +1 -0
  4. package/dist/chunks/2OSESBIL.js +1 -0
  5. package/dist/chunks/34KW6VNQ.js +1 -0
  6. package/dist/chunks/37YOGDGY.js +1 -0
  7. package/dist/chunks/3LQHQRIU.js +6 -0
  8. package/dist/chunks/3QCW4WDJ.js +1 -0
  9. package/dist/chunks/4XVRUSCQ.js +1 -0
  10. package/dist/chunks/6ECI2GGV.js +1 -0
  11. package/dist/chunks/74HKUGRR.js +1 -0
  12. package/dist/chunks/ADHERZLW.js +2 -0
  13. package/dist/chunks/AP6Q4EPT.js +36 -0
  14. package/dist/chunks/BM3S37EZ.js +1 -0
  15. package/dist/chunks/C5LYCXJG.js +1 -0
  16. package/dist/chunks/CDQG7RKA.js +1 -0
  17. package/dist/chunks/D7E6EWIE.js +3 -0
  18. package/dist/chunks/DDANMY45.js +3 -0
  19. package/dist/chunks/DRYAN665.js +1 -0
  20. package/dist/chunks/DXFXAYDN.js +17 -0
  21. package/dist/chunks/EGZI4XQX.js +24 -0
  22. package/dist/chunks/ENWFSU27.js +41 -0
  23. package/dist/chunks/G6RXC3AL.js +1 -0
  24. package/dist/chunks/G74AGRAJ.js +15 -0
  25. package/dist/chunks/GQGNVSKB.js +1 -0
  26. package/dist/chunks/IJ2WNMD5.js +7 -0
  27. package/dist/chunks/J5S4URAW.js +1 -0
  28. package/dist/chunks/JPZFKSWT.js +1 -0
  29. package/dist/chunks/KESCTXGO.js +5 -0
  30. package/dist/chunks/L3N4FIEN.js +1 -0
  31. package/dist/chunks/NAOUTU7A.js +4 -0
  32. package/dist/chunks/NDIM7MEW.js +1 -0
  33. package/dist/chunks/O4PPOM5Z.js +2 -0
  34. package/dist/chunks/OVZFGILJ.js +1 -0
  35. package/dist/chunks/P5ZHHOOM.js +6 -0
  36. package/dist/chunks/PUSUPTAT.js +1 -0
  37. package/dist/chunks/QMMLQJBS.js +1 -0
  38. package/dist/chunks/RK3VP2Q4.js +3 -0
  39. package/dist/chunks/SGGMBSBA.js +1 -0
  40. package/dist/chunks/SL2YLB2F.js +1 -0
  41. package/dist/chunks/TOOIB6J2.js +1 -0
  42. package/dist/chunks/TYTQRLTX.js +1 -0
  43. package/dist/chunks/UC3FVVJ2.js +5 -0
  44. package/dist/chunks/UCTEU62F.js +1 -0
  45. package/dist/chunks/UD3VJER6.js +1 -0
  46. package/dist/chunks/UKXMCHHT.js +1 -0
  47. package/dist/chunks/WUUKTSRT.js +7 -0
  48. package/dist/chunks/X65X4KP5.js +7 -0
  49. package/dist/chunks/YIR5MYGU.js +1 -0
  50. package/dist/chunks/YLNJXQ3Z.js +1 -0
  51. package/dist/context.d.ts +101 -0
  52. package/dist/index.d.ts +47 -0
  53. package/dist/index.js +77 -0
  54. package/dist/journal/catalog.d.ts +112 -0
  55. package/dist/journal/forkChain.d.ts +16 -0
  56. package/dist/journal/index.d.ts +15 -0
  57. package/dist/journal/journal.d.ts +475 -0
  58. package/dist/journal/projection.d.ts +87 -0
  59. package/dist/journal/store.d.ts +181 -0
  60. package/dist/kit/claim.d.ts +13 -0
  61. package/dist/kit/drain.d.ts +10 -0
  62. package/dist/kit/extension.d.ts +180 -0
  63. package/dist/kit/grants.d.ts +9 -0
  64. package/dist/kit/modes.d.ts +65 -0
  65. package/dist/kit/retries.d.ts +25 -0
  66. package/dist/kit/scope.d.ts +32 -0
  67. package/dist/kit/service.d.ts +7 -0
  68. package/dist/kit/strategy.d.ts +10 -0
  69. package/dist/kit/timers.d.ts +43 -0
  70. package/dist/kit/tool.d.ts +72 -0
  71. package/dist/llm/client.d.ts +135 -0
  72. package/dist/llm/content.d.ts +52 -0
  73. package/dist/llm/door.d.ts +21 -0
  74. package/dist/llm/door.js +1 -0
  75. package/dist/llm/failure.d.ts +82 -0
  76. package/dist/llm/index.d.ts +16 -0
  77. package/dist/llm/primitives.d.ts +286 -0
  78. package/dist/llm/prompt.d.ts +21 -0
  79. package/dist/llm/provider.d.ts +63 -0
  80. package/dist/llm/readLedger.d.ts +24 -0
  81. package/dist/llm/request.d.ts +58 -0
  82. package/dist/loop/agent.d.ts +134 -0
  83. package/dist/loop/auxiliary.d.ts +54 -0
  84. package/dist/loop/chain.d.ts +46 -0
  85. package/dist/loop/chunks.d.ts +27 -0
  86. package/dist/loop/continuation.d.ts +166 -0
  87. package/dist/loop/decide.d.ts +64 -0
  88. package/dist/loop/errors.d.ts +162 -0
  89. package/dist/loop/forks.d.ts +61 -0
  90. package/dist/loop/holds.d.ts +104 -0
  91. package/dist/loop/hooks.d.ts +241 -0
  92. package/dist/loop/inbox.d.ts +188 -0
  93. package/dist/loop/index.d.ts +37 -0
  94. package/dist/loop/limits.d.ts +14 -0
  95. package/dist/loop/opening.d.ts +42 -0
  96. package/dist/loop/prepare.d.ts +30 -0
  97. package/dist/loop/projections.d.ts +307 -0
  98. package/dist/loop/replace.d.ts +33 -0
  99. package/dist/loop/requests.d.ts +129 -0
  100. package/dist/loop/route.d.ts +53 -0
  101. package/dist/loop/runtime.d.ts +85 -0
  102. package/dist/loop/state.d.ts +70 -0
  103. package/dist/loop/strategy.d.ts +71 -0
  104. package/dist/loop/summary.d.ts +3 -0
  105. package/dist/loop/surface.d.ts +153 -0
  106. package/dist/loop/threadState.d.ts +72 -0
  107. package/dist/loop/tool.d.ts +101 -0
  108. package/dist/loop/toolCalls.d.ts +42 -0
  109. package/dist/loop/wake.d.ts +32 -0
  110. package/dist/models/config/agent.d.ts +18 -0
  111. package/dist/models/config/prompt.d.ts +18 -0
  112. package/dist/models/door.d.ts +12 -0
  113. package/dist/models/door.js +1 -0
  114. package/dist/models/index.d.ts +7 -0
  115. package/dist/models/jsonSchema.d.ts +7 -0
  116. package/dist/models/wire/journal.d.ts +2772 -0
  117. package/dist/models/wire/primitives.d.ts +65 -0
  118. package/dist/plugin.d.ts +53 -0
  119. package/dist/plugin.js +1 -0
  120. package/dist/plugins/agentsMd.d.ts +76 -0
  121. package/dist/plugins/agentsMd.js +1 -0
  122. package/dist/plugins/ask/askQuestion.d.ts +9 -0
  123. package/dist/plugins/ask/index.d.ts +26 -0
  124. package/dist/plugins/ask/index.js +1 -0
  125. package/dist/plugins/ask/service.d.ts +125 -0
  126. package/dist/plugins/commands/background.d.ts +8 -0
  127. package/dist/plugins/commands/foreground.d.ts +4 -0
  128. package/dist/plugins/commands/index.d.ts +40 -0
  129. package/dist/plugins/commands/index.js +1 -0
  130. package/dist/plugins/commands/output.d.ts +115 -0
  131. package/dist/plugins/commands/record.d.ts +549 -0
  132. package/dist/plugins/commands/service.d.ts +314 -0
  133. package/dist/plugins/commands/shell.d.ts +25 -0
  134. package/dist/plugins/compaction/checkpoint.d.ts +91 -0
  135. package/dist/plugins/compaction/config.d.ts +133 -0
  136. package/dist/plugins/compaction/estimate.d.ts +29 -0
  137. package/dist/plugins/compaction/index.d.ts +2 -0
  138. package/dist/plugins/compaction/index.js +7 -0
  139. package/dist/plugins/compaction/log.d.ts +61 -0
  140. package/dist/plugins/compaction/planner.d.ts +207 -0
  141. package/dist/plugins/compaction/preset.d.ts +86 -0
  142. package/dist/plugins/compaction/prune.d.ts +9 -0
  143. package/dist/plugins/compaction/records.d.ts +227 -0
  144. package/dist/plugins/compaction/segments.d.ts +23 -0
  145. package/dist/plugins/compaction/shared.d.ts +14 -0
  146. package/dist/plugins/compaction/summarize.d.ts +13 -0
  147. package/dist/plugins/dateAndBranch.d.ts +47 -0
  148. package/dist/plugins/dateAndBranch.js +1 -0
  149. package/dist/plugins/delegations/answers.d.ts +39 -0
  150. package/dist/plugins/delegations/assignmentKey.d.ts +22 -0
  151. package/dist/plugins/delegations/cleanup.d.ts +161 -0
  152. package/dist/plugins/delegations/constraint.d.ts +76 -0
  153. package/dist/plugins/delegations/index.d.ts +39 -0
  154. package/dist/plugins/delegations/index.js +1 -0
  155. package/dist/plugins/delegations/notices.d.ts +261 -0
  156. package/dist/plugins/delegations/obligations.d.ts +210 -0
  157. package/dist/plugins/delegations/record.d.ts +632 -0
  158. package/dist/plugins/delegations/request.d.ts +381 -0
  159. package/dist/plugins/delegations/role.d.ts +30 -0
  160. package/dist/plugins/delegations/service.d.ts +562 -0
  161. package/dist/plugins/delegations/standing.d.ts +19 -0
  162. package/dist/plugins/delegations/stopCheck.d.ts +21 -0
  163. package/dist/plugins/delegations/testing.d.ts +16 -0
  164. package/dist/plugins/delegations/tools.d.ts +71 -0
  165. package/dist/plugins/delegations/waitBook.d.ts +116 -0
  166. package/dist/plugins/delegations/waits.d.ts +118 -0
  167. package/dist/plugins/diagnostics/digest.d.ts +14 -0
  168. package/dist/plugins/diagnostics/index.d.ts +68 -0
  169. package/dist/plugins/diagnostics/index.js +1 -0
  170. package/dist/plugins/historyRecall/history.d.ts +9 -0
  171. package/dist/plugins/historyRecall/index.d.ts +31 -0
  172. package/dist/plugins/historyRecall/index.js +1 -0
  173. package/dist/plugins/historyRecall/render.d.ts +34 -0
  174. package/dist/plugins/historyRecall/tools.d.ts +4 -0
  175. package/dist/plugins/mcp/catalog.d.ts +36 -0
  176. package/dist/plugins/mcp/config.d.ts +63 -0
  177. package/dist/plugins/mcp/connection.d.ts +53 -0
  178. package/dist/plugins/mcp/errors.d.ts +32 -0
  179. package/dist/plugins/mcp/index.d.ts +33 -0
  180. package/dist/plugins/mcp/index.js +5 -0
  181. package/dist/plugins/mcp/mount.d.ts +98 -0
  182. package/dist/plugins/mcp/names.d.ts +25 -0
  183. package/dist/plugins/mcp/render.d.ts +29 -0
  184. package/dist/plugins/mcp/slots.d.ts +15 -0
  185. package/dist/plugins/mcp/stdio.d.ts +49 -0
  186. package/dist/plugins/mcp/tool.d.ts +41 -0
  187. package/dist/plugins/providers/anthropic.d.ts +175 -0
  188. package/dist/plugins/providers/anthropicMessages.d.ts +65 -0
  189. package/dist/plugins/providers/codex.d.ts +60 -0
  190. package/dist/plugins/providers/codexAuth.d.ts +116 -0
  191. package/dist/plugins/providers/deepseek.d.ts +55 -0
  192. package/dist/plugins/providers/door.d.ts +61 -0
  193. package/dist/plugins/providers/door.js +1 -0
  194. package/dist/plugins/providers/failure.d.ts +23 -0
  195. package/dist/plugins/providers/gateway.d.ts +23 -0
  196. package/dist/plugins/providers/index.d.ts +6 -0
  197. package/dist/plugins/providers/openaiChat.d.ts +62 -0
  198. package/dist/plugins/providers/openaiResponses.d.ts +100 -0
  199. package/dist/plugins/providers/opencodeCopilot.d.ts +23 -0
  200. package/dist/plugins/providers/opencodeGo.d.ts +98 -0
  201. package/dist/plugins/providers/shared.d.ts +104 -0
  202. package/dist/plugins/providers/stream.d.ts +154 -0
  203. package/dist/plugins/routeContext.d.ts +22 -0
  204. package/dist/plugins/routeContext.js +1 -0
  205. package/dist/plugins/schedules/index.d.ts +21 -0
  206. package/dist/plugins/schedules/index.js +5 -0
  207. package/dist/plugins/schedules/record.d.ts +396 -0
  208. package/dist/plugins/schedules/service.d.ts +120 -0
  209. package/dist/plugins/schedules/time.d.ts +26 -0
  210. package/dist/plugins/schedules/tools.d.ts +6 -0
  211. package/dist/plugins/skills/discovery.d.ts +54 -0
  212. package/dist/plugins/skills/index.d.ts +31 -0
  213. package/dist/plugins/skills/index.js +1 -0
  214. package/dist/plugins/skills/tool.d.ts +11 -0
  215. package/dist/plugins/store/durableObject.d.ts +23 -0
  216. package/dist/plugins/store/durableObject.js +1 -0
  217. package/dist/plugins/store/durableObjectConnection.d.ts +27 -0
  218. package/dist/plugins/store/memory.d.ts +29 -0
  219. package/dist/plugins/store/sql.d.ts +59 -0
  220. package/dist/plugins/store/sqlite.d.ts +31 -0
  221. package/dist/plugins/store/sqlite.js +1 -0
  222. package/dist/plugins/subAgents/availableRoutes.d.ts +53 -0
  223. package/dist/plugins/subAgents/fork.d.ts +32 -0
  224. package/dist/plugins/subAgents/index.d.ts +67 -0
  225. package/dist/plugins/subAgents/index.js +1 -0
  226. package/dist/plugins/subAgents/tool.d.ts +38 -0
  227. package/dist/plugins/toolPolicy/index.d.ts +61 -0
  228. package/dist/plugins/toolPolicy/index.js +1 -0
  229. package/dist/plugins/tools/editFiles.d.ts +128 -0
  230. package/dist/plugins/tools/fsRead.d.ts +17 -0
  231. package/dist/plugins/tools/html.d.ts +23 -0
  232. package/dist/plugins/tools/index.d.ts +59 -0
  233. package/dist/plugins/tools/index.js +1 -0
  234. package/dist/plugins/tools/network.d.ts +23 -0
  235. package/dist/plugins/tools/ripgrep.d.ts +20 -0
  236. package/dist/plugins/tools/textEdit.d.ts +67 -0
  237. package/dist/plugins/tools/web.d.ts +32 -0
  238. package/dist/plugins/truncateToolOutput.d.ts +26 -0
  239. package/dist/plugins/truncateToolOutput.js +1 -0
  240. package/dist/plugins/usage/index.d.ts +24 -0
  241. package/dist/plugins/usage/index.js +1 -0
  242. package/dist/plugins/usage/tracker.d.ts +34 -0
  243. package/dist/plugins/verify/facts.d.ts +356 -0
  244. package/dist/plugins/verify/index.d.ts +75 -0
  245. package/dist/plugins/verify/index.js +1 -0
  246. package/dist/plugins/verify/service.d.ts +90 -0
  247. package/dist/plugins/workflows/assignment.d.ts +16 -0
  248. package/dist/plugins/workflows/bindings.d.ts +95 -0
  249. package/dist/plugins/workflows/cleanup.d.ts +38 -0
  250. package/dist/plugins/workflows/definition.d.ts +184 -0
  251. package/dist/plugins/workflows/fold.d.ts +373 -0
  252. package/dist/plugins/workflows/index.d.ts +28 -0
  253. package/dist/plugins/workflows/index.js +6 -0
  254. package/dist/plugins/workflows/observations.d.ts +81 -0
  255. package/dist/plugins/workflows/participants.d.ts +143 -0
  256. package/dist/plugins/workflows/policy.d.ts +112 -0
  257. package/dist/plugins/workflows/providers.d.ts +146 -0
  258. package/dist/plugins/workflows/record.d.ts +2863 -0
  259. package/dist/plugins/workflows/routing.d.ts +116 -0
  260. package/dist/plugins/workflows/schema.d.ts +26 -0
  261. package/dist/plugins/workflows/service.d.ts +477 -0
  262. package/dist/plugins/workflows/submit.d.ts +8 -0
  263. package/dist/plugins/workflows/view.d.ts +103 -0
  264. package/dist/plugins/workflows/work.d.ts +21 -0
  265. package/dist/plugins/workspaceContext.d.ts +38 -0
  266. package/dist/plugins/workspaceContext.js +1 -0
  267. package/dist/presets/agents.d.ts +7 -0
  268. package/dist/presets/base.d.ts +10 -0
  269. package/dist/presets/codex.d.ts +60 -0
  270. package/dist/presets/index.d.ts +4 -0
  271. package/dist/presets/prompts.d.ts +56 -0
  272. package/dist/sandbox/absent.d.ts +32 -0
  273. package/dist/sandbox/client.d.ts +39 -0
  274. package/dist/sandbox/files.d.ts +28 -0
  275. package/dist/sandbox/index.d.ts +49 -0
  276. package/dist/sandbox/index.js +4 -0
  277. package/dist/sandbox/mode.d.ts +57 -0
  278. package/dist/sandbox/policy.d.ts +86 -0
  279. package/dist/sandbox/protocol.d.ts +107 -0
  280. package/dist/sandbox/service.d.ts +143 -0
  281. package/dist/sandbox/worker.d.ts +12 -0
  282. package/dist/sandbox/worker.js +1 -0
  283. package/dist/services/admission.d.ts +174 -0
  284. package/dist/services/agents.d.ts +37 -0
  285. package/dist/services/birth.d.ts +220 -0
  286. package/dist/services/contributions.d.ts +73 -0
  287. package/dist/services/core.d.ts +34 -0
  288. package/dist/services/journal.d.ts +26 -0
  289. package/dist/services/launches.d.ts +104 -0
  290. package/dist/services/llm.d.ts +80 -0
  291. package/dist/services/owner.d.ts +367 -0
  292. package/dist/services/preset.d.ts +154 -0
  293. package/dist/services/presets.d.ts +139 -0
  294. package/dist/services/prompts.d.ts +67 -0
  295. package/dist/services/runEvents.d.ts +79 -0
  296. package/dist/services/thread.d.ts +267 -0
  297. package/dist/services/threadActivation.d.ts +100 -0
  298. package/dist/services/threadDirs.d.ts +38 -0
  299. package/dist/services/threadView.d.ts +92 -0
  300. package/dist/services/threads.d.ts +449 -0
  301. package/dist/services/tools.d.ts +41 -0
  302. package/dist/sys/environment.d.ts +47 -0
  303. package/dist/sys/git.d.ts +60 -0
  304. package/dist/sys/paths.d.ts +56 -0
  305. package/dist/sys/spawn.d.ts +176 -0
  306. package/dist/testing/clock.d.ts +17 -0
  307. package/dist/testing/fakeLlm.d.ts +74 -0
  308. package/dist/testing/fedLlm.d.ts +32 -0
  309. package/dist/testing/helpers.d.ts +344 -0
  310. package/dist/testing/index.d.ts +13 -0
  311. package/dist/testing/index.js +1 -0
  312. package/dist/testing/processes.d.ts +13 -0
  313. package/dist/testing/sandbox.d.ts +71 -0
  314. package/dist/testing/settlement.d.ts +6 -0
  315. package/dist/testing/tmpDirs.d.ts +17 -0
  316. package/dist/util/async.d.ts +8 -0
  317. package/dist/util/diff.d.ts +25 -0
  318. package/dist/util/dispatch.d.ts +8 -0
  319. package/dist/util/errors.d.ts +28 -0
  320. package/dist/util/foldState.d.ts +17 -0
  321. package/dist/util/freeze.d.ts +2 -0
  322. package/dist/util/ids.d.ts +2 -0
  323. package/dist/util/json.d.ts +37 -0
  324. package/dist/util/log.d.ts +6 -0
  325. package/dist/util/text.d.ts +21 -0
  326. package/dist/util/version.d.ts +2 -0
  327. package/package.json +175 -3
@@ -0,0 +1,176 @@
1
+ import type { Readable, Writable } from 'node:stream';
2
+ /**
3
+ * Process containment for everything lagent runs: one process group per command, a bounded stop,
4
+ * and cleanup however lagent ends. POSIX only.
5
+ *
6
+ * Every command is spawned detached, so it leads a new session and process group whose id is its
7
+ * pid, with no controlling terminal. A short `/bin/sh` wrapper holds it at a startup gate until the
8
+ * group is registered with the exit listener and the reaper, joins stderr into stdout unless the
9
+ * streams are wired as separate pipes, and then `exec`s the command, so the leader keeps the pid
10
+ * and therefore the group. If lagent dies before the gate opens, the wrapper reads EOF and runs
11
+ * nothing. Nothing is inherited implicitly: every spawn names its whole environment.
12
+ *
13
+ * Process-wide, because process exit is: the exit listener and the reaper are installed with the
14
+ * first spawn and shared by every root in the process. Stopping commands while the process lives
15
+ * is their owners' job, through the signal every spawn requires.
16
+ */
17
+ /** How long SIGTERM gets before SIGKILL, and SIGKILL before a stop is reported failed. */
18
+ export declare const DEFAULT_KILL_GRACE_MS = 3000;
19
+ /**
20
+ * A process's whole environment; an `undefined` value leaves the variable unset. lagent never
21
+ * builds one from its own: a host does (`sys/environment`), and every spawn passes one.
22
+ */
23
+ export type SpawnEnvironment = Readonly<Record<string, string | undefined>>;
24
+ /**
25
+ * How the command's standard streams are wired. `joined`: no input, and stderr joined into stdout
26
+ * as one `output` stream, in exact order. `pipes`: a writable stdin and separate stdout and stderr,
27
+ * for a process that speaks a protocol on its standard streams.
28
+ */
29
+ export type SpawnStdio = 'joined' | 'pipes';
30
+ export type SpawnOptions = {
31
+ /** A shell command line, run by `/bin/sh -c`, or an argv run without a shell. */
32
+ command: string | readonly [string, ...string[]];
33
+ cwd: string;
34
+ /**
35
+ * The command's whole environment, passed as it is. An argv's program is resolved through its
36
+ * `PATH`, or the shell's default search path when it has none.
37
+ */
38
+ env: SpawnEnvironment;
39
+ /** Defaults to `joined`. */
40
+ stdio?: SpawnStdio;
41
+ /** The owner: aborting it stops the command's group within the bound. */
42
+ signal: AbortSignal;
43
+ /** Stops the group when this elapses; omitted for a command meant to outlive its call. */
44
+ timeoutMs?: number;
45
+ killGraceMs?: number;
46
+ /**
47
+ * Awaited after the group is registered and before the gate opens, with the group's id: what a
48
+ * caller must have durable before the command runs — a record that it may have started, say — goes
49
+ * here. A rejection kills the group, which has run nothing, and rejects the spawn.
50
+ */
51
+ beforeRelease?: (pgid: number) => void | Promise<void>;
52
+ };
53
+ /** Why lagent stopped a group: its deadline, its owner's signal, or an explicit `stop()`. */
54
+ export type StopCause = 'timeout' | 'abort' | 'stop';
55
+ export type SpawnResult = {
56
+ /** `null` when the leader never exited, or was killed by a signal. */
57
+ exitCode: number | null;
58
+ exitSignal: NodeJS.Signals | null;
59
+ /** `null` when the leader exited on its own. */
60
+ stoppedBy: StopCause | null;
61
+ /** The leader exited leaving processes in its group, and they were stopped. */
62
+ leftoversStopped: boolean;
63
+ /**
64
+ * A live process remained in the group when the bound ran out. The group stays tracked by the
65
+ * exit listener and the reaper, and is polled until it empties.
66
+ */
67
+ stopFailed: boolean;
68
+ durationMs: number;
69
+ };
70
+ /** A spawned process group, whichever way its streams are wired. */
71
+ export interface SpawnedGroup {
72
+ readonly pid: number;
73
+ /**
74
+ * Settles within `2 × killGraceMs` of the deadline, the abort, `stop()`, or the leader exiting
75
+ * with processes left behind, and sooner once the leader, the group and the pipe are all done.
76
+ * Never rejects.
77
+ */
78
+ readonly result: Promise<SpawnResult>;
79
+ /** Stops the group, as an abort would, and resolves with `result`. */
80
+ stop(): Promise<SpawnResult>;
81
+ }
82
+ /** A group spawned with `stdio: 'joined'`. */
83
+ export interface SpawnedCommand extends SpawnedGroup {
84
+ /** stdout with stderr joined, in exact order. Destroyed once the command settles. */
85
+ readonly output: Readable;
86
+ }
87
+ /**
88
+ * A group spawned with `stdio: 'pipes'`. Every stream is destroyed once the command settles.
89
+ *
90
+ * Read stdout and stderr continuously, from the start, even when only one matters. A child blocks
91
+ * on a write once that pipe's buffer fills, so an unread stderr eventually hangs a process that
92
+ * only logs to it. Keep a bounded tail if the content is not needed.
93
+ */
94
+ export interface SpawnedPipes extends SpawnedGroup {
95
+ readonly stdin: Writable;
96
+ readonly stdout: Readable;
97
+ readonly stderr: Readable;
98
+ }
99
+ /**
100
+ * Spawns a command in a process group of its own. Resolves once the group is registered,
101
+ * `beforeRelease` has settled and the command released; rejects, having run nothing, when it cannot
102
+ * be spawned or registered, when `beforeRelease` rejects, or when `signal` aborts first.
103
+ */
104
+ export declare function spawnCommand(options: SpawnOptions & {
105
+ stdio?: 'joined';
106
+ }): Promise<SpawnedCommand>;
107
+ export declare function spawnCommand(options: SpawnOptions & {
108
+ stdio: 'pipes';
109
+ }): Promise<SpawnedPipes>;
110
+ export declare function spawnCommand(options: SpawnOptions): Promise<SpawnedCommand | SpawnedPipes>;
111
+ /**
112
+ * Runs a command to completion and collects its output: for tools that need a command's answer,
113
+ * not its stream. Output is held in memory, so use it only for commands whose output is bounded.
114
+ */
115
+ export declare function runToCompletion(options: SpawnOptions & {
116
+ stdio?: 'joined';
117
+ }): Promise<SpawnResult & {
118
+ output: string;
119
+ }>;
120
+ /** How {@link runLines} hands a command's stdout over. */
121
+ export type LineReader = {
122
+ /**
123
+ * Called with each line of stdout, without its delimiter, as it arrives. Returning `false` stops the
124
+ * group: no later line is delivered, and the result reports `stoppedBy: 'stop'`.
125
+ */
126
+ onLine: (line: string, clipped: boolean) => boolean | void;
127
+ /**
128
+ * What ends a line: a newline, or NUL for output whose records may hold a newline, such as paths
129
+ * printed with `--null`.
130
+ */
131
+ delimiter: '\n' | '\0';
132
+ /** A longer line reaches `onLine` cut to this many characters, with `clipped` set; the rest is never held. */
133
+ maxLineChars: number;
134
+ };
135
+ /**
136
+ * Runs a command and hands its stdout to `onLine` line by line, holding at most one line: for
137
+ * output whose size is not bounded, such as a search's, where the caller stops the command once it
138
+ * has what it needs. The command gets no input. stderr is read separately and kept as a bounded
139
+ * tail, for diagnostics.
140
+ */
141
+ export declare function runLines(options: Omit<SpawnOptions, 'stdio' | 'beforeRelease'> & LineReader): Promise<SpawnResult & {
142
+ stderr: string;
143
+ }>;
144
+ /** Whether any process remains in the group; a zombie still counts. */
145
+ export declare function groupAlive(pgid: number): boolean;
146
+ /** Live groups, as the exit listener and the reaper know them. One per process. */
147
+ declare class Containment {
148
+ private readonly groups;
149
+ private readonly stuck;
150
+ private reaper;
151
+ private exitListener;
152
+ private watcher;
153
+ /**
154
+ * Adds a group to the exit listener's set and the reaper's. A reaper that died is replaced and
155
+ * given every live group; this fails only when its replacement fails too.
156
+ */
157
+ register(pgid: number): Promise<void>;
158
+ /** Forgets a group proven empty. A reaper that misses the line is replaced from `groups` anyway. */
159
+ unregister(pgid: number): void;
160
+ /** Keeps a group whose stop failed tracked, polling it until it empties. */
161
+ watchUntilEmpty(pgid: number): void;
162
+ /** The groups currently tracked, for tests and diagnostics. */
163
+ tracked(): number[];
164
+ /** The current reaper's pid, for tests. */
165
+ reaperPid(): number | undefined;
166
+ /** Kills the current reaper, as the OOM killer might, for tests. */
167
+ killReaper(): void;
168
+ private ensureReaper;
169
+ /** A reaper that died while lagent lives is replaced at once, seeded with every live group. */
170
+ private onReaperExit;
171
+ /** Node runs nothing asynchronous in `exit`, so this is a synchronous SIGKILL of every live group. */
172
+ private installExitListener;
173
+ }
174
+ /** The process's containment state; exported for tests. */
175
+ export declare const containment: Containment;
176
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { Clock } from '../kit/timers.js';
2
+ /**
3
+ * A {@link Clock} a test moves by hand. Wall time and elapsed time move apart: `advance` moves
4
+ * both, as time passing does; `jump` sets the wall clock forward or back without any timer
5
+ * noticing, as a clock change does; `elapse` lets timers run without the wall clock moving.
6
+ */
7
+ export type ManualClock = Clock & {
8
+ /** Lets `ms` pass: the wall clock moves, and every timer due by then fires, in order. */
9
+ advance(ms: number): void;
10
+ /** Moves the wall clock by `ms`, either way; no timer fires. */
11
+ jump(ms: number): void;
12
+ /** Lets `ms` of timer time pass with the wall clock standing still. */
13
+ elapse(ms: number): void;
14
+ /** The delay of each pending timer from now, soonest first. */
15
+ pending(): number[];
16
+ };
17
+ export declare function manualClock(start?: number): ManualClock;
@@ -0,0 +1,74 @@
1
+ import type { z } from 'zod';
2
+ import type { ConversationMessage, LLMClient, LLMStreamChunk, MalformedToolCall, ModelRequest, ModelResponse, ResponseFinish, StreamingLLMResponse } from '../llm/index.js';
3
+ import { LLMStreamChunkSchema } from '../llm/index.js';
4
+ /** A chunk as a test writes one: anything the schema defaults may be left out. */
5
+ export type FakeChunk = z.input<typeof LLMStreamChunkSchema>;
6
+ /** Every request the fake was handed, exactly as the loop built it. */
7
+ export type FakeChatRequest = ModelRequest;
8
+ /**
9
+ * A canned reply: a message, a message streamed as `chunks`, or `chunks` followed by a `failure`
10
+ * that `completed()` throws, as an adapter throws a `ResponseSettlementError` for a response it
11
+ * could not settle.
12
+ */
13
+ export type FakeChatReply = ConversationMessage | {
14
+ chunks: FakeChunk[];
15
+ message: ConversationMessage;
16
+ } | {
17
+ chunks: FakeChunk[];
18
+ failure: Error;
19
+ };
20
+ export type FakeChatHandler = (messages: ModelRequest['messages'], request: ModelRequest) => FakeChatReply | Promise<FakeChatReply>;
21
+ /** How a {@link FakeStream} settles: with the message, with a whole response, or by throwing the failure. */
22
+ type FakeSettlement = ConversationMessage | ModelResponse | Error;
23
+ export interface FakeStreamOptions {
24
+ readonly finish?: ResponseFinish;
25
+ readonly readFailure?: boolean;
26
+ }
27
+ /**
28
+ * A response stream that keeps the reader rules a real adapter does (see `StreamingLLMResponse`): it
29
+ * is read once, `completed()` drains it when unread, reports the outcome of a read that ended (a
30
+ * failed read throws its failure again) and throws while a read is under way. `chunks` is any
31
+ * source, so a test can feed one live; `settled` is what `completed()` gives for a read that ran
32
+ * through or was left early, read when asked so it can depend on the source: a message (or a whole
33
+ * response, finish included), or an error thrown as an adapter throws a `ResponseSettlementError`. `options.readFailure` makes the error be
34
+ * thrown by the read, after the chunks, as a transport failure is; `options.finish` is the reply's
35
+ * finish, `completed` otherwise. Unlike a real adapter, a reader that left early still gets the whole
36
+ * message.
37
+ */
38
+ export declare class FakeStream implements StreamingLLMResponse {
39
+ private claimed;
40
+ private readonly ledger;
41
+ private failure;
42
+ private readonly chunks;
43
+ private readonly settled;
44
+ private readonly options;
45
+ constructor(chunks: Iterable<FakeChunk> | AsyncIterable<FakeChunk>, settled: FakeSettlement | (() => FakeSettlement), options?: FakeStreamOptions);
46
+ [Symbol.asyncIterator](): AsyncIterator<LLMStreamChunk>;
47
+ private read;
48
+ private settlement;
49
+ completed(): Promise<ModelResponse>;
50
+ }
51
+ /** Scripted stand-in for an LLM provider: the handler inspects the conversation and picks a canned reply. */
52
+ export declare class FakeLLMClient implements LLMClient {
53
+ readonly providerName = "fake";
54
+ readonly requests: FakeChatRequest[];
55
+ private readonly handler;
56
+ constructor(handler: FakeChatHandler);
57
+ /** Replies in order; an extra call throws so unexpected loops fail loudly. */
58
+ static scripted(...responses: ConversationMessage[]): FakeLLMClient;
59
+ chat(request: ModelRequest): Promise<StreamingLLMResponse>;
60
+ }
61
+ export declare const assistant: (content: string, extra?: Partial<ConversationMessage>) => ConversationMessage;
62
+ /** A reply asking for `toolCalls`; omitted arguments are `{}`, and a malformed call stays malformed. */
63
+ export declare const withToolCalls: (toolCalls: ({
64
+ call_id: string;
65
+ name: string;
66
+ arguments?: Record<string, unknown>;
67
+ } | MalformedToolCall)[], content?: string | null) => ConversationMessage;
68
+ /**
69
+ * A response that streams one chunk, calls `streamed`, and then waits until `request` is aborted
70
+ * without ending: a connection that goes quiet mid-response. A test that restarts the host while it
71
+ * waits finds the model request unanswered.
72
+ */
73
+ export declare function stallingStream(request: ModelRequest, streamed: () => void): FakeStream;
74
+ export {};
@@ -0,0 +1,32 @@
1
+ import type { ConversationMessage, LLMClient, ModelRequest, StreamingLLMResponse } from '../llm/index.js';
2
+ import { type FakeChunk } from './fakeLlm.js';
3
+ /** One model call whose stream the test feeds: `push` chunks, then `finish` or `fail`. */
4
+ export declare class FedCall {
5
+ readonly request: ModelRequest;
6
+ private readonly queue;
7
+ private ended;
8
+ /** The text fed so far, which a cut-off reply carries. */
9
+ private text;
10
+ private wake;
11
+ constructor(request: ModelRequest);
12
+ push(chunk: FakeChunk): void;
13
+ finish(content: string, extra?: Partial<ConversationMessage>): void;
14
+ fail(error: unknown): void;
15
+ /**
16
+ * Reads what the test feeds, as a response stream keeps the reader rules: a failure ends the read
17
+ * and is what `completed()` throws, a cancel ends it quietly with what arrived as a cut-off reply,
18
+ * and a reader that stops before `finish` gets no answer.
19
+ */
20
+ readonly stream: StreamingLLMResponse;
21
+ private feed;
22
+ }
23
+ /** A provider whose every call is a {@link FedCall}, handed to the test in order. */
24
+ export declare class FedLLM implements LLMClient {
25
+ readonly providerName = "fake";
26
+ readonly calls: FedCall[];
27
+ private readonly waiting;
28
+ private taken;
29
+ chat(request: ModelRequest): Promise<StreamingLLMResponse>;
30
+ /** The next call the loop makes, once it has made it. */
31
+ next(): Promise<FedCall>;
32
+ }
@@ -0,0 +1,344 @@
1
+ import { Context, type Plugin } from '@leuduan/cordis';
2
+ import { Journal, type CoreFold, type JournalStore, type PluginJournal, type Summarizer } from '../journal/index.js';
3
+ import type { ConfirmationState, InvocationContext, RouteStatement, ResolvedWait, RowRef, SendOutcome, Tool, TurnLimits } from '../loop/index.js';
4
+ import type { ConversationMessage, FailureClassification, LLMClient, ModelCapacity, ModelRequest, ModelRoute, PromptBlock, ThinkingEffort } from '../llm/index.js';
5
+ import type { DeepReadonly, ReadonlyJsonValue } from '../util/json.js';
6
+ import type { AgentDefinitionInput, ExtensionRecord, JournalEvent, Seq, ThreadOrigin, ThreadSummary, TurnEndReason } from '../models/index.js';
7
+ import { type TrustedPrograms } from '../sys/environment.js';
8
+ import { MemoryJournalStore } from '../plugins/store/memory.js';
9
+ import { type PromptContribution } from '../services/prompts.js';
10
+ import { type RetryPolicy } from '../services/llm.js';
11
+ import type { Thread } from '../services/thread.js';
12
+ import type { BirthInput, CreateThreadOptions, Opening } from '../services/birth.js';
13
+ import type { InitialInstallationInput } from '../services/presets.js';
14
+ import type { OwnerHandle, StartOptions } from '../services/owner.js';
15
+ import type { DelegationsConfig } from '../plugins/delegations/service.js';
16
+ import type { ThreadView } from '../services/threadView.js';
17
+ import { ownedTmpDir, tmpWorkspace } from './tmpDirs.js';
18
+ /**
19
+ * A provider plugin that hands out one fixed client, for tests and embedding. Like the real ones,
20
+ * it takes an optional default thinking effort; it declares what `capacity` answers, by default no
21
+ * window, no provider-executed tools and no images in tool results, and classifies every failure
22
+ * the engine's typed evidence leaves to it as `classify` says, by default `other`.
23
+ */
24
+ export declare const fakeProvider: (client: LLMClient, name?: string) => Plugin.Object<{
25
+ thinkingEffort?: ThinkingEffort;
26
+ capacity?: (model: string) => ModelCapacity;
27
+ classify?: (error: unknown) => FailureClassification;
28
+ retry?: RetryPolicy;
29
+ } | undefined>;
30
+ /** The route of the fake provider a test root registers: what a test thread is born on. */
31
+ export declare const TEST_ROUTE: ModelRoute;
32
+ /** The retry policy a test provider gets unless it asks for one: a call is never sent twice. */
33
+ export declare const NO_RETRY: RetryPolicy;
34
+ export type TestRootOptions = {
35
+ llm: LLMClient;
36
+ /** Start from an existing journal, as reopening does. */
37
+ store?: JournalStore;
38
+ cwd?: string;
39
+ /** Where thread directories live; a temporary directory, removed with the root, when omitted; `null` for none. */
40
+ dataDir?: string | null;
41
+ limits?: Partial<TurnLimits>;
42
+ /** Agents to register, each granted its `tools`; one `main_agent` that can read files when omitted. */
43
+ agents?: readonly TestAgent[];
44
+ /** Tools to register; just the test reader when omitted. */
45
+ tools?: readonly TestToolEntry[];
46
+ /** What the fake provider declares it can carry; no window when omitted. */
47
+ capacity?: (model: string) => ModelCapacity;
48
+ /** How the fake provider classifies a failure the engine's typed evidence leaves to it; `other` when omitted. */
49
+ classify?: (error: unknown) => FailureClassification;
50
+ /** How the fake provider's calls are retried; never when omitted. */
51
+ retry?: RetryPolicy;
52
+ /**
53
+ * `defer` leaves `ctx.threads.start` to the test, which calls it once after composing what it
54
+ * mounts beside: a reopened journal's recovered runs would otherwise start before it. Started at
55
+ * once, with every pending run resumed, when omitted.
56
+ */
57
+ start?: 'defer';
58
+ /**
59
+ * What the threads service does with a thread nothing holds any more. `keep` when omitted, so a
60
+ * test reads a thread's rows after its run; a host's test passes what the host configures.
61
+ */
62
+ retention?: 'retire' | 'keep';
63
+ /** How many reactive turns in a row a thread may run; {@link TEST_CHAIN_LIMIT} when omitted. */
64
+ chainLimit?: number;
65
+ /**
66
+ * `manual` leaves disposing the root to the caller, for one that a whole file's tests share, such
67
+ * as a root holding a process-wide service. Disposed when the test that made it finishes when omitted.
68
+ */
69
+ disposal?: 'manual';
70
+ };
71
+ /** The chain limit a test root gets unless it asks for one: room for any test's notices. */
72
+ export declare const TEST_CHAIN_LIMIT = 10;
73
+ /** How a test root starts: every pending run resumed, with room for every thread a test runs. */
74
+ export declare const TEST_START: StartOptions;
75
+ /** `Omit` over each member of a union, which a plain `Omit` would collapse into one object type. */
76
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
77
+ type BirthFields = Partial<BirthInput<InitialInstallationInput>>;
78
+ /**
79
+ * A fresh thread's `birth` on {@link TEST_ROUTE} from `defaults`, with `fields` beside its route, in
80
+ * the shape `create` takes. `from: 'caller'` says the birth reads the origin's live caller instead,
81
+ * which the options then name an `origin` for.
82
+ */
83
+ export declare function testBirth(fields?: BirthFields): {
84
+ from: 'defaults';
85
+ } & BirthInput<InitialInstallationInput>;
86
+ export declare function testBirth(fields: BirthFields & {
87
+ from: 'caller';
88
+ }): {
89
+ from: 'caller';
90
+ } & BirthInput<InitialInstallationInput>;
91
+ /**
92
+ * What {@link TestRoot.newThread} takes: `create`'s options, with a fresh id and `main_agent`.
93
+ * `reuse` and `birth`, given together as `create` takes them, default as a whole to `identity`
94
+ * reuse of {@link testBirth}`()`.
95
+ */
96
+ export type TestThreadOptions = Omit<Opening, 'id'> & {
97
+ /** A fresh id is generated when omitted. */
98
+ id?: string;
99
+ /** `main_agent` when omitted. */
100
+ agent?: string;
101
+ } & ({
102
+ reuse?: undefined;
103
+ birth?: undefined;
104
+ origin?: undefined;
105
+ } | DistributiveOmit<CreateThreadOptions, keyof Opening>);
106
+ export type TestRoot = Context & {
107
+ /** Every journal event appended since the root was made. */
108
+ appended: JournalEvent[];
109
+ /** `threads.create` with a fresh id and test defaults: `main_agent`, born on {@link TEST_ROUTE} (see {@link TestThreadOptions}). */
110
+ newThread(options?: TestThreadOptions): Promise<Thread>;
111
+ };
112
+ /**
113
+ * A root with the core, a fake provider, and the given agents and tools mounted. It lives as long
114
+ * as the test that made it, in its body or a `beforeEach`: disposed once that test finishes, unless
115
+ * the test disposed it before.
116
+ */
117
+ export declare function createTestRoot(options: TestRootOptions): Promise<TestRoot>;
118
+ /**
119
+ * The spawn environment tests run programs with: this process's, filtered as the hosts filter
120
+ * theirs. Tests are the host here, so they may read `process.env`.
121
+ */
122
+ export declare const testEnvironment: () => Record<string, string>;
123
+ /** The trusted programs tests run, found on this process's `PATH`. */
124
+ export declare const testPrograms: () => TrustedPrograms;
125
+ /** Minimal file reader so loop tests can exercise the tool path without the builtin tools. */
126
+ export declare const testReadFileTool: Tool;
127
+ export type TestToolEntry = {
128
+ name: string;
129
+ tool: Tool;
130
+ };
131
+ /** The default tool set for loop tests: just the reader. */
132
+ export declare const testTools: () => TestToolEntry[];
133
+ /** An agent definition and the tools a fixture grants it, written together for brevity. */
134
+ export type TestAgent = AgentDefinitionInput & {
135
+ readonly tools?: readonly string[];
136
+ };
137
+ /** Registers each agent and grants it its `tools`, in order, for the life of the plugin. */
138
+ export declare const testAgentsPlugin: (agents: readonly TestAgent[]) => Plugin.Object<undefined>;
139
+ /** One agent that can read files, the shape most loop tests need. */
140
+ export declare const testAgent: (overrides?: Partial<TestAgent>) => TestAgent;
141
+ export declare const testAgents: (...agents: Partial<TestAgent>[]) => TestAgent[];
142
+ /** What a test's {@link drive} asks: a route statement for the cause it writes, and a signal that cancels it. */
143
+ export type DriveOptions = {
144
+ readonly route?: RouteStatement;
145
+ readonly signal?: AbortSignal;
146
+ /** Resolve at a `wait` hold too, rather than follow the run until its waits resolve. */
147
+ readonly untilWait?: boolean;
148
+ };
149
+ /**
150
+ * How a driven run ended, as a test reads it: `turns` when it opened a turn, `none` when it opened
151
+ * none, `cancelled` when it was cancelled or shutdown held it; how the last turn it closed ended;
152
+ * and the confirmations it left waiting, `null` when cancelled before the run was read. A run held
153
+ * on an approval ends `turns`, its last turn `blocked`.
154
+ */
155
+ export type DriveResult = {
156
+ readonly thread_id: string;
157
+ readonly ended: 'none' | 'turns' | 'cancelled';
158
+ readonly reason: DeepReadonly<TurnEndReason> | null;
159
+ readonly pending_confirmations: DeepReadonly<ConfirmationState[]> | null;
160
+ };
161
+ /**
162
+ * The testing door into execution: asks `thread` to run as a person does (`resume`, a cause that
163
+ * releases its holds), drives it once, and resolves when its run is held or settled; a run held on a
164
+ * call's `wait` goes on once the wait resolves, so it is followed through it unless `untilWait`
165
+ * says to stop there. Applications
166
+ * never drive a thread; they send to it. `route` is the cause's route statement; a `signal` that aborts cancels the thread as a person's stop does (`Thread.cancel`, which
167
+ * also clears its queued input), one already aborted before anything runs.
168
+ */
169
+ export declare function drive(thread: Thread, options?: DriveOptions): Promise<DriveResult>;
170
+ /** The delegations plugin's configuration for a test: 8 open tasks per origin tree and the system clock unless given. */
171
+ export declare const testDelegations: (overrides?: Partial<DelegationsConfig>) => DelegationsConfig;
172
+ /** The owner `testWaitOwner` registers by default. */
173
+ export declare const TEST_WAIT_OWNER = "test:waits";
174
+ /**
175
+ * Registers a constraint owner a test's tools may wait on (`waitFor({ owner: TEST_WAIT_OWNER, … })`)
176
+ * and returns its handle, whose `resolveWait` the test resolves them with. It constrains no thread.
177
+ */
178
+ export declare function testWaitOwner(root: Context, owner?: string): OwnerHandle;
179
+ /**
180
+ * Holds `threadIds` from running, as a constraint holding them does, until `release()`: a test that
181
+ * answers or sends and must look before the owner runs the thread takes this first.
182
+ */
183
+ export declare function holdRuns(root: Context, ...threadIds: string[]): {
184
+ release(): void;
185
+ };
186
+ /**
187
+ * Resolves once nothing on the root runs or waits to run: every pending run held or settled, as a
188
+ * started root leaves things once the work its causes asked for is done.
189
+ */
190
+ export declare function untilQuiet(root: Context): Promise<void>;
191
+ /** Starts a thread on `agent` (the first registered agent by default) and drives one turn on `text`. */
192
+ export declare function runTestTurn(root: Context, text?: string, options?: {
193
+ agent?: string;
194
+ threadId?: string;
195
+ }): Promise<{
196
+ thread: Thread;
197
+ result: DriveResult;
198
+ }>;
199
+ /**
200
+ * What a turn opened on the fake provider records, for a test that writes `turn/start` itself: the
201
+ * turn serves run `runId`, whose execution was admitted at `attemptCutoff`.
202
+ */
203
+ export declare const turnStartData: (turn: number, runId?: string, attemptCutoff?: number) => {
204
+ turn: number;
205
+ route: {
206
+ provider: string;
207
+ model: string;
208
+ thinking_effort: "low" | "high" | "none" | "medium" | "max" | null;
209
+ };
210
+ run_id: string;
211
+ attempt_cutoff: number;
212
+ };
213
+ /** An invocation context for calling a tool directly, without a loop around it. */
214
+ export declare function testInvocationContext(root: Context, options?: {
215
+ threadId?: string;
216
+ agentName?: string;
217
+ callId?: string;
218
+ callSeq?: number;
219
+ cwd?: string;
220
+ /** The thread's read roots; none when omitted. */
221
+ readRoots?: string[];
222
+ /** The turn's route; the fake provider's when omitted. */
223
+ route?: ModelRoute;
224
+ /** The resolved wait of a dispatch again; none when omitted. */
225
+ wait?: ResolvedWait | null;
226
+ }): InvocationContext;
227
+ export { ownedTmpDir, tmpWorkspace };
228
+ /**
229
+ * Replaces `rows`, a run of a thread's surface, with one stand-in, between turns, as a manual
230
+ * compaction records it: through `replaceSurface`, which refuses it with a
231
+ * `ReplacementRefusedError` where the surface would not take it.
232
+ */
233
+ export declare function replaceSpan(journal: PluginJournal, threadId: string, rows: readonly RowRef[], text: string, options?: {
234
+ role?: 'user' | 'assistant';
235
+ source?: ExtensionRecord;
236
+ }): Promise<JournalEvent<'surface/replaced'>>;
237
+ /** The text of a message that may not exist — the shape an assertion usually wants. */
238
+ export declare const textOf: (message: DeepReadonly<ConversationMessage> | undefined) => string;
239
+ /** A model request with everything defaulted, for a test that cares about one field. */
240
+ export declare const modelRequest: (overrides?: Partial<ModelRequest>) => ModelRequest;
241
+ /** The system prompt a request carried, as one string. */
242
+ export declare const promptTextOf: (request: {
243
+ systemPrompt: readonly PromptBlock[];
244
+ }) => string;
245
+ /** Content of the last `role: tool` message the model received in a request. */
246
+ export declare const lastToolMessage: (messages: readonly DeepReadonly<ConversationMessage>[]) => string | null;
247
+ /**
248
+ * A thread's view, read now, for a test that has just run or written to it in this process, which
249
+ * loaded it: the synchronous counterpart of `thread.read` that tests use for brevity. Throws
250
+ * `ThreadNotLoadedError` for a thread nothing loaded.
251
+ */
252
+ export declare const viewOf: (thread: Thread) => ThreadView;
253
+ /** The row a `send` queued its input at, or found it queued at; a refused send fails the test. */
254
+ export declare function sentAt(outcome: SendOutcome): Seq;
255
+ /** A thread's tool results as its conversation holds them, in order. */
256
+ export declare const toolResultsOf: (thread: Thread) => string[];
257
+ /** Every tool result the model saw, across every thread and request. */
258
+ export declare const toolMessages: (requests: readonly ModelRequest[]) => string[];
259
+ /**
260
+ * A summary from a thread's rows alone, consulting no fold: for tests of `journal/` itself, whose
261
+ * folds are their own and must not meet the loop's. The identity comes from the spawn row when
262
+ * there is one; nothing is ever pending.
263
+ */
264
+ export declare const bareSummary: Summarizer;
265
+ export interface TestJournalOptions {
266
+ /** How summaries are computed: the loop's `summarizeThread` unless given, `bareSummary` for `journal/` tests. */
267
+ summarize?: Summarizer;
268
+ onListenerError?: (error: unknown) => void;
269
+ /**
270
+ * Core folds the journal registers at construction. When omitted: `threadState`, which the loop's
271
+ * summary reads, unless `summarize` is given, as a test of `journal/` alone does, and then none.
272
+ */
273
+ coreFolds?: readonly CoreFold<unknown>[];
274
+ }
275
+ /**
276
+ * A journal over `store` for tests that drive the journal without a root. Not opened.
277
+ */
278
+ export declare function testJournal(store?: JournalStore, options?: TestJournalOptions): Journal;
279
+ /**
280
+ * Every row the journal holds, across its threads, in seq order: what a test asserts over a whole
281
+ * journal. Loads every thread first.
282
+ */
283
+ export declare function journalRows(journal: PluginJournal): Promise<JournalEvent[]>;
284
+ /** Every row a store holds, across its threads, in seq order; opens and closes a closed store around the read. */
285
+ export declare function storeRows(store: JournalStore): Promise<JournalEvent[]>;
286
+ /**
287
+ * Writes `rows` into an open `store`, one attempt per row, under a writer lease it takes and gives back:
288
+ * rows written past the journal, as a test that bypasses its gate needs. No journal may hold the store.
289
+ */
290
+ export declare function writeRows(store: JournalStore, rows: readonly JournalEvent[], summaries: readonly ThreadSummary[]): Promise<void>;
291
+ /** The value of the contribution under `key` in `summary`'s catalog entry, past its cache envelope; `undefined` without an entry. */
292
+ export declare const contributedValue: (summary: ThreadSummary, key: string) => unknown;
293
+ /** Replaces a store's catalog past the journal, under a writer lease it takes and gives back: damage a test inflicts. */
294
+ export declare function writeCatalog(store: JournalStore, summaries: readonly ThreadSummary[]): Promise<void>;
295
+ /**
296
+ * A memory store holding exactly what `source` holds — its rows and its catalog, as they are — the
297
+ * way a process that crashed leaves them for the next one to open. Returned open, its lease free.
298
+ */
299
+ export declare function copyStore(source: JournalStore): Promise<MemoryJournalStore>;
300
+ /**
301
+ * A memory store holding exactly `events` — seqs and timestamps as they are, gaps included — with
302
+ * a catalog rebuilt from them: a journal cut off, or thinned, where a test says. Returned open, its
303
+ * lease free.
304
+ */
305
+ export declare function replayStore(events: readonly JournalEvent[]): Promise<MemoryJournalStore>;
306
+ /**
307
+ * Makes core's fold throw on every row `fails` accepts, as a thread core cannot read behaves: a
308
+ * thread whose rows it cannot fold does not load. Scoped to the test that calls it, undone when it
309
+ * finishes or when the returned function is called, whichever comes first.
310
+ */
311
+ export declare function breakCoreFold(fails: (event: JournalEvent) => boolean): () => void;
312
+ /**
313
+ * `rows` with the origin rule broken above `threadId`, damage a test inflicts: its caller's rows
314
+ * dropped (`missing`), or its caller's spawn row naming `threadId` as its own caller (`cycle`).
315
+ */
316
+ export declare function breakCallerChain(rows: readonly JournalEvent[], threadId: string, damage: 'missing' | 'cycle'): JournalEvent[];
317
+ /**
318
+ * The origin of a thread a test creates under `callerId`, citing a real `tool/call` row recorded on
319
+ * the caller now: what `origins.validate` requires of a call cause. The call is not on any turn, so
320
+ * only a caller that no test drives afterwards should be given one.
321
+ */
322
+ export declare function callOrigin(root: Context, callerId: string): Promise<{
323
+ caller_thread_id: string;
324
+ source_seq: Seq;
325
+ cause: {
326
+ kind: 'call';
327
+ };
328
+ }>;
329
+ /**
330
+ * The invocation context of a call `threadId` made, named by `label`: a real `tool/call` row recorded
331
+ * on the thread the first time the label is asked for, so the origin a delegation records cites a row
332
+ * of its caller (`origins.validate`), and the same label is the same call afterwards, as a
333
+ * re-dispatch of it is. `options` are {@link testInvocationContext}'s, without the call's identity.
334
+ */
335
+ export declare function callAt(root: Context, threadId: string, label: string | number, options?: Omit<NonNullable<Parameters<typeof testInvocationContext>[1]>, 'threadId' | 'callSeq' | 'callId'>): Promise<InvocationContext>;
336
+ /**
337
+ * The origin of a thread an owner's assignment creates under `callerId`: the row that granted it is
338
+ * the caller's spawn row, a real row of the caller, and `key` is the owner's.
339
+ */
340
+ export declare function assignmentOrigin(root: Context, callerId: string, owner: string, key: ReadonlyJsonValue): ThreadOrigin;
341
+ /** Mounts one agent as a plugin, for tests that need a registration with a fiber of its own. */
342
+ export declare const testAgentPlugin: (agent: AgentDefinitionInput) => Plugin.Object<undefined>;
343
+ /** Mounts one prompt contribution as a plugin, for tests that need it with a fiber of its own. */
344
+ export declare const testPromptPlugin: (contribution: PromptContribution) => Plugin.Object<undefined>;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Test doubles and helpers, available at `@leuduan/lagent/testing` so they stay out of the engine's
3
+ * public surface. They are not part of the semver contract the root entry point offers.
4
+ *
5
+ * The door lists exactly the names hosts outside this package use. lagent's own tests import the
6
+ * helper modules by relative path, so a helper only they need stays in its module and off this list.
7
+ */
8
+ export { FakeLLMClient, assistant, withToolCalls } from './fakeLlm.js';
9
+ export type { FakeChatHandler } from './fakeLlm.js';
10
+ export { FedLLM } from './fedLlm.js';
11
+ export { TEST_START, callOrigin, copyStore, createTestRoot, drive, fakeProvider, journalRows, lastToolMessage, replaceSpan, replayStore, storeRows, testAgents, testAgentsPlugin, testBirth, testDelegations, testEnvironment, testPrograms, testPromptPlugin, textOf, tmpWorkspace, viewOf, } from './helpers.js';
12
+ export type { TestRoot, TestRootOptions } from './helpers.js';
13
+ export { manualClock } from './clock.js';