@strands-agents/sdk 1.13.0 → 1.15.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 (260) hide show
  1. package/dist/src/agent/agent-as-tool.d.ts.map +1 -1
  2. package/dist/src/agent/agent-as-tool.js +8 -2
  3. package/dist/src/agent/agent-as-tool.js.map +1 -1
  4. package/dist/src/agent/agent-delegation.d.ts +9 -30
  5. package/dist/src/agent/agent-delegation.d.ts.map +1 -1
  6. package/dist/src/agent/agent-delegation.js +26 -98
  7. package/dist/src/agent/agent-delegation.js.map +1 -1
  8. package/dist/src/agent/agent.d.ts +49 -15
  9. package/dist/src/agent/agent.d.ts.map +1 -1
  10. package/dist/src/agent/agent.js +193 -60
  11. package/dist/src/agent/agent.js.map +1 -1
  12. package/dist/src/agent/continuation.d.ts +77 -0
  13. package/dist/src/agent/continuation.d.ts.map +1 -0
  14. package/dist/src/agent/continuation.js +167 -0
  15. package/dist/src/agent/continuation.js.map +1 -0
  16. package/dist/src/agent/tool-caller.d.ts.map +1 -1
  17. package/dist/src/agent/tool-caller.js +1 -0
  18. package/dist/src/agent/tool-caller.js.map +1 -1
  19. package/dist/src/background-tasks/errors.d.ts +8 -0
  20. package/dist/src/background-tasks/errors.d.ts.map +1 -0
  21. package/dist/src/background-tasks/errors.js +11 -0
  22. package/dist/src/background-tasks/errors.js.map +1 -0
  23. package/dist/src/background-tasks/in-process/engine.d.ts +96 -0
  24. package/dist/src/background-tasks/in-process/engine.d.ts.map +1 -0
  25. package/dist/src/background-tasks/in-process/engine.js +322 -0
  26. package/dist/src/background-tasks/in-process/engine.js.map +1 -0
  27. package/dist/src/background-tasks/in-process/types.d.ts +76 -0
  28. package/dist/src/background-tasks/in-process/types.d.ts.map +1 -0
  29. package/dist/src/background-tasks/in-process/types.js +2 -0
  30. package/dist/src/background-tasks/in-process/types.js.map +1 -0
  31. package/dist/src/context-manager/context-manager.d.ts +30 -0
  32. package/dist/src/context-manager/context-manager.d.ts.map +1 -0
  33. package/dist/src/context-manager/context-manager.js +89 -0
  34. package/dist/src/context-manager/context-manager.js.map +1 -0
  35. package/dist/src/context-manager/methods/summarize.d.ts +41 -0
  36. package/dist/src/context-manager/methods/summarize.d.ts.map +1 -0
  37. package/dist/src/context-manager/methods/summarize.js +108 -0
  38. package/dist/src/context-manager/methods/summarize.js.map +1 -0
  39. package/dist/src/context-manager/methods/truncate.d.ts +36 -0
  40. package/dist/src/context-manager/methods/truncate.d.ts.map +1 -0
  41. package/dist/src/context-manager/methods/truncate.js +86 -0
  42. package/dist/src/context-manager/methods/truncate.js.map +1 -0
  43. package/dist/src/context-manager/modes/agentic/agentic-context.d.ts +1 -2
  44. package/dist/src/context-manager/modes/agentic/agentic-context.d.ts.map +1 -1
  45. package/dist/src/context-manager/modes/agentic/agentic-context.js +6 -3
  46. package/dist/src/context-manager/modes/agentic/agentic-context.js.map +1 -1
  47. package/dist/src/context-manager/strategies/offload/base.d.ts +140 -0
  48. package/dist/src/context-manager/strategies/offload/base.d.ts.map +1 -0
  49. package/dist/src/context-manager/strategies/offload/base.js +376 -0
  50. package/dist/src/context-manager/strategies/offload/base.js.map +1 -0
  51. package/dist/src/context-manager/strategies/offload/drop.d.ts +19 -0
  52. package/dist/src/context-manager/strategies/offload/drop.d.ts.map +1 -0
  53. package/dist/src/context-manager/strategies/offload/drop.js +31 -0
  54. package/dist/src/context-manager/strategies/offload/drop.js.map +1 -0
  55. package/dist/src/context-manager/strategies/offload/index.d.ts +43 -0
  56. package/dist/src/context-manager/strategies/offload/index.d.ts.map +1 -0
  57. package/dist/src/context-manager/strategies/offload/index.js +37 -0
  58. package/dist/src/context-manager/strategies/offload/index.js.map +1 -0
  59. package/dist/src/context-manager/strategies/offload/summarize.d.ts +22 -0
  60. package/dist/src/context-manager/strategies/offload/summarize.d.ts.map +1 -0
  61. package/dist/src/context-manager/strategies/offload/summarize.js +104 -0
  62. package/dist/src/context-manager/strategies/offload/summarize.js.map +1 -0
  63. package/dist/src/context-manager/strategies/offload/truncate.d.ts +21 -0
  64. package/dist/src/context-manager/strategies/offload/truncate.d.ts.map +1 -0
  65. package/dist/src/context-manager/strategies/offload/truncate.js +66 -0
  66. package/dist/src/context-manager/strategies/offload/truncate.js.map +1 -0
  67. package/dist/src/context-manager/types.d.ts +50 -0
  68. package/dist/src/context-manager/types.d.ts.map +1 -0
  69. package/dist/src/context-manager/types.js +5 -0
  70. package/dist/src/context-manager/types.js.map +1 -0
  71. package/dist/src/hooks/events.d.ts +6 -5
  72. package/dist/src/hooks/events.d.ts.map +1 -1
  73. package/dist/src/hooks/events.js +5 -4
  74. package/dist/src/hooks/events.js.map +1 -1
  75. package/dist/src/hooks/types.d.ts +1 -0
  76. package/dist/src/hooks/types.d.ts.map +1 -1
  77. package/dist/src/hooks/types.js +1 -0
  78. package/dist/src/hooks/types.js.map +1 -1
  79. package/dist/src/index.d.ts +4 -2
  80. package/dist/src/index.d.ts.map +1 -1
  81. package/dist/src/index.js +3 -1
  82. package/dist/src/index.js.map +1 -1
  83. package/dist/src/injection/message-injection.d.ts +12 -9
  84. package/dist/src/injection/message-injection.d.ts.map +1 -1
  85. package/dist/src/injection/message-injection.js +17 -16
  86. package/dist/src/injection/message-injection.js.map +1 -1
  87. package/dist/src/mcp/client.d.ts.map +1 -1
  88. package/dist/src/mcp/client.js +9 -0
  89. package/dist/src/mcp/client.js.map +1 -1
  90. package/dist/src/middleware/stages.d.ts +15 -2
  91. package/dist/src/middleware/stages.d.ts.map +1 -1
  92. package/dist/src/middleware/stages.js.map +1 -1
  93. package/dist/src/mime.d.ts +1 -0
  94. package/dist/src/mime.d.ts.map +1 -1
  95. package/dist/src/mime.js.map +1 -1
  96. package/dist/src/models/anthropic.d.ts +26 -1
  97. package/dist/src/models/anthropic.d.ts.map +1 -1
  98. package/dist/src/models/anthropic.js +178 -54
  99. package/dist/src/models/anthropic.js.map +1 -1
  100. package/dist/src/models/bedrock.d.ts +36 -17
  101. package/dist/src/models/bedrock.d.ts.map +1 -1
  102. package/dist/src/models/bedrock.js +166 -34
  103. package/dist/src/models/bedrock.js.map +1 -1
  104. package/dist/src/models/google/adapters.d.ts.map +1 -1
  105. package/dist/src/models/google/adapters.js +18 -4
  106. package/dist/src/models/google/adapters.js.map +1 -1
  107. package/dist/src/models/google/model.d.ts.map +1 -1
  108. package/dist/src/models/google/model.js +9 -7
  109. package/dist/src/models/google/model.js.map +1 -1
  110. package/dist/src/models/google/types.d.ts +2 -0
  111. package/dist/src/models/google/types.d.ts.map +1 -1
  112. package/dist/src/models/model.d.ts +72 -5
  113. package/dist/src/models/model.d.ts.map +1 -1
  114. package/dist/src/models/model.js +15 -0
  115. package/dist/src/models/model.js.map +1 -1
  116. package/dist/src/models/openai/cache.d.ts +31 -0
  117. package/dist/src/models/openai/cache.d.ts.map +1 -0
  118. package/dist/src/models/openai/cache.js +56 -0
  119. package/dist/src/models/openai/cache.js.map +1 -0
  120. package/dist/src/models/openai/chat-adapter.d.ts.map +1 -1
  121. package/dist/src/models/openai/chat-adapter.js +2 -0
  122. package/dist/src/models/openai/chat-adapter.js.map +1 -1
  123. package/dist/src/models/openai/model.d.ts.map +1 -1
  124. package/dist/src/models/openai/model.js +14 -3
  125. package/dist/src/models/openai/model.js.map +1 -1
  126. package/dist/src/models/openai/responses-adapter.d.ts.map +1 -1
  127. package/dist/src/models/openai/responses-adapter.js +3 -1
  128. package/dist/src/models/openai/responses-adapter.js.map +1 -1
  129. package/dist/src/models/openai/types.d.ts +5 -1
  130. package/dist/src/models/openai/types.d.ts.map +1 -1
  131. package/dist/src/models/routing/fallback-strategy.d.ts +13 -0
  132. package/dist/src/models/routing/fallback-strategy.d.ts.map +1 -0
  133. package/dist/src/models/routing/fallback-strategy.js +33 -0
  134. package/dist/src/models/routing/fallback-strategy.js.map +1 -0
  135. package/dist/src/models/routing/index.d.ts +11 -0
  136. package/dist/src/models/routing/index.d.ts.map +1 -0
  137. package/dist/src/models/routing/index.js +9 -0
  138. package/dist/src/models/routing/index.js.map +1 -0
  139. package/dist/src/models/routing/router.d.ts +161 -0
  140. package/dist/src/models/routing/router.d.ts.map +1 -0
  141. package/dist/src/models/routing/router.js +474 -0
  142. package/dist/src/models/routing/router.js.map +1 -0
  143. package/dist/src/models/routing/strategy.d.ts +53 -0
  144. package/dist/src/models/routing/strategy.d.ts.map +1 -0
  145. package/dist/src/models/routing/strategy.js +2 -0
  146. package/dist/src/models/routing/strategy.js.map +1 -0
  147. package/dist/src/models/streaming.d.ts +12 -0
  148. package/dist/src/models/streaming.d.ts.map +1 -1
  149. package/dist/src/models/streaming.js +17 -0
  150. package/dist/src/models/streaming.js.map +1 -1
  151. package/dist/src/multiagent/plugins.d.ts +3 -1
  152. package/dist/src/multiagent/plugins.d.ts.map +1 -1
  153. package/dist/src/multiagent/plugins.js +6 -1
  154. package/dist/src/multiagent/plugins.js.map +1 -1
  155. package/dist/src/plugins/registry.d.ts +3 -1
  156. package/dist/src/plugins/registry.d.ts.map +1 -1
  157. package/dist/src/plugins/registry.js +6 -1
  158. package/dist/src/plugins/registry.js.map +1 -1
  159. package/dist/src/retry/default-model-retry-strategy.d.ts +3 -8
  160. package/dist/src/retry/default-model-retry-strategy.d.ts.map +1 -1
  161. package/dist/src/retry/default-model-retry-strategy.js +3 -8
  162. package/dist/src/retry/default-model-retry-strategy.js.map +1 -1
  163. package/dist/src/retry/model-retry-strategy.d.ts +6 -9
  164. package/dist/src/retry/model-retry-strategy.d.ts.map +1 -1
  165. package/dist/src/retry/model-retry-strategy.js +8 -12
  166. package/dist/src/retry/model-retry-strategy.js.map +1 -1
  167. package/dist/src/session/session-manager.d.ts +4 -0
  168. package/dist/src/session/session-manager.d.ts.map +1 -1
  169. package/dist/src/session/session-manager.js +6 -0
  170. package/dist/src/session/session-manager.js.map +1 -1
  171. package/dist/src/storage/in-memory-storage.d.ts +8 -1
  172. package/dist/src/storage/in-memory-storage.d.ts.map +1 -1
  173. package/dist/src/storage/in-memory-storage.js +10 -0
  174. package/dist/src/storage/in-memory-storage.js.map +1 -1
  175. package/dist/src/storage/local-file-storage.d.ts +8 -1
  176. package/dist/src/storage/local-file-storage.d.ts.map +1 -1
  177. package/dist/src/storage/local-file-storage.js +10 -0
  178. package/dist/src/storage/local-file-storage.js.map +1 -1
  179. package/dist/src/storage/s3-storage.d.ts +8 -1
  180. package/dist/src/storage/s3-storage.d.ts.map +1 -1
  181. package/dist/src/storage/s3-storage.js +10 -0
  182. package/dist/src/storage/s3-storage.js.map +1 -1
  183. package/dist/src/storage/search/index.d.ts +13 -0
  184. package/dist/src/storage/search/index.d.ts.map +1 -0
  185. package/dist/src/storage/search/index.js +11 -0
  186. package/dist/src/storage/search/index.js.map +1 -0
  187. package/dist/src/storage/search/keyword.d.ts +23 -0
  188. package/dist/src/storage/search/keyword.d.ts.map +1 -0
  189. package/dist/src/storage/search/keyword.js +53 -0
  190. package/dist/src/storage/search/keyword.js.map +1 -0
  191. package/dist/src/storage/search/types.d.ts +24 -0
  192. package/dist/src/storage/search/types.d.ts.map +1 -0
  193. package/dist/src/storage/search/types.js +2 -0
  194. package/dist/src/storage/search/types.js.map +1 -0
  195. package/dist/src/storage/storage.d.ts +46 -1
  196. package/dist/src/storage/storage.d.ts.map +1 -1
  197. package/dist/src/storage/storage.js +22 -0
  198. package/dist/src/storage/storage.js.map +1 -1
  199. package/dist/src/telemetry/meter.d.ts +8 -6
  200. package/dist/src/telemetry/meter.d.ts.map +1 -1
  201. package/dist/src/telemetry/meter.js +8 -7
  202. package/dist/src/telemetry/meter.js.map +1 -1
  203. package/dist/src/telemetry/tracer.d.ts.map +1 -1
  204. package/dist/src/telemetry/tracer.js +9 -2
  205. package/dist/src/telemetry/tracer.js.map +1 -1
  206. package/dist/src/tools/executors/executor.d.ts +2 -0
  207. package/dist/src/tools/executors/executor.d.ts.map +1 -1
  208. package/dist/src/tools/executors/executor.js +6 -4
  209. package/dist/src/tools/executors/executor.js.map +1 -1
  210. package/dist/src/tools/executors/sequential.d.ts +1 -1
  211. package/dist/src/tools/executors/sequential.js +2 -2
  212. package/dist/src/tools/executors/sequential.js.map +1 -1
  213. package/dist/src/tools/mcp-tool.d.ts +2 -1
  214. package/dist/src/tools/mcp-tool.d.ts.map +1 -1
  215. package/dist/src/tools/mcp-tool.js +2 -1
  216. package/dist/src/tools/mcp-tool.js.map +1 -1
  217. package/dist/src/tools/tool.d.ts +2 -0
  218. package/dist/src/tools/tool.d.ts.map +1 -1
  219. package/dist/src/tools/tool.js.map +1 -1
  220. package/dist/src/tools/types.d.ts +8 -0
  221. package/dist/src/tools/types.d.ts.map +1 -1
  222. package/dist/src/tsconfig.tsbuildinfo +1 -1
  223. package/dist/src/types/agent.d.ts +14 -4
  224. package/dist/src/types/agent.d.ts.map +1 -1
  225. package/dist/src/types/agent.js +3 -2
  226. package/dist/src/types/agent.js.map +1 -1
  227. package/dist/src/types/media.d.ts +65 -3
  228. package/dist/src/types/media.d.ts.map +1 -1
  229. package/dist/src/types/media.js +68 -1
  230. package/dist/src/types/media.js.map +1 -1
  231. package/dist/src/types/messages.d.ts +6 -4
  232. package/dist/src/types/messages.d.ts.map +1 -1
  233. package/dist/src/types/messages.js +4 -1
  234. package/dist/src/types/messages.js.map +1 -1
  235. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.d.ts +11 -0
  236. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.d.ts.map +1 -1
  237. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.js +53 -3
  238. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.js.map +1 -1
  239. package/dist/src/vended-memory-stores/file-memory-store/index.d.ts +3 -0
  240. package/dist/src/vended-memory-stores/file-memory-store/index.d.ts.map +1 -0
  241. package/dist/src/vended-memory-stores/file-memory-store/index.js +2 -0
  242. package/dist/src/vended-memory-stores/file-memory-store/index.js.map +1 -0
  243. package/dist/src/vended-memory-stores/file-memory-store/store.d.ts +104 -0
  244. package/dist/src/vended-memory-stores/file-memory-store/store.d.ts.map +1 -0
  245. package/dist/src/vended-memory-stores/file-memory-store/store.js +237 -0
  246. package/dist/src/vended-memory-stores/file-memory-store/store.js.map +1 -0
  247. package/dist/src/vended-memory-stores/test-memory-store/store.d.ts.map +1 -1
  248. package/dist/src/vended-memory-stores/test-memory-store/store.js +2 -25
  249. package/dist/src/vended-memory-stores/test-memory-store/store.js.map +1 -1
  250. package/dist/src/vended-plugins/context-offloader/plugin.d.ts.map +1 -1
  251. package/dist/src/vended-plugins/context-offloader/plugin.js +2 -8
  252. package/dist/src/vended-plugins/context-offloader/plugin.js.map +1 -1
  253. package/dist/src/vended-tools/http-request/http-request.d.ts +1 -1
  254. package/dist/src/vended-tools/http-request/http-request.js +2 -2
  255. package/dist/src/vended-tools/http-request/http-request.js.map +1 -1
  256. package/dist/src/vended-tools/sleep/make-sleep.d.ts +4 -6
  257. package/dist/src/vended-tools/sleep/make-sleep.d.ts.map +1 -1
  258. package/dist/src/vended-tools/sleep/make-sleep.js +5 -7
  259. package/dist/src/vended-tools/sleep/make-sleep.js.map +1 -1
  260. package/package.json +16 -4
