@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,587 +0,0 @@
1
- /**
2
- * @import { ModelInput, Message, AssistantMessage, ModelOutput, PartialMessageContent } from "../model";
3
- * @import { AnthropicChatCompletion, AnthropicMessage, AnthropicToolDefinition, AnthropicModelConfig, AnthropicAssistantMessage, AnthropicStreamEvent, AnthropicAssistantMessageContent, AnthropicChatCompletionUsage, AnthropicRequestInput } from "./anthropic";
4
- * @import { ToolDefinition } from "../tool";
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
- import { getGoogleCloudAccessToken } from "./platform/googleCloud.mjs";
12
-
13
- /**
14
- * @param {import("../modelDefinition").PlatformConfig} platformConfig
15
- * @param {AnthropicModelConfig} modelConfig
16
- * @param {ModelInput} input
17
- * @param {number} [retryCount]
18
- * @returns {Promise<ModelOutput | Error>}
19
- */
20
- export async function callAnthropicModel(
21
- platformConfig,
22
- modelConfig,
23
- input,
24
- retryCount = 0,
25
- ) {
26
- return await noThrow(async () => {
27
- const messages = convertGenericMessageToAnthropicFormat(input.messages);
28
- const cacheEnabledMessages = enableContextCaching(messages);
29
- const tools = convertGenericToolDefinitionToAnthropicFormat(
30
- input.tools || [],
31
- );
32
-
33
- const url = (() => {
34
- const baseURL = platformConfig.baseURL;
35
-
36
- switch (platformConfig.name) {
37
- case "anthropic":
38
- return `${baseURL}/v1/messages`;
39
- case "bedrock":
40
- return `${baseURL}/model/${modelConfig.model}/invoke-with-response-stream`;
41
- case "vertex-ai":
42
- return `${baseURL}/publishers/anthropic/models/${modelConfig.model}:streamRawPredict`;
43
- default:
44
- throw new Error(`Unsupported platform: ${platformConfig.name}`);
45
- }
46
- })();
47
-
48
- /** @type {Record<string,string>} */
49
- const headers = await (async () => {
50
- switch (platformConfig.name) {
51
- case "anthropic":
52
- return {
53
- ...platformConfig.customHeaders,
54
- "anthropic-version": "2023-06-01",
55
- "x-api-key": `${platformConfig.apiKey}`,
56
- };
57
- case "bedrock":
58
- return platformConfig.customHeaders ?? {};
59
- case "vertex-ai":
60
- return {
61
- ...platformConfig.customHeaders,
62
- Authorization: `Bearer ${await getGoogleCloudAccessToken()}`,
63
- };
64
- }
65
- })();
66
-
67
- const { model: _, ...modelConfigWithoutName } = modelConfig;
68
- const platformRequest = (() => {
69
- switch (platformConfig.name) {
70
- case "anthropic":
71
- return {
72
- ...modelConfig,
73
- stream: true,
74
- };
75
- case "bedrock":
76
- return {
77
- anthropic_version: "bedrock-2023-05-31",
78
- ...modelConfigWithoutName,
79
- };
80
- case "vertex-ai":
81
- return {
82
- anthropic_version: "vertex-2023-10-16",
83
- stream: true,
84
- ...modelConfigWithoutName,
85
- };
86
- }
87
- })();
88
-
89
- /** @type {AnthropicRequestInput} */
90
- const request = {
91
- ...platformRequest,
92
- system: messages
93
- .filter((m) => m.role === "system")
94
- .flatMap((m) => m.content),
95
- messages: cacheEnabledMessages.filter((m) => m.role !== "system"),
96
- tools: tools.length ? tools : undefined,
97
- };
98
-
99
- const runFetchDefault = async () =>
100
- fetch(url, {
101
- method: "POST",
102
- headers: {
103
- ...headers,
104
- "Content-Type": "application/json",
105
- },
106
- body: JSON.stringify(request),
107
- signal: AbortSignal.timeout(8 * 60 * 1000),
108
- });
109
-
110
- // bedrock + sso profile
111
- const runFetchForBedrock = async () => {
112
- const region =
113
- url.match(/bedrock-runtime\.([\w-]+)\.amazonaws\.com/)?.[1] ?? "";
114
- const urlParsed = new URL(url);
115
- const { hostname, pathname } = urlParsed;
116
-
117
- const credentials = await loadAwsCredentials(
118
- platformConfig.name === "bedrock" ? platformConfig.awsProfile : "",
119
- );
120
-
121
- const signed = signAwsRequest(
122
- {
123
- method: "POST",
124
- hostname,
125
- path: pathname,
126
- headers: {
127
- host: hostname,
128
- "Content-Type": "application/json",
129
- },
130
- body: JSON.stringify(request),
131
- },
132
- { region, service: "bedrock", credentials },
133
- );
134
-
135
- return fetch(url, {
136
- method: signed.method,
137
- headers: signed.headers,
138
- body: signed.body,
139
- signal: AbortSignal.timeout(8 * 60 * 1000),
140
- });
141
- };
142
-
143
- const runFetch =
144
- platformConfig.name === "bedrock" ? runFetchForBedrock : runFetchDefault;
145
-
146
- const response = await runFetch();
147
-
148
- if (response.status === 429 || response.status >= 500) {
149
- const interval = Math.min(2 * 2 ** retryCount, 16);
150
- console.error(
151
- styleText(
152
- "yellow",
153
- `Anthropic rate limit exceeded. Retry in ${interval} seconds...`,
154
- ),
155
- );
156
- await new Promise((resolve) => setTimeout(resolve, interval * 1000));
157
- return callAnthropicModel(
158
- platformConfig,
159
- modelConfig,
160
- input,
161
- retryCount + 1,
162
- );
163
- }
164
-
165
- if (response.status !== 200) {
166
- return new Error(
167
- `Failed to call Anthropic model: status=${response.status}, body=${await response.text()}`,
168
- );
169
- }
170
-
171
- if (!response.body) {
172
- throw new Error("Response body is empty");
173
- }
174
-
175
- const reader = response.body.getReader();
176
- const eventStreamReader =
177
- platformConfig.name === "bedrock"
178
- ? /** @type {typeof readAnthropicStreamEvents} */ (
179
- readBedrockStreamEvents
180
- )
181
- : readAnthropicStreamEvents;
182
-
183
- /** @type {AnthropicStreamEvent[]} */
184
- const events = [];
185
- /** @type {PartialMessageContent | undefined} */
186
- let previousPartialContent;
187
- for await (const event of eventStreamReader(reader)) {
188
- events.push(event);
189
-
190
- const partialContent = convertAnthropicStreamEventToAgentPartialContent(
191
- event,
192
- previousPartialContent,
193
- );
194
-
195
- if (partialContent) {
196
- previousPartialContent = partialContent;
197
- }
198
-
199
- if (input.onPartialMessageContent && partialContent) {
200
- input.onPartialMessageContent(partialContent);
201
- }
202
- }
203
-
204
- /** @type {AnthropicChatCompletion} */
205
- const chatCompletion = convertAnthropicStreamEventsToChatCompletion(events);
206
-
207
- return {
208
- message: convertAnthropicAssistantMessageToGenericFormat(chatCompletion),
209
- providerTokenUsage: chatCompletion.usage,
210
- };
211
- });
212
- }
213
-
214
- /**
215
- * @param {Message[]} genericMessages
216
- * @returns {AnthropicMessage[]}
217
- */
218
- function convertGenericMessageToAnthropicFormat(genericMessages) {
219
- /** @type {AnthropicMessage[]} */
220
- const anthropicMessages = [];
221
- for (const genericMessage of genericMessages) {
222
- switch (genericMessage.role) {
223
- case "system": {
224
- anthropicMessages.push({
225
- role: "system",
226
- content: genericMessage.content.map((part) => {
227
- if (part.type === "text") {
228
- return { type: "text", text: part.text };
229
- }
230
- throw new Error(
231
- `Unsupported content part: ${JSON.stringify(part)}`,
232
- );
233
- }),
234
- });
235
- break;
236
- }
237
- case "user": {
238
- anthropicMessages.push({
239
- role: "user",
240
- content: genericMessage.content.map((part) => {
241
- if (part.type === "text") {
242
- return { type: "text", text: part.text };
243
- }
244
- if (part.type === "image") {
245
- return {
246
- type: "image",
247
- source: {
248
- type: "base64",
249
- media_type: part.mimeType,
250
- data: part.data,
251
- },
252
- };
253
- }
254
- if (part.type === "tool_result") {
255
- return {
256
- type: "tool_result",
257
- tool_use_id: part.toolUseId,
258
- content: part.content.map((contentPart) => {
259
- switch (contentPart.type) {
260
- case "text":
261
- return { type: "text", text: contentPart.text };
262
- case "image":
263
- return {
264
- type: "image",
265
- source: {
266
- type: "base64",
267
- media_type: contentPart.mimeType,
268
- data: contentPart.data,
269
- },
270
- };
271
- default:
272
- throw new Error(
273
- `Unsupported content part: ${JSON.stringify(contentPart)}`,
274
- );
275
- }
276
- }),
277
- is_error: part.isError,
278
- };
279
- }
280
- throw new Error(
281
- `Unsupported content part: ${JSON.stringify(part)}`,
282
- );
283
- }),
284
- });
285
- break;
286
- }
287
- case "assistant": {
288
- anthropicMessages.push({
289
- role: "assistant",
290
- content: genericMessage.content.map((part) => {
291
- if (part.type === "thinking") {
292
- const signature = /** @type {string} */ (
293
- part.provider?.fields?.signature
294
- );
295
- return {
296
- type: "thinking",
297
- thinking: part.thinking,
298
- signature,
299
- };
300
- }
301
- if (part.type === "redacted_thinking") {
302
- const data = /** @type {string} */ (part.provider?.fields?.data);
303
- return {
304
- type: "redacted_thinking",
305
- data,
306
- };
307
- }
308
- if (part.type === "text") {
309
- return { type: "text", text: part.text };
310
- }
311
- if (part.type === "tool_use") {
312
- return {
313
- type: "tool_use",
314
- id: part.toolUseId,
315
- name: part.toolName,
316
- input: part.input,
317
- };
318
- }
319
- throw new Error(`Unknown message part type: ${part}`);
320
- }),
321
- });
322
- break;
323
- }
324
- }
325
- }
326
-
327
- return anthropicMessages;
328
- }
329
-
330
- /**
331
- * @param {ToolDefinition[]} genericToolDefs
332
- * @returns {AnthropicToolDefinition[]}
333
- */
334
- function convertGenericToolDefinitionToAnthropicFormat(genericToolDefs) {
335
- /** @type {AnthropicToolDefinition[]} */
336
- const anthropicToolDefs = [];
337
- for (const tool of genericToolDefs) {
338
- anthropicToolDefs.push({
339
- name: tool.name,
340
- description: tool.description,
341
- input_schema: tool.inputSchema,
342
- });
343
- }
344
- return anthropicToolDefs;
345
- }
346
-
347
- /**
348
- * @param {AnthropicAssistantMessage} anthropicAssistantMessage
349
- * @returns {AssistantMessage}
350
- */
351
- function convertAnthropicAssistantMessageToGenericFormat(
352
- anthropicAssistantMessage,
353
- ) {
354
- /** @type {AssistantMessage["content"]} */
355
- const content = [];
356
- for (const part of anthropicAssistantMessage.content) {
357
- if (part.type === "thinking") {
358
- content.push({
359
- type: "thinking",
360
- thinking: part.thinking,
361
- provider: { fields: { signature: part.signature } },
362
- });
363
- } else if (part.type === "redacted_thinking") {
364
- content.push({
365
- type: "redacted_thinking",
366
- provider: { fields: { data: part.data } },
367
- });
368
- } else if (part.type === "text") {
369
- content.push({ type: "text", text: part.text });
370
- } else if (part.type === "tool_use") {
371
- content.push({
372
- type: "tool_use",
373
- toolUseId: part.id,
374
- toolName: part.name,
375
- input: part.input,
376
- });
377
- }
378
- }
379
-
380
- return {
381
- role: "assistant",
382
- content,
383
- };
384
- }
385
-
386
- /**
387
- * @param {AnthropicStreamEvent[]} events
388
- * @returns {AnthropicChatCompletion}
389
- */
390
- function convertAnthropicStreamEventsToChatCompletion(events) {
391
- /** @type {Partial<AnthropicChatCompletion>} */
392
- let chatCompletion = {};
393
- /** @type {string[]} */
394
- const toolUseInputJsonBuffer = [];
395
- for (const event of events) {
396
- if (event.type === "message_start") {
397
- chatCompletion = Object.assign(chatCompletion, event.message);
398
- } else if (event.type === "message_delta") {
399
- Object.assign(chatCompletion, event.delta);
400
- if (event.usage?.output_tokens) {
401
- const usage = /** @type {AnthropicChatCompletionUsage} */ (
402
- chatCompletion.usage || {}
403
- );
404
- usage.output_tokens += event.usage.output_tokens;
405
- chatCompletion.usage = usage;
406
- }
407
- } else if (event.type === "content_block_start") {
408
- chatCompletion.content = chatCompletion.content || [];
409
- chatCompletion.content.push(
410
- /** @type {AnthropicAssistantMessageContent} */ (event.content_block),
411
- );
412
- } else if (event.type === "content_block_delta") {
413
- const lastContentPart = chatCompletion.content?.at(-1);
414
- if (lastContentPart) {
415
- switch (event.delta.type) {
416
- case "text_delta": {
417
- if (lastContentPart.type === "text") {
418
- lastContentPart.text = lastContentPart.text + event.delta.text;
419
- }
420
- break;
421
- }
422
- case "thinking_delta": {
423
- if (lastContentPart.type === "thinking") {
424
- lastContentPart.thinking =
425
- lastContentPart.thinking + event.delta.thinking;
426
- }
427
- break;
428
- }
429
- case "signature_delta": {
430
- if (lastContentPart.type === "thinking") {
431
- lastContentPart.signature = event.delta.signature;
432
- }
433
- break;
434
- }
435
- case "input_json_delta": {
436
- if (lastContentPart.type === "tool_use") {
437
- toolUseInputJsonBuffer.push(event.delta.partial_json);
438
- }
439
- break;
440
- }
441
- }
442
- } else {
443
- console.error(
444
- `Received content block delta without a content block: ${JSON.stringify(event)}`,
445
- );
446
- }
447
- } else if (event.type === "content_block_stop") {
448
- const lastContentPart = chatCompletion.content?.at(-1);
449
- if (lastContentPart?.type === "tool_use") {
450
- lastContentPart.input = JSON.parse(
451
- toolUseInputJsonBuffer.join("") || "{}",
452
- );
453
- toolUseInputJsonBuffer.length = 0;
454
- }
455
- }
456
- }
457
-
458
- return /** @type {AnthropicChatCompletion} */ (chatCompletion);
459
- }
460
-
461
- /**
462
- * @param {AnthropicStreamEvent} event
463
- * @param {PartialMessageContent | undefined} previousPartialContent
464
- * @returns {PartialMessageContent | undefined}
465
- */
466
- function convertAnthropicStreamEventToAgentPartialContent(
467
- event,
468
- previousPartialContent,
469
- ) {
470
- switch (event.type) {
471
- case "content_block_start":
472
- return {
473
- type: event.content_block.type,
474
- position: "start",
475
- };
476
- case "content_block_delta":
477
- switch (event.delta.type) {
478
- case "text_delta":
479
- return {
480
- type: "text",
481
- content: event.delta.text,
482
- position: "delta",
483
- };
484
- case "thinking_delta":
485
- return {
486
- type: "thinking",
487
- content: event.delta.thinking,
488
- position: "delta",
489
- };
490
- case "input_json_delta":
491
- return {
492
- type: "tool_use",
493
- content: event.delta.partial_json,
494
- position: "delta",
495
- };
496
- }
497
- break;
498
- case "content_block_stop":
499
- return {
500
- type: previousPartialContent?.type || "unknown",
501
- position: "stop",
502
- };
503
- }
504
- }
505
-
506
- /**
507
- * @param {AnthropicMessage[]} messages
508
- * @returns {AnthropicMessage[]}
509
- */
510
- function enableContextCaching(messages) {
511
- /** @type {number[]} */
512
- const userMessageIndices = [];
513
- for (let i = 0; i < messages.length; i++) {
514
- if (messages[i].role === "user") {
515
- userMessageIndices.push(i);
516
- }
517
- }
518
- const cacheTargetIndices = [
519
- // last user message
520
- userMessageIndices.at(-1),
521
- // second last user message
522
- userMessageIndices.at(-2),
523
- ].filter((index) => index !== undefined);
524
-
525
- const contextCachingEnabledMessages = messages.map((message, index) => {
526
- if (
527
- (index === 0 && message.role === "system") ||
528
- cacheTargetIndices.includes(index)
529
- ) {
530
- return {
531
- ...message,
532
- content: message.content.map((part, partIndex) =>
533
- partIndex === message.content.length - 1
534
- ? { ...part, cache_control: { type: "ephemeral" } }
535
- : part,
536
- ),
537
- };
538
- }
539
- return message;
540
- });
541
-
542
- return /** @type {AnthropicMessage[]} */ (contextCachingEnabledMessages);
543
- }
544
-
545
- /**
546
- * @param {ReadableStreamDefaultReader<Uint8Array>} reader
547
- */
548
- async function* readAnthropicStreamEvents(reader) {
549
- let buffer = new Uint8Array();
550
-
551
- while (true) {
552
- const { done, value } = await reader.read();
553
- if (done) {
554
- break;
555
- }
556
-
557
- const nextBuffer = new Uint8Array(buffer.length + value.length);
558
- nextBuffer.set(buffer);
559
- nextBuffer.set(value, buffer.length);
560
- buffer = nextBuffer;
561
-
562
- const lineFeed = "\n".charCodeAt(0);
563
- const eventEndIndices = [];
564
- for (let i = 0; i < buffer.length - 1; i++) {
565
- if (buffer[i] === lineFeed && buffer[i + 1] === lineFeed) {
566
- eventEndIndices.push(i);
567
- }
568
- }
569
-
570
- for (let i = 0; i < eventEndIndices.length; i++) {
571
- const eventStartIndex = i === 0 ? 0 : eventEndIndices[i - 1] + 2;
572
- const eventEndIndex = eventEndIndices[i];
573
- const event = buffer.slice(eventStartIndex, eventEndIndex);
574
- const decodedEvent = new TextDecoder().decode(event);
575
- const data = decodedEvent.split("\n").at(-1);
576
- if (data?.startsWith("data: ")) {
577
- /** @type {AnthropicStreamEvent} */
578
- const parsedData = JSON.parse(data.slice("data: ".length));
579
- yield parsedData;
580
- }
581
- }
582
-
583
- if (eventEndIndices.length) {
584
- buffer = buffer.slice(eventEndIndices[eventEndIndices.length - 1] + 2);
585
- }
586
- }
587
- }