@clinebot/core 0.0.36 → 0.0.37

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 (228) hide show
  1. package/dist/ClineCore.d.ts +312 -3
  2. package/dist/ClineCore.d.ts.map +1 -1
  3. package/dist/account/cline-account-service.d.ts.map +1 -1
  4. package/dist/cron/cron-event-ingress.d.ts +38 -0
  5. package/dist/cron/cron-event-ingress.d.ts.map +1 -0
  6. package/dist/cron/cron-materializer.d.ts +36 -0
  7. package/dist/cron/cron-materializer.d.ts.map +1 -0
  8. package/dist/cron/cron-reconciler.d.ts +62 -0
  9. package/dist/cron/cron-reconciler.d.ts.map +1 -0
  10. package/dist/cron/cron-report-writer.d.ts +41 -0
  11. package/dist/cron/cron-report-writer.d.ts.map +1 -0
  12. package/dist/cron/cron-runner.d.ts +43 -0
  13. package/dist/cron/cron-runner.d.ts.map +1 -0
  14. package/dist/cron/cron-schema.d.ts +3 -0
  15. package/dist/cron/cron-schema.d.ts.map +1 -0
  16. package/dist/cron/cron-service.d.ts +57 -0
  17. package/dist/cron/cron-service.d.ts.map +1 -0
  18. package/dist/cron/cron-spec-parser.d.ts +27 -0
  19. package/dist/cron/cron-spec-parser.d.ts.map +1 -0
  20. package/dist/cron/cron-watcher.d.ts +23 -0
  21. package/dist/cron/cron-watcher.d.ts.map +1 -0
  22. package/dist/cron/scheduler.d.ts +3 -1
  23. package/dist/cron/scheduler.d.ts.map +1 -1
  24. package/dist/cron/sqlite-cron-store.d.ts +230 -0
  25. package/dist/cron/sqlite-cron-store.d.ts.map +1 -0
  26. package/dist/extensions/plugin/plugin-config-loader.d.ts +7 -1
  27. package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -1
  28. package/dist/extensions/plugin/plugin-loader.d.ts +10 -6
  29. package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -1
  30. package/dist/extensions/plugin/plugin-sandbox.d.ts +7 -1
  31. package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -1
  32. package/dist/extensions/plugin-sandbox-bootstrap.js +236 -275
  33. package/dist/extensions/tools/constants.d.ts +1 -0
  34. package/dist/extensions/tools/constants.d.ts.map +1 -1
  35. package/dist/extensions/tools/definitions.d.ts +2 -3
  36. package/dist/extensions/tools/definitions.d.ts.map +1 -1
  37. package/dist/extensions/tools/executors/editor.d.ts.map +1 -1
  38. package/dist/extensions/tools/helpers.d.ts +1 -0
  39. package/dist/extensions/tools/helpers.d.ts.map +1 -1
  40. package/dist/extensions/tools/index.d.ts +1 -2
  41. package/dist/extensions/tools/index.d.ts.map +1 -1
  42. package/dist/extensions/tools/presets.d.ts +1 -1
  43. package/dist/extensions/tools/schemas.d.ts +25 -3
  44. package/dist/extensions/tools/schemas.d.ts.map +1 -1
  45. package/dist/extensions/tools/team/delegated-agent.d.ts +2 -2
  46. package/dist/extensions/tools/team/delegated-agent.d.ts.map +1 -1
  47. package/dist/extensions/tools/team/multi-agent.d.ts +7 -3
  48. package/dist/extensions/tools/team/multi-agent.d.ts.map +1 -1
  49. package/dist/extensions/tools/team/team-tools.d.ts.map +1 -1
  50. package/dist/extensions/tools/types.d.ts +0 -5
  51. package/dist/extensions/tools/types.d.ts.map +1 -1
  52. package/dist/hooks/hook-bridge.d.ts +118 -0
  53. package/dist/hooks/hook-bridge.d.ts.map +1 -0
  54. package/dist/hooks/hook-file-hooks.d.ts +2 -1
  55. package/dist/hooks/hook-file-hooks.d.ts.map +1 -1
  56. package/dist/hooks/hook-registry.d.ts +16 -0
  57. package/dist/hooks/hook-registry.d.ts.map +1 -0
  58. package/dist/hub/browser-websocket.d.ts.map +1 -1
  59. package/dist/hub/client.d.ts +7 -1
  60. package/dist/hub/client.d.ts.map +1 -1
  61. package/dist/hub/daemon-entry.js +721 -461
  62. package/dist/hub/daemon.d.ts.map +1 -1
  63. package/dist/hub/defaults.d.ts +8 -4
  64. package/dist/hub/defaults.d.ts.map +1 -1
  65. package/dist/hub/index.js +665 -415
  66. package/dist/hub/runtime-handlers.d.ts.map +1 -1
  67. package/dist/hub/server.d.ts +18 -0
  68. package/dist/hub/server.d.ts.map +1 -1
  69. package/dist/hub/session-client.d.ts +3 -0
  70. package/dist/hub/session-client.d.ts.map +1 -1
  71. package/dist/hub/start-shared-server.d.ts.map +1 -1
  72. package/dist/hub/ui-client.d.ts +1 -0
  73. package/dist/hub/ui-client.d.ts.map +1 -1
  74. package/dist/index.d.ts +9 -7
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +756 -467
  77. package/dist/llms/cline-recommended-models.d.ts +20 -0
  78. package/dist/llms/cline-recommended-models.d.ts.map +1 -0
  79. package/dist/llms/handler-factory.d.ts +16 -0
  80. package/dist/llms/handler-factory.d.ts.map +1 -0
  81. package/dist/llms/provider-defaults.d.ts.map +1 -1
  82. package/dist/llms/provider-settings.d.ts +45 -2
  83. package/dist/llms/provider-settings.d.ts.map +1 -1
  84. package/dist/llms/runtime-registry.d.ts.map +1 -1
  85. package/dist/runtime/agent-config-adapter.d.ts +148 -0
  86. package/dist/runtime/agent-config-adapter.d.ts.map +1 -0
  87. package/dist/runtime/agent-runtime-config-builder.d.ts +96 -0
  88. package/dist/runtime/agent-runtime-config-builder.d.ts.map +1 -0
  89. package/dist/runtime/history.d.ts +6 -0
  90. package/dist/runtime/history.d.ts.map +1 -1
  91. package/dist/runtime/host.d.ts.map +1 -1
  92. package/dist/runtime/loop-detection.d.ts +59 -0
  93. package/dist/runtime/loop-detection.d.ts.map +1 -0
  94. package/dist/runtime/mistake-tracker.d.ts +69 -0
  95. package/dist/runtime/mistake-tracker.d.ts.map +1 -0
  96. package/dist/runtime/runtime-builder.d.ts.map +1 -1
  97. package/dist/runtime/runtime-event-adapter.d.ts +102 -0
  98. package/dist/runtime/runtime-event-adapter.d.ts.map +1 -0
  99. package/dist/runtime/runtime-host.d.ts +28 -3
  100. package/dist/runtime/runtime-host.d.ts.map +1 -1
  101. package/dist/runtime/session-runtime-orchestrator.d.ts +261 -0
  102. package/dist/runtime/session-runtime-orchestrator.d.ts.map +1 -0
  103. package/dist/runtime/session-runtime.d.ts +16 -3
  104. package/dist/runtime/session-runtime.d.ts.map +1 -1
  105. package/dist/runtime/user-input-builder.d.ts +24 -0
  106. package/dist/runtime/user-input-builder.d.ts.map +1 -0
  107. package/dist/services/index.js +28 -0
  108. package/dist/services/local-runtime-bootstrap.d.ts.map +1 -1
  109. package/dist/services/plugin-tools.d.ts.map +1 -1
  110. package/dist/services/providers/local-provider-registry.d.ts +197 -21
  111. package/dist/services/providers/local-provider-registry.d.ts.map +1 -1
  112. package/dist/services/providers/local-provider-service.d.ts +3 -1
  113. package/dist/services/providers/local-provider-service.d.ts.map +1 -1
  114. package/dist/services/session-data.d.ts.map +1 -1
  115. package/dist/services/session-telemetry.d.ts +7 -2
  116. package/dist/services/session-telemetry.d.ts.map +1 -1
  117. package/dist/services/storage/file-team-store.d.ts.map +1 -1
  118. package/dist/services/storage/provider-settings-legacy-migration.d.ts.map +1 -1
  119. package/dist/services/storage/provider-settings-manager.d.ts +1 -0
  120. package/dist/services/storage/provider-settings-manager.d.ts.map +1 -1
  121. package/dist/services/storage/sqlite-team-store.d.ts.map +1 -1
  122. package/dist/session/conversation-store.d.ts +30 -0
  123. package/dist/session/conversation-store.d.ts.map +1 -0
  124. package/dist/session/message-builder.d.ts +65 -0
  125. package/dist/session/message-builder.d.ts.map +1 -0
  126. package/dist/session/session-manifest.d.ts +1 -1
  127. package/dist/transports/hub.d.ts +14 -3
  128. package/dist/transports/hub.d.ts.map +1 -1
  129. package/dist/transports/local.d.ts +14 -4
  130. package/dist/transports/local.d.ts.map +1 -1
  131. package/dist/transports/remote.d.ts.map +1 -1
  132. package/dist/types/chat-schema.d.ts +5 -5
  133. package/dist/types/config.d.ts +9 -0
  134. package/dist/types/config.d.ts.map +1 -1
  135. package/dist/types/events.d.ts +7 -6
  136. package/dist/types/events.d.ts.map +1 -1
  137. package/dist/types/provider-settings.d.ts +2 -2
  138. package/dist/types/provider-settings.d.ts.map +1 -1
  139. package/dist/types/session.d.ts +5 -2
  140. package/dist/types/session.d.ts.map +1 -1
  141. package/dist/types.d.ts +4 -4
  142. package/dist/types.d.ts.map +1 -1
  143. package/package.json +4 -4
  144. package/src/ClineCore.ts +691 -6
  145. package/src/account/cline-account-service.ts +44 -6
  146. package/src/cron/cron-event-ingress.ts +357 -0
  147. package/src/cron/cron-materializer.ts +97 -0
  148. package/src/cron/cron-reconciler.ts +241 -0
  149. package/src/cron/cron-report-writer.ts +153 -0
  150. package/src/cron/cron-runner.ts +495 -0
  151. package/src/cron/cron-schema.ts +127 -0
  152. package/src/cron/cron-service.ts +163 -0
  153. package/src/cron/cron-spec-parser.ts +489 -0
  154. package/src/cron/cron-watcher.ts +102 -0
  155. package/src/cron/index.ts +10 -0
  156. package/src/cron/scheduler.ts +141 -6
  157. package/src/cron/sqlite-cron-store.ts +1286 -0
  158. package/src/extensions/plugin/plugin-config-loader.ts +21 -1
  159. package/src/extensions/plugin/plugin-loader.ts +25 -9
  160. package/src/extensions/plugin/plugin-sandbox-bootstrap.ts +151 -1
  161. package/src/extensions/plugin/plugin-sandbox.ts +131 -7
  162. package/src/extensions/tools/constants.ts +2 -0
  163. package/src/extensions/tools/definitions.ts +31 -22
  164. package/src/extensions/tools/executors/editor.ts +4 -3
  165. package/src/extensions/tools/helpers.ts +24 -0
  166. package/src/extensions/tools/index.ts +1 -2
  167. package/src/extensions/tools/presets.ts +1 -1
  168. package/src/extensions/tools/schemas.ts +13 -18
  169. package/src/extensions/tools/team/delegated-agent.ts +8 -3
  170. package/src/extensions/tools/team/multi-agent.ts +135 -19
  171. package/src/extensions/tools/team/team-tools.ts +151 -91
  172. package/src/extensions/tools/types.ts +0 -6
  173. package/src/hooks/hook-bridge.ts +489 -0
  174. package/src/hooks/hook-file-hooks.ts +58 -3
  175. package/src/hooks/hook-registry.ts +257 -0
  176. package/src/hub/browser-websocket.ts +26 -4
  177. package/src/hub/client.ts +72 -13
  178. package/src/hub/daemon-entry.ts +35 -0
  179. package/src/hub/daemon.ts +117 -14
  180. package/src/hub/defaults.ts +39 -12
  181. package/src/hub/runtime-handlers.ts +4 -3
  182. package/src/hub/server.ts +506 -77
  183. package/src/hub/session-client.ts +43 -1
  184. package/src/hub/start-shared-server.ts +3 -0
  185. package/src/hub/ui-client.ts +4 -0
  186. package/src/index.ts +46 -1
  187. package/src/llms/cline-recommended-models.ts +167 -0
  188. package/src/llms/handler-factory.ts +56 -0
  189. package/src/llms/provider-defaults.ts +17 -1
  190. package/src/llms/provider-settings.ts +48 -1
  191. package/src/llms/runtime-registry.ts +1 -0
  192. package/src/runtime/agent-config-adapter.ts +636 -0
  193. package/src/runtime/agent-runtime-config-builder.ts +205 -0
  194. package/src/runtime/error-feedback.ts +142 -0
  195. package/src/runtime/history.ts +137 -0
  196. package/src/runtime/host.ts +22 -0
  197. package/src/runtime/loop-detection.ts +162 -0
  198. package/src/runtime/mistake-tracker.ts +221 -0
  199. package/src/runtime/runtime-builder.ts +61 -5
  200. package/src/runtime/runtime-event-adapter.ts +412 -0
  201. package/src/runtime/runtime-host.ts +45 -1
  202. package/src/runtime/session-runtime-orchestrator.ts +1253 -0
  203. package/src/runtime/session-runtime.ts +16 -2
  204. package/src/runtime/user-input-builder.ts +167 -0
  205. package/src/services/local-runtime-bootstrap.ts +128 -22
  206. package/src/services/plugin-tools.ts +1 -0
  207. package/src/services/providers/local-provider-registry.ts +273 -57
  208. package/src/services/providers/local-provider-service.ts +67 -7
  209. package/src/services/session-data.ts +16 -14
  210. package/src/services/session-telemetry.ts +6 -15
  211. package/src/services/storage/file-team-store.ts +1 -5
  212. package/src/services/storage/provider-settings-legacy-migration.ts +8 -47
  213. package/src/services/storage/provider-settings-manager.ts +16 -1
  214. package/src/services/storage/sqlite-team-store.ts +1 -5
  215. package/src/session/conversation-store.ts +77 -0
  216. package/src/session/message-builder.ts +941 -0
  217. package/src/transports/hub.ts +458 -33
  218. package/src/transports/local.ts +296 -65
  219. package/src/transports/remote.ts +1 -0
  220. package/src/types/config.ts +9 -0
  221. package/src/types/events.ts +8 -6
  222. package/src/types/index.ts +3 -0
  223. package/src/types/provider-settings.ts +8 -1
  224. package/src/types/session.ts +5 -2
  225. package/src/types.ts +15 -1
  226. package/dist/cron/index.d.ts +0 -6
  227. package/dist/cron/index.d.ts.map +0 -1
  228. package/dist/services/telemetry/index.js +0 -28
