@iinm/plain-agent 1.8.2 → 1.8.4

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 (86) hide show
  1. package/README.md +6 -2
  2. package/bin/plain +1 -1
  3. package/config/config.predefined.json +1 -1
  4. package/config/prompts.predefined/shortcuts/configure.md +1 -1
  5. package/dist/main.mjs +473 -0
  6. package/dist/main.mjs.map +7 -0
  7. package/package.json +5 -7
  8. package/src/agent.d.ts +0 -52
  9. package/src/agent.mjs +0 -204
  10. package/src/agentLoop.mjs +0 -419
  11. package/src/agentState.mjs +0 -41
  12. package/src/claudeCodePlugin.mjs +0 -164
  13. package/src/cliArgs.mjs +0 -175
  14. package/src/cliBatch.mjs +0 -144
  15. package/src/cliCommands.mjs +0 -283
  16. package/src/cliCompleter.mjs +0 -227
  17. package/src/cliCost.mjs +0 -309
  18. package/src/cliFormatter.mjs +0 -413
  19. package/src/cliInteractive.mjs +0 -526
  20. package/src/cliInterruptTransform.mjs +0 -51
  21. package/src/cliMuteTransform.mjs +0 -26
  22. package/src/cliPasteTransform.mjs +0 -183
  23. package/src/config.d.ts +0 -36
  24. package/src/config.mjs +0 -197
  25. package/src/context/loadAgentRoles.mjs +0 -283
  26. package/src/context/loadPrompts.mjs +0 -324
  27. package/src/context/loadUserMessageContext.mjs +0 -147
  28. package/src/costTracker.mjs +0 -210
  29. package/src/env.mjs +0 -44
  30. package/src/main.mjs +0 -278
  31. package/src/mcpClient.mjs +0 -351
  32. package/src/mcpIntegration.mjs +0 -160
  33. package/src/model.d.ts +0 -109
  34. package/src/modelCaller.mjs +0 -32
  35. package/src/modelDefinition.d.ts +0 -92
  36. package/src/prompt.mjs +0 -138
  37. package/src/providers/anthropic.d.ts +0 -248
  38. package/src/providers/anthropic.mjs +0 -587
  39. package/src/providers/bedrock.d.ts +0 -249
  40. package/src/providers/bedrock.mjs +0 -700
  41. package/src/providers/gemini.d.ts +0 -208
  42. package/src/providers/gemini.mjs +0 -754
  43. package/src/providers/openai.d.ts +0 -281
  44. package/src/providers/openai.mjs +0 -544
  45. package/src/providers/openaiCompatible.d.ts +0 -147
  46. package/src/providers/openaiCompatible.mjs +0 -652
  47. package/src/providers/platform/awsSigV4.mjs +0 -184
  48. package/src/providers/platform/azure.mjs +0 -42
  49. package/src/providers/platform/bedrock.mjs +0 -78
  50. package/src/providers/platform/googleCloud.mjs +0 -34
  51. package/src/subagent.mjs +0 -265
  52. package/src/tmpfile.mjs +0 -27
  53. package/src/tool.d.ts +0 -74
  54. package/src/toolExecutor.mjs +0 -236
  55. package/src/toolInputValidator.mjs +0 -183
  56. package/src/toolUseApprover.mjs +0 -99
  57. package/src/tools/askURL.mjs +0 -209
  58. package/src/tools/askWeb.mjs +0 -208
  59. package/src/tools/compactContext.d.ts +0 -4
  60. package/src/tools/compactContext.mjs +0 -87
  61. package/src/tools/delegateToSubagent.d.ts +0 -4
  62. package/src/tools/delegateToSubagent.mjs +0 -48
  63. package/src/tools/execCommand.d.ts +0 -22
  64. package/src/tools/execCommand.mjs +0 -200
  65. package/src/tools/patchFile.d.ts +0 -4
  66. package/src/tools/patchFile.mjs +0 -133
  67. package/src/tools/reportAsSubagent.d.ts +0 -3
  68. package/src/tools/reportAsSubagent.mjs +0 -44
  69. package/src/tools/tmuxCommand.d.ts +0 -14
  70. package/src/tools/tmuxCommand.mjs +0 -194
  71. package/src/tools/writeFile.d.ts +0 -4
  72. package/src/tools/writeFile.mjs +0 -56
  73. package/src/usageStore.mjs +0 -167
  74. package/src/utils/evalJSONConfig.mjs +0 -72
  75. package/src/utils/matchValue.d.ts +0 -6
  76. package/src/utils/matchValue.mjs +0 -40
  77. package/src/utils/noThrow.mjs +0 -31
  78. package/src/utils/notify.mjs +0 -29
  79. package/src/utils/parseFileRange.mjs +0 -18
  80. package/src/utils/readFileRange.mjs +0 -33
  81. package/src/utils/retryOnError.mjs +0 -41
  82. package/src/voiceInput.mjs +0 -61
  83. package/src/voiceInputGemini.mjs +0 -105
  84. package/src/voiceInputOpenAI.mjs +0 -104
  85. package/src/voiceInputSession.mjs +0 -543
  86. package/src/voiceToggleKey.mjs +0 -62
