@iinm/plain-agent 1.10.28 → 1.11.1

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.
package/README.md CHANGED
@@ -1,18 +1,21 @@
1
1
  # Plain Agent
2
2
 
3
3
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/iinm/plain-agent)
4
- [![Socket Badge](https://badge.socket.dev/npm/package/@iinm/plain-agent/1.10.28)](https://socket.dev/npm/package/@iinm/plain-agent)
4
+ [![npm version](https://img.shields.io/npm/v/@iinm/plain-agent)](https://www.npmjs.com/package/@iinm/plain-agent)
5
+ [![install size](https://packagephobia.com/badge?p=@iinm/plain-agent)](https://packagephobia.com/result?p=@iinm/plain-agent)
6
+ [![Socket Badge](https://badge.socket.dev/npm/package/@iinm/plain-agent/1.11.1)](https://socket.dev/npm/package/@iinm/plain-agent)
7
+ [![CodeQL](https://github.com/iinm/plain-agent/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/iinm/plain-agent/actions/workflows/github-code-scanning/codeql)
5
8
 
6
9
  A lightweight terminal-based coding agent focused on safety and low token cost
7
10
 
8
11
  ## Table of Contents
9
12
 
10
13
  - [Design](#design)
11
- - [Multi-provider support](#multi-provider-support)
12
- - [Auto-approval](#auto-approval)
14
+ - [Multi-Provider Support](#multi-provider-support)
15
+ - [Auto-Approval](#auto-approval)
13
16
  - [Path Validation](#path-validation)
14
17
  - [Sandbox](#sandbox)
15
- - [Memory file](#memory-file)
18
+ - [Memory File](#memory-file)
16
19
  - [Token Efficiency](#token-efficiency)
17
20
  - [Claude Code Compatibility](#claude-code-compatibility)
18
21
  - [Requirements](#requirements)
@@ -28,7 +31,7 @@ A lightweight terminal-based coding agent focused on safety and low token cost
28
31
 
29
32
  ## Design
30
33
 
31
- ### Multi-provider support
34
+ ### Multi-Provider Support
32
35
 
33
36
  Supports Claude, OpenAI, Gemini, and any OpenAI-compatible provider. Bedrock, Vertex AI, and Azure are also supported for teams working in environments restricted to managed cloud providers.
34
37
 
@@ -83,10 +86,12 @@ Models are identified by `name+variant` (e.g., `claude-sonnet-4-6+thinking-high`
83
86
 
84
87
  You can also add entries to `platforms` and `models` to use any OpenAI-compatible endpoint, such as Ollama or Fireworks. See the Quick Start section for examples.
85
88
 
86
- ### Auto-approval
89
+ ### Auto-Approval
87
90
 
88
91
  Configure what the agent can do automatically using a small DSL with regex matching. Below is an excerpt from the [default config](https://github.com/iinm/plain-agent/blob/main/config/config.predefined.json).
89
92
 
93
+ **Note**: Commands are executed without a shell — shell operators like `&&`, `|`, `;`, and redirects are not interpreted unless the agent explicitly uses `bash -c`. This makes each argument a discrete token that can be validated individually.
94
+
90
95
  ```js
91
96
  {
92
97
  "autoApproval": {
@@ -162,8 +167,6 @@ String values in tool inputs are treated as file paths and validated against the
162
167
  - Symlinks are resolved to their real path before validation — a symlink inside the working directory that points outside is rejected. Broken and circular symlinks are also rejected.
163
168
  - The file must be tracked by Git (not ignored)
164
169
 
165
- Commands are executed without a shell — shell operators like `&&`, `|`, `;`, and redirects are not interpreted unless the agent explicitly uses `bash -c`. This makes each argument a discrete token that can be validated individually.
166
-
167
170
  Compound arguments are decomposed before validation — embedded paths are extracted and checked individually:
168
171
 
169
172
  | Pattern | Example | Extracted |
@@ -176,7 +179,7 @@ Compound arguments are decomposed before validation — embedded paths are extra
176
179
 
177
180
  `--opt=<val>`, `-X<val>`, and `VAR=<val>` are checked recursively, so chained patterns like `-DINSTALL_DIR=/etc` decompose fully (`-D` → `INSTALL_DIR=/etc` → `/etc`).
178
181
 
179
- **Note**: validation only applies when the agent explicitly passes file paths to tools. It cannot catch file access inside scripts the agent writes — something like `bash -c "rm -rf /"` is beyond its reach. Always use a sandbox when auto-approving script execution.
182
+ **Note**: Validation only applies when the agent explicitly passes file paths to tools. It cannot catch file access inside scripts the agent writes — something like `bash -c "rm -rf /"` is beyond its reach. Always use a sandbox when auto-approving script execution.
180
183
 
181
184
  ### Sandbox
182
185
 
@@ -216,7 +219,7 @@ A Docker-based wrapper called `plain-sandbox` is included, but the interface is
216
219
  }
217
220
  ```
218
221
 
219
- ### Memory file
222
+ ### Memory File
220
223
 
221
224
  The agent maintains a memory file (`.plain-agent/memory/`) for each session to:
222
225
 
@@ -450,7 +453,7 @@ Create a configuration file.
450
453
  "cost": {
451
454
  "currency": "USD",
452
455
  "unit": "1M",
453
- "costs": {
456
+ "prices": {
454
457
  "input_tokens": 1.1,
455
458
  "output_tokens": 5.5,
456
459
  "cache_read_input_tokens": 0.11,
@@ -477,7 +480,7 @@ Create a configuration file.
477
480
  "cost": {
478
481
  "currency": "USD",
479
482
  "unit": "1M",
480
- "costs": {
483
+ "prices": {
481
484
  "input_tokens": 3.3,
482
485
  "output_tokens": 16.5,
483
486
  "cache_read_input_tokens": 0.33,
@@ -448,7 +448,7 @@
448
448
  "cost": {
449
449
  "currency": "USD",
450
450
  "unit": "1M",
451
- "costs": {
451
+ "prices": {
452
452
  "input_tokens": 1.0,
453
453
  "output_tokens": 5.0,
454
454
  "cache_read_input_tokens": 0.1,
@@ -475,7 +475,7 @@
475
475
  "cost": {
476
476
  "currency": "USD",
477
477
  "unit": "1M",
478
- "costs": {
478
+ "prices": {
479
479
  "input_tokens": 1.0,
480
480
  "output_tokens": 5.0,
481
481
  "cache_read_input_tokens": 0.1,
@@ -503,7 +503,7 @@
503
503
  "cost": {
504
504
  "currency": "USD",
505
505
  "unit": "1M",
506
- "costs": {
506
+ "prices": {
507
507
  "input_tokens": 3,
508
508
  "output_tokens": 15,
509
509
  "cache_read_input_tokens": 0.3,
@@ -531,7 +531,7 @@
531
531
  "cost": {
532
532
  "currency": "USD",
533
533
  "unit": "1M",
534
- "costs": {
534
+ "prices": {
535
535
  "input_tokens": 3,
536
536
  "output_tokens": 15,
537
537
  "cache_read_input_tokens": 0.3,
@@ -559,7 +559,7 @@
559
559
  "cost": {
560
560
  "currency": "USD",
561
561
  "unit": "1M",
562
- "costs": {
562
+ "prices": {
563
563
  "input_tokens": 5,
564
564
  "output_tokens": 25,
565
565
  "cache_read_input_tokens": 0.5,
@@ -587,7 +587,7 @@
587
587
  "cost": {
588
588
  "currency": "USD",
589
589
  "unit": "1M",
590
- "costs": {
590
+ "prices": {
591
591
  "input_tokens": 5,
592
592
  "output_tokens": 25,
593
593
  "cache_read_input_tokens": 0.5,
@@ -614,7 +614,7 @@
614
614
  "cost": {
615
615
  "currency": "USD",
616
616
  "unit": "1M",
617
- "costs": {
617
+ "prices": {
618
618
  "input_tokens": 1.0,
619
619
  "output_tokens": 5.0,
620
620
  "cache_read_input_tokens": 0.1,
@@ -640,7 +640,7 @@
640
640
  "cost": {
641
641
  "currency": "USD",
642
642
  "unit": "1M",
643
- "costs": {
643
+ "prices": {
644
644
  "input_tokens": 1.0,
645
645
  "output_tokens": 5.0,
646
646
  "cache_read_input_tokens": 0.1,
@@ -667,7 +667,7 @@
667
667
  "cost": {
668
668
  "currency": "USD",
669
669
  "unit": "1M",
670
- "costs": {
670
+ "prices": {
671
671
  "input_tokens": 3,
672
672
  "output_tokens": 15,
673
673
  "cache_read_input_tokens": 0.3,
@@ -694,7 +694,7 @@
694
694
  "cost": {
695
695
  "currency": "USD",
696
696
  "unit": "1M",
697
- "costs": {
697
+ "prices": {
698
698
  "input_tokens": 3,
699
699
  "output_tokens": 15,
700
700
  "cache_read_input_tokens": 0.3,
@@ -721,7 +721,7 @@
721
721
  "cost": {
722
722
  "currency": "USD",
723
723
  "unit": "1M",
724
- "costs": {
724
+ "prices": {
725
725
  "input_tokens": 5,
726
726
  "output_tokens": 25,
727
727
  "cache_read_input_tokens": 0.5,
@@ -748,7 +748,7 @@
748
748
  "cost": {
749
749
  "currency": "USD",
750
750
  "unit": "1M",
751
- "costs": {
751
+ "prices": {
752
752
  "input_tokens": 5,
753
753
  "output_tokens": 25,
754
754
  "cache_read_input_tokens": 0.5,
@@ -769,15 +769,11 @@
769
769
  "format": "gemini",
770
770
  "config": {
771
771
  "model": "gemini-3.5-flash",
772
- "config": {
773
- "requestConfig": {
774
- "generationConfig": {
775
- "maxOutputTokens": 32768,
776
- "thinkingConfig": {
777
- "includeThoughts": true,
778
- "thinkingLevel": "medium"
779
- }
780
- }
772
+ "generationConfig": {
773
+ "maxOutputTokens": 32768,
774
+ "thinkingConfig": {
775
+ "includeThoughts": true,
776
+ "thinkingLevel": "medium"
781
777
  }
782
778
  }
783
779
  }
@@ -785,7 +781,7 @@
785
781
  "cost": {
786
782
  "currency": "USD",
787
783
  "unit": "1M",
788
- "costs": {
784
+ "prices": {
789
785
  "promptTokenCount": 1.5,
790
786
  "cachedContentTokenCount": -1.35,
791
787
  "candidatesTokenCount": 9
@@ -804,15 +800,11 @@
804
800
  "format": "gemini",
805
801
  "config": {
806
802
  "model": "gemini-3.5-flash",
807
- "config": {
808
- "requestConfig": {
809
- "generationConfig": {
810
- "maxOutputTokens": 65536,
811
- "thinkingConfig": {
812
- "includeThoughts": true,
813
- "thinkingLevel": "high"
814
- }
815
- }
803
+ "generationConfig": {
804
+ "maxOutputTokens": 65536,
805
+ "thinkingConfig": {
806
+ "includeThoughts": true,
807
+ "thinkingLevel": "high"
816
808
  }
817
809
  }
818
810
  }
@@ -820,7 +812,7 @@
820
812
  "cost": {
821
813
  "currency": "USD",
822
814
  "unit": "1M",
823
- "costs": {
815
+ "prices": {
824
816
  "promptTokenCount": 1.5,
825
817
  "cachedContentTokenCount": -1.35,
826
818
  "candidatesTokenCount": 9
@@ -839,15 +831,11 @@
839
831
  "format": "gemini",
840
832
  "config": {
841
833
  "model": "gemini-3.1-pro-preview",
842
- "config": {
843
- "requestConfig": {
844
- "generationConfig": {
845
- "maxOutputTokens": 32768,
846
- "thinkingConfig": {
847
- "includeThoughts": true,
848
- "thinkingLevel": "medium"
849
- }
850
- }
834
+ "generationConfig": {
835
+ "maxOutputTokens": 32768,
836
+ "thinkingConfig": {
837
+ "includeThoughts": true,
838
+ "thinkingLevel": "medium"
851
839
  }
852
840
  }
853
841
  }
@@ -855,7 +843,7 @@
855
843
  "cost": {
856
844
  "currency": "USD",
857
845
  "unit": "1M",
858
- "costs": {
846
+ "prices": {
859
847
  "promptTokenCount": 2,
860
848
  "cachedContentTokenCount": -1.8,
861
849
  "candidatesTokenCount": 12
@@ -874,15 +862,11 @@
874
862
  "format": "gemini",
875
863
  "config": {
876
864
  "model": "gemini-3.1-pro-preview",
877
- "config": {
878
- "requestConfig": {
879
- "generationConfig": {
880
- "maxOutputTokens": 65536,
881
- "thinkingConfig": {
882
- "includeThoughts": true,
883
- "thinkingLevel": "high"
884
- }
885
- }
865
+ "generationConfig": {
866
+ "maxOutputTokens": 65536,
867
+ "thinkingConfig": {
868
+ "includeThoughts": true,
869
+ "thinkingLevel": "high"
886
870
  }
887
871
  }
888
872
  }
@@ -890,7 +874,7 @@
890
874
  "cost": {
891
875
  "currency": "USD",
892
876
  "unit": "1M",
893
- "costs": {
877
+ "prices": {
894
878
  "promptTokenCount": 2,
895
879
  "cachedContentTokenCount": -1.8,
896
880
  "candidatesTokenCount": 12
@@ -909,15 +893,11 @@
909
893
  "format": "gemini",
910
894
  "config": {
911
895
  "model": "gemini-3.5-flash",
912
- "config": {
913
- "requestConfig": {
914
- "generationConfig": {
915
- "maxOutputTokens": 32768,
916
- "thinkingConfig": {
917
- "includeThoughts": true,
918
- "thinkingLevel": "medium"
919
- }
920
- }
896
+ "generationConfig": {
897
+ "maxOutputTokens": 32768,
898
+ "thinkingConfig": {
899
+ "includeThoughts": true,
900
+ "thinkingLevel": "medium"
921
901
  }
922
902
  }
923
903
  }
@@ -925,7 +905,7 @@
925
905
  "cost": {
926
906
  "currency": "USD",
927
907
  "unit": "1M",
928
- "costs": {
908
+ "prices": {
929
909
  "promptTokenCount": 1.5,
930
910
  "cachedContentTokenCount": -1.35,
931
911
  "candidatesTokenCount": 9
@@ -943,15 +923,11 @@
943
923
  "format": "gemini",
944
924
  "config": {
945
925
  "model": "gemini-3.5-flash",
946
- "config": {
947
- "requestConfig": {
948
- "generationConfig": {
949
- "maxOutputTokens": 65536,
950
- "thinkingConfig": {
951
- "includeThoughts": true,
952
- "thinkingLevel": "high"
953
- }
954
- }
926
+ "generationConfig": {
927
+ "maxOutputTokens": 65536,
928
+ "thinkingConfig": {
929
+ "includeThoughts": true,
930
+ "thinkingLevel": "high"
955
931
  }
956
932
  }
957
933
  }
@@ -959,7 +935,7 @@
959
935
  "cost": {
960
936
  "currency": "USD",
961
937
  "unit": "1M",
962
- "costs": {
938
+ "prices": {
963
939
  "promptTokenCount": 1.5,
964
940
  "cachedContentTokenCount": -1.35,
965
941
  "candidatesTokenCount": 9
@@ -977,15 +953,11 @@
977
953
  "format": "gemini",
978
954
  "config": {
979
955
  "model": "gemini-3.1-pro-preview",
980
- "config": {
981
- "requestConfig": {
982
- "generationConfig": {
983
- "maxOutputTokens": 32768,
984
- "thinkingConfig": {
985
- "includeThoughts": true,
986
- "thinkingLevel": "medium"
987
- }
988
- }
956
+ "generationConfig": {
957
+ "maxOutputTokens": 32768,
958
+ "thinkingConfig": {
959
+ "includeThoughts": true,
960
+ "thinkingLevel": "medium"
989
961
  }
990
962
  }
991
963
  }
@@ -993,7 +965,7 @@
993
965
  "cost": {
994
966
  "currency": "USD",
995
967
  "unit": "1M",
996
- "costs": {
968
+ "prices": {
997
969
  "promptTokenCount": 2,
998
970
  "cachedContentTokenCount": -1.8,
999
971
  "candidatesTokenCount": 12
@@ -1011,15 +983,11 @@
1011
983
  "format": "gemini",
1012
984
  "config": {
1013
985
  "model": "gemini-3.1-pro-preview",
1014
- "config": {
1015
- "requestConfig": {
1016
- "generationConfig": {
1017
- "maxOutputTokens": 65536,
1018
- "thinkingConfig": {
1019
- "includeThoughts": true,
1020
- "thinkingLevel": "high"
1021
- }
1022
- }
986
+ "generationConfig": {
987
+ "maxOutputTokens": 65536,
988
+ "thinkingConfig": {
989
+ "includeThoughts": true,
990
+ "thinkingLevel": "high"
1023
991
  }
1024
992
  }
1025
993
  }
@@ -1027,7 +995,7 @@
1027
995
  "cost": {
1028
996
  "currency": "USD",
1029
997
  "unit": "1M",
1030
- "costs": {
998
+ "prices": {
1031
999
  "promptTokenCount": 2,
1032
1000
  "cachedContentTokenCount": -1.8,
1033
1001
  "candidatesTokenCount": 12
@@ -1055,7 +1023,7 @@
1055
1023
  "cost": {
1056
1024
  "currency": "USD",
1057
1025
  "unit": "1M",
1058
- "costs": {
1026
+ "prices": {
1059
1027
  "input_tokens": 0.75,
1060
1028
  "input_tokens_details.cached_tokens": -0.675,
1061
1029
  "output_tokens": 4.5
@@ -1082,7 +1050,7 @@
1082
1050
  "cost": {
1083
1051
  "currency": "USD",
1084
1052
  "unit": "1M",
1085
- "costs": {
1053
+ "prices": {
1086
1054
  "input_tokens": 0.75,
1087
1055
  "input_tokens_details.cached_tokens": -0.675,
1088
1056
  "output_tokens": 4.5
@@ -1109,7 +1077,7 @@
1109
1077
  "cost": {
1110
1078
  "currency": "USD",
1111
1079
  "unit": "1M",
1112
- "costs": {
1080
+ "prices": {
1113
1081
  "input_tokens": 0.75,
1114
1082
  "input_tokens_details.cached_tokens": -0.675,
1115
1083
  "output_tokens": 4.5
@@ -1136,7 +1104,7 @@
1136
1104
  "cost": {
1137
1105
  "currency": "USD",
1138
1106
  "unit": "1M",
1139
- "costs": {
1107
+ "prices": {
1140
1108
  "input_tokens": 5,
1141
1109
  "input_tokens_details.cached_tokens": -4.5,
1142
1110
  "output_tokens": 30
@@ -1163,7 +1131,7 @@
1163
1131
  "cost": {
1164
1132
  "currency": "USD",
1165
1133
  "unit": "1M",
1166
- "costs": {
1134
+ "prices": {
1167
1135
  "input_tokens": 5,
1168
1136
  "input_tokens_details.cached_tokens": -4.5,
1169
1137
  "output_tokens": 30
@@ -1190,7 +1158,7 @@
1190
1158
  "cost": {
1191
1159
  "currency": "USD",
1192
1160
  "unit": "1M",
1193
- "costs": {
1161
+ "prices": {
1194
1162
  "input_tokens": 5,
1195
1163
  "input_tokens_details.cached_tokens": -4.5,
1196
1164
  "output_tokens": 30
@@ -1217,7 +1185,7 @@
1217
1185
  "cost": {
1218
1186
  "currency": "USD",
1219
1187
  "unit": "1M",
1220
- "costs": {
1188
+ "prices": {
1221
1189
  "input_tokens": 0.75,
1222
1190
  "input_tokens_details.cached_tokens": -0.675,
1223
1191
  "output_tokens": 4.5
@@ -1243,7 +1211,7 @@
1243
1211
  "cost": {
1244
1212
  "currency": "USD",
1245
1213
  "unit": "1M",
1246
- "costs": {
1214
+ "prices": {
1247
1215
  "input_tokens": 0.75,
1248
1216
  "input_tokens_details.cached_tokens": -0.675,
1249
1217
  "output_tokens": 4.5
@@ -1269,7 +1237,7 @@
1269
1237
  "cost": {
1270
1238
  "currency": "USD",
1271
1239
  "unit": "1M",
1272
- "costs": {
1240
+ "prices": {
1273
1241
  "input_tokens": 0.75,
1274
1242
  "input_tokens_details.cached_tokens": -0.675,
1275
1243
  "output_tokens": 4.5
@@ -1295,7 +1263,7 @@
1295
1263
  "cost": {
1296
1264
  "currency": "USD",
1297
1265
  "unit": "1M",
1298
- "costs": {
1266
+ "prices": {
1299
1267
  "input_tokens": 5,
1300
1268
  "input_tokens_details.cached_tokens": -4.5,
1301
1269
  "output_tokens": 30
@@ -1321,7 +1289,7 @@
1321
1289
  "cost": {
1322
1290
  "currency": "USD",
1323
1291
  "unit": "1M",
1324
- "costs": {
1292
+ "prices": {
1325
1293
  "input_tokens": 5,
1326
1294
  "input_tokens_details.cached_tokens": -4.5,
1327
1295
  "output_tokens": 30
@@ -1347,7 +1315,7 @@
1347
1315
  "cost": {
1348
1316
  "currency": "USD",
1349
1317
  "unit": "1M",
1350
- "costs": {
1318
+ "prices": {
1351
1319
  "input_tokens": 5,
1352
1320
  "input_tokens_details.cached_tokens": -4.5,
1353
1321
  "output_tokens": 30
@@ -1371,7 +1339,7 @@
1371
1339
  "cost": {
1372
1340
  "currency": "USD",
1373
1341
  "unit": "1M",
1374
- "costs": {
1342
+ "prices": {
1375
1343
  "prompt_tokens": 0.15,
1376
1344
  "prompt_tokens_details.cached_tokens": -0.14,
1377
1345
  "completion_tokens": 0.6
@@ -1395,7 +1363,7 @@
1395
1363
  "cost": {
1396
1364
  "currency": "USD",
1397
1365
  "unit": "1M",
1398
- "costs": {
1366
+ "prices": {
1399
1367
  "prompt_tokens": 1,
1400
1368
  "completion_tokens": 3.2,
1401
1369
  "prompt_tokens_details.cached_tokens": -0.9
@@ -1419,7 +1387,7 @@
1419
1387
  "cost": {
1420
1388
  "currency": "USD",
1421
1389
  "unit": "1M",
1422
- "costs": {
1390
+ "prices": {
1423
1391
  "prompt_tokens": 1,
1424
1392
  "completion_tokens": 3.2
1425
1393
  }
@@ -1442,7 +1410,7 @@
1442
1410
  "cost": {
1443
1411
  "currency": "USD",
1444
1412
  "unit": "1M",
1445
- "costs": {
1413
+ "prices": {
1446
1414
  "prompt_tokens": 1.4,
1447
1415
  "prompt_tokens_details.cached_tokens": -1.14,
1448
1416
  "completion_tokens": 4.4
@@ -1465,7 +1433,7 @@
1465
1433
  "cost": {
1466
1434
  "currency": "USD",
1467
1435
  "unit": "1M",
1468
- "costs": {
1436
+ "prices": {
1469
1437
  "prompt_tokens": 1.4,
1470
1438
  "prompt_tokens_details.cached_tokens": -1.14,
1471
1439
  "completion_tokens": 4.4
@@ -1489,7 +1457,7 @@
1489
1457
  "cost": {
1490
1458
  "currency": "USD",
1491
1459
  "unit": "1M",
1492
- "costs": {
1460
+ "prices": {
1493
1461
  "prompt_tokens": 0.95,
1494
1462
  "prompt_tokens_details.cached_tokens": -0.79,
1495
1463
  "completion_tokens": 4
@@ -1512,7 +1480,7 @@
1512
1480
  "cost": {
1513
1481
  "currency": "USD",
1514
1482
  "unit": "1M",
1515
- "costs": {
1483
+ "prices": {
1516
1484
  "prompt_tokens": 0.95,
1517
1485
  "prompt_tokens_details.cached_tokens": -0.79,
1518
1486
  "completion_tokens": 4
@@ -1551,7 +1519,7 @@
1551
1519
  "cost": {
1552
1520
  "currency": "USD",
1553
1521
  "unit": "1M",
1554
- "costs": {
1522
+ "prices": {
1555
1523
  "prompt_tokens": 1.74,
1556
1524
  "prompt_tokens_details.cached_tokens": -1.595,
1557
1525
  "completion_tokens": 3.48
@@ -1574,7 +1542,7 @@
1574
1542
  "cost": {
1575
1543
  "currency": "USD",
1576
1544
  "unit": "1M",
1577
- "costs": {
1545
+ "prices": {
1578
1546
  "prompt_tokens": 1.74,
1579
1547
  "prompt_tokens_details.cached_tokens": -1.6,
1580
1548
  "completion_tokens": 3.48
@@ -1613,7 +1581,7 @@
1613
1581
  "cost": {
1614
1582
  "currency": "USD",
1615
1583
  "unit": "1M",
1616
- "costs": {
1584
+ "prices": {
1617
1585
  "prompt_tokens": 0.3,
1618
1586
  "prompt_tokens_details.cached_tokens": -0.24,
1619
1587
  "completion_tokens": 1.2
@@ -1636,7 +1604,7 @@
1636
1604
  "cost": {
1637
1605
  "currency": "USD",
1638
1606
  "unit": "1M",
1639
- "costs": {
1607
+ "prices": {
1640
1608
  "prompt_tokens": 0.3,
1641
1609
  "prompt_tokens_details.cached_tokens": -0.24,
1642
1610
  "completion_tokens": 1.2
@@ -1659,7 +1627,7 @@
1659
1627
  "cost": {
1660
1628
  "currency": "USD",
1661
1629
  "unit": "1M",
1662
- "costs": {
1630
+ "prices": {
1663
1631
  "prompt_tokens": 0.3,
1664
1632
  "prompt_tokens_details.cached_tokens": -0.24,
1665
1633
  "completion_tokens": 1.2
@@ -1683,7 +1651,7 @@
1683
1651
  "cost": {
1684
1652
  "currency": "USD",
1685
1653
  "unit": "1M",
1686
- "costs": {
1654
+ "prices": {
1687
1655
  "prompt_tokens": 0.5,
1688
1656
  "prompt_tokens_details.cached_tokens": -0.4,
1689
1657
  "completion_tokens": 3
@@ -1706,7 +1674,7 @@
1706
1674
  "cost": {
1707
1675
  "currency": "USD",
1708
1676
  "unit": "1M",
1709
- "costs": {
1677
+ "prices": {
1710
1678
  "prompt_tokens": 0.6,
1711
1679
  "completion_tokens": 3.6
1712
1680
  }
@@ -1728,7 +1696,7 @@
1728
1696
  "cost": {
1729
1697
  "currency": "USD",
1730
1698
  "unit": "1M",
1731
- "costs": {
1699
+ "prices": {
1732
1700
  "prompt_tokens": 1.5625,
1733
1701
  "prompt_tokens_details.cached_tokens": -1.4375,
1734
1702
  "completion_tokens": 3.75
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iinm/plain-agent",
3
- "version": "1.10.28",
3
+ "version": "1.11.1",
4
4
  "description": "A lightweight terminal-based coding agent focused on safety and low token cost",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -32,9 +32,10 @@
32
32
  "node": ">=22"
33
33
  },
34
34
  "scripts": {
35
- "check": "npm run lint && tsc && npm run test && npm run test:predefined-approval",
36
- "test": "node --test",
37
- "coverage": "node --experimental-test-coverage --test-coverage-exclude='src/**/*.test.mjs' --test",
35
+ "check": "npm run lint && tsc && npm run test && npm run test:e2e && npm run test:predefined-approval",
36
+ "test": "node --test 'src/**/*.test.mjs'",
37
+ "test:e2e": "node --test --test-timeout=30000 'e2e/**/*.test.mjs'",
38
+ "coverage": "c8 --exclude='src/**/*.test.mjs' --exclude='e2e/**' node --test --test-force-exit 'src/**/*.test.mjs' 'e2e/**/*.test.mjs'",
38
39
  "test:predefined-approval": "bash -c 'set -e; mkdir -p tmp; cd tmp; env HOME=. ../bin/plain test-approval'",
39
40
  "lint": "npx @biomejs/biome check",
40
41
  "fix": "npx @biomejs/biome check --fix",
@@ -44,6 +45,7 @@
44
45
  "devDependencies": {
45
46
  "@biomejs/biome": "^2.4.12",
46
47
  "@types/node": "^22.19.17",
48
+ "c8": "^11.0.0",
47
49
  "typescript": "^6.0.2"
48
50
  }
49
51
  }
@@ -20,7 +20,7 @@
20
20
  * @typedef {Object} CostConfig
21
21
  * @property {string} currency
22
22
  * @property {string} unit
23
- * @property {Record<string, number>} costs
23
+ * @property {Record<string, number>} prices
24
24
  */
25
25
 
26
26
  /**
@@ -49,15 +49,15 @@ function validateCostConfig(config) {
49
49
  if (typeof c.unit !== "string") {
50
50
  throw new TypeError("CostConfig.unit must be a string");
51
51
  }
52
- if (typeof c.costs !== "object" || c.costs === null) {
53
- throw new TypeError("CostConfig.costs must be an object");
52
+ if (typeof c.prices !== "object" || c.prices === null) {
53
+ throw new TypeError("CostConfig.prices must be an object");
54
54
  }
55
55
  for (const [key, value] of Object.entries(
56
- /** @type {Record<string, unknown>} */ (c.costs),
56
+ /** @type {Record<string, unknown>} */ (c.prices),
57
57
  )) {
58
58
  if (typeof value !== "number") {
59
59
  throw new TypeError(
60
- `CostConfig.costs["${key}"] must be a number, got ${typeof value}`,
60
+ `CostConfig.prices["${key}"] must be a number, got ${typeof value}`,
61
61
  );
62
62
  }
63
63
  }
@@ -200,16 +200,16 @@ function calculateCostFromConfig(aggregated, config) {
200
200
  for (const [key, tokens] of Object.entries(aggregated)) {
201
201
  breakdown[key] = Object.freeze({ tokens, cost: undefined });
202
202
 
203
- if (!config?.costs?.[key]) {
203
+ if (!config?.prices?.[key]) {
204
204
  continue;
205
205
  }
206
206
 
207
- const costValue = config.costs[key];
207
+ const costValue = config.prices[key];
208
208
  const unitSize = parseUnit(config.unit);
209
209
 
210
210
  if (typeof costValue !== "number") {
211
211
  throw new TypeError(
212
- `config.costs["${key}"] must be a number, got ${typeof costValue}`,
212
+ `config.prices["${key}"] must be a number, got ${typeof costValue}`,
213
213
  );
214
214
  }
215
215
 
@@ -222,7 +222,7 @@ function calculateCostFromConfig(aggregated, config) {
222
222
  currency: config?.currency ?? "USD",
223
223
  unit: config?.unit ?? "1M",
224
224
  breakdown,
225
- totalCost: config?.costs ? totalCost : undefined,
225
+ totalCost: config?.prices ? totalCost : undefined,
226
226
  });
227
227
  }
228
228
 
@@ -88,5 +88,5 @@ export type ModelConfig =
88
88
  export type CostConfig = {
89
89
  currency: string;
90
90
  unit: string;
91
- costs: Record<string, number>;
91
+ prices: Record<string, number>;
92
92
  };
@@ -2,10 +2,8 @@
2
2
  export type GeminiModelConfig = {
3
3
  // https://ai.google.dev/gemini-api/docs/models
4
4
  model: string;
5
- requestConfig?: {
6
- generationConfig: GeminiGenerationConfig;
7
- safetySettings?: GeminiSafetySetting[];
8
- };
5
+ generationConfig?: GeminiGenerationConfig;
6
+ safetySettings?: GeminiSafetySetting[];
9
7
  };
10
8
 
11
9
  // https://ai.google.dev/api/generate-content
@@ -21,7 +21,7 @@ import { getGoogleCloudAccessToken } from "./platform/googleCloud.mjs";
21
21
  * - https://ai.google.dev/gemini-api/docs/caching
22
22
  * - https://ai.google.dev/api/caching
23
23
  * @param {import("../modelDefinition").PlatformConfig} platformConfig
24
- * @param {Pick<GeminiModelConfig, "model">} modelConfig
24
+ * @param {GeminiModelConfig} modelConfig
25
25
  * @returns {GeminiModelCaller}
26
26
  */
27
27
  export function createCacheEnabledGeminiModelCaller(
@@ -99,12 +99,11 @@ export function createCacheEnabledGeminiModelCaller(
99
99
  }
100
100
  })();
101
101
 
102
+ const { model, ...modelConfigWithoutName } = modelConfig;
103
+
102
104
  /** @type {Pick<GeminiGenerateContentInput, "generationConfig" | "safetySettings">} */
103
105
  const baseRequest = {
104
- // default
105
- generationConfig: {
106
- temperature: 0,
107
- },
106
+ generationConfig: {},
108
107
  safetySettings: [
109
108
  {
110
109
  category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
@@ -117,7 +116,7 @@ export function createCacheEnabledGeminiModelCaller(
117
116
  threshold: "BLOCK_NONE",
118
117
  },
119
118
  ],
120
- ...config.requestConfig,
119
+ ...modelConfigWithoutName,
121
120
  };
122
121
 
123
122
  /** @type {GeminiGenerateContentInput} */