@@ -7,8 +7,9 @@ import { isValidToolName } from '../tools/tool.js';
7
7
  import { cloneSystemPrompt, systemPromptFromData } from '../types/messages.js';
8
8
  import { normalizeError, ConcurrentInvocationError, StructuredOutputError } from '../errors.js';
9
9
  import { Model } from '../models/model.js';
10
+ import { ModelRouter } from '../models/routing/router.js';
10
11
  import { ModelPlugin } from '../plugins/model-plugin.js';
11
- import { isModelStreamEvent } from '../models/streaming.js';
12
+ import { totalPromptTokens, isModelStreamEvent } from '../models/streaming.js';
12
13
  import { ToolRegistry } from '../registry/tool-registry.js';
13
14
  import { StateStore } from '../state-store.js';
14
15
  import { serializeStateSerializable, loadStateSerializable } from '../types/serializable.js';
@@ -30,6 +31,7 @@ import { ConcurrentToolExecutor } from '../tools/executors/concurrent.js';
30
31
  import { SequentialToolExecutor } from '../tools/executors/sequential.js';
31
32
  import { AgentAsTool } from './agent-as-tool.js';
32
33
  import { ToolCaller } from './tool-caller.js';
34
+ import { continuations } from './continuation.js';
33
35
  import { MemoryManager } from '../memory/memory-manager.js';
