@botbotgo/runtime 1.0.0

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 (313) hide show
  1. package/.github/workflows/ci.yml +46 -0
  2. package/.github/workflows/release.yml +79 -0
  3. package/README.md +71 -0
  4. package/config/examples/memory.yaml +39 -0
  5. package/config/examples/model.yaml +21 -0
  6. package/config/examples/runtime.yaml +44 -0
  7. package/config/examples/skills.yaml +8 -0
  8. package/config/examples/tool.yaml +16 -0
  9. package/dist/config/index.d.ts +7 -0
  10. package/dist/config/index.d.ts.map +1 -0
  11. package/dist/config/index.js +4 -0
  12. package/dist/config/index.js.map +1 -0
  13. package/dist/config/loader.d.ts +41 -0
  14. package/dist/config/loader.d.ts.map +1 -0
  15. package/dist/config/loader.js +140 -0
  16. package/dist/config/loader.js.map +1 -0
  17. package/dist/config/resolveRuntimeConfig.d.ts +83 -0
  18. package/dist/config/resolveRuntimeConfig.d.ts.map +1 -0
  19. package/dist/config/resolveRuntimeConfig.js +85 -0
  20. package/dist/config/resolveRuntimeConfig.js.map +1 -0
  21. package/dist/config/resources.d.ts +112 -0
  22. package/dist/config/resources.d.ts.map +1 -0
  23. package/dist/config/resources.js +20 -0
  24. package/dist/config/resources.js.map +1 -0
  25. package/dist/config/runtimeConfigLoader.d.ts +28 -0
  26. package/dist/config/runtimeConfigLoader.d.ts.map +1 -0
  27. package/dist/config/runtimeConfigLoader.js +38 -0
  28. package/dist/config/runtimeConfigLoader.js.map +1 -0
  29. package/dist/index.d.ts +9 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +5 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/runtime/agentMiddleware.d.ts +3 -0
  34. package/dist/runtime/agentMiddleware.d.ts.map +1 -0
  35. package/dist/runtime/agentMiddleware.js +2 -0
  36. package/dist/runtime/agentMiddleware.js.map +1 -0
  37. package/dist/runtime/bootstrap/runtimeFactory.d.ts +7 -0
  38. package/dist/runtime/bootstrap/runtimeFactory.d.ts.map +1 -0
  39. package/dist/runtime/bootstrap/runtimeFactory.js +139 -0
  40. package/dist/runtime/bootstrap/runtimeFactory.js.map +1 -0
  41. package/dist/runtime/bootstrap/runtimeModuleInitializer.d.ts +5 -0
  42. package/dist/runtime/bootstrap/runtimeModuleInitializer.d.ts.map +1 -0
  43. package/dist/runtime/bootstrap/runtimeModuleInitializer.js +32 -0
  44. package/dist/runtime/bootstrap/runtimeModuleInitializer.js.map +1 -0
  45. package/dist/runtime/context/agentContext.d.ts +5 -0
  46. package/dist/runtime/context/agentContext.d.ts.map +1 -0
  47. package/dist/runtime/context/agentContext.js +19 -0
  48. package/dist/runtime/context/agentContext.js.map +1 -0
  49. package/dist/runtime/context/globalContext.d.ts +19 -0
  50. package/dist/runtime/context/globalContext.d.ts.map +1 -0
  51. package/dist/runtime/context/globalContext.js +47 -0
  52. package/dist/runtime/context/globalContext.js.map +1 -0
  53. package/dist/runtime/events/eventPublisher.d.ts +15 -0
  54. package/dist/runtime/events/eventPublisher.d.ts.map +1 -0
  55. package/dist/runtime/events/eventPublisher.js +23 -0
  56. package/dist/runtime/events/eventPublisher.js.map +1 -0
  57. package/dist/runtime/execution/agentRunExecutor.d.ts +38 -0
  58. package/dist/runtime/execution/agentRunExecutor.d.ts.map +1 -0
  59. package/dist/runtime/execution/agentRunExecutor.js +378 -0
  60. package/dist/runtime/execution/agentRunExecutor.js.map +1 -0
  61. package/dist/runtime/execution/agentRunExecutor.types.d.ts +37 -0
  62. package/dist/runtime/execution/agentRunExecutor.types.d.ts.map +1 -0
  63. package/dist/runtime/execution/agentRunExecutor.types.js +2 -0
  64. package/dist/runtime/execution/agentRunExecutor.types.js.map +1 -0
  65. package/dist/runtime/execution/agentRunProgress.d.ts +8 -0
  66. package/dist/runtime/execution/agentRunProgress.d.ts.map +1 -0
  67. package/dist/runtime/execution/agentRunProgress.js +46 -0
  68. package/dist/runtime/execution/agentRunProgress.js.map +1 -0
  69. package/dist/runtime/execution/agentRunState.d.ts +44 -0
  70. package/dist/runtime/execution/agentRunState.d.ts.map +1 -0
  71. package/dist/runtime/execution/agentRunState.js +17 -0
  72. package/dist/runtime/execution/agentRunState.js.map +1 -0
  73. package/dist/runtime/execution/policies/runCompletionPolicy.d.ts +5 -0
  74. package/dist/runtime/execution/policies/runCompletionPolicy.d.ts.map +1 -0
  75. package/dist/runtime/execution/policies/runCompletionPolicy.js +30 -0
  76. package/dist/runtime/execution/policies/runCompletionPolicy.js.map +1 -0
  77. package/dist/runtime/execution/policies/runErrorPolicy.d.ts +7 -0
  78. package/dist/runtime/execution/policies/runErrorPolicy.d.ts.map +1 -0
  79. package/dist/runtime/execution/policies/runErrorPolicy.js +40 -0
  80. package/dist/runtime/execution/policies/runErrorPolicy.js.map +1 -0
  81. package/dist/runtime/execution/policies/runPromptBuilder.d.ts +10 -0
  82. package/dist/runtime/execution/policies/runPromptBuilder.d.ts.map +1 -0
  83. package/dist/runtime/execution/policies/runPromptBuilder.js +34 -0
  84. package/dist/runtime/execution/policies/runPromptBuilder.js.map +1 -0
  85. package/dist/runtime/execution/policies/runSummaryFormatter.d.ts +6 -0
  86. package/dist/runtime/execution/policies/runSummaryFormatter.d.ts.map +1 -0
  87. package/dist/runtime/execution/policies/runSummaryFormatter.js +47 -0
  88. package/dist/runtime/execution/policies/runSummaryFormatter.js.map +1 -0
  89. package/dist/runtime/execution/policies/runtimeDelay.d.ts +4 -0
  90. package/dist/runtime/execution/policies/runtimeDelay.d.ts.map +1 -0
  91. package/dist/runtime/execution/policies/runtimeDelay.js +6 -0
  92. package/dist/runtime/execution/policies/runtimeDelay.js.map +1 -0
  93. package/dist/runtime/execution/policies/toolConfigReader.d.ts +8 -0
  94. package/dist/runtime/execution/policies/toolConfigReader.d.ts.map +1 -0
  95. package/dist/runtime/execution/policies/toolConfigReader.js +91 -0
  96. package/dist/runtime/execution/policies/toolConfigReader.js.map +1 -0
  97. package/dist/runtime/human-loop/consoleHumanLoop.d.ts +21 -0
  98. package/dist/runtime/human-loop/consoleHumanLoop.d.ts.map +1 -0
  99. package/dist/runtime/human-loop/consoleHumanLoop.js +102 -0
  100. package/dist/runtime/human-loop/consoleHumanLoop.js.map +1 -0
  101. package/dist/runtime/human-loop/humanLoop.d.ts +44 -0
  102. package/dist/runtime/human-loop/humanLoop.d.ts.map +1 -0
  103. package/dist/runtime/human-loop/humanLoop.js +36 -0
  104. package/dist/runtime/human-loop/humanLoop.js.map +1 -0
  105. package/dist/runtime/index.d.ts +10 -0
  106. package/dist/runtime/index.d.ts.map +1 -0
  107. package/dist/runtime/index.js +6 -0
  108. package/dist/runtime/index.js.map +1 -0
  109. package/dist/runtime/middleware/agentToolMiddleware.d.ts +30 -0
  110. package/dist/runtime/middleware/agentToolMiddleware.d.ts.map +1 -0
  111. package/dist/runtime/middleware/agentToolMiddleware.js +259 -0
  112. package/dist/runtime/middleware/agentToolMiddleware.js.map +1 -0
  113. package/dist/runtime/middleware/commandPolicy.d.ts +8 -0
  114. package/dist/runtime/middleware/commandPolicy.d.ts.map +1 -0
  115. package/dist/runtime/middleware/commandPolicy.js +55 -0
  116. package/dist/runtime/middleware/commandPolicy.js.map +1 -0
  117. package/dist/runtime/middleware/frameworkPrompt.d.ts +4 -0
  118. package/dist/runtime/middleware/frameworkPrompt.d.ts.map +1 -0
  119. package/dist/runtime/middleware/frameworkPrompt.js +44 -0
  120. package/dist/runtime/middleware/frameworkPrompt.js.map +1 -0
  121. package/dist/runtime/middleware/index.d.ts +4 -0
  122. package/dist/runtime/middleware/index.d.ts.map +1 -0
  123. package/dist/runtime/middleware/index.js +3 -0
  124. package/dist/runtime/middleware/index.js.map +1 -0
  125. package/dist/runtime/middleware/skillPathMap.d.ts +3 -0
  126. package/dist/runtime/middleware/skillPathMap.d.ts.map +1 -0
  127. package/dist/runtime/middleware/skillPathMap.js +44 -0
  128. package/dist/runtime/middleware/skillPathMap.js.map +1 -0
  129. package/dist/runtime/middleware/toolArgsNormalizer.d.ts +21 -0
  130. package/dist/runtime/middleware/toolArgsNormalizer.d.ts.map +1 -0
  131. package/dist/runtime/middleware/toolArgsNormalizer.js +227 -0
  132. package/dist/runtime/middleware/toolArgsNormalizer.js.map +1 -0
  133. package/dist/runtime/middleware/toolCallGuard.d.ts +8 -0
  134. package/dist/runtime/middleware/toolCallGuard.d.ts.map +1 -0
  135. package/dist/runtime/middleware/toolCallGuard.js +60 -0
  136. package/dist/runtime/middleware/toolCallGuard.js.map +1 -0
  137. package/dist/runtime/middleware/toolFsHandlers.d.ts +12 -0
  138. package/dist/runtime/middleware/toolFsHandlers.d.ts.map +1 -0
  139. package/dist/runtime/middleware/toolFsHandlers.js +79 -0
  140. package/dist/runtime/middleware/toolFsHandlers.js.map +1 -0
  141. package/dist/runtime/middleware/toolRequestParser.d.ts +14 -0
  142. package/dist/runtime/middleware/toolRequestParser.d.ts.map +1 -0
  143. package/dist/runtime/middleware/toolRequestParser.js +72 -0
  144. package/dist/runtime/middleware/toolRequestParser.js.map +1 -0
  145. package/dist/runtime/middleware/types.d.ts +87 -0
  146. package/dist/runtime/middleware/types.d.ts.map +1 -0
  147. package/dist/runtime/middleware/types.js +3 -0
  148. package/dist/runtime/middleware/types.js.map +1 -0
  149. package/dist/runtime/providers/backendResolver.d.ts +26 -0
  150. package/dist/runtime/providers/backendResolver.d.ts.map +1 -0
  151. package/dist/runtime/providers/backendResolver.js +73 -0
  152. package/dist/runtime/providers/backendResolver.js.map +1 -0
  153. package/dist/runtime/providers/index.d.ts +14 -0
  154. package/dist/runtime/providers/index.d.ts.map +1 -0
  155. package/dist/runtime/providers/index.js +10 -0
  156. package/dist/runtime/providers/index.js.map +1 -0
  157. package/dist/runtime/providers/langchainRuntime.d.ts +11 -0
  158. package/dist/runtime/providers/langchainRuntime.d.ts.map +1 -0
  159. package/dist/runtime/providers/langchainRuntime.js +45 -0
  160. package/dist/runtime/providers/langchainRuntime.js.map +1 -0
  161. package/dist/runtime/providers/localShellBackend.d.ts +10 -0
  162. package/dist/runtime/providers/localShellBackend.d.ts.map +1 -0
  163. package/dist/runtime/providers/localShellBackend.js +23 -0
  164. package/dist/runtime/providers/localShellBackend.js.map +1 -0
  165. package/dist/runtime/providers/openaiDeepAgent.d.ts +9 -0
  166. package/dist/runtime/providers/openaiDeepAgent.d.ts.map +1 -0
  167. package/dist/runtime/providers/openaiDeepAgent.js +7 -0
  168. package/dist/runtime/providers/openaiDeepAgent.js.map +1 -0
  169. package/dist/runtime/providers/openaiLocalAgent.d.ts +17 -0
  170. package/dist/runtime/providers/openaiLocalAgent.d.ts.map +1 -0
  171. package/dist/runtime/providers/openaiLocalAgent.js +26 -0
  172. package/dist/runtime/providers/openaiLocalAgent.js.map +1 -0
  173. package/dist/runtime/providers/openaiLocalAgentFactory.d.ts +36 -0
  174. package/dist/runtime/providers/openaiLocalAgentFactory.d.ts.map +1 -0
  175. package/dist/runtime/providers/openaiLocalAgentFactory.js +139 -0
  176. package/dist/runtime/providers/openaiLocalAgentFactory.js.map +1 -0
  177. package/dist/runtime/providers/shared/agentProbe.d.ts +4 -0
  178. package/dist/runtime/providers/shared/agentProbe.d.ts.map +1 -0
  179. package/dist/runtime/providers/shared/agentProbe.js +17 -0
  180. package/dist/runtime/providers/shared/agentProbe.js.map +1 -0
  181. package/dist/runtime/providers/shared/moduleFallback.d.ts +2 -0
  182. package/dist/runtime/providers/shared/moduleFallback.d.ts.map +1 -0
  183. package/dist/runtime/providers/shared/moduleFallback.js +34 -0
  184. package/dist/runtime/providers/shared/moduleFallback.js.map +1 -0
  185. package/dist/runtime/providers/shared/resourceCloser.d.ts +6 -0
  186. package/dist/runtime/providers/shared/resourceCloser.d.ts.map +1 -0
  187. package/dist/runtime/providers/shared/resourceCloser.js +30 -0
  188. package/dist/runtime/providers/shared/resourceCloser.js.map +1 -0
  189. package/dist/runtime/runtimeService.d.ts +109 -0
  190. package/dist/runtime/runtimeService.d.ts.map +1 -0
  191. package/dist/runtime/runtimeService.js +90 -0
  192. package/dist/runtime/runtimeService.js.map +1 -0
  193. package/dist/runtime/stream/agentMessages.d.ts +40 -0
  194. package/dist/runtime/stream/agentMessages.d.ts.map +1 -0
  195. package/dist/runtime/stream/agentMessages.js +44 -0
  196. package/dist/runtime/stream/agentMessages.js.map +1 -0
  197. package/dist/runtime/stream/agentStream.d.ts +31 -0
  198. package/dist/runtime/stream/agentStream.d.ts.map +1 -0
  199. package/dist/runtime/stream/agentStream.js +106 -0
  200. package/dist/runtime/stream/agentStream.js.map +1 -0
  201. package/dist/runtime/stream/messageSummary.d.ts +20 -0
  202. package/dist/runtime/stream/messageSummary.d.ts.map +1 -0
  203. package/dist/runtime/stream/messageSummary.js +150 -0
  204. package/dist/runtime/stream/messageSummary.js.map +1 -0
  205. package/dist/runtime/stream/runArtifacts.d.ts +9 -0
  206. package/dist/runtime/stream/runArtifacts.d.ts.map +1 -0
  207. package/dist/runtime/stream/runArtifacts.js +48 -0
  208. package/dist/runtime/stream/runArtifacts.js.map +1 -0
  209. package/dist/state/index.d.ts +7 -0
  210. package/dist/state/index.d.ts.map +1 -0
  211. package/dist/state/index.js +4 -0
  212. package/dist/state/index.js.map +1 -0
  213. package/dist/state/runState.d.ts +36 -0
  214. package/dist/state/runState.d.ts.map +1 -0
  215. package/dist/state/runState.js +51 -0
  216. package/dist/state/runState.js.map +1 -0
  217. package/dist/state/todos.d.ts +19 -0
  218. package/dist/state/todos.d.ts.map +1 -0
  219. package/dist/state/todos.js +45 -0
  220. package/dist/state/todos.js.map +1 -0
  221. package/dist/state/workspaceRunSession.d.ts +32 -0
  222. package/dist/state/workspaceRunSession.d.ts.map +1 -0
  223. package/dist/state/workspaceRunSession.js +67 -0
  224. package/dist/state/workspaceRunSession.js.map +1 -0
  225. package/dist/state/workspaceState.d.ts +94 -0
  226. package/dist/state/workspaceState.d.ts.map +1 -0
  227. package/dist/state/workspaceState.js +119 -0
  228. package/dist/state/workspaceState.js.map +1 -0
  229. package/dist/types.d.ts +2 -0
  230. package/dist/types.d.ts.map +1 -0
  231. package/dist/types.js +2 -0
  232. package/dist/types.js.map +1 -0
  233. package/example/.tsbuildinfo +1 -0
  234. package/example/build/.tsbuildinfo +1 -0
  235. package/example/config/memory.yaml +35 -0
  236. package/example/config/model.yaml +14 -0
  237. package/example/config/runtime.yaml +46 -0
  238. package/example/config/tool.yaml +24 -0
  239. package/example/index.ts +159 -0
  240. package/example/package-lock.json +1396 -0
  241. package/example/package.json +26 -0
  242. package/example/serve-output.mjs +52 -0
  243. package/example/tsconfig.json +19 -0
  244. package/package.json +73 -0
  245. package/scripts/resolve-deps.js +40 -0
  246. package/src/config/index.ts +29 -0
  247. package/src/config/loader.ts +174 -0
  248. package/src/config/resolveRuntimeConfig.ts +160 -0
  249. package/src/config/resources.ts +152 -0
  250. package/src/config/runtimeConfigLoader.ts +68 -0
  251. package/src/external-modules.d.ts +20 -0
  252. package/src/index.ts +36 -0
  253. package/src/runtime/agentMiddleware.ts +6 -0
  254. package/src/runtime/bootstrap/runtimeFactory.ts +212 -0
  255. package/src/runtime/bootstrap/runtimeModuleInitializer.ts +34 -0
  256. package/src/runtime/context/agentContext.ts +24 -0
  257. package/src/runtime/context/globalContext.ts +70 -0
  258. package/src/runtime/events/eventPublisher.ts +45 -0
  259. package/src/runtime/execution/agentRunExecutor.ts +499 -0
  260. package/src/runtime/execution/agentRunExecutor.types.ts +39 -0
  261. package/src/runtime/execution/agentRunProgress.ts +67 -0
  262. package/src/runtime/execution/agentRunState.ts +66 -0
  263. package/src/runtime/execution/policies/runCompletionPolicy.ts +30 -0
  264. package/src/runtime/execution/policies/runErrorPolicy.ts +50 -0
  265. package/src/runtime/execution/policies/runPromptBuilder.ts +39 -0
  266. package/src/runtime/execution/policies/runSummaryFormatter.ts +57 -0
  267. package/src/runtime/execution/policies/runtimeDelay.ts +5 -0
  268. package/src/runtime/execution/policies/toolConfigReader.ts +107 -0
  269. package/src/runtime/human-loop/consoleHumanLoop.ts +113 -0
  270. package/src/runtime/human-loop/humanLoop.ts +90 -0
  271. package/src/runtime/index.ts +36 -0
  272. package/src/runtime/middleware/agentToolMiddleware.ts +329 -0
  273. package/src/runtime/middleware/commandPolicy.ts +65 -0
  274. package/src/runtime/middleware/frameworkPrompt.ts +51 -0
  275. package/src/runtime/middleware/index.ts +7 -0
  276. package/src/runtime/middleware/skillPathMap.ts +49 -0
  277. package/src/runtime/middleware/toolArgsNormalizer.ts +277 -0
  278. package/src/runtime/middleware/toolCallGuard.ts +73 -0
  279. package/src/runtime/middleware/toolFsHandlers.ts +94 -0
  280. package/src/runtime/middleware/toolRequestParser.ts +84 -0
  281. package/src/runtime/middleware/types.ts +91 -0
  282. package/src/runtime/providers/backendResolver.ts +127 -0
  283. package/src/runtime/providers/index.ts +19 -0
  284. package/src/runtime/providers/langchainRuntime.ts +67 -0
  285. package/src/runtime/providers/localShellBackend.ts +54 -0
  286. package/src/runtime/providers/openaiDeepAgent.ts +17 -0
  287. package/src/runtime/providers/openaiLocalAgent.ts +35 -0
  288. package/src/runtime/providers/openaiLocalAgentFactory.ts +185 -0
  289. package/src/runtime/providers/shared/agentProbe.ts +24 -0
  290. package/src/runtime/providers/shared/moduleFallback.ts +41 -0
  291. package/src/runtime/providers/shared/resourceCloser.ts +30 -0
  292. package/src/runtime/runtimeService.ts +205 -0
  293. package/src/runtime/stream/agentMessages.ts +72 -0
  294. package/src/runtime/stream/agentStream.ts +168 -0
  295. package/src/runtime/stream/messageSummary.ts +164 -0
  296. package/src/runtime/stream/runArtifacts.ts +72 -0
  297. package/src/state/index.ts +19 -0
  298. package/src/state/runState.ts +84 -0
  299. package/src/state/todos.ts +59 -0
  300. package/src/state/workspaceRunSession.ts +96 -0
  301. package/src/state/workspaceState.ts +199 -0
  302. package/src/types/easynet-agent-common.d.ts +62 -0
  303. package/src/types.ts +1 -0
  304. package/test/integration/runtime-live.integration.test.ts +30 -0
  305. package/test/unit/config/loader.test.ts +429 -0
  306. package/test/unit/runtime/agentContext.test.ts +23 -0
  307. package/test/unit/runtime/agentRuntime.test.ts +99 -0
  308. package/test/unit/runtime/consoleHumanLoop.test.ts +52 -0
  309. package/test/unit/runtime/events.test.ts +11 -0
  310. package/test/unit/runtime/globalContext.test.ts +34 -0
  311. package/test/unit/runtime/humanLoop.test.ts +104 -0
  312. package/test/unit/runtime/toolArgsNormalizer.test.ts +136 -0
  313. package/tsconfig.json +18 -0
