@ariaflowagents/core 0.7.1 → 0.9.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 (338) hide show
  1. package/README.md +90 -1
  2. package/dist/agents/Agent.d.ts +188 -9
  3. package/dist/agents/Agent.d.ts.map +1 -1
  4. package/dist/agents/Agent.js +246 -24
  5. package/dist/agents/Agent.js.map +1 -1
  6. package/dist/agents/CompositeAgent.d.ts +4 -3
  7. package/dist/agents/CompositeAgent.d.ts.map +1 -1
  8. package/dist/agents/CompositeAgent.js +19 -9
  9. package/dist/agents/CompositeAgent.js.map +1 -1
  10. package/dist/agents/FlowAgent.d.ts +3 -2
  11. package/dist/agents/FlowAgent.d.ts.map +1 -1
  12. package/dist/agents/FlowAgent.js +16 -6
  13. package/dist/agents/FlowAgent.js.map +1 -1
  14. package/dist/agents/TriageAgent.d.ts +8 -2
  15. package/dist/agents/TriageAgent.d.ts.map +1 -1
  16. package/dist/agents/TriageAgent.js +39 -6
  17. package/dist/agents/TriageAgent.js.map +1 -1
  18. package/dist/agents/index.d.ts +1 -1
  19. package/dist/agents/index.d.ts.map +1 -1
  20. package/dist/agents/index.js +0 -1
  21. package/dist/agents/index.js.map +1 -1
  22. package/dist/capabilities/AutoRetrieveCapability.d.ts +30 -0
  23. package/dist/capabilities/AutoRetrieveCapability.d.ts.map +1 -0
  24. package/dist/capabilities/AutoRetrieveCapability.js +36 -0
  25. package/dist/capabilities/AutoRetrieveCapability.js.map +1 -0
  26. package/dist/capabilities/ExtractionCapability.d.ts +25 -0
  27. package/dist/capabilities/ExtractionCapability.d.ts.map +1 -0
  28. package/dist/capabilities/ExtractionCapability.js +74 -0
  29. package/dist/capabilities/ExtractionCapability.js.map +1 -0
  30. package/dist/capabilities/FlowCapability.d.ts +81 -0
  31. package/dist/capabilities/FlowCapability.d.ts.map +1 -0
  32. package/dist/capabilities/FlowCapability.js +482 -0
  33. package/dist/capabilities/FlowCapability.js.map +1 -0
  34. package/dist/capabilities/GuardrailCapability.d.ts +30 -0
  35. package/dist/capabilities/GuardrailCapability.d.ts.map +1 -0
  36. package/dist/capabilities/GuardrailCapability.js +38 -0
  37. package/dist/capabilities/GuardrailCapability.js.map +1 -0
  38. package/dist/capabilities/HandoffCapability.d.ts +19 -0
  39. package/dist/capabilities/HandoffCapability.d.ts.map +1 -0
  40. package/dist/capabilities/HandoffCapability.js +58 -0
  41. package/dist/capabilities/HandoffCapability.js.map +1 -0
  42. package/dist/capabilities/LivePromptAssembler.d.ts +108 -0
  43. package/dist/capabilities/LivePromptAssembler.d.ts.map +1 -0
  44. package/dist/capabilities/LivePromptAssembler.js +157 -0
  45. package/dist/capabilities/LivePromptAssembler.js.map +1 -0
  46. package/dist/capabilities/TriageCapability.d.ts +16 -0
  47. package/dist/capabilities/TriageCapability.d.ts.map +1 -0
  48. package/dist/capabilities/TriageCapability.js +61 -0
  49. package/dist/capabilities/TriageCapability.js.map +1 -0
  50. package/dist/capabilities/adapters/ai-sdk.d.ts +14 -0
  51. package/dist/capabilities/adapters/ai-sdk.d.ts.map +1 -0
  52. package/dist/capabilities/adapters/ai-sdk.js +29 -0
  53. package/dist/capabilities/adapters/ai-sdk.js.map +1 -0
  54. package/dist/capabilities/adapters/gemini.d.ts +15 -0
  55. package/dist/capabilities/adapters/gemini.d.ts.map +1 -0
  56. package/dist/capabilities/adapters/gemini.js +40 -0
  57. package/dist/capabilities/adapters/gemini.js.map +1 -0
  58. package/dist/capabilities/index.d.ts +154 -0
  59. package/dist/capabilities/index.d.ts.map +1 -0
  60. package/dist/capabilities/index.js +128 -0
  61. package/dist/capabilities/index.js.map +1 -0
  62. package/dist/eval/EvalRunner.d.ts +12 -0
  63. package/dist/eval/EvalRunner.d.ts.map +1 -0
  64. package/dist/eval/EvalRunner.js +64 -0
  65. package/dist/eval/EvalRunner.js.map +1 -0
  66. package/dist/eval/scoring.d.ts +15 -0
  67. package/dist/eval/scoring.d.ts.map +1 -0
  68. package/dist/eval/scoring.js +152 -0
  69. package/dist/eval/scoring.js.map +1 -0
  70. package/dist/eval/types.d.ts +59 -0
  71. package/dist/eval/types.d.ts.map +1 -0
  72. package/dist/eval/types.js +2 -0
  73. package/dist/eval/types.js.map +1 -0
  74. package/dist/flows/FlowGraph.d.ts +3 -1
  75. package/dist/flows/FlowGraph.d.ts.map +1 -1
  76. package/dist/flows/FlowGraph.js +5 -0
  77. package/dist/flows/FlowGraph.js.map +1 -1
  78. package/dist/flows/FlowManager.d.ts +68 -1
  79. package/dist/flows/FlowManager.d.ts.map +1 -1
  80. package/dist/flows/FlowManager.js +499 -36
  81. package/dist/flows/FlowManager.js.map +1 -1
  82. package/dist/flows/extraction.d.ts +16 -1
  83. package/dist/flows/extraction.d.ts.map +1 -1
  84. package/dist/flows/extraction.js +34 -0
  85. package/dist/flows/extraction.js.map +1 -1
  86. package/dist/flows/index.d.ts +2 -0
  87. package/dist/flows/index.d.ts.map +1 -1
  88. package/dist/flows/index.js +1 -0
  89. package/dist/flows/index.js.map +1 -1
  90. package/dist/flows/validation.d.ts +1 -1
  91. package/dist/flows/validation.d.ts.map +1 -1
  92. package/dist/flows/validation.js +13 -1
  93. package/dist/flows/validation.js.map +1 -1
  94. package/dist/foundation/AgentDefinition.d.ts +18 -0
  95. package/dist/foundation/AgentDefinition.d.ts.map +1 -0
  96. package/dist/foundation/AgentDefinition.js +2 -0
  97. package/dist/foundation/AgentDefinition.js.map +1 -0
  98. package/dist/foundation/AgentStateController.d.ts +26 -0
  99. package/dist/foundation/AgentStateController.d.ts.map +1 -0
  100. package/dist/foundation/AgentStateController.js +2 -0
  101. package/dist/foundation/AgentStateController.js.map +1 -0
  102. package/dist/foundation/ConversationEventLog.d.ts +72 -0
  103. package/dist/foundation/ConversationEventLog.d.ts.map +1 -0
  104. package/dist/foundation/ConversationEventLog.js +2 -0
  105. package/dist/foundation/ConversationEventLog.js.map +1 -0
  106. package/dist/foundation/ConversationState.d.ts +31 -0
  107. package/dist/foundation/ConversationState.d.ts.map +1 -0
  108. package/dist/foundation/ConversationState.js +2 -0
  109. package/dist/foundation/ConversationState.js.map +1 -0
  110. package/dist/foundation/DefaultAgentStateController.d.ts +24 -0
  111. package/dist/foundation/DefaultAgentStateController.d.ts.map +1 -0
  112. package/dist/foundation/DefaultAgentStateController.js +49 -0
  113. package/dist/foundation/DefaultAgentStateController.js.map +1 -0
  114. package/dist/foundation/DefaultConversationEventLog.d.ts +28 -0
  115. package/dist/foundation/DefaultConversationEventLog.d.ts.map +1 -0
  116. package/dist/foundation/DefaultConversationEventLog.js +195 -0
  117. package/dist/foundation/DefaultConversationEventLog.js.map +1 -0
  118. package/dist/foundation/DefaultConversationState.d.ts +34 -0
  119. package/dist/foundation/DefaultConversationState.d.ts.map +1 -0
  120. package/dist/foundation/DefaultConversationState.js +100 -0
  121. package/dist/foundation/DefaultConversationState.js.map +1 -0
  122. package/dist/foundation/DefaultToolExecutor.d.ts +58 -0
  123. package/dist/foundation/DefaultToolExecutor.d.ts.map +1 -0
  124. package/dist/foundation/DefaultToolExecutor.js +128 -0
  125. package/dist/foundation/DefaultToolExecutor.js.map +1 -0
  126. package/dist/foundation/ToolExecutor.d.ts +44 -0
  127. package/dist/foundation/ToolExecutor.d.ts.map +1 -0
  128. package/dist/foundation/ToolExecutor.js +2 -0
  129. package/dist/foundation/ToolExecutor.js.map +1 -0
  130. package/dist/foundation/createFoundation.d.ts +33 -0
  131. package/dist/foundation/createFoundation.d.ts.map +1 -0
  132. package/dist/foundation/createFoundation.js +34 -0
  133. package/dist/foundation/createFoundation.js.map +1 -0
  134. package/dist/foundation/index.d.ts +15 -0
  135. package/dist/foundation/index.d.ts.map +1 -0
  136. package/dist/foundation/index.js +8 -0
  137. package/dist/foundation/index.js.map +1 -0
  138. package/dist/hooks/HookRunner.d.ts +5 -1
  139. package/dist/hooks/HookRunner.d.ts.map +1 -1
  140. package/dist/hooks/HookRunner.js +7 -0
  141. package/dist/hooks/HookRunner.js.map +1 -1
  142. package/dist/hooks/builtin/metrics.d.ts.map +1 -1
  143. package/dist/hooks/builtin/metrics.js +12 -0
  144. package/dist/hooks/builtin/metrics.js.map +1 -1
  145. package/dist/hooks/builtin/observability.d.ts +21 -0
  146. package/dist/hooks/builtin/observability.d.ts.map +1 -0
  147. package/dist/hooks/builtin/observability.js +535 -0
  148. package/dist/hooks/builtin/observability.js.map +1 -0
  149. package/dist/index.d.ts +24 -3
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/index.js +16 -2
  152. package/dist/index.js.map +1 -1
  153. package/dist/memory/MemoryService.d.ts +40 -0
  154. package/dist/memory/MemoryService.d.ts.map +1 -0
  155. package/dist/memory/MemoryService.js +2 -0
  156. package/dist/memory/MemoryService.js.map +1 -0
  157. package/dist/memory/index.d.ts +5 -0
  158. package/dist/memory/index.d.ts.map +1 -0
  159. package/dist/memory/index.js +3 -0
  160. package/dist/memory/index.js.map +1 -0
  161. package/dist/memory/preloadMemory.d.ts +17 -0
  162. package/dist/memory/preloadMemory.d.ts.map +1 -0
  163. package/dist/memory/preloadMemory.js +62 -0
  164. package/dist/memory/preloadMemory.js.map +1 -0
  165. package/dist/memory/stores/InMemoryMemoryService.d.ts +20 -0
  166. package/dist/memory/stores/InMemoryMemoryService.d.ts.map +1 -0
  167. package/dist/memory/stores/InMemoryMemoryService.js +92 -0
  168. package/dist/memory/stores/InMemoryMemoryService.js.map +1 -0
  169. package/dist/memory/types.d.ts +49 -0
  170. package/dist/memory/types.d.ts.map +1 -0
  171. package/dist/memory/types.js +8 -0
  172. package/dist/memory/types.js.map +1 -0
  173. package/dist/orchestration/DefaultOrchestrationAuthority.d.ts +91 -0
  174. package/dist/orchestration/DefaultOrchestrationAuthority.d.ts.map +1 -0
  175. package/dist/orchestration/DefaultOrchestrationAuthority.js +786 -0
  176. package/dist/orchestration/DefaultOrchestrationAuthority.js.map +1 -0
  177. package/dist/orchestration/OrchestrationAuthority.d.ts +119 -0
  178. package/dist/orchestration/OrchestrationAuthority.d.ts.map +1 -0
  179. package/dist/orchestration/OrchestrationAuthority.js +2 -0
  180. package/dist/orchestration/OrchestrationAuthority.js.map +1 -0
  181. package/dist/orchestration/RealtimeExtractionRunner.d.ts +25 -0
  182. package/dist/orchestration/RealtimeExtractionRunner.d.ts.map +1 -0
  183. package/dist/orchestration/RealtimeExtractionRunner.js +62 -0
  184. package/dist/orchestration/RealtimeExtractionRunner.js.map +1 -0
  185. package/dist/orchestration/index.d.ts +5 -0
  186. package/dist/orchestration/index.d.ts.map +1 -0
  187. package/dist/orchestration/index.js +4 -0
  188. package/dist/orchestration/index.js.map +1 -0
  189. package/dist/orchestration/types.d.ts +134 -0
  190. package/dist/orchestration/types.d.ts.map +1 -0
  191. package/dist/orchestration/types.js +2 -0
  192. package/dist/orchestration/types.js.map +1 -0
  193. package/dist/prompts/AgentPrompt.d.ts +110 -0
  194. package/dist/prompts/AgentPrompt.d.ts.map +1 -0
  195. package/dist/prompts/AgentPrompt.js +373 -0
  196. package/dist/prompts/AgentPrompt.js.map +1 -0
  197. package/dist/prompts/PromptAssembly.d.ts +119 -0
  198. package/dist/prompts/PromptAssembly.d.ts.map +1 -0
  199. package/dist/prompts/PromptAssembly.js +150 -0
  200. package/dist/prompts/PromptAssembly.js.map +1 -0
  201. package/dist/prompts/PromptBuilder.d.ts +22 -3
  202. package/dist/prompts/PromptBuilder.d.ts.map +1 -1
  203. package/dist/prompts/PromptBuilder.js +242 -13
  204. package/dist/prompts/PromptBuilder.js.map +1 -1
  205. package/dist/prompts/PromptRenderer.d.ts +43 -0
  206. package/dist/prompts/PromptRenderer.d.ts.map +1 -0
  207. package/dist/prompts/PromptRenderer.js +114 -0
  208. package/dist/prompts/PromptRenderer.js.map +1 -0
  209. package/dist/prompts/brandVoice.d.ts +10 -0
  210. package/dist/prompts/brandVoice.d.ts.map +1 -0
  211. package/dist/prompts/brandVoice.js +87 -0
  212. package/dist/prompts/brandVoice.js.map +1 -0
  213. package/dist/prompts/index.d.ts +11 -4
  214. package/dist/prompts/index.d.ts.map +1 -1
  215. package/dist/prompts/index.js +7 -2
  216. package/dist/prompts/index.js.map +1 -1
  217. package/dist/prompts/security.d.ts +5 -0
  218. package/dist/prompts/security.d.ts.map +1 -0
  219. package/dist/prompts/security.js +52 -0
  220. package/dist/prompts/security.js.map +1 -0
  221. package/dist/prompts/types.d.ts +65 -1
  222. package/dist/prompts/types.d.ts.map +1 -1
  223. package/dist/prompts/types.js +26 -0
  224. package/dist/prompts/types.js.map +1 -1
  225. package/dist/realtime/RealtimeAudioClient.d.ts +105 -0
  226. package/dist/realtime/RealtimeAudioClient.d.ts.map +1 -0
  227. package/dist/realtime/RealtimeAudioClient.js +15 -0
  228. package/dist/realtime/RealtimeAudioClient.js.map +1 -0
  229. package/dist/realtime/RealtimeRuntime.d.ts +136 -0
  230. package/dist/realtime/RealtimeRuntime.d.ts.map +1 -0
  231. package/dist/realtime/RealtimeRuntime.js +270 -0
  232. package/dist/realtime/RealtimeRuntime.js.map +1 -0
  233. package/dist/realtime/index.d.ts +4 -0
  234. package/dist/realtime/index.d.ts.map +1 -0
  235. package/dist/realtime/index.js +2 -0
  236. package/dist/realtime/index.js.map +1 -0
  237. package/dist/runtime/ContextBudget.d.ts +57 -0
  238. package/dist/runtime/ContextBudget.d.ts.map +1 -0
  239. package/dist/runtime/ContextBudget.js +103 -0
  240. package/dist/runtime/ContextBudget.js.map +1 -0
  241. package/dist/runtime/ContextManager.d.ts +8 -5
  242. package/dist/runtime/ContextManager.d.ts.map +1 -1
  243. package/dist/runtime/ContextManager.js +47 -14
  244. package/dist/runtime/ContextManager.js.map +1 -1
  245. package/dist/runtime/ExtractionEngine.d.ts +2 -1
  246. package/dist/runtime/ExtractionEngine.d.ts.map +1 -1
  247. package/dist/runtime/ExtractionEngine.js +11 -0
  248. package/dist/runtime/ExtractionEngine.js.map +1 -1
  249. package/dist/runtime/FlowExecutor.d.ts +22 -15
  250. package/dist/runtime/FlowExecutor.d.ts.map +1 -1
  251. package/dist/runtime/FlowExecutor.js +102 -149
  252. package/dist/runtime/FlowExecutor.js.map +1 -1
  253. package/dist/runtime/Runtime.d.ts +53 -78
  254. package/dist/runtime/Runtime.d.ts.map +1 -1
  255. package/dist/runtime/Runtime.js +272 -1406
  256. package/dist/runtime/Runtime.js.map +1 -1
  257. package/dist/runtime/SessionCache.d.ts +16 -0
  258. package/dist/runtime/SessionCache.d.ts.map +1 -0
  259. package/dist/runtime/SessionCache.js +49 -0
  260. package/dist/runtime/SessionCache.js.map +1 -0
  261. package/dist/runtime/SessionMutex.d.ts +37 -0
  262. package/dist/runtime/SessionMutex.d.ts.map +1 -0
  263. package/dist/runtime/SessionMutex.js +59 -0
  264. package/dist/runtime/SessionMutex.js.map +1 -0
  265. package/dist/runtime/StreamEmitter.d.ts +34 -0
  266. package/dist/runtime/StreamEmitter.d.ts.map +1 -0
  267. package/dist/runtime/StreamEmitter.js +91 -0
  268. package/dist/runtime/StreamEmitter.js.map +1 -0
  269. package/dist/runtime/handoffFilters.d.ts +60 -0
  270. package/dist/runtime/handoffFilters.d.ts.map +1 -0
  271. package/dist/runtime/handoffFilters.js +95 -0
  272. package/dist/runtime/handoffFilters.js.map +1 -0
  273. package/dist/runtime/pipeline/AgentExecuteStage.d.ts +22 -0
  274. package/dist/runtime/pipeline/AgentExecuteStage.d.ts.map +1 -0
  275. package/dist/runtime/pipeline/AgentExecuteStage.js +958 -0
  276. package/dist/runtime/pipeline/AgentExecuteStage.js.map +1 -0
  277. package/dist/runtime/pipeline/ContextAssembleStage.d.ts +26 -0
  278. package/dist/runtime/pipeline/ContextAssembleStage.d.ts.map +1 -0
  279. package/dist/runtime/pipeline/ContextAssembleStage.js +253 -0
  280. package/dist/runtime/pipeline/ContextAssembleStage.js.map +1 -0
  281. package/dist/runtime/pipeline/ContextGatherStage.d.ts +21 -0
  282. package/dist/runtime/pipeline/ContextGatherStage.d.ts.map +1 -0
  283. package/dist/runtime/pipeline/ContextGatherStage.js +161 -0
  284. package/dist/runtime/pipeline/ContextGatherStage.js.map +1 -0
  285. package/dist/runtime/pipeline/IntakeStage.d.ts +25 -0
  286. package/dist/runtime/pipeline/IntakeStage.d.ts.map +1 -0
  287. package/dist/runtime/pipeline/IntakeStage.js +126 -0
  288. package/dist/runtime/pipeline/IntakeStage.js.map +1 -0
  289. package/dist/runtime/pipeline/PostStreamStage.d.ts +26 -0
  290. package/dist/runtime/pipeline/PostStreamStage.d.ts.map +1 -0
  291. package/dist/runtime/pipeline/PostStreamStage.js +129 -0
  292. package/dist/runtime/pipeline/PostStreamStage.js.map +1 -0
  293. package/dist/runtime/pipeline/TurnPipeline.d.ts +54 -0
  294. package/dist/runtime/pipeline/TurnPipeline.d.ts.map +1 -0
  295. package/dist/runtime/pipeline/TurnPipeline.js +15 -0
  296. package/dist/runtime/pipeline/TurnPipeline.js.map +1 -0
  297. package/dist/runtime/pipeline/TurnServices.d.ts +48 -0
  298. package/dist/runtime/pipeline/TurnServices.d.ts.map +1 -0
  299. package/dist/runtime/pipeline/TurnServices.js +2 -0
  300. package/dist/runtime/pipeline/TurnServices.js.map +1 -0
  301. package/dist/runtime/pipeline/agentTypeGuards.d.ts +4 -0
  302. package/dist/runtime/pipeline/agentTypeGuards.d.ts.map +1 -0
  303. package/dist/runtime/pipeline/agentTypeGuards.js +7 -0
  304. package/dist/runtime/pipeline/agentTypeGuards.js.map +1 -0
  305. package/dist/runtime/pipeline/index.d.ts +11 -0
  306. package/dist/runtime/pipeline/index.d.ts.map +1 -0
  307. package/dist/runtime/pipeline/index.js +13 -0
  308. package/dist/runtime/pipeline/index.js.map +1 -0
  309. package/dist/runtime/pipeline/outputProcessing.d.ts +23 -0
  310. package/dist/runtime/pipeline/outputProcessing.d.ts.map +1 -0
  311. package/dist/runtime/pipeline/outputProcessing.js +63 -0
  312. package/dist/runtime/pipeline/outputProcessing.js.map +1 -0
  313. package/dist/runtime/pipeline/sessionUtils.d.ts +12 -0
  314. package/dist/runtime/pipeline/sessionUtils.d.ts.map +1 -0
  315. package/dist/runtime/pipeline/sessionUtils.js +73 -0
  316. package/dist/runtime/pipeline/sessionUtils.js.map +1 -0
  317. package/dist/tools/Tool.d.ts +7 -0
  318. package/dist/tools/Tool.d.ts.map +1 -1
  319. package/dist/tools/Tool.js +12 -3
  320. package/dist/tools/Tool.js.map +1 -1
  321. package/dist/tools/memory.d.ts +26 -0
  322. package/dist/tools/memory.d.ts.map +1 -0
  323. package/dist/tools/memory.js +51 -0
  324. package/dist/tools/memory.js.map +1 -0
  325. package/dist/types/index.d.ts +238 -9
  326. package/dist/types/index.d.ts.map +1 -1
  327. package/dist/types/index.js +4 -0
  328. package/dist/types/index.js.map +1 -1
  329. package/dist/types/telemetry.d.ts +107 -0
  330. package/dist/types/telemetry.d.ts.map +1 -1
  331. package/guides/AGENTS.md +173 -0
  332. package/guides/README.md +12 -0
  333. package/guides/TOOLS.md +93 -27
  334. package/package.json +25 -4
  335. package/dist/agents/LLMAgent.d.ts +0 -11
  336. package/dist/agents/LLMAgent.d.ts.map +0 -1
  337. package/dist/agents/LLMAgent.js +0 -31
  338. package/dist/agents/LLMAgent.js.map +0 -1