34
36
  import { SessionManager } from '../session/session-manager.js';
35
37
  import { Tracer } from '../telemetry/tracer.js';
@@ -44,8 +46,9 @@ import { isInterruptResponseContent } from '../types/interrupt.js';
44
46
  import { takeSnapshot as takeSnapshotInternal, loadSnapshot as loadSnapshotInternal } from './snapshot.js';
45
47
  import { defaultSandbox } from '../sandbox/default.js';
46
48
  import { summarizeContextTool, truncateContextTool, pinContextTool, createTokenUsageMiddleware, } from '../context-manager/modes/agentic/agentic-context.js';
49
+ import { ContextManager } from '../context-manager/context-manager.js';
47
50
  /**
48
- * Supported values for the `contextManager` parameter.
51
+ * Supported string presets for the `contextManager` parameter.
49
52
  */
50
53
  export const CONTEXT_MANAGER_STRATEGIES = ['auto', 'agentic'];
51
54
  /** Benchmark-validated token threshold for offloading tool results. */
@@ -65,8 +68,16 @@ const CONTEXT_MANAGER_COMPRESSION_THRESHOLD = 0.85;
65
68
  * When "auto", uses SummarizingConversationManager with proactive compression.
66
69
  * When "agentic", uses SummarizingConversationManager without proactive compression