@@ -0,0 +1,205 @@
1
+ import type { DrainAgentStreamResult } from "./stream/agentStream.js";
2
+ import {
3
+ type AgentRuntimeRunOptions,
4
+ AgentRunExecutor,
5
+ } from "./execution/agentRunExecutor.js";
6
+ import { AgentRuntimeFactory } from "./bootstrap/runtimeFactory.js";
7
+ import { AgentRuntimeModuleInitializer } from "./bootstrap/runtimeModuleInitializer.js";
8
+ import type { CreateAgentToolMiddlewareOptions } from "./middleware/index.js";
9
+ import type {
10
+ AgentWorkspaceState,
11
+ CreateAgentWorkspaceStateOptions,
12
+ } from "../state/workspaceState.js";
13
+ import type { AgentBackendConfig, OpenAILocalAgentService } from "./providers/index.js";
14
+ import {
15
+ AgentRuntimeEventPublisher,
16
+ type AgentEventBus,
17
+ type AgentEventListener,
18
+ } from "./events/eventPublisher.js";
19
+ import type { ResolvedAgentRuntimeConfig } from "../config/index.js";
20
+ import type { HumanLoopService, HumanLoopToolPolicy } from "./human-loop/humanLoop.js";
21
+
22
+ export interface CreateAgentRuntimeOptions
23
+ extends Omit<CreateAgentWorkspaceStateOptions, "todosLogger" | "events">,
24
+ Omit<
25
+ CreateAgentToolMiddlewareOptions,
26
+ "persistTodos" | "createMiddleware" | "ToolMessage" | "rootDir" | "events"
27
+ > {
28
+ eventBus?: AgentEventBus;
29
+ humanLoop?: HumanLoopService;
30
+ humanLoopToolPolicy?: HumanLoopToolPolicy;
31
+ configDir?: string;
32
+ systemPrompt?: string;
33
+ toolConfigPath?: string;
34
+ modelConfigPath?: string;
35
+ modelCheckConnectivity?: boolean;
36
+ model?: string;
37
+ temperature?: number;
38
+ maxTokens?: number;
39
+ backend?: AgentBackendConfig;
40
+ todosLogger?: CreateAgentWorkspaceStateOptions["todosLogger"];
41
+ runLogger?: (message: string) => void;
42
+ incompleteRunMaxRetries?: number;
43
+ emptyRunMaxRetries?: number;
44
+ malformedToolCallMaxRetries?: number;
45
+ idleTimeoutMaxRetries?: number;
46
+ heartbeatIntervalMs?: number;
47
+ idleTimeoutMs?: number;
48
+ artifactValidator?: (context: {
49
+ rootDir: string;
50
+ trackedArtifactFiles: string[];
51
+ requiredArtifactPaths: string[];
52
+ }) => boolean | Promise<boolean>;
53
+ }
54
+
55
+ export interface CreateAgentRuntimeFrameworkOptions extends CreateAgentRuntimeOptions {
56
+ memoryConfigPath?: string;
57
+ forceInitializeModules?: boolean;
58
+ }
59
+
60
+ export interface AgentRuntime {
61
+ events: AgentEventBus;
62
+ eventListener: AgentEventListener | undefined;
63
+ runtimeConfig: ResolvedAgentRuntimeConfig;
64
+ middleware: unknown;
65
+ workspace: AgentWorkspaceState;
66
+ agent: {
67
+ stream: (input: unknown, options: { configurable: { thread_id: string }; streamMode: "values" }) => Promise<AsyncIterable<unknown>>;
68
+ };
69
+ prepareRun: AgentWorkspaceState["prepareRun"];
70
+ close(): Promise<void>;
71
+ runStream<TChunk>(params: {
72
+ prompt: string;
73
+ createStream: (context: {
74
+ input: Awaited<ReturnType<AgentWorkspaceState["prepareRun"]>>["input"];
75
+ config: Awaited<ReturnType<AgentWorkspaceState["prepareRun"]>>["config"];
76
+ }) => Promise<AsyncIterable<TChunk>> | AsyncIterable<TChunk>;
77
+ } & AgentRuntimeRunOptions<TChunk>): Promise<DrainAgentStreamResult<TChunk>>;
78
+ run(prompt: string, options?: AgentRuntimeRunOptions<unknown>): Promise<DrainAgentStreamResult<unknown>>;
79
+ }
80
+
81
+ export class AgentRuntimeService implements AgentRuntime {
82
+ public readonly events: AgentEventBus;
83
+ public readonly runtimeConfig: ResolvedAgentRuntimeConfig;
84
+ public readonly middleware: unknown;
85
+ public readonly workspace: AgentWorkspaceState;
86
+ public readonly agent: AgentRuntime["agent"];
87
+ public readonly prepareRun: AgentWorkspaceState["prepareRun"];
88
+
89
+ private readonly eventsPublisher: AgentRuntimeEventPublisher;
90
+ private readonly runtimeAgent: OpenAILocalAgentService;
91
+ private readonly runExecutor: AgentRunExecutor;
92
+ private eventListenerUnsubscribe?: () => void;
93
+ private currentEventListener?: AgentEventListener;
94
+
95
+ public constructor(params: {
96
+ rootDir: string;
97
+ runLogger: (message: string) => void;
98
+ events: AgentRuntimeEventPublisher;
99
+ runtimeConfig: ResolvedAgentRuntimeConfig;
100
+ middleware: unknown;
101
+ workspace: AgentWorkspaceState;
102
+ runtimeAgent: OpenAILocalAgentService;
103
+ defaultIncompleteRunMaxRetries?: number;
104
+ defaultEmptyRunMaxRetries?: number;
105
+ defaultMalformedToolCallMaxRetries?: number;
106
+ defaultIdleTimeoutMaxRetries?: number;
107
+ defaultHeartbeatIntervalMs?: number;
108
+ defaultIdleTimeoutMs?: number;
109
+ artifactValidator?: CreateAgentRuntimeOptions["artifactValidator"];
110
+ }) {
111
+ this.eventsPublisher = params.events;
112
+ this.events = params.events.bus;
113
+ this.runtimeConfig = params.runtimeConfig;
114
+ this.middleware = params.middleware;
115
+ this.workspace = params.workspace;
116
+ this.runtimeAgent = params.runtimeAgent;
117
+ this.agent = {
118
+ stream: (input, options) => {
119
+ const current = this.runtimeAgent.agent as AgentRuntime["agent"];
120
+ return current.stream(input, options);
121
+ },
122
+ };
123
+ this.prepareRun = params.workspace.prepareRun.bind(params.workspace);
124
+ this.runExecutor = new AgentRunExecutor({
125
+ rootDir: params.rootDir,
126
+ workspace: params.workspace,
127
+ events: params.events,
128
+ runLogger: params.runLogger,
129
+ defaultIncompleteRunMaxRetries: params.defaultIncompleteRunMaxRetries,
130
+ defaultEmptyRunMaxRetries: params.defaultEmptyRunMaxRetries,
131
+ defaultMalformedToolCallMaxRetries: params.defaultMalformedToolCallMaxRetries,
132
+ defaultIdleTimeoutMaxRetries: params.defaultIdleTimeoutMaxRetries,
133
+ defaultHeartbeatIntervalMs: params.defaultHeartbeatIntervalMs,
134
+ defaultIdleTimeoutMs: params.defaultIdleTimeoutMs,
135
+ artifactValidator: params.artifactValidator,
136
+ });
137
+ }
138
+
139
+ public get eventListener(): AgentEventListener | undefined {
140
+ return this.currentEventListener;
141
+ }
142
+
143
+ public set eventListener(listener: AgentEventListener | undefined) {
144
+ this.eventListenerUnsubscribe?.();
145
+ this.eventListenerUnsubscribe = undefined;
146
+ this.currentEventListener = listener;
147
+ if (listener) {
148
+ this.eventListenerUnsubscribe = this.events.subscribe(listener);
149
+ }
150
+ }
151
+
152
+ public static async create(options: CreateAgentRuntimeOptions): Promise<AgentRuntimeService> {
153
+ return AgentRuntimeFactory.create(options);
154
+ }
155
+
156
+ public async close(): Promise<void> {
157
+ this.eventListener = undefined;
158
+ this.eventsPublisher.emit({
159
+ name: "agent.runtime2.close.start",
160
+ from: "agent-runtime2.runtime",
161
+ to: "agent-runtime2.runtime",
162
+ });
163
+ await this.runtimeAgent.close();
164
+ this.eventsPublisher.emit({
165
+ name: "agent.runtime2.close.done",
166
+ from: "agent-runtime2.runtime",
167
+ to: "agent-runtime2.runtime",
168
+ });
169
+ }
170
+
171
+ public runStream<TChunk>(
172
+ params: {
173
+ prompt: string;
174
+ createStream: (context: {
175
+ input: Awaited<ReturnType<AgentWorkspaceState["prepareRun"]>>["input"];
176
+ config: Awaited<ReturnType<AgentWorkspaceState["prepareRun"]>>["config"];
177
+ }) => Promise<AsyncIterable<TChunk>> | AsyncIterable<TChunk>;
178
+ } & AgentRuntimeRunOptions<TChunk>,
179
+ ): Promise<DrainAgentStreamResult<TChunk>> {
180
+ return this.runExecutor.runStream(params);
181
+ }
182
+
183
+ public run(prompt: string, options: AgentRuntimeRunOptions<unknown> = {}): Promise<DrainAgentStreamResult<unknown>> {
184
+ return this.runStream({
185
+ prompt,
186
+ ...options,
187
+ createStream: ({ input, config }) => this.agent.stream(input, { ...config, streamMode: "values" }),
188
+ });
189
+ }
190
+ }
191
+
192
+ export async function initializeAgentRuntimeModules(
193
+ options: CreateAgentRuntimeFrameworkOptions,
194
+ ): Promise<void> {
195
+ await AgentRuntimeModuleInitializer.initialize(options);
196
+ }
197
+
198
+ export async function createAgentRuntime2(
199
+ options: CreateAgentRuntimeFrameworkOptions,
200
+ ): Promise<AgentRuntimeService> {
201
+ await initializeAgentRuntimeModules(options);
202
+ return AgentRuntimeService.create(options);
203
+ }
204
+
205
+ export type { AgentRuntimeRunOptions } from "./execution/agentRunExecutor.js";
@@ -0,0 +1,72 @@
1
+ import { MessageSummary } from "./messageSummary.js";
2
+
3
+ export interface AgentMessageLike extends Record<string, unknown> {
4
+ id?: unknown;
5
+ role?: unknown;
6
+ type?: unknown;
7
+ content?: unknown;
8
+ kwargs?: {
9
+ content?: unknown;
10
+ tool_calls?: Array<{ name?: string; function?: { name?: string; arguments?: unknown }; args?: unknown }>;
11
+ };
12
+ tool_calls?: Array<{ name?: string; function?: { name?: string; arguments?: unknown }; args?: unknown }>;
13
+ }
14
+
15
+ export interface SummarizeAgentChunkOptions {
16
+ emptyLabel?: string;
17
+ toolCallLabel?: string;
18
+ toolResultLabel?: string;
19
+ toolResultUnit?: string;
20
+ messageLabel?: string;
21
+ userLabel?: string;
22
+ fallbackLabel?: string;
23
+ textPreviewLength?: number;
24
+ }
25
+
26
+ const DEFAULT_SUMMARY_OPTIONS: Required<SummarizeAgentChunkOptions> = {
27
+ emptyLabel: "Waiting...",
28
+ toolCallLabel: "Tool call",
29
+ toolResultLabel: "Tool result",
30
+ toolResultUnit: "chars",
31
+ messageLabel: "Message",
32
+ userLabel: "User message received",
33
+ fallbackLabel: "",
34
+ textPreviewLength: 60,
35
+ };
36
+
37
+ export class AgentMessageService {
38
+ public static getAgentMessages(chunk: unknown): AgentMessageLike[] {
39
+ const state = chunk as { messages?: unknown[]; state?: { messages?: unknown[] } };
40
+ return (state?.messages ?? state?.state?.messages ?? []) as AgentMessageLike[];
41
+ }
42
+
43
+ public static summarizeAgentChunk(
44
+ chunk: unknown,
45
+ options: SummarizeAgentChunkOptions = {},
46
+ ): string | null {
47
+ const config = { ...DEFAULT_SUMMARY_OPTIONS, ...options };
48
+ const messages = AgentMessageService.getAgentMessages(chunk);
49
+ if (messages.length === 0) return config.emptyLabel;
50
+
51
+ const last = messages[messages.length - 1];
52
+ const previous = messages.length > 1 ? messages[messages.length - 2] : undefined;
53
+ const kwargs = last?.kwargs as AgentMessageLike["kwargs"] | undefined;
54
+ const idStr = Array.isArray(last?.id) ? last.id.join(" ") : String(last?.id ?? "");
55
+ const toolCalls = (last?.tool_calls ?? kwargs?.tool_calls) as AgentMessageLike["tool_calls"] | undefined;
56
+
57
+ if (MessageSummary.looksLikeStartPrompt(last)) return "Started run";
58
+ if (Array.isArray(toolCalls) && toolCalls.length > 0) {
59
+ return MessageSummary.summarizeToolCall(toolCalls, config);
60
+ }
61
+ if (String(last?.role) === "user" || String(last?.role) === "human") return "Started run";
62
+
63
+ const content = last?.content ?? kwargs?.content;
64
+ if (String(last?.type).includes("Tool") || String(last?.role) === "tool" || idStr.includes("ToolMessage")) {
65
+ return MessageSummary.summarizeToolResult(content, config);
66
+ }
67
+ if (typeof content === "string" && content.trim()) {
68
+ return MessageSummary.summarizeContextualMessage(content, previous);
69
+ }
70
+ return config.fallbackLabel || null;
71
+ }
72
+ }
@@ -0,0 +1,168 @@
1
+ export interface AgentStreamSummaryEvent<TChunk> {
2
+ chunk: TChunk;
3
+ stepIndex: number;
4
+ elapsedMs: number;
5
+ summary: string;
6
+ }
7
+
8
+ export interface AgentStreamHeartbeatEvent {
9
+ elapsedMs: number;
10
+ }
11
+
12
+ export interface DrainAgentStreamOptions<TChunk> {
13
+ heartbeatIntervalMs?: number;
14
+ idleTimeoutMs?: number;
15
+ getNow?: () => number;
16
+ summarizeChunk?: (chunk: TChunk) => string | null | undefined;
17
+ onChunk?: (chunk: TChunk) => Promise<void> | void;
18
+ onSummary?: (event: AgentStreamSummaryEvent<TChunk>) => Promise<void> | void;
19
+ shouldStopOnSummary?: (event: AgentStreamSummaryEvent<TChunk>) => Promise<boolean> | boolean;
20
+ onHeartbeat?: (event: AgentStreamHeartbeatEvent) => Promise<void> | void;
21
+ shouldStop?: (chunk: TChunk) => Promise<boolean> | boolean;
22
+ }
23
+
24
+ export interface DrainAgentStreamResult<TChunk> {
25
+ lastChunk: TChunk | null;
26
+ lastSummary: string;
27
+ stepCount: number;
28
+ durationMs: number;
29
+ stoppedEarly: boolean;
30
+ }
31
+
32
+ export class AgentStreamService {
33
+ public static async drain<TChunk>(
34
+ stream: AsyncIterable<TChunk>,
35
+ options: DrainAgentStreamOptions<TChunk> = {},
36
+ ): Promise<DrainAgentStreamResult<TChunk>> {
37
+ const {
38
+ heartbeatIntervalMs = 5_000,
39
+ idleTimeoutMs,
40
+ getNow = () => Date.now(),
41
+ summarizeChunk,
42
+ onChunk,
43
+ onSummary,
44
+ shouldStopOnSummary,
45
+ onHeartbeat,
46
+ shouldStop,
47
+ } = options;
48
+
49
+ const startedAt = getNow();
50
+ let lastChunk: TChunk | null = null;
51
+ let lastSummary = "";
52
+ let stepCount = 0;
53
+ let lastChunkTime = startedAt;
54
+ let lastHeartbeatTime = startedAt;
55
+ let stoppedEarly = false;
56
+ let stopRequested = false;
57
+
58
+ const heartbeatInterval = onHeartbeat
59
+ ? setInterval(() => {
60
+ if (getNow() - lastHeartbeatTime >= heartbeatIntervalMs) {
61
+ void onHeartbeat({ elapsedMs: getNow() - startedAt });
62
+ lastHeartbeatTime = getNow();
63
+ }
64
+ }, heartbeatIntervalMs)
65
+ : null;
66
+
67
+ const iterator = stream[Symbol.asyncIterator]();
68
+ try {
69
+ while (true) {
70
+ const { value: chunk, done } = await nextWithIdleTimeout(
71
+ iterator,
72
+ idleTimeoutMs,
73
+ getNow() - lastChunkTime,
74
+ );
75
+ if (done) break;
76
+
77
+ lastChunk = chunk;
78
+ lastChunkTime = getNow();
79
+ lastHeartbeatTime = lastChunkTime;
80
+
81
+ await onChunk?.(chunk);
82
+
83
+ const summary = summarizeChunk?.(chunk);
84
+ if (summary && summary !== lastSummary) {
85
+ lastSummary = summary;
86
+ stepCount += 1;
87
+ const summaryEvent = {
88
+ chunk,
89
+ stepIndex: stepCount,
90
+ elapsedMs: getNow() - startedAt,
91
+ summary,
92
+ };
93
+ await onSummary?.(summaryEvent);
94
+ if (await shouldStopOnSummary?.(summaryEvent)) {
95
+ stoppedEarly = true;
96
+ stopRequested = true;
97
+ await returnIteratorWithTimeout(iterator, 1_500);
98
+ break;
99
+ }
100
+ }
101
+
102
+ if (await shouldStop?.(chunk)) {
103
+ stoppedEarly = true;
104
+ stopRequested = true;
105
+ await returnIteratorWithTimeout(iterator, 1_500);
106
+ break;
107
+ }
108
+ }
109
+ } finally {
110
+ if (heartbeatInterval) clearInterval(heartbeatInterval);
111
+ if (!stopRequested) {
112
+ await returnIteratorWithTimeout(iterator, 1_500);
113
+ }
114
+ }
115
+
116
+ return {
117
+ lastChunk,
118
+ lastSummary,
119
+ stepCount,
120
+ durationMs: getNow() - startedAt,
121
+ stoppedEarly,
122
+ };
123
+ }
124
+ }
125
+
126
+ async function returnIteratorWithTimeout<TChunk>(
127
+ iterator: AsyncIterator<TChunk>,
128
+ timeoutMs: number,
129
+ ): Promise<void> {
130
+ if (typeof iterator.return !== "function") {
131
+ return;
132
+ }
133
+
134
+ await Promise.race([
135
+ iterator.return(),
136
+ new Promise<void>((resolve) => setTimeout(resolve, timeoutMs)),
137
+ ]).catch(() => {});
138
+ }
139
+
140
+ async function nextWithIdleTimeout<TChunk>(
141
+ iterator: AsyncIterator<TChunk>,
142
+ idleTimeoutMs: number | undefined,
143
+ elapsedSinceLastChunkMs: number,
144
+ ): Promise<IteratorResult<TChunk>> {
145
+ if (!idleTimeoutMs || idleTimeoutMs <= 0) {
146
+ return iterator.next();
147
+ }
148
+
149
+ const remainingMs = Math.max(1, idleTimeoutMs - elapsedSinceLastChunkMs);
150
+ let timer: ReturnType<typeof setTimeout> | undefined;
151
+ try {
152
+ return await Promise.race([
153
+ iterator.next(),
154
+ new Promise<never>((_, reject) => {
155
+ timer = setTimeout(() => {
156
+ reject(new Error(`Agent stream idle timeout after ${idleTimeoutMs}ms without new output`));
157
+ }, remainingMs);
158
+ if (timer && typeof timer.unref === "function") {
159
+ timer.unref();
160
+ }
161
+ }),
162
+ ]);
163
+ } finally {
164
+ if (timer) {
165
+ clearTimeout(timer);
166
+ }
167
+ }
168
+ }
@@ -0,0 +1,164 @@
1
+ import type { AgentMessageLike, SummarizeAgentChunkOptions } from "./agentMessages.js";
2
+
3
+ export class MessageSummary {
4
+ public static looksLikeStartPrompt(message: AgentMessageLike | undefined): boolean {
5
+ const content = typeof message?.content === "string" ? message.content : "";
6
+ if (String(message?.role) !== "user" && String(message?.role) !== "human") {
7
+ return false;
8
+ }
9
+ return content.includes("Tool-call rules:") || content.includes("Continue in the current thread");
10
+ }
11
+
12
+ public static summarizeToolCall(
13
+ toolCalls: NonNullable<AgentMessageLike["tool_calls"]>,
14
+ config: Required<SummarizeAgentChunkOptions>,
15
+ ): string | null {
16
+ const labels = toolCalls.map((tool) => this.describeToolCall(tool));
17
+ if (labels.every((label) => label === "update todo list")) {
18
+ return null;
19
+ }
20
+ return labels.length === 1
21
+ ? `${config.toolCallLabel}: ${labels[0]}`
22
+ : `${config.toolCallLabel}: ${labels.join(", ")}`;
23
+ }
24
+
25
+ public static summarizeToolResult(
26
+ content: unknown,
27
+ config: Required<SummarizeAgentChunkOptions>,
28
+ ): string | null {
29
+ if (typeof content !== "string" || !content.trim()) {
30
+ return `${config.toolResultLabel} (empty)`;
31
+ }
32
+
33
+ const text = this.sanitizePreview(content);
34
+ if (text.startsWith("Successfully wrote to")) {
35
+ return `Saved file${this.extractQuotedPath(text) ? ` to ${this.extractQuotedPath(text)}` : ""}`;
36
+ }
37
+ if (text.includes("Too Many Requests")) return "Rate limited, continuing";
38
+ if (text.includes("Updated todo list to")) return null;
39
+ if (this.looksLikeFileListing(text)) return "Checked workspace files";
40
+ if (text.startsWith("Usage:")) return "Command returned a usage error";
41
+ if (text.startsWith("OK:")) return "Command reported success";
42
+ if (text.includes("[Command succeeded")) return "Command completed";
43
+
44
+ const preview = text.slice(0, config.textPreviewLength);
45
+ return `${config.toolResultLabel}: ${preview}${text.length > config.textPreviewLength ? "…" : ""}`;
46
+ }
47
+
48
+ public static summarizeContextualMessage(
49
+ content: string,
50
+ previous: AgentMessageLike | undefined,
51
+ ): string | null {
52
+ const text = this.sanitizePreview(content);
53
+ if (!text || text.includes("Updated todo list to") || this.looksLikeInternalPlanning(text)) {
54
+ return null;
55
+ }
56
+ if (text.startsWith("✅")) return "Run completed";
57
+ if (text.startsWith("Successfully wrote to")) {
58
+ return `Saved file${this.extractQuotedPath(text) ? ` to ${this.extractQuotedPath(text)}` : ""}`;
59
+ }
60
+ if (text.includes("Too Many Requests")) return "Rate limited, continuing";
61
+ if (this.looksLikeFileListing(text)) return "Checked workspace files";
62
+ if (text.startsWith("<!-- <!DOCTYPE HTML") || text.startsWith("<!DOCTYPE HTML") || text.startsWith("<html")) {
63
+ return "Reviewed generated content";
64
+ }
65
+ if (this.looksLikeSkillFrontMatter(content)) return "Loaded instructions";
66
+ if (text.startsWith("OK:")) return "Command reported success";
67
+ if (this.isToolMessage(previous) && this.looksLikeFileDump(content)) return "Reviewed file contents";
68
+ return null;
69
+ }
70
+
71
+ private static describeToolCall(tool: NonNullable<AgentMessageLike["tool_calls"]>[number]): string {
72
+ const name = tool?.name ?? tool?.function?.name ?? "?";
73
+ const normalizedName = this.humanizeToolName(name);
74
+ const args = this.normalizeToolCallArgs(tool);
75
+
76
+ if ((name === "read_file" || name === "write_file" || name === "edit_file") && typeof args.file_path === "string") {
77
+ return `${normalizedName} ${this.formatPath(args.file_path)}`;
78
+ }
79
+ if ((name === "read_file" || name === "write_file" || name === "edit_file") && typeof args.path === "string") {
80
+ return `${normalizedName} ${this.formatPath(args.path)}`;
81
+ }
82
+ if (name === "execute" && typeof args.command === "string") {
83
+ return `${normalizedName} ${this.truncateText(args.command, 72)}`;
84
+ }
85
+ return name === "write_todos" ? "update todo list" : normalizedName;
86
+ }
87
+
88
+ private static normalizeToolCallArgs(
89
+ tool: NonNullable<AgentMessageLike["tool_calls"]>[number],
90
+ ): Record<string, unknown> {
91
+ const directArgs = tool?.args;
92
+ if (directArgs && typeof directArgs === "object") {
93
+ return directArgs as Record<string, unknown>;
94
+ }
95
+ const rawArguments = tool?.function?.arguments;
96
+ if (typeof rawArguments === "string") {
97
+ try {
98
+ const parsed = JSON.parse(rawArguments) as unknown;
99
+ if (parsed && typeof parsed === "object") {
100
+ return parsed as Record<string, unknown>;
101
+ }
102
+ } catch {}
103
+ } else if (rawArguments && typeof rawArguments === "object") {
104
+ return rawArguments as Record<string, unknown>;
105
+ }
106
+ return {};
107
+ }
108
+
109
+ private static sanitizePreview(content: string): string {
110
+ return content.replace(/\s+/g, " ").trim();
111
+ }
112
+
113
+ private static humanizeToolName(name: string): string {
114
+ switch (name) {
115
+ case "read_file": return "read file";
116
+ case "write_file": return "write file";
117
+ case "edit_file": return "edit file";
118
+ case "write_todos": return "update todo list";
119
+ case "ls": return "inspect files";
120
+ default: return name.replace(/_/g, " ");
121
+ }
122
+ }
123
+
124
+ private static extractQuotedPath(text: string): string | null {
125
+ const match = text.match(/'([^']+)'/);
126
+ return match?.[1] ? this.formatPath(match[1]) : null;
127
+ }
128
+
129
+ private static truncateText(text: string, maxLength: number): string {
130
+ return text.length > maxLength ? `${text.slice(0, maxLength - 1)}...` : text;
131
+ }
132
+
133
+ private static formatPath(path: string): string {
134
+ const normalized = path.replace(/\\/g, "/");
135
+ for (const marker of ["/skills/", "/output/", "/config/", "/.agent/"]) {
136
+ const index = normalized.indexOf(marker);
137
+ if (index >= 0) return normalized.slice(index + 1);
138
+ }
139
+ const segments = normalized.split("/").filter(Boolean);
140
+ return segments.length <= 3 ? normalized : segments.slice(-3).join("/");
141
+ }
142
+
143
+ private static looksLikeFileListing(text: string): boolean {
144
+ return text.includes("\n[file]") || text.includes("\n... truncated ") || /^[./\w-]+\n[\w./@-]+/m.test(text);
145
+ }
146
+
147
+ private static looksLikeInternalPlanning(text: string): boolean {
148
+ return text.startsWith("<think") || text.includes("I should") || text.includes("I'll ");
149
+ }
150
+
151
+ private static looksLikeSkillFrontMatter(content: string): boolean {
152
+ return content.includes("## Skills") || content.includes("### Available skills");
153
+ }
154
+
155
+ private static isToolMessage(message: AgentMessageLike | undefined): boolean {
156
+ if (!message) return false;
157
+ const idStr = Array.isArray(message.id) ? message.id.join(" ") : String(message.id ?? "");
158
+ return String(message.role) === "tool" || String(message.type).includes("Tool") || idStr.includes("ToolMessage");
159
+ }
160
+
161
+ private static looksLikeFileDump(content: string): boolean {
162
+ return content.includes("\n") && !content.includes("Tool-call rules:") && !content.startsWith("{\"messages\"");
163
+ }
164
+ }
@@ -0,0 +1,72 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { unlink } from "node:fs/promises";
3
+ import { relative, resolve } from "node:path";
4
+
5
+ export class RunArtifactService {
6
+ public static inferPromptArtifactPaths(prompt: string): string[] {
7
+ const matches = [...prompt.matchAll(/\b(?:save(?: it)? to|write to)\s+([^\s"'`]+?\.[a-z0-9]+)/gi)];
8
+ return matches.map((match) => match[1]).filter(Boolean);
9
+ }
10
+
11
+ public static resolveTrackedArtifactFiles(
12
+ rootDir: string,
13
+ artifactFiles: Record<string, string>,
14
+ prompt: string,
15
+ ): string[] {
16
+ const configuredArtifacts = Object.values(artifactFiles);
17
+ if (configuredArtifacts.length > 0) {
18
+ return configuredArtifacts;
19
+ }
20
+
21
+ return RunArtifactService.inferPromptArtifactPaths(prompt).map((file) => resolve(rootDir, file));
22
+ }
23
+
24
+ public static buildTrackedPaths(
25
+ rootDir: string,
26
+ runStateFile: string,
27
+ todosFile: string,
28
+ trackedArtifactFiles: string[],
29
+ ): string[] {
30
+ return [runStateFile, todosFile, ...trackedArtifactFiles]
31
+ .map((file) => relative(rootDir, file))
32
+ .filter(Boolean);
33
+ }
34
+
35
+ public static async resetTrackedArtifactsForFreshRun(
36
+ previousStatus: string | undefined,
37
+ trackedArtifactFiles: string[],
38
+ ): Promise<void> {
39
+ if (previousStatus === "running") {
40
+ return;
41
+ }
42
+
43
+ await Promise.all(
44
+ trackedArtifactFiles
45
+ .filter((file) => existsSync(file))
46
+ .map((file) => unlink(file).catch(() => {})),
47
+ );
48
+ }
49
+
50
+ public static areArtifactsUsable(trackedArtifactFiles: string[]): boolean {
51
+ return trackedArtifactFiles.length === 0 ||
52
+ trackedArtifactFiles.every((file) => RunArtifactService.isArtifactUsable(file));
53
+ }
54
+
55
+ private static isArtifactUsable(filePath: string): boolean {
56
+ if (!existsSync(filePath)) {
57
+ return false;
58
+ }
59
+
60
+ const stat = statSync(filePath, { throwIfNoEntry: false });
61
+ if (!stat || !stat.isFile() || stat.size <= 0) {
62
+ return false;
63
+ }
64
+
65
+ if (!/\.html?$/i.test(filePath)) {
66
+ return true;
67
+ }
68
+
69
+ const text = readFileSync(filePath, "utf8").trim().toLowerCase();
70
+ return text.includes("<html") || text.includes("<!doctype html");
71
+ }
72
+ }