@@ -0,0 +1,636 @@
1
+ /**
2
+ * Cross-shape adapters between the **legacy host API** types
3
+ * (`ApiHandler`, `Tool`, `MessageWithMetadata`) and the **new
4
+ * runtime contract** types (`AgentModel`, `AgentTool`,
5
+ * `AgentMessage`) consumed by `AgentRuntime`.
6
+ *
7
+ * @see PLAN.md §3.1 — pure adapters; introduced alongside the core
8
+ * runtime port.
9
+ * @see PLAN.md §3.2.1 — consumed by `createAgentRuntimeConfig` to
10
+ * populate the `model`, `tools`, and
11
+ * `initialMessages` fields.
12
+ *
13
+ * These functions are intentionally **pure** (stateless adapters —
14
+ * they allocate fresh closures / arrays on every call and hold no
15
+ * internal state). Any state a consumer needs (e.g. an `AbortSignal`
16
+ * or a conversation id for `ToolContext.conversationId`) is injected
17
+ * per-call via the second-arg options object.
18
+ */
19
+
20
+ import type {
21
+ ApiHandler,
22
+ ApiStreamChunk,
23
+ ToolDefinition,
24
+ } from "@clinebot/llms";
25
+ import type {
26
+ AgentMessage,
27
+ AgentMessagePart,
28
+ AgentModel,
29
+ AgentModelEvent,
30
+ AgentModelRequest,
31
+ AgentTextPart,
32
+ AgentTool,
33
+ AgentToolContext,
34
+ AgentToolResult,
35
+ ContentBlock,
36
+ FileContent,
37
+ ImageContent,
38
+ Message,
39
+ MessageWithMetadata,
40
+ RedactedThinkingContent,
41
+ TextContent,
42
+ ThinkingContent,
43
+ Tool,
44
+ ToolContext,
45
+ ToolResultContent,
46
+ ToolUseContent,
47
+ } from "@clinebot/shared";
48
+
49
+ // =============================================================================
50
+ // ApiHandler → AgentModel
51
+ // =============================================================================
52
+
53
+ /**
54
+ * Context describing the model the `ApiHandler` talks to. This is
55
+ * used to populate `AgentMessage.modelInfo` on assistant messages
56
+ * emitted by the runtime. Matches PLAN.md §3.2.1 —
57
+ * `messageModelInfo = { id: modelId, provider: providerId, family:
58
+ * providerConfig?.family }`.
59
+ */
60
+ export interface ApiHandlerAgentModelOptions {
61
+ /** Optional provider-family hint (e.g. `"claude-3.5"`). Attached as metadata. */
62
+ readonly family?: string;
63
+ /**
64
+ * Optional abort-signal factory. When provided, the returned
65
+ * `AgentModel.stream()` installs the signal on the handler via
66
+ * `handler.setAbortSignal()` before each call, and threads
67
+ * `request.signal` through for per-turn abort semantics.
68
+ */
69
+ readonly getAbortSignal?: () => AbortSignal | undefined;
70
+ /**
71
+ * Last-mile message preparation before the legacy handler builds a provider
72
+ * request. SessionRuntime uses this to apply API-safe normalization,
73
+ * tool-result truncation, and stale read-result rewriting with its
74
+ * session-owned MessageBuilder.
75
+ */
76
+ readonly prepareMessages?: (
77
+ messages: Message[],
78
+ ) => Message[] | Promise<Message[]>;
79
+ }
80
+
81
+ /**
82
+ * Build an `AgentModel` adapter around an existing `ApiHandler`.
83
+ *
84
+ * The returned adapter:
85
+ *
86
+ * 1. Converts `AgentModelRequest.messages` (`AgentMessage[]`) back
87
+ * into `LlmsProviders.Message[]` via
88
+ * {@link agentMessagesToMessages}.
89
+ * 2. Converts `AgentModelRequest.tools`
90
+ * (`AgentToolDefinition[]`) to `LlmsProviders.ToolDefinition[]`.
91
+ * 3. Invokes `handler.createMessage(systemPrompt, messages, tools)`.
92
+ * 4. Iterates the returned `ApiStream` and yields
93
+ * `AgentModelEvent`s produced by {@link apiStreamChunkToAgentModelEvent}.
94
+ */
95
+ export function apiHandlerToAgentModel(
96
+ handler: ApiHandler,
97
+ options: ApiHandlerAgentModelOptions = {},
98
+ ): AgentModel {
99
+ return {
100
+ async stream(
101
+ request: AgentModelRequest,
102
+ ): Promise<AsyncIterable<AgentModelEvent>> {
103
+ const signal = options.getAbortSignal?.() ?? request.signal;
104
+ handler.setAbortSignal?.(signal);
105
+ const systemPrompt = request.systemPrompt ?? "";
106
+ const messages = options.prepareMessages
107
+ ? await options.prepareMessages(
108
+ agentMessagesToMessages(request.messages),
109
+ )
110
+ : agentMessagesToMessages(request.messages);
111
+ const tools = agentToolDefinitionsToToolDefinitions(request.tools);
112
+ const stream = handler.createMessage(systemPrompt, messages, tools);
113
+ return translateApiStream(stream);
114
+ },
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Translate an `ApiStream` (async generator of `ApiStreamChunk`)
120
+ * into an async iterable of `AgentModelEvent`. Exposed for tests;
121
+ * production callers go through {@link apiHandlerToAgentModel}.
122
+ */
123
+ export async function* translateApiStream(
124
+ stream: AsyncIterable<ApiStreamChunk>,
125
+ ): AsyncIterable<AgentModelEvent> {
126
+ let sawFinish = false;
127
+ try {
128
+ for await (const chunk of stream) {
129
+ const event = apiStreamChunkToAgentModelEvent(chunk);
130
+ if (event) {
131
+ yield event;
132
+ }
133
+ if (chunk.type === "done") {
134
+ sawFinish = true;
135
+ }
136
+ }
137
+ } catch (error) {
138
+ yield {
139
+ type: "finish",
140
+ reason: "error",
141
+ error: error instanceof Error ? error.message : String(error),
142
+ };
143
+ return;
144
+ }
145
+ if (!sawFinish) {
146
+ yield { type: "finish", reason: "stop" };
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Map a single `ApiStreamChunk` to the corresponding
152
+ * `AgentModelEvent`. Returns `undefined` for chunks that carry no
153
+ * runtime-observable payload (currently none — but the hook exists
154
+ * so we can silently drop future additions without type breakage).
155
+ */
156
+ export function apiStreamChunkToAgentModelEvent(
157
+ chunk: ApiStreamChunk,
158
+ ): AgentModelEvent | undefined {
159
+ switch (chunk.type) {
160
+ case "text":
161
+ return { type: "text-delta", text: chunk.text };
162
+ case "reasoning": {
163
+ const metadata: Record<string, unknown> = {};
164
+ if (chunk.signature !== undefined) {
165
+ metadata.signature = chunk.signature;
166
+ }
167
+ if (chunk.details !== undefined) {
168
+ metadata.details = chunk.details;
169
+ }
170
+ return {
171
+ type: "reasoning-delta",
172
+ text: chunk.reasoning,
173
+ redacted: chunk.redacted_data !== undefined,
174
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
175
+ };
176
+ }
177
+ case "tool_calls": {
178
+ const fn = chunk.tool_call.function;
179
+ const args = fn.arguments;
180
+ const inputText = typeof args === "string" ? args : undefined;
181
+ const input =
182
+ args && typeof args === "object" ? (args as unknown) : undefined;
183
+ const metadata: Record<string, unknown> = {};
184
+ if (chunk.signature !== undefined) {
185
+ metadata.thoughtSignature = chunk.signature;
186
+ }
187
+ return {
188
+ type: "tool-call-delta",
189
+ toolCallId: fn.id ?? chunk.tool_call.call_id,
190
+ toolName: fn.name,
191
+ inputText,
192
+ input,
193
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
194
+ };
195
+ }
196
+ case "usage":
197
+ return {
198
+ type: "usage",
199
+ usage: {
200
+ inputTokens: chunk.inputTokens,
201
+ outputTokens: chunk.outputTokens,
202
+ cacheReadTokens: chunk.cacheReadTokens,
203
+ cacheWriteTokens: chunk.cacheWriteTokens,
204
+ totalCost: chunk.totalCost,
205
+ },
206
+ };
207
+ case "done":
208
+ if (chunk.success === false) {
209
+ return { type: "finish", reason: "error", error: chunk.error };
210
+ }
211
+ if (chunk.incompleteReason === "max_tokens") {
212
+ return { type: "finish", reason: "max-tokens" };
213
+ }
214
+ return { type: "finish", reason: "stop" };
215
+ default: {
216
+ const _exhaustive: never = chunk;
217
+ return _exhaustive;
218
+ }
219
+ }
220
+ }
221
+
222
+ // =============================================================================
223
+ // Tool[] → AgentTool[]
224
+ // =============================================================================
225
+
226
+ /**
227
+ * Options threaded into every adapted tool's `ToolContext`.
228
+ */
229
+ export interface ToolAdapterOptions {
230
+ /**
231
+ * Conversation id bound to every `ToolContext.conversationId`
232
+ * the tool's `execute()` receives. The new runtime scopes tools
233
+ * by `AgentToolContext.agentId`/`.runId`; the legacy `ToolContext`
234
+ * requires `conversationId` separately, so the caller
235
+ * (`SessionRuntime`) supplies it once at adapter-construction
236
+ * time.
237
+ */
238
+ readonly conversationId: string;
239
+ /**
240
+ * Optional metadata merged into every tool execution
241
+ * (`AgentConfig.toolContextMetadata` equivalent).
242
+ */
243
+ readonly metadata?: Record<string, unknown>;
244
+ }
245
+
246
+ /**
247
+ * Adapt a legacy `Tool<TInput, TOutput>` to the new
248
+ * `AgentTool<TInput, TOutput>`.
249
+ *
250
+ * - name/description/inputSchema flow through verbatim;
251
+ * - `execute(input, AgentToolContext)` is wrapped so the legacy
252
+ * signature `execute(input, ToolContext, onChange)` receives the
253
+ * correct fields and the return value is boxed into an
254
+ * `AgentToolResult`.
255
+ */
256
+ export function toolToAgentTool<TInput, TOutput>(
257
+ tool: Tool<TInput, TOutput>,
258
+ options: ToolAdapterOptions,
259
+ ): AgentTool<TInput, TOutput> {
260
+ return {
261
+ name: tool.name,
262
+ description: tool.description,
263
+ inputSchema: tool.inputSchema,
264
+ async execute(
265
+ input: TInput,
266
+ context: AgentToolContext,
267
+ ): Promise<AgentToolResult<TOutput>> {
268
+ const legacyContext: ToolContext = {
269
+ agentId: context.agentId,
270
+ conversationId: options.conversationId,
271
+ iteration: context.iteration,
272
+ abortSignal: context.signal,
273
+ metadata: options.metadata,
274
+ };
275
+ try {
276
+ const output = await tool.execute(
277
+ input,
278
+ legacyContext,
279
+ context.emitUpdate,
280
+ );
281
+ return { output };
282
+ } catch (error) {
283
+ const message = error instanceof Error ? error.message : String(error);
284
+ return {
285
+ output: message as unknown as TOutput,
286
+ isError: true,
287
+ };
288
+ }
289
+ },
290
+ };
291
+ }
292
+
293
+ /**
294
+ * Convenience bulk-adapter for `AgentConfig.tools`.
295
+ */
296
+ export function toolsToAgentTools(
297
+ tools: readonly Tool[],
298
+ options: ToolAdapterOptions,
299
+ ): AgentTool[] {
300
+ return tools.map((tool) => toolToAgentTool(tool, options));
301
+ }
302
+
303
+ // =============================================================================
304
+ // MessageWithMetadata[] ↔ AgentMessage[]
305
+ // =============================================================================
306
+
307
+ /**
308
+ * Normalize a single `MessageWithMetadata` into an `AgentMessage`.
309
+ *
310
+ * Role mapping: the legacy `MessageRole` is `"user" | "assistant"`.
311
+ * `tool_result` content blocks on user messages are hoisted into a
312
+ * dedicated `AgentMessage` with role `"tool"` — matching how the new
313
+ * runtime's tool executor emits tool messages
314
+ * (`packages/agents/src/agent-runtime.ts` tool-finished path).
315
+ */
316
+ export function messageToAgentMessages(
317
+ message: MessageWithMetadata,
318
+ ): AgentMessage[] {
319
+ const blocks = normalizeContentBlocks(message.content);
320
+ const toolResults = blocks.filter(
321
+ (block): block is ToolResultContent => block.type === "tool_result",
322
+ );
323
+ const nonToolResults = blocks.filter((block) => block.type !== "tool_result");
324
+
325
+ const out: AgentMessage[] = [];
326
+
327
+ if (nonToolResults.length > 0 || toolResults.length === 0) {
328
+ out.push({
329
+ id: message.id ?? generateMessageId(),
330
+ role: message.role,
331
+ content: nonToolResults.map(contentBlockToAgentPart),
332
+ createdAt: message.ts ?? Date.now(),
333
+ metadata: message.metadata,
334
+ modelInfo: message.modelInfo,
335
+ metrics: metricsToAgentMetrics(message.metrics),
336
+ });
337
+ }
338
+
339
+ for (const toolResult of toolResults) {
340
+ out.push({
341
+ id: `${message.id ?? generateMessageId()}_tool_${toolResult.tool_use_id}`,
342
+ role: "tool",
343
+ content: [toolResultContentToAgentPart(toolResult)],
344
+ createdAt: message.ts ?? Date.now(),
345
+ metadata: message.metadata,
346
+ });
347
+ }
348
+
349
+ return out;
350
+ }
351
+
352
+ /**
353
+ * Bulk-adapter: `MessageWithMetadata[]` → `AgentMessage[]`. A single
354
+ * legacy message with both text and tool-result blocks may expand
355
+ * into multiple agent messages (see {@link messageToAgentMessages}).
356
+ */
357
+ export function messagesToAgentMessages(
358
+ messages: readonly MessageWithMetadata[],
359
+ ): AgentMessage[] {
360
+ const out: AgentMessage[] = [];
361
+ for (const message of messages) {
362
+ out.push(...messageToAgentMessages(message));
363
+ }
364
+ return out;
365
+ }
366
+
367
+ /**
368
+ * Reverse adapter: `AgentMessage` → `MessageWithMetadata`. Best-effort
369
+ * preservation of id/metadata/modelInfo/metrics; tool-message
370
+ * tool-result parts are rendered as `tool_result` content blocks on
371
+ * a user message (matching legacy storage conventions).
372
+ */
373
+ export function agentMessageToMessageWithMetadata(
374
+ message: AgentMessage,
375
+ ): MessageWithMetadata {
376
+ const content = message.content
377
+ .map(agentPartToContentBlock)
378
+ .filter((block): block is ContentBlock => block !== undefined);
379
+ return {
380
+ id: message.id,
381
+ role: message.role === "tool" ? "user" : message.role,
382
+ content,
383
+ ts: message.createdAt,
384
+ metadata: message.metadata,
385
+ modelInfo: message.modelInfo,
386
+ metrics: agentMetricsToMetrics(message.metrics),
387
+ };
388
+ }
389
+
390
+ /**
391
+ * Bulk-adapter for the reverse direction.
392
+ */
393
+ export function agentMessagesToMessagesWithMetadata(
394
+ messages: readonly AgentMessage[],
395
+ ): MessageWithMetadata[] {
396
+ return messages.map(agentMessageToMessageWithMetadata);
397
+ }
398
+
399
+ /**
400
+ * Lossy but adequate: `AgentMessage[]` → `LlmsProviders.Message[]`.
401
+ * Used to feed the legacy `ApiHandler.createMessage()` in
402
+ * {@link apiHandlerToAgentModel}. Drops fields that the handler
403
+ * does not consume (id, ts, metrics, modelInfo).
404
+ */
405
+ export function agentMessagesToMessages(
406
+ messages: readonly AgentMessage[],
407
+ ): Message[] {
408
+ const out: Message[] = [];
409
+ for (const message of messages) {
410
+ const content = message.content
411
+ .map(agentPartToContentBlock)
412
+ .filter((block): block is ContentBlock => block !== undefined);
413
+ const role = message.role === "tool" ? "user" : message.role;
414
+
415
+ // AI SDK validates that assistant tool calls are followed by a single
416
+ // tool-result message containing results for every call in that turn.
417
+ // The AgentRuntime stores each executed tool as its own role:"tool"
418
+ // message; merge adjacent tool messages back into one legacy user
419
+ // message so multi-tool turns round-trip correctly.
420
+ const previous = out[out.length - 1];
421
+ if (
422
+ role === "user" &&
423
+ content.length > 0 &&
424
+ content.every((block) => block.type === "tool_result") &&
425
+ previous?.role === "user" &&
426
+ Array.isArray(previous.content) &&
427
+ previous.content.every((block) => block.type === "tool_result")
428
+ ) {
429
+ previous.content.push(...content);
430
+ continue;
431
+ }
432
+
433
+ out.push({ role, content });
434
+ }
435
+ return out;
436
+ }
437
+
438
+ /**
439
+ * Map runtime `AgentToolDefinition[]` to legacy
440
+ * `LlmsProviders.ToolDefinition[]`. Structurally identical today —
441
+ * but typing them separately keeps the adapter explicit.
442
+ */
443
+ export function agentToolDefinitionsToToolDefinitions(
444
+ tools: AgentModelRequest["tools"],
445
+ ): ToolDefinition[] {
446
+ return tools.map((tool) => ({
447
+ name: tool.name,
448
+ description: tool.description,
449
+ inputSchema: tool.inputSchema,
450
+ }));
451
+ }
452
+
453
+ // =============================================================================
454
+ // Content-block / part translation
455
+ // =============================================================================
456
+
457
+ function normalizeContentBlocks(content: Message["content"]): ContentBlock[] {
458
+ if (typeof content === "string") {
459
+ return content.length > 0
460
+ ? [{ type: "text", text: content } as TextContent]
461
+ : [];
462
+ }
463
+ return [...content];
464
+ }
465
+
466
+ function contentBlockToAgentPart(block: ContentBlock): AgentMessagePart {
467
+ switch (block.type) {
468
+ case "text":
469
+ return { type: "text", text: block.text };
470
+ case "thinking":
471
+ return {
472
+ type: "reasoning",
473
+ text: block.thinking,
474
+ metadata: block.signature
475
+ ? { signature: block.signature, details: block.details }
476
+ : block.details
477
+ ? { details: block.details }
478
+ : undefined,
479
+ };
480
+ case "redacted_thinking":
481
+ return {
482
+ type: "reasoning",
483
+ text: "",
484
+ redacted: true,
485
+ metadata: { data: block.data },
486
+ };
487
+ case "image":
488
+ return {
489
+ type: "image",
490
+ image: block.data,
491
+ mediaType: block.mediaType,
492
+ };
493
+ case "file":
494
+ return {
495
+ type: "file",
496
+ path: block.path,
497
+ content: block.content,
498
+ };
499
+ case "tool_use":
500
+ return {
501
+ type: "tool-call",
502
+ toolCallId: block.id,
503
+ toolName: block.name,
504
+ input: block.input,
505
+ metadata: block.signature ? { signature: block.signature } : undefined,
506
+ };
507
+ case "tool_result":
508
+ return toolResultContentToAgentPart(block);
509
+ default: {
510
+ const _exhaustive: never = block;
511
+ return _exhaustive;
512
+ }
513
+ }
514
+ }
515
+
516
+ function toolResultContentToAgentPart(
517
+ block: ToolResultContent,
518
+ ): AgentMessagePart {
519
+ return {
520
+ type: "tool-result",
521
+ toolCallId: block.tool_use_id,
522
+ toolName: "",
523
+ output: block.content,
524
+ isError: block.is_error,
525
+ };
526
+ }
527
+
528
+ function agentPartToContentBlock(
529
+ part: AgentMessagePart,
530
+ ): ContentBlock | undefined {
531
+ switch (part.type) {
532
+ case "text":
533
+ return { type: "text", text: (part as AgentTextPart).text };
534
+ case "reasoning": {
535
+ if (part.redacted === true) {
536
+ const data =
537
+ (part.metadata as { data?: string } | undefined)?.data ?? "";
538
+ return {
539
+ type: "redacted_thinking",
540
+ data,
541
+ } satisfies RedactedThinkingContent;
542
+ }
543
+ const metadata = part.metadata as
544
+ | { signature?: string; details?: unknown[] }
545
+ | undefined;
546
+ return {
547
+ type: "thinking",
548
+ thinking: part.text,
549
+ signature: metadata?.signature,
550
+ details: metadata?.details,
551
+ } satisfies ThinkingContent;
552
+ }
553
+ case "image": {
554
+ if (typeof part.image !== "string") {
555
+ // Binary images are not round-trippable through the legacy
556
+ // `ImageContent.data: string` field; drop them.
557
+ return undefined;
558
+ }
559
+ return {
560
+ type: "image",
561
+ data: part.image,
562
+ mediaType: part.mediaType ?? "image/png",
563
+ } satisfies ImageContent;
564
+ }
565
+ case "file":
566
+ return {
567
+ type: "file",
568
+ path: part.path,
569
+ content: part.content,
570
+ } satisfies FileContent;
571
+ case "tool-call":
572
+ return {
573
+ type: "tool_use",
574
+ id: part.toolCallId,
575
+ name: part.toolName,
576
+ input: (part.input as Record<string, unknown>) ?? {},
577
+ signature: (part.metadata as { signature?: string } | undefined)
578
+ ?.signature,
579
+ } satisfies ToolUseContent;
580
+ case "tool-result": {
581
+ const output = part.output;
582
+ const content =
583
+ typeof output === "string"
584
+ ? output
585
+ : Array.isArray(output)
586
+ ? (output as ToolResultContent["content"])
587
+ : JSON.stringify(output);
588
+ return {
589
+ type: "tool_result",
590
+ tool_use_id: part.toolCallId,
591
+ content,
592
+ is_error: part.isError,
593
+ } satisfies ToolResultContent;
594
+ }
595
+ default: {
596
+ const _exhaustive: never = part;
597
+ return _exhaustive;
598
+ }
599
+ }
600
+ }
601
+
602
+ function metricsToAgentMetrics(
603
+ metrics: MessageWithMetadata["metrics"],
604
+ ): AgentMessage["metrics"] {
605
+ if (!metrics) {
606
+ return undefined;
607
+ }
608
+ return {
609
+ inputTokens: metrics.inputTokens ?? 0,
610
+ outputTokens: metrics.outputTokens ?? 0,
611
+ cacheReadTokens: metrics.cacheReadTokens ?? 0,
612
+ cacheWriteTokens: metrics.cacheWriteTokens ?? 0,
613
+ cost: metrics.cost,
614
+ };
615
+ }
616
+
617
+ function agentMetricsToMetrics(
618
+ metrics: AgentMessage["metrics"],
619
+ ): MessageWithMetadata["metrics"] {
620
+ if (!metrics) {
621
+ return undefined;
622
+ }
623
+ return {
624
+ inputTokens: metrics.inputTokens,
625
+ outputTokens: metrics.outputTokens,
626
+ cacheReadTokens: metrics.cacheReadTokens,
627
+ cacheWriteTokens: metrics.cacheWriteTokens,
628
+ cost: metrics.cost,
629
+ };
630
+ }
631
+
632
+ let _msgSeq = 0;
633
+ function generateMessageId(): string {
634
+ _msgSeq += 1;
635
+ return `msg_${Date.now().toString(36)}_${_msgSeq.toString(36)}`;
636
+ }