@@ -0,0 +1,958 @@
1
+ /**
2
+ * AgentExecuteStage — Stage 4 of the turn pipeline.
3
+ *
4
+ * Absorbs the full runLoop from Runtime: handoff loop, flow agent execution,
5
+ * LLM agent execution (structured triage, streamText, tool loop),
6
+ * tool enforcement, output processing, result handling, and handoff delegation.
7
+ */
8
+ import crypto from 'node:crypto';
9
+ import { streamText, streamObject, stepCountIs } from 'ai';
10
+ import { z } from 'zod';
11
+ import { runContextGatherStage, emitGatherEvents } from './ContextGatherStage.js';
12
+ import { runContextAssembleStage, buildStructuredTriagePrompt } from './ContextAssembleStage.js';
13
+ import { isFlowAgent, isTriageAgent } from './agentTypeGuards.js';
14
+ import { getFlowState, setFlowState, clearFlowState, updateFlowStateSnapshot, appendSessionMessage, appendSessionMessages, } from './sessionUtils.js';
15
+ import { runOutputProcessing, postProcessPersistedAssistantMessages, getAgentOutputProcessors, } from './outputProcessing.js';
16
+ import { checkStopConditions } from '../../guards/StopConditions.js';
17
+ import { isHandoffResult } from '../../tools/handoff.js';
18
+ import { isFinalResult } from '../../tools/final.js';
19
+ import { CapabilityHost, TriageCapability, HandoffCapability, ExtractionCapability, AutoRetrieveCapability, } from '../../capabilities/index.js';
20
+ import { toAISDKTools } from '../../capabilities/adapters/ai-sdk.js';
21
+ import { runInputProcessors } from '../../processors/ProcessorRunner.js';
22
+ import { getChunkArgs, getChunkErrorMessage, getChunkResult, getChunkToolCallId } from '../../utils/streamChunk.js';
23
+ import { isRecord } from '../../utils/isRecord.js';
24
+ import { estimateTokenCount } from '../ContextBudget.js';
25
+ /**
26
+ * Runs the full agent execution loop (Stage 4).
27
+ * Handles handoffs, flow agents, LLM agents, structured triage, tool loops.
28
+ */
29
+ export async function* runAgentExecuteStage(execInput, services) {
30
+ const { context, injectionQueue, abortController } = execInput;
31
+ let currentInput = execInput.input;
32
+ const abortSignal = abortController?.signal;
33
+ let interruptionEmitted = false;
34
+ let circularRecoveryActive = false;
35
+ let circularRecoveryAttempts = 0;
36
+ while (context.handoffStack.length < services.maxHandoffs) {
37
+ if (abortSignal?.aborted) {
38
+ if (!interruptionEmitted) {
39
+ interruptionEmitted = true;
40
+ yield* services.streamEmitter.emit(context, {
41
+ type: 'interrupted',
42
+ sessionId: context.session.id,
43
+ reason: abortSignal.reason ?? 'Operation cancelled',
44
+ timestamp: new Date(),
45
+ lastAgentId: context.agentId,
46
+ lastStep: context.stepCount,
47
+ });
48
+ }
49
+ return;
50
+ }
51
+ // Circular handoff detection
52
+ if (services.agentStateController.isCircularHandoff(context.handoffStack, context.agentId) && !circularRecoveryActive) {
53
+ const err = `Circular handoff detected: ${context.handoffStack.join(' -> ')} -> ${context.agentId}`;
54
+ yield* services.streamEmitter.emit(context, { type: 'error', error: err });
55
+ circularRecoveryAttempts += 1;
56
+ if (circularRecoveryAttempts > 1) {
57
+ yield* emitCircularHandoffFallback(context, services);
58
+ return;
59
+ }
60
+ circularRecoveryActive = true;
61
+ context.handoffStack = [];
62
+ }
63
+ context.handoffStack.push(context.agentId);
64
+ const agent = services.agents.get(context.agentId);
65
+ if (!agent) {
66
+ yield* services.streamEmitter.emit(context, { type: 'error', error: `Agent "${context.agentId}" not found` });
67
+ return;
68
+ }
69
+ // Agent-specific input processors
70
+ if (agent.inputProcessors && agent.inputProcessors.length > 0) {
71
+ const procCtx = {
72
+ session: context.session,
73
+ agentId: agent.id,
74
+ toolCallHistory: context.toolCallHistory,
75
+ abortSignal,
76
+ };
77
+ const outcome = await runInputProcessors({
78
+ processors: agent.inputProcessors,
79
+ input: currentInput,
80
+ messages: context.session.messages,
81
+ context: procCtx,
82
+ });
83
+ if (outcome.blocked) {
84
+ yield* services.streamEmitter.emit(context, {
85
+ type: 'tripwire',
86
+ phase: 'input',
87
+ processorId: outcome.processorId,
88
+ reason: outcome.reason,
89
+ message: outcome.message,
90
+ });
91
+ yield* services.streamEmitter.emit(context, { type: 'text-delta', text: outcome.message });
92
+ yield* services.streamEmitter.emit(context, { type: 'turn-end' });
93
+ return;
94
+ }
95
+ if (outcome.input !== currentInput) {
96
+ currentInput = outcome.input;
97
+ const last = context.session.messages[context.session.messages.length - 1];
98
+ if (last && last.role === 'user' && typeof last.content === 'string') {
99
+ last.content = currentInput;
100
+ services.conversationState.touchSession(context.session);
101
+ }
102
+ }
103
+ }
104
+ yield* services.streamEmitter.emit(context, { type: 'agent-start', agentId: agent.id });
105
+ await services.hookRunner.onAgentStart(context, agent.id);
106
+ // Stage 2: Context Gather
107
+ const gatherResult = await runContextGatherStage(agent, context, currentInput, services, abortSignal);
108
+ yield* emitGatherEvents(gatherResult.autoRetrieveEvents, context, services);
109
+ const { extractionSnapshot, activeRoutes } = gatherResult;
110
+ // Stage 3: Context Assemble
111
+ const turnToolErrors = [];
112
+ const assembleGen = runContextAssembleStage({ agent, context, injectionQueue, gatherResult, currentInput, circularRecoveryActive }, services);
113
+ let assembleNext = await assembleGen.next();
114
+ while (!assembleNext.done) {
115
+ yield assembleNext.value;
116
+ assembleNext = await assembleGen.next();
117
+ }
118
+ let system = assembleNext.value.systemPrompt;
119
+ // Build CapabilityHost for this agent turn
120
+ const host = new CapabilityHost();
121
+ if (!circularRecoveryActive) {
122
+ if (isTriageAgent(agent) && agent.routes) {
123
+ host.use(new TriageCapability(activeRoutes ?? agent.routes, agent.defaultAgent));
124
+ }
125
+ else if (agent.canHandoffTo?.length) {
126
+ const handoffTargets = agent.canHandoffTo
127
+ .map(id => services.agents.get(id))
128
+ .filter((a) => Boolean(a))
129
+ .map(a => ({ id: a.id, name: a.name ?? a.id, description: a.description }));
130
+ if (handoffTargets.length > 0) {
131
+ host.use(new HandoffCapability(handoffTargets));
132
+ }
133
+ }
134
+ }
135
+ // Only add ExtractionCapability when the agent uses pure capability-based
136
+ // extraction (schema + requiredFields only). Agents that have systemPrompt,
137
+ // memoryKey, or includeInSystemPrompt set are using ExtractionEngine
138
+ // (ContextGatherStage already runs an extraction LLM call and stores data in
139
+ // working memory). Adding ExtractionCapability on top would create duplicate
140
+ // tools and conflicting prompt sections.
141
+ if (agent.extraction) {
142
+ const ex = agent.extraction;
143
+ const usesExtractionEngine = !!(ex.systemPrompt || ex.memoryKey || ex.includeInSystemPrompt !== undefined);
144
+ if (!usesExtractionEngine) {
145
+ host.use(new ExtractionCapability({
146
+ schema: ex.schema,
147
+ requiredFields: ex.requiredFields,
148
+ }));
149
+ }
150
+ }
151
+ if (agent.autoRetrieve) {
152
+ const retrieveProvider = {
153
+ run: (opts) => agent.autoRetrieve.run({ input: opts.input, context, abortSignal }),
154
+ label: agent.autoRetrieve.label,
155
+ };
156
+ host.use(new AutoRetrieveCapability({ provider: retrieveProvider }));
157
+ }
158
+ // Extract handoff/triage tool for flow agent node injection (buildFlowWithHandoff compatibility)
159
+ const hostHandoffTools = toAISDKTools(host.getAllTools().filter(t => t.name === 'transfer_to_agent' || t.name.startsWith('route_to_')));
160
+ const handoffTool = (Object.values(hostHandoffTools)[0] ?? null);
161
+ // Agent's own tools are kept as-is (already AI SDK format) — avoid double-wrapping
162
+ // through ToolDeclaration → toAISDKTools which loses execute binding context.
163
+ const agentOwnTools = agent.tools ? { ...agent.tools } : {};
164
+ let handoffTo = null;
165
+ let handoffReason = 'No reason provided';
166
+ if (isFlowAgent(agent)) {
167
+ yield* runFlowAgentStep(agent, context, currentInput, system, handoffTool, abortSignal, services, turnToolErrors, interruptionEmitted, (to, reason) => { handoffTo = to; handoffReason = reason ?? 'No reason provided'; }, (v) => { interruptionEmitted = v; });
168
+ }
169
+ else {
170
+ const result = yield* runLLMAgentSteps(agent, context, currentInput, system, host, agentOwnTools, abortSignal, services, turnToolErrors, interruptionEmitted, activeRoutes, (to, reason) => { handoffTo = to; handoffReason = reason ?? 'No reason provided'; }, (v) => { interruptionEmitted = v; });
171
+ if (result === 'return')
172
+ return;
173
+ }
174
+ yield* services.streamEmitter.emit(context, { type: 'agent-end', agentId: agent.id });
175
+ await services.hookRunner.onAgentEnd(context, agent.id);
176
+ if (circularRecoveryActive && handoffTo) {
177
+ yield* services.streamEmitter.emit(context, {
178
+ type: 'error',
179
+ error: `Circular handoff recovery failed: attempted handoff ${context.agentId} -> ${handoffTo}`,
180
+ });
181
+ yield* emitCircularHandoffFallback(context, services);
182
+ return;
183
+ }
184
+ if (circularRecoveryActive) {
185
+ circularRecoveryActive = false;
186
+ }
187
+ if (!handoffTo)
188
+ break;
189
+ await services.hookRunner.onHandoff(context, context.agentId, handoffTo, handoffReason);
190
+ yield* services.streamEmitter.emit(context, {
191
+ type: 'handoff',
192
+ from: context.agentId,
193
+ to: handoffTo,
194
+ reason: handoffReason,
195
+ });
196
+ services.agentStateController.recordHandoff({
197
+ session: context.session,
198
+ fromAgentId: context.agentId,
199
+ toAgentId: handoffTo,
200
+ reason: handoffReason,
201
+ });
202
+ // Apply handoff input filter if configured
203
+ const handoffAgent = services.agents.get(context.agentId);
204
+ const agentRoutes = handoffAgent?.routes;
205
+ const matchedRoute = agentRoutes?.find((r) => r.agentId === handoffTo);
206
+ if (matchedRoute?.inputFilter) {
207
+ try {
208
+ const filterInput = {
209
+ messages: [...context.session.messages],
210
+ workingMemory: { ...context.session.workingMemory },
211
+ sourceAgentId: context.agentId,
212
+ targetAgentId: handoffTo,
213
+ reason: handoffReason,
214
+ };
215
+ const filterResult = await matchedRoute.inputFilter(filterInput);
216
+ context.session.messages = filterResult.messages;
217
+ const internalState = {};
218
+ const internalKeys = [
219
+ 'runtimeEventLog',
220
+ '__ariaSessionTurn',
221
+ '__ariaRedactCarry',
222
+ 'flowStateByAgent',
223
+ '__ariaAssistantText',
224
+ '__ariaContextBudget',
225
+ ];
226
+ for (const key of internalKeys) {
227
+ if (key in context.session.workingMemory) {
228
+ internalState[key] = context.session.workingMemory[key];
229
+ }
230
+ }
231
+ context.session.workingMemory = { ...filterResult.workingMemory, ...internalState };
232
+ }
233
+ catch (err) {
234
+ console.warn('[AriaFlow] Handoff input filter failed, using full context:', err);
235
+ }
236
+ }
237
+ services.agentStateController.setActiveAgent(context.session, handoffTo);
238
+ await services.eventLog.checkpoint(context.session);
239
+ if (services.sessionCache) {
240
+ services.sessionCache.put(context.session);
241
+ }
242
+ context.agentId = handoffTo;
243
+ }
244
+ if (context.handoffStack.length >= services.maxHandoffs) {
245
+ yield* services.streamEmitter.emit(context, { type: 'error', error: `Maximum handoffs (${services.maxHandoffs}) exceeded` });
246
+ }
247
+ }
248
+ // --- Flow agent step ---
249
+ async function* runFlowAgentStep(agent, context, currentInput, system, handoffTool, abortSignal, services, turnToolErrors, interruptionEmitted, onHandoff, setInterruptionEmitted) {
250
+ const stopResult = checkStopConditions(context, services.stopConditions);
251
+ if (stopResult.shouldStop) {
252
+ yield* services.streamEmitter.emit(context, { type: 'error', error: `Stopped: ${stopResult.reason}` });
253
+ return;
254
+ }
255
+ const agentMaxSteps = agent.maxSteps ?? agent.maxTurns ?? services.maxSteps;
256
+ if (agentMaxSteps <= 0) {
257
+ yield* services.streamEmitter.emit(context, { type: 'error', error: `Max steps exceeded for agent "${agent.id}"` });
258
+ return;
259
+ }
260
+ context.stepCount += 1;
261
+ yield* services.streamEmitter.emit(context, { type: 'step-start', step: context.stepCount, agentId: agent.id });
262
+ await services.hookRunner.onStepStart(context, context.stepCount);
263
+ const toolCalls = [];
264
+ let handoffTo = null;
265
+ let handoffReason = 'No reason provided';
266
+ try {
267
+ const helpers = buildFlowExecutorHelpers(services);
268
+ for await (const part of services.flowExecutor.runFlowAgent(agent, context, currentInput, system, handoffTool ?? undefined, (target, reason) => {
269
+ handoffTo = target;
270
+ handoffReason = reason ?? 'No reason provided';
271
+ onHandoff(target, reason);
272
+ }, toolCalls, helpers, abortSignal)) {
273
+ yield part;
274
+ }
275
+ context.consecutiveErrors = 0;
276
+ if (context.session.metadata) {
277
+ context.session.metadata.totalSteps += 1;
278
+ }
279
+ yield* services.streamEmitter.emit(context, { type: 'step-end', step: context.stepCount, agentId: agent.id });
280
+ await services.hookRunner.onStepEnd(context, context.stepCount, {
281
+ toolCalls,
282
+ finishReason: 'flow',
283
+ tokensUsed: 0,
284
+ handoffTo: handoffTo ?? undefined,
285
+ });
286
+ }
287
+ catch (error) {
288
+ if (abortSignal?.aborted) {
289
+ if (!interruptionEmitted) {
290
+ setInterruptionEmitted(true);
291
+ yield* services.streamEmitter.emit(context, {
292
+ type: 'interrupted',
293
+ sessionId: context.session.id,
294
+ reason: abortSignal.reason ?? 'Operation cancelled',
295
+ timestamp: new Date(),
296
+ lastAgentId: context.agentId,
297
+ lastStep: context.stepCount,
298
+ });
299
+ }
300
+ return;
301
+ }
302
+ context.consecutiveErrors += 1;
303
+ await services.hookRunner.onError(context, error);
304
+ yield* services.streamEmitter.emit(context, { type: 'error', error: error.message });
305
+ if (context.consecutiveErrors >= 3) {
306
+ return;
307
+ }
308
+ }
309
+ }
310
+ // --- LLM agent step loop ---
311
+ async function* runLLMAgentSteps(agent, context, currentInput, system, host, agentOwnTools, abortSignal, services, turnToolErrors, interruptionEmitted, activeRoutes, onHandoff, setInterruptionEmitted) {
312
+ // Merge capability-generated tools with agent's own tools (kept in original AI SDK format).
313
+ // Agent tools take precedence on name collision to preserve explicit tool definitions.
314
+ const capabilityAITools = toAISDKTools(host.getAllTools());
315
+ const mergedTools = { ...capabilityAITools, ...agentOwnTools };
316
+ const tools = wrapToolsWithEnforcement(context, mergedTools, services);
317
+ let agentSteps = 0;
318
+ const agentMaxSteps = agent.maxSteps ?? agent.maxTurns ?? services.maxSteps;
319
+ let handoffTo = null;
320
+ let handoffReason = 'No reason provided';
321
+ while (agentSteps < agentMaxSteps) {
322
+ const stopResult = checkStopConditions(context, services.stopConditions);
323
+ if (stopResult.shouldStop) {
324
+ yield* services.streamEmitter.emit(context, { type: 'error', error: `Stopped: ${stopResult.reason}` });
325
+ return 'return';
326
+ }
327
+ agentSteps += 1;
328
+ context.stepCount += 1;
329
+ yield* services.streamEmitter.emit(context, { type: 'step-start', step: context.stepCount, agentId: agent.id });
330
+ await services.hookRunner.onStepStart(context, context.stepCount);
331
+ try {
332
+ const model = agent.model ?? services.defaultModel;
333
+ if (!model) {
334
+ throw new Error(`Agent "${agent.id}" is missing a model`);
335
+ }
336
+ // Structured triage
337
+ if (isTriageAgent(agent) && agent.triageMode === 'structured') {
338
+ const triageResult = yield* runStructuredTriage(agent, context, system, abortSignal, services, activeRoutes);
339
+ handoffTo = triageResult.handoffTo;
340
+ handoffReason = triageResult.handoffReason;
341
+ onHandoff(handoffTo, handoffReason);
342
+ yield* services.streamEmitter.emit(context, { type: 'step-end', step: context.stepCount, agentId: agent.id });
343
+ await services.hookRunner.onStepEnd(context, context.stepCount, {
344
+ toolCalls: [],
345
+ finishReason: 'handoff',
346
+ tokensUsed: 0,
347
+ handoffTo: handoffTo ?? undefined,
348
+ });
349
+ break;
350
+ }
351
+ // onBeforeModelCall hook
352
+ if (services.config.hooks?.onBeforeModelCall) {
353
+ system = await runBeforeModelCallHook(agent, context, system, services);
354
+ }
355
+ const result = streamText({
356
+ model: model,
357
+ system,
358
+ messages: context.session.messages,
359
+ tools,
360
+ abortSignal,
361
+ stopWhen: stepCountIs(agent.toolMaxSteps ?? 5),
362
+ experimental_context: {
363
+ session: context.session,
364
+ agentId: agent.id,
365
+ },
366
+ experimental_telemetry: agent.telemetry ?? services.config.telemetry,
367
+ });
368
+ const toolCalls = [];
369
+ let finalResult = null;
370
+ let finalEmitted = false;
371
+ const outputProcessors = getAgentOutputProcessors(agent, services.outputProcessors);
372
+ const bufferOutput = services.outputProcessorMode === 'buffer' && outputProcessors.length > 0;
373
+ const bufferedParts = [];
374
+ let stoppedByGuard = false;
375
+ // Process stream chunks
376
+ for await (const chunk of result.fullStream) {
377
+ const chunkResult = yield* processStreamChunk(chunk, context, agent, services, toolCalls, turnToolErrors, finalResult, finalEmitted, bufferOutput, bufferedParts, host);
378
+ if (chunkResult.finalResult)
379
+ finalResult = chunkResult.finalResult;
380
+ if (chunkResult.finalEmitted)
381
+ finalEmitted = chunkResult.finalEmitted;
382
+ if (chunkResult.handoffTo) {
383
+ handoffTo = chunkResult.handoffTo;
384
+ handoffReason = chunkResult.handoffReason ?? 'No reason provided';
385
+ onHandoff(handoffTo, handoffReason);
386
+ }
387
+ if (chunkResult.stoppedByGuard) {
388
+ stoppedByGuard = true;
389
+ break;
390
+ }
391
+ }
392
+ if (stoppedByGuard)
393
+ return 'return';
394
+ const response = await result.response;
395
+ const finishReason = finalResult ? 'final' : await result.finishReason;
396
+ // Finalize buffered output
397
+ if (finalResult || (bufferOutput && finishReason !== 'tool-calls') || turnToolErrors.length > 0) {
398
+ let rawText = finalResult ? finalResult.text : bufferedParts.join('');
399
+ if (turnToolErrors.length > 0) {
400
+ const failedTools = turnToolErrors.map(e => `\`${e.toolName}\``).join(', ');
401
+ rawText = `I encountered an error while using the following tools: ${failedTools}. I cannot proceed with the requested action at this time.`;
402
+ yield* services.streamEmitter.emit(context, {
403
+ type: 'error',
404
+ error: `Turn blocked by critical tool failures: ${failedTools}`,
405
+ });
406
+ }
407
+ const getAbortSignal = () => abortSignal;
408
+ const processed = await runOutputProcessing(agent, context, rawText, services.outputProcessors, services.outputRedactions, getAbortSignal);
409
+ if (processed.tripwire) {
410
+ yield* services.streamEmitter.emit(context, {
411
+ type: 'tripwire',
412
+ phase: 'output',
413
+ processorId: processed.tripwire.processorId,
414
+ reason: processed.tripwire.reason,
415
+ message: processed.tripwire.message,
416
+ });
417
+ }
418
+ if (bufferOutput || turnToolErrors.length > 0) {
419
+ yield* services.streamEmitter.emit(context, { type: 'text-delta', text: processed.text });
420
+ }
421
+ appendSessionMessage(context.session, { role: 'assistant', content: processed.text }, services.conversationState);
422
+ }
423
+ else {
424
+ const beforeLen = context.session.messages.length;
425
+ appendSessionMessages(context.session, response.messages, services.conversationState);
426
+ const getAbortSignal = () => abortSignal;
427
+ const tripwires = await postProcessPersistedAssistantMessages(agent, context, beforeLen, services.outputProcessors, services.outputRedactions, getAbortSignal);
428
+ for (const t of tripwires) {
429
+ yield* services.streamEmitter.emit(context, {
430
+ type: 'tripwire',
431
+ phase: 'output',
432
+ processorId: t.processorId,
433
+ reason: t.reason,
434
+ message: t.message,
435
+ });
436
+ }
437
+ }
438
+ const usage = await result.usage;
439
+ const totalTokens = usage.totalTokens ?? 0;
440
+ context.totalTokens += totalTokens;
441
+ if (context.session.metadata) {
442
+ context.session.metadata.totalTokens += totalTokens;
443
+ context.session.metadata.totalSteps += 1;
444
+ }
445
+ context.consecutiveErrors = 0;
446
+ yield* services.streamEmitter.emit(context, { type: 'step-end', step: context.stepCount, agentId: agent.id });
447
+ await services.hookRunner.onStepEnd(context, context.stepCount, {
448
+ toolCalls,
449
+ finishReason,
450
+ tokensUsed: totalTokens,
451
+ handoffTo: handoffTo ?? undefined,
452
+ });
453
+ if (finalResult || finishReason !== 'tool-calls' || handoffTo) {
454
+ break;
455
+ }
456
+ }
457
+ catch (error) {
458
+ console.error('RUNTIME_LOOP_ERROR:', error);
459
+ if (abortSignal?.aborted) {
460
+ if (!interruptionEmitted) {
461
+ setInterruptionEmitted(true);
462
+ yield* services.streamEmitter.emit(context, {
463
+ type: 'interrupted',
464
+ sessionId: context.session.id,
465
+ reason: abortSignal.reason ?? 'Operation cancelled',
466
+ timestamp: new Date(),
467
+ lastAgentId: context.agentId,
468
+ lastStep: context.stepCount,
469
+ });
470
+ }
471
+ return 'return';
472
+ }
473
+ context.consecutiveErrors += 1;
474
+ await services.hookRunner.onError(context, error);
475
+ yield* services.streamEmitter.emit(context, { type: 'error', error: error.message });
476
+ if (context.consecutiveErrors >= 3) {
477
+ return 'return';
478
+ }
479
+ }
480
+ }
481
+ return 'break';
482
+ }
483
+ async function* processStreamChunk(chunk, context, agent, services, toolCalls, turnToolErrors, finalResult, finalEmitted, bufferOutput, bufferedParts, host) {
484
+ const result = {};
485
+ if (chunk.type === 'text-delta') {
486
+ if (finalResult)
487
+ return result;
488
+ if (turnToolErrors.length > 0)
489
+ return result;
490
+ if (bufferOutput) {
491
+ bufferedParts.push(chunk.text);
492
+ }
493
+ else {
494
+ yield* services.streamEmitter.emit(context, { type: 'text-delta', text: chunk.text });
495
+ }
496
+ }
497
+ if (chunk.type === 'tool-call') {
498
+ const args = getChunkArgs(chunk);
499
+ const idempotencyKey = services.toolExecutor.buildIdempotencyKey({
500
+ sessionId: context.session.id,
501
+ agentId: context.agentId,
502
+ step: context.stepCount,
503
+ toolName: chunk.toolName,
504
+ toolCallId: chunk.toolCallId,
505
+ });
506
+ const callRecord = {
507
+ toolCallId: chunk.toolCallId,
508
+ toolName: chunk.toolName,
509
+ args,
510
+ idempotencyKey,
511
+ success: true,
512
+ timestamp: Date.now(),
513
+ };
514
+ toolCalls.push(callRecord);
515
+ await services.hookRunner.onToolCall(context, callRecord);
516
+ const toolConfig = services.agents.get(context.agentId)?.tools;
517
+ const toolDef = toolConfig?.[chunk.toolName];
518
+ const filler = toolDef?.filler ?? `Let me check ${chunk.toolName}...`;
519
+ yield* services.streamEmitter.emit(context, {
520
+ type: 'tool-start',
521
+ toolCallId: chunk.toolCallId,
522
+ toolName: chunk.toolName,
523
+ message: filler,
524
+ });
525
+ yield* services.streamEmitter.emit(context, {
526
+ type: 'tool-call',
527
+ toolCallId: chunk.toolCallId,
528
+ toolName: chunk.toolName,
529
+ args,
530
+ });
531
+ }
532
+ if (chunk.type === 'tool-error') {
533
+ const errText = getChunkErrorMessage(chunk);
534
+ const args = getChunkArgs(chunk);
535
+ const toolCallId = getChunkToolCallId(chunk);
536
+ const callRecord = toolCalls.find(call => call.toolCallId === toolCallId);
537
+ if (callRecord) {
538
+ callRecord.success = false;
539
+ callRecord.error = new Error(errText);
540
+ if (args !== undefined)
541
+ callRecord.args = args;
542
+ context.toolCallHistory.push(callRecord);
543
+ await services.hookRunner.onToolError(context, callRecord, callRecord.error);
544
+ const toolPolicy = agent.toolPolicies?.[callRecord.toolName];
545
+ const toolDef = agent.tools?.[callRecord.toolName];
546
+ const isCritical = toolPolicy?.critical ?? toolDef?.critical ?? true;
547
+ if (isCritical) {
548
+ turnToolErrors.push(callRecord);
549
+ }
550
+ }
551
+ yield* services.streamEmitter.emit(context, {
552
+ type: 'tool-error',
553
+ toolCallId: toolCallId ?? chunk.toolCallId,
554
+ toolName: chunk.toolName,
555
+ error: errText,
556
+ });
557
+ }
558
+ if (chunk.type === 'tool-result') {
559
+ const startTime = toolCalls.find(call => call.toolCallId === chunk.toolCallId)?.timestamp ?? Date.now();
560
+ const toolResult = getChunkResult(chunk);
561
+ const callRecord = toolCalls.find(call => call.toolCallId === chunk.toolCallId);
562
+ if (callRecord) {
563
+ callRecord.result = toolResult;
564
+ callRecord.durationMs = Date.now() - callRecord.timestamp;
565
+ context.toolCallHistory.push(callRecord);
566
+ await services.hookRunner.onToolResult(context, callRecord);
567
+ }
568
+ const durationMs = Date.now() - startTime;
569
+ yield* services.streamEmitter.emit(context, {
570
+ type: 'tool-done',
571
+ toolCallId: chunk.toolCallId,
572
+ toolName: chunk.toolName,
573
+ durationMs,
574
+ });
575
+ if (callRecord) {
576
+ const enforcement = await services.enforcer.checkResult(callRecord, {
577
+ previousCalls: context.toolCallHistory,
578
+ currentStep: context.stepCount,
579
+ sessionState: context.session.state ?? {},
580
+ });
581
+ if (!enforcement.allowed) {
582
+ const reason = enforcement.reason ?? 'Tool result blocked by enforcement';
583
+ callRecord.success = false;
584
+ callRecord.error = new Error(reason);
585
+ await services.hookRunner.onToolError(context, callRecord, callRecord.error);
586
+ turnToolErrors.push(callRecord);
587
+ yield* services.streamEmitter.emit(context, {
588
+ type: 'tool-error',
589
+ toolCallId: chunk.toolCallId,
590
+ toolName: chunk.toolName,
591
+ error: reason,
592
+ });
593
+ result.finalResult = { type: 'final', text: reason };
594
+ if (!finalEmitted) {
595
+ result.finalEmitted = true;
596
+ if (bufferOutput) {
597
+ bufferedParts.push(reason);
598
+ }
599
+ else {
600
+ yield* services.streamEmitter.emit(context, { type: 'text-delta', text: reason });
601
+ }
602
+ }
603
+ return result;
604
+ }
605
+ }
606
+ yield* services.streamEmitter.emit(context, {
607
+ type: 'tool-result',
608
+ toolCallId: chunk.toolCallId,
609
+ toolName: chunk.toolName,
610
+ result: toolResult,
611
+ });
612
+ const stopCheck = checkStopConditions(context, services.stopConditions);
613
+ if (stopCheck.shouldStop) {
614
+ yield* services.streamEmitter.emit(context, { type: 'error', error: `Stopped: ${stopCheck.reason}` });
615
+ result.stoppedByGuard = true;
616
+ return result;
617
+ }
618
+ // Route through CapabilityHost — capability claims first, legacy fallback below
619
+ const capArgs = getChunkArgs(chunk);
620
+ const capAction = host.processToolResult(chunk.toolName, capArgs, toolResult);
621
+ if (capAction.type === 'handoff') {
622
+ result.handoffTo = capAction.targetAgent;
623
+ result.handoffReason = capAction.reason ?? 'No reason provided';
624
+ return result;
625
+ }
626
+ if (capAction.type === 'end') {
627
+ const endText = capAction.reason ?? 'Conversation ended';
628
+ result.finalResult = { type: 'final', text: endText };
629
+ if (!finalEmitted) {
630
+ result.finalEmitted = true;
631
+ if (bufferOutput) {
632
+ bufferedParts.push(endText);
633
+ }
634
+ else {
635
+ yield* services.streamEmitter.emit(context, { type: 'text-delta', text: endText });
636
+ }
637
+ }
638
+ return result;
639
+ }
640
+ // capAction.type === 'continue' | 'extraction-complete' | 'reconfigure'
641
+ // Fall through to legacy checks for tools not claimed by any capability
642
+ if (isFinalResult(toolResult)) {
643
+ result.finalResult = toolResult;
644
+ if (!finalEmitted) {
645
+ result.finalEmitted = true;
646
+ if (bufferOutput) {
647
+ bufferedParts.push(toolResult.text);
648
+ }
649
+ else {
650
+ yield* services.streamEmitter.emit(context, { type: 'text-delta', text: toolResult.text });
651
+ }
652
+ }
653
+ return result;
654
+ }
655
+ if (isHandoffResult(toolResult)) {
656
+ const targetAgent = toolResult.targetAgent ?? toolResult.targetAgentId;
657
+ result.handoffTo = targetAgent;
658
+ result.handoffReason = toolResult.reason ?? 'No reason provided';
659
+ }
660
+ }
661
+ return result;
662
+ }
663
+ // --- Structured triage ---
664
+ async function* runStructuredTriage(agent, context, system, abortSignal, services, activeRoutes) {
665
+ const allowed = new Set((activeRoutes ?? agent.routes).map(route => route.agentId));
666
+ // Single-candidate short-circuit
667
+ if (allowed.size === 1) {
668
+ const [onlyAgent] = allowed;
669
+ return { handoffTo: onlyAgent, handoffReason: 'Single active route — direct handoff' };
670
+ }
671
+ const model = agent.model ?? services.defaultModel;
672
+ const schema = z.object({
673
+ agentId: z.string().describe('Target agent ID for this request.'),
674
+ reason: z.string().describe('Short, concrete reason grounded in the user request.'),
675
+ confidence: z.number().min(0).max(1).describe('Routing confidence from 0 to 1.'),
676
+ stayWithCurrent: z.boolean().describe('True only if the current agent is best fit.'),
677
+ });
678
+ const result = streamObject({
679
+ model: model,
680
+ schema,
681
+ system: buildStructuredTriagePrompt(agent, activeRoutes),
682
+ messages: context.session.messages,
683
+ abortSignal,
684
+ experimental_telemetry: agent.telemetry ?? services.config.telemetry,
685
+ });
686
+ let handoffTo;
687
+ let handoffReason = 'Routed by triage';
688
+ let resolved = false;
689
+ for await (const partial of result.partialObjectStream) {
690
+ if (partial.agentId && allowed.has(partial.agentId)) {
691
+ handoffTo = partial.agentId;
692
+ handoffReason = partial.reason ?? 'Routed by triage';
693
+ resolved = true;
694
+ break;
695
+ }
696
+ }
697
+ if (!resolved) {
698
+ const fullObject = await result.object;
699
+ let target = fullObject.agentId;
700
+ if (!allowed.has(target)) {
701
+ const fallback = agent.defaultAgent ?? agent.routes[0]?.agentId;
702
+ if (fallback && fallback !== agent.id) {
703
+ target = fallback;
704
+ }
705
+ else {
706
+ // No valid target -- respond directly from triage
707
+ handoffTo = agent.id;
708
+ handoffReason = 'No valid routing target. Responding directly.';
709
+ return { handoffTo: handoffTo, handoffReason };
710
+ }
711
+ }
712
+ handoffTo = target;
713
+ handoffReason = fullObject.reason ?? 'Routed by triage';
714
+ }
715
+ return { handoffTo: handoffTo, handoffReason };
716
+ }
717
+ // --- Tool enforcement wrapper ---
718
+ function wrapToolsWithEnforcement(context, tools, services) {
719
+ const wrapped = {};
720
+ for (const [toolName, toolDef] of Object.entries(tools ?? {})) {
721
+ if (!('execute' in toolDef) || typeof toolDef.execute !== 'function') {
722
+ wrapped[toolName] = toolDef;
723
+ continue;
724
+ }
725
+ const exec = toolDef.execute;
726
+ wrapped[toolName] = {
727
+ ...toolDef,
728
+ execute: async (args, options) => {
729
+ const toolCallId = typeof options?.toolCallId === 'string'
730
+ ? options.toolCallId
731
+ : crypto.randomUUID();
732
+ const idempotencyKey = services.toolExecutor.buildIdempotencyKey({
733
+ sessionId: context.session.id,
734
+ agentId: context.agentId,
735
+ step: context.stepCount,
736
+ toolName,
737
+ toolCallId,
738
+ });
739
+ const callRecord = {
740
+ toolCallId,
741
+ toolName,
742
+ args,
743
+ idempotencyKey,
744
+ success: true,
745
+ timestamp: Date.now(),
746
+ };
747
+ const enforcement = await services.enforcer.check(callRecord, {
748
+ previousCalls: context.toolCallHistory,
749
+ currentStep: context.stepCount,
750
+ sessionState: context.session.state ?? {},
751
+ });
752
+ if (!enforcement.allowed) {
753
+ const reason = enforcement.reason ?? 'Tool call blocked by enforcement';
754
+ callRecord.success = false;
755
+ callRecord.error = new Error(reason);
756
+ context.toolCallHistory.push(callRecord);
757
+ await services.hookRunner.onToolError(context, callRecord, callRecord.error);
758
+ throw callRecord.error;
759
+ }
760
+ const enrichedOptions = withToolExecutionMetadata(options, context, toolName, toolCallId, idempotencyKey, services);
761
+ try {
762
+ return await exec(args, enrichedOptions);
763
+ }
764
+ catch (error) {
765
+ console.error(`[Runtime] Tool execution failed for ${toolName}:`, error);
766
+ throw error;
767
+ }
768
+ },
769
+ };
770
+ }
771
+ return wrapped;
772
+ }
773
+ function withToolExecutionMetadata(options, context, toolName, toolCallId, idempotencyKey, services) {
774
+ const baseOptions = options ?? {};
775
+ const existingContext = isRecord(baseOptions.experimental_context)
776
+ ? baseOptions.experimental_context
777
+ : {};
778
+ return {
779
+ ...baseOptions,
780
+ toolCallId,
781
+ experimental_context: {
782
+ ...existingContext,
783
+ session: context.session,
784
+ sessionId: context.session.id,
785
+ agentId: context.agentId,
786
+ step: context.stepCount,
787
+ turn: services.conversationState.getSessionTurn(context.session),
788
+ toolName,
789
+ toolCallId,
790
+ idempotencyKey,
791
+ memoryService: services.memoryService,
792
+ },
793
+ };
794
+ }
795
+ // --- Circular handoff fallback ---
796
+ function getCircularHandoffFallbackMessage(services) {
797
+ const configured = services.config.circularHandoffFallbackMessage?.trim();
798
+ if (configured)
799
+ return configured;
800
+ return 'I hit a routing issue between agents. I will continue here. Tell me the exact outcome you need in one sentence.';
801
+ }
802
+ async function* emitCircularHandoffFallback(context, services) {
803
+ const fallback = getCircularHandoffFallbackMessage(services);
804
+ appendSessionMessage(context.session, { role: 'assistant', content: fallback }, services.conversationState);
805
+ yield* services.streamEmitter.emit(context, { type: 'text-delta', text: fallback });
806
+ }
807
+ // --- onBeforeModelCall hook ---
808
+ async function runBeforeModelCallHook(agent, context, system, services) {
809
+ try {
810
+ const messageTokens = context.session.messages.reduce((sum, m) => {
811
+ const txt = typeof m.content === 'string'
812
+ ? m.content
813
+ : Array.isArray(m.content)
814
+ ? m.content
815
+ .filter((p) => p.type === 'text')
816
+ .map((p) => p.text)
817
+ .join('')
818
+ : '';
819
+ return sum + estimateTokenCount(txt);
820
+ }, 0);
821
+ const systemTokens = estimateTokenCount(system);
822
+ const budgetData = (context.session.workingMemory['__ariaContextBudget'] ?? {});
823
+ const allocations = (budgetData.allocations ?? {});
824
+ const hookData = {
825
+ systemPrompt: system,
826
+ messages: context.session.messages,
827
+ estimatedTokens: systemTokens + messageTokens,
828
+ agentId: agent.id,
829
+ tokenBreakdown: {
830
+ basePrompt: allocations.basePrompt ?? 0,
831
+ autoRetrieve: allocations.autoRetrieve ?? 0,
832
+ workingMemory: allocations.workingMemory ?? 0,
833
+ extraction: 0,
834
+ longTermMemory: allocations.longTermMemory ?? 0,
835
+ policyInjections: allocations.policyInjections ?? 0,
836
+ messageHistory: messageTokens,
837
+ },
838
+ };
839
+ const hookResult = await services.config.hooks.onBeforeModelCall(context, hookData);
840
+ if (hookResult?.systemPrompt !== undefined) {
841
+ system = hookResult.systemPrompt;
842
+ }
843
+ if (hookResult?.messages !== undefined) {
844
+ context.session.messages = hookResult.messages;
845
+ }
846
+ }
847
+ catch (err) {
848
+ console.warn('[AriaFlow] onBeforeModelCall hook failed, using originals:', err);
849
+ }
850
+ return system;
851
+ }
852
+ // --- FlowExecutorHelpers builder ---
853
+ function matchesDetourRule(input, patterns) {
854
+ if (!patterns || patterns.length === 0)
855
+ return false;
856
+ for (const pattern of patterns) {
857
+ try {
858
+ const regex = new RegExp(pattern, 'i');
859
+ if (regex.test(input))
860
+ return true;
861
+ }
862
+ catch {
863
+ if (input.includes(pattern.toLowerCase()))
864
+ return true;
865
+ }
866
+ }
867
+ return false;
868
+ }
869
+ function buildFlowExecutorHelpers(services) {
870
+ return {
871
+ emit: (ctx, part) => services.streamEmitter.emit(ctx, part),
872
+ runOutputProcessing: (agent, ctx, text) => runOutputProcessing(agent, ctx, text, services.outputProcessors, services.outputRedactions),
873
+ appendSessionMessage: (s, m) => appendSessionMessage(s, m, services.conversationState),
874
+ appendSessionMessages: (s, ms) => appendSessionMessages(s, ms, services.conversationState),
875
+ postProcessPersistedAssistantMessages: (agent, ctx, start) => postProcessPersistedAssistantMessages(agent, ctx, start, services.outputProcessors, services.outputRedactions),
876
+ getAgentOutputProcessors: (agent) => getAgentOutputProcessors(agent, services.outputProcessors),
877
+ getSessionTurn: (s) => services.conversationState.getSessionTurn(s),
878
+ buildToolIdempotencyKey: (ctx, tn, tcid) => services.toolExecutor.buildIdempotencyKey({
879
+ sessionId: ctx.session.id,
880
+ agentId: ctx.agentId,
881
+ step: ctx.stepCount,
882
+ toolName: tn,
883
+ toolCallId: tcid,
884
+ }),
885
+ setFlowState: (s, id, st) => setFlowState(s, id, st, services.conversationState),
886
+ getFlowState: (s, id) => getFlowState(s, id),
887
+ updateFlowStateSnapshot: (s, id, st) => updateFlowStateSnapshot(s, id, st),
888
+ clearFlowState: (s, id) => clearFlowState(s, id, services.conversationState),
889
+ buildFlowWithHandoff: (a, ht, suppress) => buildFlowWithHandoff(a, ht, suppress),
890
+ getFlowNode: (a, nid) => a.flow.nodes.find(node => node.id === nid),
891
+ touchSession: (s) => services.conversationState.touchSession(s),
892
+ matchesDetourRule: (i, p) => matchesDetourRule(i, p),
893
+ enforcecheck: (call, ctx) => services.enforcer.check(call, {
894
+ previousCalls: ctx.toolCallHistory,
895
+ currentStep: ctx.stepCount,
896
+ sessionState: ctx.session.state ?? {},
897
+ }),
898
+ enforcecheckResult: (call, ctx) => services.enforcer.checkResult(call, {
899
+ previousCalls: ctx.toolCallHistory,
900
+ currentStep: ctx.stepCount,
901
+ sessionState: ctx.session.state ?? {},
902
+ }),
903
+ onToolCallHook: (ctx, call) => services.hookRunner.onToolCall(ctx, call),
904
+ onToolResultHook: (ctx, call) => services.hookRunner.onToolResult(ctx, call),
905
+ onToolErrorHook: (ctx, call, error) => services.hookRunner.onToolError(ctx, call, error),
906
+ emitMetric: services.hookRunner.has('onStreamPart')
907
+ ? (name, data) => {
908
+ // Bridge flow-level metrics into hooks as custom stream events.
909
+ // Picked up by createMetricsHooks' onStreamPart handler.
910
+ // Fire-and-forget: metrics must not block the flow.
911
+ services.hookRunner.run('onStreamPart', {}, {
912
+ type: 'custom',
913
+ name,
914
+ data,
915
+ timestamp: new Date(),
916
+ }).catch(() => { });
917
+ }
918
+ : undefined,
919
+ };
920
+ }
921
+ // --- buildFlowWithHandoff (moved from Runtime) ---
922
+ function buildFlowWithHandoff(agent, handoffTool, suppressAutoRespond) {
923
+ const needsInitialSuppression = suppressAutoRespond &&
924
+ agent.flow.nodes.some(n => n.id === agent.initialNode && n.autoRespond === undefined);
925
+ if (!handoffTool && !needsInitialSuppression)
926
+ return agent.flow;
927
+ return {
928
+ ...agent.flow,
929
+ nodes: agent.flow.nodes.map(node => {
930
+ const shouldSuppress = suppressAutoRespond
931
+ && node.id === agent.initialNode
932
+ && node.autoRespond === undefined;
933
+ const existingTools = node.tools;
934
+ if (typeof existingTools === 'function') {
935
+ return {
936
+ ...node,
937
+ tools: (ctx) => {
938
+ const resolved = existingTools(ctx) ?? {};
939
+ if (!handoffTool)
940
+ return resolved;
941
+ return resolved.handoff ? resolved : { ...resolved, handoff: handoffTool };
942
+ },
943
+ ...(shouldSuppress ? { autoRespond: false } : {}),
944
+ };
945
+ }
946
+ const toolSet = existingTools ?? {};
947
+ if (!handoffTool || toolSet.handoff) {
948
+ return shouldSuppress ? { ...node, autoRespond: false } : node;
949
+ }
950
+ return {
951
+ ...node,
952
+ tools: { ...toolSet, handoff: handoffTool },
953
+ ...(shouldSuppress ? { autoRespond: false } : {}),
954
+ };
955
+ }),
956
+ };
957
+ }
958
+ //# sourceMappingURL=AgentExecuteStage.js.map