67
70
  * (the agent manages its context via tools; the context manager is only a reactive safety net).
71
+ * When a ContextManager instance, uses NullConversationManager — the ContextManager owns
72
+ * overflow recovery via apply().
68
73
  */
69
74
  function resolveConversationManager(contextManager, conversationManager) {
75
+ if (contextManager === false) {
76
+ return conversationManager ?? new NullConversationManager();
77
+ }
78
+ if (contextManager instanceof ContextManager) {
79
+ return new NullConversationManager();
80
+ }
70
81
  if (contextManager === 'agentic') {
71
82
  return (conversationManager ??
72
83
  new SummarizingConversationManager({
@@ -132,6 +143,7 @@ export class Agent {
132
143
  * The model provider used by the agent for inference.
133
144
  */
134
145
  model;
146
+ _modelRouter;
135
147
  /**
136
148
  * The system prompt to pass to the model provider.
137
149
  */
@@ -148,10 +160,15 @@ export class Agent {
148
160
  * Optional description of what the agent does.
149
161
  */
150
162
  description;
163
+ /**
164
+ * The context manager for strategy-driven offloading, if configured.
165
+ */
166
+ contextManager;
151
167
  /**
152
168
  * The session manager for saving and restoring agent sessions, if configured.
153
169
  */
154
170
  sessionManager;
171
+ _sessionId;
155
172
  /**
156
173
  * The memory manager for cross-session memory retrieval and storage, if configured.
157
174
  */
@@ -170,6 +187,20 @@ export class Agent {
170
187
  get sandbox() {
171
188
  return this._sandbox || defaultSandbox.get();
172
189
  }
190
+ /**
191
+ * A stable, unique identifier for the current conversation session.
192
+ *
193
+ * If a SessionManager is attached, delegates to its sessionId.
194
+ * Otherwise, lazily generates and caches a random 8-character hex string.
195
+ */
196
+ get sessionId() {
197
+ if (this.sessionManager)
198
+ return this.sessionManager.sessionId;
199
+ if (!this._sessionId) {
200
+ this._sessionId = globalThis.crypto.randomUUID().slice(0, 8);
201
+ }
202
+ return this._sessionId;
203
+ }
173
204
  _hooksRegistry;
174
205
  _middlewareRegistry;
175
206
  _pluginRegistry;
@@ -207,6 +238,7 @@ export class Agent {
207
238
  this.id = config?.id ?? DEFAULT_AGENT_ID;
208
239
  if (config?.description !== undefined)
209
240
  this.description = config.description;
241
+ this.contextManager = config?.contextManager instanceof ContextManager ? config.contextManager : undefined;
210
242
  this.sessionManager = config?.sessionManager;
211
243
  this.storage = config?.storage;
212
244
  this.memoryManager =
@@ -216,11 +248,19 @@ export class Agent {
216
248
  ? new MemoryManager(config.memoryManager)
217
249
  : undefined;
218
250
  this._sandbox = config?.sandbox;
219
- if (typeof config?.model === 'string') {
220
- this.model = new BedrockModel({ modelId: config.model });
251
+ const configuredModel = config?.model;
252
+ if (typeof configuredModel === 'string') {
253
+ this.model = new BedrockModel({ modelId: configuredModel });
254
+ }
255
+ else if (configuredModel instanceof ModelRouter) {
256
+ this._modelRouter = configuredModel;
257
+ this.model = configuredModel.defaultModel;
221
258
  }
222
259
  else {
223
- this.model = config?.model ?? new BedrockModel();
260
+ this.model = configuredModel ?? new BedrockModel();
261
+ }
262
+ if (config?.plugins?.some((plugin) => plugin instanceof ModelRouter)) {
263
+ throw new Error('ModelRouter must be passed through Agent({ model }), not plugins');
224
264
  }
225
265
  // Validate and assign conversation manager
226
266
  if (this.model.stateful) {
@@ -243,8 +283,9 @@ export class Agent {
243
283
  this._interventionRegistry = new InterventionRegistry(config?.interventions ?? [], this._hooksRegistry);
244
284
  // Initialize middleware registry
245
285
  this._middlewareRegistry = new MiddlewareRegistry();
286
+ this._modelRouter?.attachToAgent(this);
246
287
  if (config?.contextManager === 'agentic') {
247
- this._middlewareRegistry.addInput(InvokeModelStage.Input, createTokenUsageMiddleware(this.model));
288
+ this._middlewareRegistry.addInput(InvokeModelStage.Input, createTokenUsageMiddleware());
248
289
  }
249
290
  // `undefined` (omitted) → install the default; `null`/`[]` → explicit opt-out.
250
291
  const retryStrategies = config?.retryStrategy === null
@@ -268,7 +309,9 @@ export class Agent {
268
309
  // when a delegate tool is added (construction, plugin getTools, MCP, runtime).
269
310
  // The plugin is a no-op when no delegation tools fire.
270
311
  const hasAgentDelegation = (config?.plugins ?? []).some((p) => p.name === 'strands:agent-delegation');
312
+ const contextManagerPlugin = config?.contextManager instanceof ContextManager ? config.contextManager : undefined;
271
313
  this._pluginRegistry = new PluginRegistry([
314
+ ...(this._modelRouter ? [this._modelRouter] : []),
272
315
  this._conversationManager,
273
316
  ...retryStrategies,
274
317
  ...(config?.plugins ?? []),
@@ -284,6 +327,7 @@ export class Agent {
284
327
  ]
285
328
  : []),
286
329
  ...(this.memoryManager ? [this.memoryManager] : []),
330
+ ...(contextManagerPlugin ? [contextManagerPlugin] : []),
287
331
  ...(config?.sessionManager ? [config.sessionManager] : []),
288
332
  new ModelPlugin(this.model),
289
333
  ]);
@@ -533,7 +577,7 @@ export class Agent {
533
577
  /**
534
578
  * The cancellation signal for the current invocation.
535
579
  *
536
- * Tools can pass this to cancellable operations (e.g., `fetch(url, { signal: agent.cancelSignal })`).
580
+ * SDK-managed tool contexts receive this as `context.cancelSignal` for cancellable operations.
537
581
  * Hooks can check `event.agent.cancelSignal.aborted` to detect cancellation.
538
582
  */
539
583
  get cancelSignal() {
@@ -549,7 +593,7 @@ export class Agent {
549
593
  * - At the top of each agent loop cycle
550
594
  *
551
595
  * If a tool is already executing, it will run to completion unless
552
- * the tool checks {@link LocalAgent.cancelSignal | cancelSignal} internally.
596
+ * the tool checks `context.cancelSignal` internally.
553
597
  *
554
598
  * Hook callbacks can check `event.agent.cancelSignal.aborted` to detect
555
599
  * cancellation and adjust their behavior accordingly.
@@ -637,6 +681,7 @@ export class Agent {
637
681
  */
638
682
  async *stream(args, options) {
639
683
  this.acquireLock();
684
+ let continuationEvent;
640
685
  try {
641
686
  await this.initialize();
642
687
  // Thread the resolved invocationState so all layers share the same reference.
@@ -664,6 +709,8 @@ export class Agent {
664
709
  const message = new Message({ role: 'assistant', content: [new TextBlock(cancelText)] });
665
710
  yield this._appendMessage(message, invocationState);
666
711
  const afterEvent = new AfterInvocationEvent({ agent: this, invocationState });
712
+ await continuations.abandon(continuationEvent, new Error('Continuation was not incorporated into agent history'));
713
+ continuationEvent = afterEvent;
667
714
  await this._invokeCallbacks(afterEvent);
668
715
  yield afterEvent;
669
716
  return new AgentResult({
@@ -678,7 +725,7 @@ export class Agent {
678
725
  let caughtError;
679
726
  const afterInvocationEvent = new AfterInvocationEvent({ agent: this, invocationState });
680
727
  try {
681
- result = yield* this._streamWithMiddleware(currentArgs, resolvedOptions, invocationState);
728
+ result = yield* this._streamWithMiddleware(currentArgs, resolvedOptions, invocationState, continuationEvent);
682
729
  }
683
730
  catch (error) {
684
731
  caughtError = error;
@@ -687,6 +734,8 @@ export class Agent {
687
734
  // AfterInvocationEvent always fires — even on error or consumer break. Outside middleware.
688
735
  // Invoke hooks (so .resume can be set) but don't yield in finally (yields in finally
689
736
  // suspend the generator on consumer break instead of completing cleanup).
737
+ await continuations.abandon(continuationEvent, new Error('Continuation was not incorporated into agent history'));
738
+ continuationEvent = afterInvocationEvent;
690
739
  await this._invokeCallbacks(afterInvocationEvent);
691
740
  }
692
741
  // Yield outside finally — in JS, a `yield` inside `finally` suspends the generator
@@ -697,9 +746,15 @@ export class Agent {
697
746
  if (caughtError) {
698
747
  throw caughtError;
699
748
  }
700
- // Resume only on a clean invocation — errors propagate above.
701
- if (afterInvocationEvent.resume !== undefined) {
702
- currentArgs = afterInvocationEvent.resume;
749
+ const stopReason = result.stopReason;
750
+ const allowsContinuation = stopReason === 'endTurn' || stopReason === 'stopSequence';
751
+ if (!allowsContinuation && stopReason !== 'interrupt') {
752
+ await continuations.abandon(afterInvocationEvent, new Error(`Continuation abandoned after ${stopReason}`));
753
+ }
754
+ const hasContinuation = (await continuations.prepare(afterInvocationEvent, (continuationArgs) => this._normalizeInput(continuationArgs), stopReason)) !== undefined;
755
+ continuationEvent = hasContinuation ? afterInvocationEvent : undefined;
756
+ if (hasContinuation || afterInvocationEvent.resume !== undefined) {
757
+ currentArgs = afterInvocationEvent.resume ?? [];
703
758
  continue;
704
759
  }
705
760
  // Only emit AgentResultEvent on the final iteration (not on resumed ones).
@@ -712,6 +767,7 @@ export class Agent {
712
767
  }
713
768
  }
714
769
  finally {
770
+ await continuations.abandon(continuationEvent, new Error('Agent stream closed before continuation input was incorporated into agent history'));
715
771
  this._isInvoking = false;
716
772
  }
717
773
  }
@@ -719,7 +775,7 @@ export class Agent {
719
775
  * Invokes the AgentStreamStage middleware chain.
720
776
  * Hooks fire outside this method (in stream()'s resume loop).
721
777
  */
722
- async *_streamWithMiddleware(args, options, invocationState) {
778
+ async *_streamWithMiddleware(args, options, invocationState, continuationEvent) {
723
779
  // Snapshot so a gate that re-reads its response after next() still resolves even if a tool cycle called deactivate().
724
780
  const interruptsSnapshot = { ...this._interruptState.interrupts };
725
781
  const context = {
@@ -733,7 +789,8 @@ export class Agent {
733
789
  const self = this;
734
790
  try {
735
791
  const { result } = yield* this._middlewareRegistry.invoke(AgentStreamStage, context, async function* (ctx) {
736
- const result = yield* self._streamCore(ctx.args, ctx.options);
792
+ const streamArgs = continuations.combine(continuationEvent, ctx.args, (continuationArgs) => self._normalizeInput(continuationArgs));
793
+ const result = yield* self._streamCore(streamArgs, ctx.options, streamArgs === ctx.args ? undefined : continuationEvent);
737
794
  return { result };
738
795
  });
739
796
  if (this._interruptState.activated &&
@@ -770,8 +827,8 @@ export class Agent {
770
827
  * Single-pass stream through _stream() with event processing.
771
828
  * No resume loop, no lifecycle events — those are handled by stream()'s resume loop.
772
829
  */
773
- async *_streamCore(args, options) {
774
- const streamGenerator = this._stream(args, options);
830
+ async *_streamCore(args, options, continuationEvent) {
831
+ const streamGenerator = this._stream(args, options, continuationEvent);
775
832
  let caughtError;
776
833
  let iterationResult;
777
834
  try {
@@ -933,7 +990,7 @@ export class Agent {
933
990
  * @param options - Optional per-invocation options
934
991
  * @returns Async generator that yields AgentStreamEvent objects and returns AgentResult
935
992
  */
936
- async *_stream(args, options) {
993
+ async *_stream(args, options, continuationEvent) {
937
994
  let currentArgs = args;
938
995
  let result;
939
996
  this._validateLimits(options);
@@ -1016,8 +1073,13 @@ export class Agent {
1016
1073
  // Normalize input and append user messages on first invocation only
1017
1074
  if (currentArgs !== undefined) {
1018
1075
  const messagesToAppend = this._normalizeInput(currentArgs);
1019
- for (const message of messagesToAppend) {
1020
- yield this._appendMessage(message, invocationState);
1076
+ if (continuationEvent) {
1077
+ yield* await this._appendContinuationMessages(messagesToAppend, continuationEvent, invocationState);
1078
+ }
1079
+ else {
1080
+ for (const message of messagesToAppend) {
1081
+ yield this._appendMessage(message, invocationState);
1082
+ }
1021
1083
  }
1022
1084
  currentArgs = undefined;
1023
1085
  }
@@ -1140,13 +1202,16 @@ export class Agent {
1140
1202
  }
1141
1203
  this._meter.endCycle(cycleStartTime);
1142
1204
  this._tracer.endAgentLoopSpan(cycleSpan);
1143
- // Hook requested halt: exit without calling the model again
1205
+ // Hook requested halt with content: exit without calling the model again.
1144
1206
  const { afterToolsEvent } = toolsResult;
1145
- if (afterToolsEvent.endTurn) {
1146
- const endTurnText = typeof afterToolsEvent.endTurn === 'string'
1147
- ? afterToolsEvent.endTurn
1148
- : 'Turn ended early by hook after tool execution';
1149
- const lastMessage = new Message({ role: 'assistant', content: [new TextBlock(endTurnText)] });
1207
+ const endTurnValue = afterToolsEvent.endTurn;
1208
+ if (endTurnValue === true || (endTurnValue !== false && endTurnValue.length > 0)) {
1209
+ const endTurnContent = Array.isArray(endTurnValue)
1210
+ ? [...endTurnValue]
1211
+ : [
1212
+ new TextBlock(typeof endTurnValue === 'string' ? endTurnValue : 'Turn ended early by hook after tool execution'),
1213
+ ];
1214
+ const lastMessage = new Message({ role: 'assistant', content: endTurnContent });
1150
1215
  yield this._appendMessage(lastMessage, invocationState);
1151
1216
  result = new AgentResult({
1152
1217
  stopReason: 'endTurn',
@@ -1203,7 +1268,9 @@ export class Agent {
1203
1268
  role: 'assistant',
1204
1269
  content: [new TextBlock('Cancelled by user')],
1205
1270
  });
1206
- yield this._appendMessage(cancelMessage, invocationState);
1271
+ if (this._hasOpenUserTurn()) {
1272
+ yield this._appendMessage(cancelMessage, invocationState);
1273
+ }
1207
1274
  result = new AgentResult({
1208
1275
  stopReason: 'cancelled',
1209
1276
  lastMessage: cancelMessage,
@@ -1233,14 +1300,16 @@ export class Agent {
1233
1300
  }
1234
1301
  finally {
1235
1302
  // If cancelled but the catch block was bypassed (generator terminated
1236
- // via .return() when the consumer breaks out of for-await), append an
1237
- // assistant message so the agent can be reinvoked with a new user prompt.
1303
+ // via .return() when the consumer breaks out of for-await), close an
1304
+ // existing user turn so the agent can be reinvoked.
1238
1305
  if (!caughtError && !result && this.isCancelled) {
1239
1306
  const cancelMessage = new Message({
1240
1307
  role: 'assistant',
1241
1308
  content: [new TextBlock('Cancelled by user')],
1242
1309
  });
1243
- yield this._appendMessage(cancelMessage, invocationState);
1310
+ if (this._hasOpenUserTurn()) {
1311
+ yield this._appendMessage(cancelMessage, invocationState);
1312
+ }
1244
1313
  }
1245
1314
  this._tracer.endAgentSpan(agentSpan, {
1246
1315
  ...(caughtError && { error: caughtError }),
@@ -1412,48 +1481,70 @@ export class Agent {
1412
1481
  if (this.systemPrompt !== undefined) {
1413
1482
  streamOptions.systemPrompt = this.systemPrompt;
1414
1483
  }
1415
- // Add tool choice if provided
1416
1484
  if (toolChoice) {
1417
1485
  streamOptions.toolChoice = toolChoice;
1418
1486
  }
1419
1487
  let attemptCount = 1;
1420
1488
  while (true) {
1421
- // Estimate input tokens for the upcoming model call (non-fatal if estimation fails)
1489
+ const selectedModel = this._modelForAttempt(invocationState);
1422
1490
  let projectedInputTokens;
1423
1491
  try {
1492
+ // Context management continues to size against the agent's default model.
1424
1493
  projectedInputTokens = await this._estimateInputTokens(streamOptions);
1425
1494
  }
1426
- catch (e) {
1427
- logger.debug(`error=<${e}> | token estimation failed, proceeding without estimate`);
1495
+ catch (error) {
1496
+ logger.debug(`error=<${error}> | token estimation failed, proceeding without estimate`);
1428
1497
  }
1429
1498
  const beforeModelCallEvent = new BeforeModelCallEvent({
1430
1499
  agent: this,
1431
- model: this.model,
1500
+ model: selectedModel,
1432
1501
  invocationState,
1433
1502
  ...(projectedInputTokens !== undefined && { projectedInputTokens }),
1434
1503
  });
1435
- yield beforeModelCallEvent;
1504
+ let modelContinuation;
1505
+ try {
1506
+ yield beforeModelCallEvent;
1507
+ modelContinuation = await continuations.prepare(beforeModelCallEvent, (continuationArgs) => this._normalizeInput(continuationArgs));
1508
+ }
1509
+ finally {
1510
+ if (modelContinuation === undefined) {
1511
+ await continuations.abandon(beforeModelCallEvent, new Error('Agent stream closed before continuation input was incorporated into agent history'));
1512
+ }
1513
+ }
1436
1514
  if (beforeModelCallEvent.cancel) {
1515
+ await continuations.abandon(beforeModelCallEvent, new Error('Continuation abandoned by BeforeModelCallEvent'));
1437
1516
  const cancelText = typeof beforeModelCallEvent.cancel === 'string' ? beforeModelCallEvent.cancel : 'model call denied by hook';
1438
1517
  const message = new Message({ role: 'assistant', content: [new TextBlock(cancelText)] });
1439
1518
  const stopData = { message, stopReason: 'endTurn' };
1440
1519
  const afterModelCallEvent = new AfterModelCallEvent({
1441
1520
  agent: this,
1442
- model: this.model,
1521
+ model: selectedModel,
1443
1522
  attemptCount,
1444
1523
  stopData,
1445
1524
  invocationState,
1446
1525
  });
1447
1526
  yield afterModelCallEvent;
1448
1527
  if (afterModelCallEvent.retry) {
1449
- attemptCount += 1;
1528
+ attemptCount = this._nextAttemptCount(selectedModel, invocationState, attemptCount);
1450
1529
  continue;
1451
1530
  }
1452
1531
  return { message, stopReason: 'endTurn' };
1453
1532
  }
1533
+ yield* await this._appendContinuationMessages(modelContinuation ?? [], beforeModelCallEvent, invocationState);
1534
+ if (modelContinuation !== undefined) {
1535
+ try {
1536
+ projectedInputTokens = await this._estimateInputTokens(streamOptions);
1537
+ }
1538
+ catch (error) {
1539
+ projectedInputTokens = undefined;
1540
+ logger.debug(`error=<${error}> | token estimation failed after continuation input, proceeding without estimate`);
1541
+ }
1542
+ }
1543
+ const invokedModelRef = {};
1454
1544
  try {
1455
- const result = yield* this._invokeModelWithMiddleware(invocationState, toolChoice, projectedInputTokens);
1456
- // Accumulate token usage and model latency metrics
1545
+ const result = yield* this._invokeModelWithMiddleware(invocationState, selectedModel, invokedModelRef, toolChoice, projectedInputTokens);
1546
+ const routedModel = this._modelRouter?.getRoutedModel(this, invocationState) ?? selectedModel;
1547
+ const model = invokedModelRef.model ?? selectedModel;
1457
1548
  this._meter.updateCycle(result.metadata);
1458
1549
  yield new ModelMessageEvent({
1459
1550
  agent: this,
@@ -1461,7 +1552,6 @@ export class Agent {
1461
1552
  stopReason: result.stopReason,
1462
1553
  invocationState,
1463
1554
  });
1464
- // Handle user content redaction if guardrails blocked input
1465
1555
  if (result.redaction?.userMessage) {
1466
1556
  this._redactLastMessage(result.redaction.userMessage);
1467
1557
  }
@@ -1472,45 +1562,50 @@ export class Agent {
1472
1562
  };
1473
1563
  const afterModelCallEvent = new AfterModelCallEvent({
1474
1564
  agent: this,
1475
- model: this.model,
1565
+ model,
1476
1566
  attemptCount,
1477
1567
  stopData,
1478
1568
  invocationState,
1479
1569
  });
1480
1570
  yield afterModelCallEvent;
1481
1571
  if (afterModelCallEvent.retry) {
1482
- attemptCount += 1;
1572
+ attemptCount = this._nextAttemptCount(routedModel, invocationState, attemptCount);
1483
1573
  continue;
1484
1574
  }
1485
1575
  return result;
1486
1576
  }
1487
1577
  catch (error) {
1578
+ const routedModel = this._modelRouter?.getRoutedModel(this, invocationState) ?? selectedModel;
1579
+ // A failure before the terminal ran (e.g. in input middleware) is attributed to the routed model.
1580
+ const failedModel = invokedModelRef.model ?? routedModel;
1488
1581
  const modelError = normalizeError(error);
1489
- // Create error event
1490
1582
  const errorEvent = new AfterModelCallEvent({
1491
1583
  agent: this,
1492
- model: this.model,
1584
+ model: failedModel,
1493
1585
  attemptCount,
1494
1586
  error: modelError,
1495
1587
  invocationState,
1496
1588
  });
1497
- // Yield error event - stream will invoke hooks
1498
1589
  yield errorEvent;
1499
- // Let CancelledError propagate directly no retry
1500
- // (we emit the AfterModelCall because we already emitted Before and we guarentee the pair)
1590
+ // Preserve the Before/After event pair, but never retry cancellation.
1501
1591
  if (error instanceof CancelledError) {
1502
1592
  throw error;
1503
1593
  }
1504
- // After yielding, hooks have been invoked and may have set retry
1505
1594
  if (errorEvent.retry) {
1506
- attemptCount += 1;
1595
+ attemptCount = this._nextAttemptCount(routedModel, invocationState, attemptCount);
1507
1596
  continue;
1508
1597
  }
1509
- // Re-throw error
1510
1598
  throw error;
1511
1599
  }
1512
1600
  }
1513
1601
  }
1602
+ _modelForAttempt(invocationState) {
1603
+ return this._modelRouter?.getRoutedModel(this, invocationState) ?? this.model;
1604
+ }
1605
+ _nextAttemptCount(model, invocationState, attemptCount) {
1606
+ const nextModel = this._modelRouter?.getRoutedModel(this, invocationState);
1607
+ return nextModel !== undefined && nextModel !== model ? 1 : attemptCount + 1;
1608
+ }
1514
1609
  /**
1515
1610
  * Invokes the model through the InvokeModelStage middleware chain.
1516
1611
  * Builds an InvokeModelContext from current agent state and composes the
@@ -1518,12 +1613,15 @@ export class Agent {
1518
1613
  * using context fields directly (not re-derived from the agent).
1519
1614
  *
1520
1615
  * @param invocationState - Per-invocation state shared across hooks and tools
1616
+ * @param selectedModel - Model the chain starts with; middleware may replace it
1617
+ * @param invokedModelRef - Slot filled with the model the terminal actually invoked
1521
1618
  * @param toolChoice - Optional tool choice to force specific tool usage
1522
1619
  * @returns StreamAggregatedResult from the model (or middleware short-circuit)
1523
1620
  */
1524
- async *_invokeModelWithMiddleware(invocationState, toolChoice, projectedInputTokens) {
1621
+ async *_invokeModelWithMiddleware(invocationState, selectedModel, invokedModelRef, toolChoice, projectedInputTokens) {
1525
1622
  const context = {
1526
1623
  agent: this,
1624
+ model: selectedModel,
1527
1625
  messages: this.messages.map((msg) => msg.clone()),
1528
1626
  ...(this.systemPrompt !== undefined && { systemPrompt: cloneSystemPrompt(this.systemPrompt) }),
1529
1627
  toolSpecs: deepCopy(this._toolRegistry.list().map((tool) => tool.toolSpec)),
@@ -1540,7 +1638,8 @@ export class Agent {
1540
1638
  // eslint-disable-next-line @typescript-eslint/no-this-alias
1541
1639
  const self = this;
1542
1640
  const middlewareResult = yield* this._middlewareRegistry.invoke(InvokeModelStage, context, async function* (ctx) {
1543
- const modelId = self.model.modelId;
1641
+ invokedModelRef.model = ctx.model;
1642
+ const modelId = ctx.model.modelId;
1544
1643
  const modelSpan = self._tracer.startModelInvokeSpan({
1545
1644
  messages: ctx.messages,
1546
1645
  ...(modelId && { modelId }),
@@ -1556,8 +1655,10 @@ export class Agent {
1556
1655
  modelState: tempModelState,
1557
1656
  ...(ctx.systemPrompt !== undefined && { systemPrompt: ctx.systemPrompt }),
1558
1657
  ...(ctx.toolChoice && { toolChoice: ctx.toolChoice }),
1658
+ // Omitted when zero, so an ordinary call's options are unchanged.
1659
+ ...(ctx.dynamicTrailingBlocks ? { dynamicTrailingBlocks: ctx.dynamicTrailingBlocks } : {}),
1559
1660
  };
1560
- const gen = self._streamFromModel(ctx.messages, streamOptions, ctx.invocationState);
1661
+ const gen = self._streamFromModel(ctx.model, ctx.messages, streamOptions, ctx.invocationState);
1561
1662
  let iterResult = await gen.next();
1562
1663
  while (!iterResult.done) {
1563
1664
  yield iterResult.value;
@@ -1598,13 +1699,14 @@ export class Agent {
1598
1699
  * These are separate event classes because they represent different granularities
1599
1700
  * (partial deltas vs finished blocks). Both are yielded in the stream and hookable.
1600
1701
  *
1702
+ * @param model - Model selected for this call
1601
1703
  * @param messages - Messages to send to the model
1602
1704
  * @param streamOptions - Options for streaming
1603
1705
  * @returns StreamAggregatedResult containing message, stop reason, and optional redaction message
1604
1706
  */
1605
- async *_streamFromModel(messages, streamOptions, invocationState) {
1707
+ async *_streamFromModel(model, messages, streamOptions, invocationState) {
1606
1708
  messages = normalizeToolUseNames(messages);
1607
- const streamGenerator = this.model.streamAggregated(messages, streamOptions);
1709
+ const streamGenerator = model.streamAggregated(messages, streamOptions);
1608
1710
  try {
1609
1711
  let result = await streamGenerator.next();
1610
1712
  while (!result.done) {
@@ -1685,6 +1787,7 @@ export class Agent {
1685
1787
  middlewareRegistry: this._middlewareRegistry,
1686
1788
  tracer: this._tracer,
1687
1789
  meter: this._meter,
1790
+ cancelSignal: this._abortSignal,
1688
1791
  }, {
1689
1792
  toolUseBlocks,
1690
1793
  toolResultBlocks,
@@ -1762,10 +1865,10 @@ export class Agent {
1762
1865
  /**
1763
1866
  * Estimate the input token count for the next model call.
1764
1867
  *
1765
- * Uses the token counting strategy: reads inputTokens + outputTokens
1766
- * from the last assistant message's metadata as a known baseline, then estimates
1767
- * only new messages added after it. Falls back to full estimation when no metadata
1768
- * is available (cold start or first call).
1868
+ * Uses the token counting strategy: reads the total prompt the model processed (including cached
1869
+ * tokens) plus outputTokens from the last assistant message's metadata as a known baseline, then
1870
+ * estimates only new messages added after it. Falls back to full estimation when no metadata is
1871
+ * available (cold start or first call).
1769
1872
  *
1770
1873
  * @param streamOptions - The stream options containing system prompt and tool specs
1771
1874
  * @returns Estimated input token count
@@ -1782,7 +1885,7 @@ export class Agent {
1782
1885
  let estimate;
1783
1886
  if (lastAssistantIdx >= 0) {
1784
1887
  const usage = this.messages[lastAssistantIdx].metadata.usage;
1785
- const knownBaseline = usage.inputTokens + usage.outputTokens;
1888
+ const knownBaseline = totalPromptTokens(usage) + usage.outputTokens;
1786
1889
  const newMessages = this.messages.slice(lastAssistantIdx + 1);
1787
1890
  if (newMessages.length === 0) {
1788
1891
  estimate = knownBaseline;
@@ -1821,6 +1924,36 @@ export class Agent {
1821
1924
  this.messages.push(message);
1822
1925
  return new MessageAddedEvent({ agent: this, message, invocationState });
1823
1926
  }
1927
+ async _appendContinuationMessages(messages, continuationEvent, invocationState) {
1928
+ const events = [];
1929
+ for (const message of messages) {
1930
+ const lastMessage = this.messages.at(-1);
1931
+ if (lastMessage?.role !== message.role) {
1932
+ events.push(this._appendMessage(message, invocationState));
1933
+ continue;
1934
+ }
1935
+ const appendedMessage = new Message({
1936
+ role: message.role,
1937
+ content: [...lastMessage.content, ...message.content],
1938
+ trackingId: lastMessage.trackingId,
1939
+ ...(lastMessage.metadata !== undefined && { metadata: lastMessage.metadata }),
1940
+ });
1941
+ this.messages[this.messages.length - 1] = appendedMessage;
1942
+ const messageEvent = new MessageAddedEvent({ agent: this, message: appendedMessage, invocationState });
1943
+ if (events.at(-1)?.message === lastMessage) {
1944
+ events[events.length - 1] = messageEvent;
1945
+ }
1946
+ else {
1947
+ events.push(messageEvent);
1948
+ }
1949
+ }
1950
+ await continuations.markAppended(continuationEvent);
1951
+ return events;
1952
+ }
1953
+ _hasOpenUserTurn() {
1954
+ const lastMessage = this.messages[this.messages.length - 1];
1955
+ return lastMessage?.role === 'user';
1956
+ }
1824
1957
  }
1825
1958
  const INVALID_TOOL_NAME_PLACEHOLDER = 'INVALID_TOOL_NAME';
1826
1959
  /**