@@ -1,700 +0,0 @@
1
- /**
2
- * @import { ModelInput, Message, AssistantMessage, ModelOutput, PartialMessageContent } from "../model";
3
- * @import { ToolDefinition } from "../tool";
4
- * @import { BedrockConverseModelConfig, BedrockMessage, BedrockContentBlock, BedrockAssistantContentBlock, BedrockAssistantContentBlockWithPartial, BedrockTool, BedrockStreamEvent, BedrockConverseRequest, BedrockUsage, BedrockToolResultContent } from "./bedrock";
5
- */
6
-
7
- import { styleText } from "node:util";
8
- import { noThrow } from "../utils/noThrow.mjs";
9
- import { loadAwsCredentials, signAwsRequest } from "./platform/awsSigV4.mjs";
10
- import { readBedrockStreamEvents } from "./platform/bedrock.mjs";
11
-
12
- /**
13
- * @param {import("../modelDefinition").PlatformConfig} platformConfig
14
- * @param {BedrockConverseModelConfig} modelConfig
15
- * @param {ModelInput} input
16
- * @param {number} [retryCount]
17
- * @returns {Promise<ModelOutput | Error>}
18
- */
19
- export async function callBedrockConverseModel(
20
- platformConfig,
21
- modelConfig,
22
- input,
23
- retryCount = 0,
24
- ) {
25
- return await noThrow(async () => {
26
- const messages = convertGenericMessageToBedrockFormat(input.messages);
27
- const cachedMessages = modelConfig.enablePromptCaching
28
- ? enablePromptCaching(messages)
29
- : messages;
30
- const tools = convertGenericToolDefinitionToBedrockFormat(
31
- input.tools || [],
32
- );
33
-
34
- const url = (() => {
35
- const baseURL = platformConfig.baseURL;
36
- if (platformConfig.name !== "bedrock") {
37
- throw new Error(`Unsupported platform: ${platformConfig.name}`);
38
- }
39
- return `${baseURL}/model/${modelConfig.model}/converse-stream`;
40
- })();
41
-
42
- const region = extractRegionFromBaseURL(platformConfig.baseURL);
43
-
44
- /** @type {BedrockConverseRequest} */
45
- const request = {
46
- messages: cachedMessages,
47
- ...(modelConfig.inferenceConfig && {
48
- inferenceConfig: modelConfig.inferenceConfig,
49
- }),
50
- ...(modelConfig.additionalModelRequestFields && {
51
- additionalModelRequestFields: modelConfig.additionalModelRequestFields,
52
- }),
53
- };
54
-
55
- // Add system messages if present
56
- const systemMessages = extractSystemMessages(
57
- input.messages,
58
- modelConfig.enablePromptCaching,
59
- );
60
- if (systemMessages.length > 0) {
61
- request.system = systemMessages;
62
- }
63
-
64
- // Add tools if present
65
- if (tools.length > 0) {
66
- request.toolConfig = {
67
- tools: tools,
68
- };
69
- }
70
-
71
- const payload = JSON.stringify(request);
72
-
73
- // Sign request with AWS Signature V4
74
- const credentials = await loadAwsCredentials(platformConfig.awsProfile);
75
- const urlParsed = new URL(url);
76
- const { hostname, pathname } = urlParsed;
77
-
78
- const signed = signAwsRequest(
79
- {
80
- method: "POST",
81
- hostname,
82
- path: pathname,
83
- headers: {
84
- host: hostname,
85
- "Content-Type": "application/json",
86
- },
87
- body: payload,
88
- },
89
- { region, service: "bedrock", credentials },
90
- );
91
-
92
- const response = await fetch(url, {
93
- method: signed.method,
94
- headers: signed.headers,
95
- body: signed.body,
96
- signal: AbortSignal.timeout(8 * 60 * 1000),
97
- });
98
-
99
- if (response.status !== 200) {
100
- const errorText = await response.text();
101
- console.error(
102
- styleText("red", `Bedrock API error: ${response.status} ${errorText}`),
103
- );
104
-
105
- // Retry on throttling or server errors
106
- if (
107
- (response.status === 429 ||
108
- response.status === 502 ||
109
- response.status === 503) &&
110
- retryCount < 3
111
- ) {
112
- const retryInterval = Math.min(2 * 2 ** retryCount, 16);
113
- console.error(
114
- styleText(
115
- "yellow",
116
- `Retrying in ${retryInterval} seconds... (attempt ${retryCount + 1})`,
117
- ),
118
- );
119
- await new Promise((resolve) =>
120
- setTimeout(resolve, retryInterval * 1000),
121
- );
122
- return callBedrockConverseModel(
123
- platformConfig,
124
- modelConfig,
125
- input,
126
- retryCount + 1,
127
- );
128
- }
129
-
130
- throw new Error(`Bedrock API error: ${response.status} ${errorText}`);
131
- }
132
-
133
- if (!response.body) {
134
- throw new Error("Response body is empty");
135
- }
136
-
137
- const reader = response.body.getReader();
138
-
139
- /** @type {BedrockAssistantContentBlockWithPartial[]} */
140
- const contentBlocks = [];
141
- /** @type {Record<number, BedrockAssistantContentBlockWithPartial>} */
142
- const contentBlockMap = {};
143
- /** @type {BedrockUsage | undefined} */
144
- let usage;
145
-
146
- // Process stream events
147
- for await (const event of readBedrockStreamEvents(reader)) {
148
- const bedrockEvent = /** @type {BedrockStreamEvent} */ (event);
149
-
150
- if (input.onPartialMessageContent) {
151
- const partialContents = convertBedrockStreamEventToPartialContent(
152
- bedrockEvent,
153
- contentBlockMap,
154
- );
155
- for (const partialContent of partialContents) {
156
- input.onPartialMessageContent(partialContent);
157
- }
158
- }
159
-
160
- // Handle Converse API events (flat structure)
161
- // Check for start event first
162
- if ("contentBlockIndex" in bedrockEvent && "start" in bedrockEvent) {
163
- const index = bedrockEvent.contentBlockIndex;
164
- const start = bedrockEvent.start;
165
-
166
- if (start.toolUse) {
167
- contentBlockMap[index] = {
168
- toolUse: {
169
- toolUseId: start.toolUse.toolUseId || "",
170
- name: start.toolUse.name || "",
171
- input: {},
172
- },
173
- };
174
- }
175
- }
176
-
177
- if ("contentBlockIndex" in bedrockEvent && "delta" in bedrockEvent) {
178
- const index = bedrockEvent.contentBlockIndex;
179
- const delta = bedrockEvent.delta;
180
-
181
- // Initialize content block if not exists
182
- if (!contentBlockMap[index]) {
183
- if (delta.text !== undefined) {
184
- contentBlockMap[index] = { text: "" };
185
- } else if (delta.toolUse) {
186
- contentBlockMap[index] = {
187
- toolUse: {
188
- toolUseId: delta.toolUse.toolUseId || "",
189
- name: delta.toolUse.name || "",
190
- input: {},
191
- },
192
- };
193
- } else if (delta.reasoningContent) {
194
- contentBlockMap[index] = {
195
- reasoningContent: {
196
- text: undefined,
197
- signature: undefined,
198
- redactedContent: undefined,
199
- },
200
- };
201
- }
202
- }
203
-
204
- const block = contentBlockMap[index];
205
-
206
- // Accumulate content
207
- if (block && delta.text !== undefined && "text" in block) {
208
- block.text += delta.text;
209
- } else if (
210
- block &&
211
- delta.toolUse &&
212
- "toolUse" in block &&
213
- block.toolUse
214
- ) {
215
- // Accumulate tool input as JSON string
216
- if (!block._partialInput) {
217
- block._partialInput = "";
218
- }
219
- block._partialInput += delta.toolUse.input || "";
220
- } else if (
221
- block &&
222
- delta.reasoningContent &&
223
- "reasoningContent" in block &&
224
- block.reasoningContent
225
- ) {
226
- if (delta.reasoningContent.text) {
227
- block.reasoningContent.text =
228
- (block.reasoningContent.text || "") + delta.reasoningContent.text;
229
- }
230
- if (delta.reasoningContent.signature) {
231
- block.reasoningContent.signature = delta.reasoningContent.signature;
232
- }
233
- if (delta.reasoningContent.redactedContent) {
234
- block.reasoningContent.redactedContent =
235
- delta.reasoningContent.redactedContent;
236
- }
237
- }
238
- }
239
-
240
- // Handle message stop
241
- if ("stopReason" in bedrockEvent) {
242
- // Finalize all content blocks
243
- for (const [_index, block] of Object.entries(contentBlockMap)) {
244
- // Parse accumulated tool input JSON
245
- if (
246
- block &&
247
- "toolUse" in block &&
248
- block.toolUse &&
249
- block._partialInput
250
- ) {
251
- try {
252
- block.toolUse.input = JSON.parse(block._partialInput);
253
- } catch (err) {
254
- console.error(
255
- styleText(
256
- "red",
257
- `Failed to parse tool input JSON for tool "${block.toolUse.name}": ${block._partialInput}`,
258
- ),
259
- );
260
- block.toolUse.input = {
261
- err: String(err),
262
- raw: block._partialInput,
263
- };
264
- }
265
- delete block._partialInput;
266
- }
267
- contentBlocks.push(block);
268
- }
269
- }
270
-
271
- // Handle metadata
272
- if ("usage" in bedrockEvent && "metrics" in bedrockEvent) {
273
- usage = bedrockEvent.usage;
274
- }
275
- }
276
-
277
- const message =
278
- convertBedrockContentBlocksToAssistantMessage(contentBlocks);
279
-
280
- const providerTokenUsage = usage
281
- ? {
282
- inputTokens: usage.inputTokens,
283
- outputTokens: usage.outputTokens,
284
- totalTokens: usage.totalTokens,
285
- ...(usage.cacheReadInputTokens && {
286
- cacheReadInputTokens: usage.cacheReadInputTokens,
287
- }),
288
- ...(usage.cacheWriteInputTokens && {
289
- cacheWriteInputTokens: usage.cacheWriteInputTokens,
290
- }),
291
- }
292
- : {};
293
-
294
- return {
295
- message,
296
- providerTokenUsage,
297
- };
298
- });
299
- }
300
-
301
- /**
302
- * @param {Message[]} messages
303
- * @returns {BedrockMessage[]}
304
- */
305
- function convertGenericMessageToBedrockFormat(messages) {
306
- /** @type {BedrockMessage[]} */
307
- const bedrockMessages = [];
308
-
309
- for (const message of messages) {
310
- if (message.role === "system") {
311
- // System messages handled separately
312
- continue;
313
- }
314
-
315
- if (message.role === "user") {
316
- /** @type {BedrockContentBlock[]} */
317
- const content = [];
318
-
319
- for (const part of message.content) {
320
- if (part.type === "text" && part.text) {
321
- // Only include non-empty text blocks
322
- content.push({ text: part.text });
323
- } else if (part.type === "image") {
324
- content.push({
325
- image: {
326
- format: /** @type {"png" | "jpeg" | "gif" | "webp"} */ (
327
- part.mimeType.split("/")[1]
328
- ),
329
- source: {
330
- bytes: part.data,
331
- },
332
- },
333
- });
334
- } else if (part.type === "tool_result") {
335
- /** @type {BedrockToolResultContent[]} */
336
- const toolResultContent = [];
337
- for (const resultPart of part.content) {
338
- if (resultPart.type === "text") {
339
- toolResultContent.push({ text: resultPart.text });
340
- } else if (resultPart.type === "image") {
341
- toolResultContent.push({
342
- image: {
343
- format: /** @type {"png" | "jpeg" | "gif" | "webp"} */ (
344
- resultPart.mimeType.split("/")[1]
345
- ),
346
- source: {
347
- bytes: resultPart.data,
348
- },
349
- },
350
- });
351
- }
352
- }
353
-
354
- content.push({
355
- toolResult: {
356
- toolUseId: part.toolUseId,
357
- content: toolResultContent,
358
- status: part.isError ? "error" : "success",
359
- },
360
- });
361
- }
362
- }
363
-
364
- bedrockMessages.push({ role: "user", content });
365
- } else if (message.role === "assistant") {
366
- /** @type {BedrockAssistantContentBlock[]} */
367
- const content = [];
368
-
369
- for (const part of message.content) {
370
- if (part.type === "text") {
371
- content.push({ text: part.text });
372
- } else if (part.type === "thinking") {
373
- // Extended thinking requires signature for multi-turn conversations
374
- const signature = /** @type {string | undefined} */ (
375
- part.provider?.fields?.signature
376
- );
377
- if (signature) {
378
- content.push({
379
- reasoningContent: {
380
- reasoningText: {
381
- text: part.thinking,
382
- signature,
383
- },
384
- },
385
- });
386
- }
387
- } else if (part.type === "redacted_thinking") {
388
- // Redacted thinking must be included in message history
389
- const data = /** @type {string | undefined} */ (
390
- part.provider?.fields?.data
391
- );
392
- if (data) {
393
- content.push({
394
- reasoningContent: {
395
- redactedContent: data,
396
- },
397
- });
398
- }
399
- } else if (part.type === "tool_use") {
400
- content.push({
401
- toolUse: {
402
- toolUseId: part.toolUseId,
403
- name: part.toolName,
404
- input: part.input,
405
- },
406
- });
407
- }
408
- }
409
-
410
- bedrockMessages.push({ role: "assistant", content });
411
- }
412
- }
413
-
414
- return bedrockMessages;
415
- }
416
-
417
- /**
418
- * @param {Message[]} messages
419
- * @param {boolean} [enablePromptCaching]
420
- * @returns {import("./bedrock").BedrockSystemContentBlock[]}
421
- */
422
- function extractSystemMessages(messages, enablePromptCaching = false) {
423
- /** @type {import("./bedrock").BedrockSystemContentBlock[]} */
424
- const systemBlocks = [];
425
-
426
- for (const message of messages) {
427
- if (message.role === "system") {
428
- for (const part of message.content) {
429
- systemBlocks.push({ text: part.text });
430
- }
431
- }
432
- }
433
-
434
- // Add cache point at the end of system messages if enabled
435
- if (enablePromptCaching && systemBlocks.length > 0) {
436
- systemBlocks.push({ cachePoint: { type: "default" } });
437
- }
438
-
439
- return systemBlocks;
440
- }
441
-
442
- /**
443
- * @param {ToolDefinition[]} tools
444
- * @returns {BedrockTool[]}
445
- */
446
- function convertGenericToolDefinitionToBedrockFormat(tools) {
447
- return tools.map((tool) => ({
448
- toolSpec: {
449
- name: tool.name,
450
- description: tool.description,
451
- inputSchema: {
452
- json: tool.inputSchema,
453
- },
454
- },
455
- }));
456
- }
457
-
458
- /**
459
- * @param {BedrockMessage[]} messages
460
- * @returns {BedrockMessage[]}
461
- */
462
- function enablePromptCaching(messages) {
463
- // Find user message indices
464
- const userMessageIndices = messages
465
- .map((msg, index) => (msg.role === "user" ? index : -1))
466
- .filter((index) => index !== -1);
467
-
468
- // Target last two user messages for caching
469
- const cacheTargetIndices = [
470
- userMessageIndices.at(-1),
471
- userMessageIndices.at(-2),
472
- ].filter((index) => index !== undefined);
473
-
474
- const cachedMessages = messages.map((message, index) => {
475
- if (cacheTargetIndices.includes(index)) {
476
- // Add cache point as a separate block at the end
477
- // Only add to messages without tool results (tool results don't support cachePoint)
478
- if (message.role === "user") {
479
- const content = /** @type {BedrockContentBlock[]} */ ([
480
- ...message.content,
481
- ]);
482
- // Check if content contains toolResult
483
- const hasToolResult = content.some(
484
- (block) => "toolResult" in block && block.toolResult,
485
- );
486
- if (!hasToolResult) {
487
- content.push({ cachePoint: { type: "default" } });
488
- return { ...message, content };
489
- }
490
- }
491
- if (message.role === "assistant") {
492
- const content = /** @type {BedrockAssistantContentBlock[]} */ ([
493
- ...message.content,
494
- ]);
495
- content.push({ cachePoint: { type: "default" } });
496
- return { ...message, content };
497
- }
498
- }
499
- return message;
500
- });
501
-
502
- return cachedMessages;
503
- }
504
-
505
- /**
506
- * @param {BedrockStreamEvent} event
507
- * @param {Record<number, import("./bedrock").BedrockAssistantContentBlockWithPartial>} contentBlockMap
508
- * @returns {PartialMessageContent[]}
509
- */
510
- function convertBedrockStreamEventToPartialContent(event, contentBlockMap) {
511
- /** @type {PartialMessageContent[]} */
512
- const partialContents = [];
513
-
514
- // Handle Converse API events (flat structure)
515
- // Note: Don't send message start event here
516
- // Each content block will send its own start event
517
-
518
- // Handle tool use start event
519
- if ("contentBlockIndex" in event && "start" in event) {
520
- const index = event.contentBlockIndex;
521
- const start = event.start;
522
-
523
- // Send stop event for previous block if exists
524
- if (index > 0 && contentBlockMap[index - 1]) {
525
- const prevBlock = contentBlockMap[index - 1];
526
- const prevType = prevBlock.text
527
- ? "text"
528
- : prevBlock.toolUse
529
- ? "tool_use"
530
- : prevBlock.reasoningContent
531
- ? "thinking"
532
- : "unknown";
533
-
534
- partialContents.push({
535
- type: prevType,
536
- position: "stop",
537
- });
538
- }
539
-
540
- if (start.toolUse) {
541
- partialContents.push({
542
- type: "tool_use",
543
- position: "start",
544
- content: JSON.stringify({
545
- toolUseId: start.toolUse.toolUseId,
546
- name: start.toolUse.name,
547
- }),
548
- });
549
- }
550
- }
551
-
552
- if ("contentBlockIndex" in event && "delta" in event) {
553
- const delta = event.delta;
554
- const index = event.contentBlockIndex;
555
-
556
- // Check if this is a new block (no entry in contentBlockMap)
557
- // If so, send stop event for previous block first
558
- if (!contentBlockMap[index] && index > 0 && contentBlockMap[index - 1]) {
559
- const prevBlock = contentBlockMap[index - 1];
560
- const prevType = prevBlock.text
561
- ? "text"
562
- : prevBlock.toolUse
563
- ? "tool_use"
564
- : prevBlock.reasoningContent
565
- ? "thinking"
566
- : "unknown";
567
-
568
- partialContents.push({
569
- type: prevType,
570
- position: "stop",
571
- });
572
- }
573
-
574
- if (delta.text !== undefined) {
575
- // Send start event if this is a new text block
576
- if (!contentBlockMap[index]) {
577
- partialContents.push({
578
- type: "text",
579
- position: "start",
580
- content: "",
581
- });
582
- }
583
- partialContents.push({
584
- type: "text",
585
- position: "delta",
586
- content: delta.text,
587
- });
588
- } else if (delta.toolUse) {
589
- // Don't send tool input deltas to onPartialMessageContent
590
- // Tool input will be shown when tool call is complete
591
- } else if (delta.reasoningContent) {
592
- // Send start event if this is a new reasoningContent block
593
- if (!contentBlockMap[index]) {
594
- partialContents.push({
595
- type: "thinking",
596
- position: "start",
597
- content: "",
598
- });
599
- }
600
- // Reasoning content (text or redactedContent)
601
- if (delta.reasoningContent.text) {
602
- partialContents.push({
603
- type: "thinking",
604
- position: "delta",
605
- content: delta.reasoningContent.text,
606
- });
607
- }
608
- // Note: redactedContent is encrypted, so we don't display it
609
- // but we still need to track it for the final message
610
- }
611
- }
612
-
613
- if ("stopReason" in event) {
614
- // Message stop event
615
- const blocks = Object.values(contentBlockMap);
616
- if (blocks.length > 0) {
617
- const lastBlock = blocks[blocks.length - 1];
618
- const type =
619
- lastBlock && "text" in lastBlock
620
- ? "text"
621
- : lastBlock && "toolUse" in lastBlock
622
- ? "tool_use"
623
- : lastBlock && "reasoningContent" in lastBlock
624
- ? "thinking"
625
- : "unknown";
626
-
627
- partialContents.push({
628
- type,
629
- position: "stop",
630
- });
631
- }
632
- }
633
-
634
- return partialContents;
635
- }
636
-
637
- /**
638
- * @param {BedrockAssistantContentBlockWithPartial[]} contentBlocks
639
- * @returns {AssistantMessage}
640
- */
641
- function convertBedrockContentBlocksToAssistantMessage(contentBlocks) {
642
- /** @type {AssistantMessage["content"]} */
643
- const content = [];
644
-
645
- for (const block of contentBlocks) {
646
- if (block.text) {
647
- // Only include non-empty text blocks
648
- content.push({
649
- type: "text",
650
- text: block.text,
651
- });
652
- } else if (block.toolUse) {
653
- content.push({
654
- type: "tool_use",
655
- toolUseId: block.toolUse.toolUseId || "",
656
- toolName: block.toolUse.name || "",
657
- input:
658
- /** @type {Record<string, unknown>} */ (block.toolUse.input) ??
659
- /** @type {Record<string, unknown>} */ ({}),
660
- });
661
- } else if (block.reasoningContent) {
662
- // Reasoning content
663
- if (block.reasoningContent.text) {
664
- content.push({
665
- type: "thinking",
666
- thinking: block.reasoningContent.text,
667
- ...(block.reasoningContent.signature && {
668
- provider: {
669
- fields: { signature: block.reasoningContent.signature },
670
- },
671
- }),
672
- });
673
- } else if (block.reasoningContent.redactedContent) {
674
- content.push({
675
- type: "redacted_thinking",
676
- provider: {
677
- fields: { data: block.reasoningContent.redactedContent },
678
- },
679
- });
680
- }
681
- }
682
- }
683
-
684
- return {
685
- role: "assistant",
686
- content,
687
- };
688
- }
689
-
690
- /**
691
- * @param {string} baseURL
692
- * @returns {string}
693
- */
694
- function extractRegionFromBaseURL(baseURL) {
695
- const match = baseURL.match(/bedrock-runtime\.([^.]+)\.amazonaws\.com/);
696
- if (!match) {
697
- throw new Error(`Failed to extract region from baseURL: ${baseURL}`);
698
- }
699
- return match[1];
700
- }