diff --git a/apps/web-roo-code/src/lib/stats.ts b/apps/web-roo-code/src/lib/stats.ts
index 4d43ca6ed68..35bb2190aa2 100644
--- a/apps/web-roo-code/src/lib/stats.ts
+++ b/apps/web-roo-code/src/lib/stats.ts
@@ -1,4 +1,7 @@
export async function getGitHubStars() {
+ if (process.env.CI) {
+ return null
+ }
try {
const res = await fetch("https://api.github.com/repos/RooCodeInc/Roo-Code")
const data = await res.json()
@@ -16,6 +19,9 @@ export async function getGitHubStars() {
}
export async function getVSCodeReviews() {
+ if (process.env.CI) {
+ return []
+ }
const res = await fetch("https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery", {
method: "POST",
headers: {
@@ -60,6 +66,9 @@ export async function getVSCodeReviews() {
}
export async function getVSCodeDownloads() {
+ if (process.env.CI) {
+ return null
+ }
const res = await fetch("https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery", {
method: "POST",
headers: {
diff --git a/locales/ca/README.md b/locales/ca/README.md
index 58bc761ba15..c653f54fbbb 100644
--- a/locales/ca/README.md
+++ b/locales/ca/README.md
@@ -186,34 +186,35 @@ Gràcies a tots els nostres col·laboradors que han ajudat a millorar Roo Code!
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## Llicència
diff --git a/locales/de/README.md b/locales/de/README.md
index 671bd503b74..24f1e4d69e5 100644
--- a/locales/de/README.md
+++ b/locales/de/README.md
@@ -186,34 +186,35 @@ Danke an alle unsere Mitwirkenden, die geholfen haben, Roo Code zu verbessern!
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## Lizenz
diff --git a/locales/es/README.md b/locales/es/README.md
index 07635a2f1d5..adeca0a876c 100644
--- a/locales/es/README.md
+++ b/locales/es/README.md
@@ -186,34 +186,35 @@ Usamos [changesets](https://github.com/changesets/changesets) para versionar y p
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## Licencia
diff --git a/locales/fr/README.md b/locales/fr/README.md
index 45ef8c4208c..80140b85fb1 100644
--- a/locales/fr/README.md
+++ b/locales/fr/README.md
@@ -186,34 +186,35 @@ Merci à tous nos contributeurs qui ont aidé à améliorer Roo Code !
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## Licence
diff --git a/locales/hi/README.md b/locales/hi/README.md
index 24dc9d1d00f..9f20242d899 100644
--- a/locales/hi/README.md
+++ b/locales/hi/README.md
@@ -186,34 +186,35 @@ Roo Code को बेहतर बनाने में मदद करने
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## लाइसेंस
diff --git a/locales/id/README.md b/locales/id/README.md
index 0a0fe7bea54..3381b865b01 100644
--- a/locales/id/README.md
+++ b/locales/id/README.md
@@ -180,34 +180,35 @@ Terima kasih kepada semua kontributor kami yang telah membantu membuat Roo Code
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## License
diff --git a/locales/it/README.md b/locales/it/README.md
index fbae96f2dd4..964db4ff347 100644
--- a/locales/it/README.md
+++ b/locales/it/README.md
@@ -186,34 +186,35 @@ Grazie a tutti i nostri contributori che hanno aiutato a migliorare Roo Code!
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## Licenza
diff --git a/locales/ja/README.md b/locales/ja/README.md
index 63f0042a8b8..90c32409fcc 100644
--- a/locales/ja/README.md
+++ b/locales/ja/README.md
@@ -186,34 +186,35 @@ Roo Codeの改善に貢献してくれたすべての貢献者に感謝します
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## ライセンス
diff --git a/locales/ko/README.md b/locales/ko/README.md
index c218927831d..dce892ffb9e 100644
--- a/locales/ko/README.md
+++ b/locales/ko/README.md
@@ -186,34 +186,35 @@ Roo Code를 더 좋게 만드는 데 도움을 준 모든 기여자에게 감사
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## 라이선스
diff --git a/locales/nl/README.md b/locales/nl/README.md
index 0945b7ce7bf..7ca8251e73f 100644
--- a/locales/nl/README.md
+++ b/locales/nl/README.md
@@ -186,34 +186,35 @@ Dank aan alle bijdragers die Roo Code beter hebben gemaakt!
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## Licentie
diff --git a/locales/pl/README.md b/locales/pl/README.md
index a68c80ae8d4..351d755a295 100644
--- a/locales/pl/README.md
+++ b/locales/pl/README.md
@@ -186,34 +186,35 @@ Dziękujemy wszystkim naszym współtwórcom, którzy pomogli ulepszyć Roo Code
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## Licencja
diff --git a/locales/pt-BR/README.md b/locales/pt-BR/README.md
index 0234615ac15..d773d8fcca5 100644
--- a/locales/pt-BR/README.md
+++ b/locales/pt-BR/README.md
@@ -186,34 +186,35 @@ Obrigado a todos os nossos contribuidores que ajudaram a tornar o Roo Code melho
|

KJ7LNW|

a8trejo|

ColemanRoo|

canrobins13|

stea9499|

joemanley201|
|

System233|

jquanton|

nissa-seru|

NyxJae|

jr|

MuriloFP|
|

elianiva|

d-oit|

punkpeye|

wkordalski|

sachasayan|

Smartsheet-JB-Brown|
-|

monotykamary|

cannuri|

feifei325|

zhangtony239|

qdaxb|

xyOz-dev|
-|

dtrugman|

vigneshsubbiah16|

lloydchang|

shariqriazz|

pugazhendhi-m|

Szpadel|
+|

monotykamary|

cannuri|

xyOz-dev|

feifei325|

zhangtony239|

qdaxb|
+|

shariqriazz|

pugazhendhi-m|

vigneshsubbiah16|

lloydchang|

dtrugman|

Szpadel|
|

chrarnoldus|

diarmidmackenzie|

olweraltuve|

psv2522|

Premshay|

lupuletic|
-|

PeterDaveHello|

aheizi|

kiwina|

afshawnlotfi|

RaySinner|

nbihan-mediware|
+|

kiwina|

aheizi|

PeterDaveHello|

afshawnlotfi|

RaySinner|

nbihan-mediware|
|

ChuKhaLi|

hassoncs|

emshvac|

kyle-apex|

noritaka1166|

pdecat|
-|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|

dleffel|
-|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

SannidhyaSah|

sammcj|
+|

SannidhyaSah|

StevenTCramer|

Lunchb0ne|

SmartManoj|

vagadiya|

slytechnical|
+|

dleffel|

arthurauffray|

upamune|

NamesMT|

taylorwilsdon|

sammcj|
|

Ruakij|

p12tic|

gtaylor|

aitoroses|

mr-ryan-james|

heyseth|
|

taisukeoe|

avtc|

dlab-anton|

eonghk|

kcwhite|

ronyblum|
|

teddyOOXX|

vincentsong|

yongjer|

zeozeozeo|

ashktn|

franekp|
-|

yt3trees|

anton-otee|

benzntech|

axkirillov|

bramburn|

olearycrew|
-|

snoyiatk|

GitlyHallows|

ross|

philfung|

napter|

mdp|
-|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|

kohii|

kinandan|
-|

jwcraig|

shoopapa|

im47cn|

hongzio|

hatsu38|

GOODBOY008|
-|

forestyoo|

dqroid|

dairui1|

bannzai|

tgfjt|

axmo|
-|

asychin|

amittell|

tmsjngx0|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|
-|

vladstudio|

nevermorec|

PretzelVector|

zetaloop|

cdlliuy|

user202729|
-|

student20880|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

roomote|
-|

robertheadley|

refactorthis|

qingyuan1109|

pokutuna|

philipnext|

oprstchn|
-|

nobu007|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|
-|

lightrabbit|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
-|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

bogdan0083|
-|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|

HadesArchitect|
-|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|

AMHesch|
-|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|

SECKainersdorfer|
-|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|

marvijo-code|
-|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

KanTakahiro|

ksze|
-|

Jdo300|

hesara|

DeXtroTip|

pfitz|

celestial-vault| |
+|

yt3trees|

benzntech|

axkirillov|

anton-otee|

bramburn|

olearycrew|
+|

snoyiatk|

GitlyHallows|

jcbdev|

Chenjiayuan195|

julionav|

SplittyDev|
+|

mdp|

napter|

ross|

philfung|

dairui1|

dqroid|
+|

forestyoo|

GOODBOY008|

hatsu38|

hongzio|

im47cn|

shoopapa|
+|

jwcraig|

kinandan|

nevermorec|

bannzai|

axmo|

asychin|
+|

amittell|

Yoshino-Yukitaro|

Yikai-Liao|

zxdvd|

vladstudio|

tmsjngx0|
+|

tgfjt|

PretzelVector|

zetaloop|

cdlliuy|

user202729|

student20880|
+|

shohei-ihaya|

shaybc|

seedlord|

samir-nimbly|

robertheadley|

refactorthis|
+|

qingyuan1109|

pokutuna|

philipnext|

village-way|

oprstchn|

nobu007|
+|

mosleyit|

moqimoqidea|

mlopezr|

mecab|

olup|

lightrabbit|
+|

kohii|

linegel|

edwin-truthsearch-io|

EamonNerbonne|

dbasclpy|

dflatline|
+|

Deon588|

dleen|

devxpain|

chadgauth|

brunobergher|

thecolorblue|
+|

bogdan0083|

Atlogit|

atlasgong|

andreastempsch|

alasano|

QuinsZouls|
+|

HadesArchitect|

alarno|

nexon33|

adilhafeez|

adamwlarson|

adamhill|
+|

AMHesch|

maekawataiki|

AlexandruSmirnov|

samsilveira|

01Rian|

RSO|
+|

SECKainersdorfer|

R-omk|

Sarke|

kvokka|

ecmasx|

mollux|
+|

marvijo-code|

mamertofabian|

monkeyDluffy6017|

libertyteeth|

shtse8|

Rexarrior|
+|

KanTakahiro|

ksze|

Jdo300|

hesara|

DeXtroTip|

pfitz|
+|

celestial-vault| | | | | |
## Licença
diff --git a/locales/ru/README.md b/locales/ru/README.md
index b2552de1ea1..368a70a9865 100644
--- a/locales/ru/README.md
+++ b/locales/ru/README.md
@@ -186,34 +186,35 @@ code --install-extension bin/roo-cline-
.vsix
|
KJ7LNW|
a8trejo|
ColemanRoo|
canrobins13|
stea9499|
joemanley201|
|
System233|
jquanton|
nissa-seru|
NyxJae|
jr|
MuriloFP|
|
elianiva|
d-oit|
punkpeye|
wkordalski|
sachasayan|
Smartsheet-JB-Brown|
-|
monotykamary|
cannuri|
feifei325|
zhangtony239|
qdaxb|
xyOz-dev|
-|
dtrugman|
vigneshsubbiah16|
lloydchang|
shariqriazz|
pugazhendhi-m|
Szpadel|
+|
monotykamary|
cannuri|
xyOz-dev|
feifei325|
zhangtony239|
qdaxb|
+|
shariqriazz|
pugazhendhi-m|
vigneshsubbiah16|
lloydchang|
dtrugman|
Szpadel|
|
chrarnoldus|
diarmidmackenzie|
olweraltuve|
psv2522|
Premshay|
lupuletic|
-|
PeterDaveHello|
aheizi|
kiwina|
afshawnlotfi|
RaySinner|
nbihan-mediware|
+|
kiwina|
aheizi|
PeterDaveHello|
afshawnlotfi|
RaySinner|
nbihan-mediware|
|
ChuKhaLi|
hassoncs|
emshvac|
kyle-apex|
noritaka1166|
pdecat|
-|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
dleffel|
-|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
SannidhyaSah|
sammcj|
+|
SannidhyaSah|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
+|
dleffel|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
sammcj|
|
Ruakij|
p12tic|
gtaylor|
aitoroses|
mr-ryan-james|
heyseth|
|
taisukeoe|
avtc|
dlab-anton|
eonghk|
kcwhite|
ronyblum|
|
teddyOOXX|
vincentsong|
yongjer|
zeozeozeo|
ashktn|
franekp|
-|
yt3trees|
anton-otee|
benzntech|
axkirillov|
bramburn|
olearycrew|
-|
snoyiatk|
GitlyHallows|
ross|
philfung|
napter|
mdp|
-|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
kohii|
kinandan|
-|
jwcraig|
shoopapa|
im47cn|
hongzio|
hatsu38|
GOODBOY008|
-|
forestyoo|
dqroid|
dairui1|
bannzai|
tgfjt|
axmo|
-|
asychin|
amittell|
tmsjngx0|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
-|
vladstudio|
nevermorec|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
-|
student20880|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
roomote|
-|
robertheadley|
refactorthis|
qingyuan1109|
pokutuna|
philipnext|
oprstchn|
-|
nobu007|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
-|
lightrabbit|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
-|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
bogdan0083|
-|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
HadesArchitect|
-|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
AMHesch|
-|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
SECKainersdorfer|
-|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
marvijo-code|
-|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
KanTakahiro|
ksze|
-|
Jdo300|
hesara|
DeXtroTip|
pfitz|
celestial-vault| |
+|
yt3trees|
benzntech|
axkirillov|
anton-otee|
bramburn|
olearycrew|
+|
snoyiatk|
GitlyHallows|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
+|
mdp|
napter|
ross|
philfung|
dairui1|
dqroid|
+|
forestyoo|
GOODBOY008|
hatsu38|
hongzio|
im47cn|
shoopapa|
+|
jwcraig|
kinandan|
nevermorec|
bannzai|
axmo|
asychin|
+|
amittell|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
vladstudio|
tmsjngx0|
+|
tgfjt|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
student20880|
+|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
robertheadley|
refactorthis|
+|
qingyuan1109|
pokutuna|
philipnext|
village-way|
oprstchn|
nobu007|
+|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
lightrabbit|
+|
kohii|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
+|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
thecolorblue|
+|
bogdan0083|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
+|
HadesArchitect|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
+|
AMHesch|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
+|
SECKainersdorfer|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
+|
marvijo-code|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
Rexarrior|
+|
KanTakahiro|
ksze|
Jdo300|
hesara|
DeXtroTip|
pfitz|
+|
celestial-vault| | | | | |
## Лицензия
diff --git a/locales/tr/README.md b/locales/tr/README.md
index 2c50fbc7f0c..6a039e79b11 100644
--- a/locales/tr/README.md
+++ b/locales/tr/README.md
@@ -186,34 +186,35 @@ Roo Code'u daha iyi hale getirmeye yardımcı olan tüm katkıda bulunanlara te
|
KJ7LNW|
a8trejo|
ColemanRoo|
canrobins13|
stea9499|
joemanley201|
|
System233|
jquanton|
nissa-seru|
NyxJae|
jr|
MuriloFP|
|
elianiva|
d-oit|
punkpeye|
wkordalski|
sachasayan|
Smartsheet-JB-Brown|
-|
monotykamary|
cannuri|
feifei325|
zhangtony239|
qdaxb|
xyOz-dev|
-|
dtrugman|
vigneshsubbiah16|
lloydchang|
shariqriazz|
pugazhendhi-m|
Szpadel|
+|
monotykamary|
cannuri|
xyOz-dev|
feifei325|
zhangtony239|
qdaxb|
+|
shariqriazz|
pugazhendhi-m|
vigneshsubbiah16|
lloydchang|
dtrugman|
Szpadel|
|
chrarnoldus|
diarmidmackenzie|
olweraltuve|
psv2522|
Premshay|
lupuletic|
-|
PeterDaveHello|
aheizi|
kiwina|
afshawnlotfi|
RaySinner|
nbihan-mediware|
+|
kiwina|
aheizi|
PeterDaveHello|
afshawnlotfi|
RaySinner|
nbihan-mediware|
|
ChuKhaLi|
hassoncs|
emshvac|
kyle-apex|
noritaka1166|
pdecat|
-|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
dleffel|
-|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
SannidhyaSah|
sammcj|
+|
SannidhyaSah|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
+|
dleffel|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
sammcj|
|
Ruakij|
p12tic|
gtaylor|
aitoroses|
mr-ryan-james|
heyseth|
|
taisukeoe|
avtc|
dlab-anton|
eonghk|
kcwhite|
ronyblum|
|
teddyOOXX|
vincentsong|
yongjer|
zeozeozeo|
ashktn|
franekp|
-|
yt3trees|
anton-otee|
benzntech|
axkirillov|
bramburn|
olearycrew|
-|
snoyiatk|
GitlyHallows|
ross|
philfung|
napter|
mdp|
-|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
kohii|
kinandan|
-|
jwcraig|
shoopapa|
im47cn|
hongzio|
hatsu38|
GOODBOY008|
-|
forestyoo|
dqroid|
dairui1|
bannzai|
tgfjt|
axmo|
-|
asychin|
amittell|
tmsjngx0|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
-|
vladstudio|
nevermorec|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
-|
student20880|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
roomote|
-|
robertheadley|
refactorthis|
qingyuan1109|
pokutuna|
philipnext|
oprstchn|
-|
nobu007|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
-|
lightrabbit|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
-|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
bogdan0083|
-|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
HadesArchitect|
-|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
AMHesch|
-|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
SECKainersdorfer|
-|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
marvijo-code|
-|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
KanTakahiro|
ksze|
-|
Jdo300|
hesara|
DeXtroTip|
pfitz|
celestial-vault| |
+|
yt3trees|
benzntech|
axkirillov|
anton-otee|
bramburn|
olearycrew|
+|
snoyiatk|
GitlyHallows|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
+|
mdp|
napter|
ross|
philfung|
dairui1|
dqroid|
+|
forestyoo|
GOODBOY008|
hatsu38|
hongzio|
im47cn|
shoopapa|
+|
jwcraig|
kinandan|
nevermorec|
bannzai|
axmo|
asychin|
+|
amittell|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
vladstudio|
tmsjngx0|
+|
tgfjt|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
student20880|
+|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
robertheadley|
refactorthis|
+|
qingyuan1109|
pokutuna|
philipnext|
village-way|
oprstchn|
nobu007|
+|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
lightrabbit|
+|
kohii|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
+|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
thecolorblue|
+|
bogdan0083|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
+|
HadesArchitect|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
+|
AMHesch|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
+|
SECKainersdorfer|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
+|
marvijo-code|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
Rexarrior|
+|
KanTakahiro|
ksze|
Jdo300|
hesara|
DeXtroTip|
pfitz|
+|
celestial-vault| | | | | |
## Lisans
diff --git a/locales/vi/README.md b/locales/vi/README.md
index 4b3ae32f101..2e59bb759f7 100644
--- a/locales/vi/README.md
+++ b/locales/vi/README.md
@@ -186,34 +186,35 @@ Cảm ơn tất cả những người đóng góp đã giúp cải thiện Roo C
|
KJ7LNW|
a8trejo|
ColemanRoo|
canrobins13|
stea9499|
joemanley201|
|
System233|
jquanton|
nissa-seru|
NyxJae|
jr|
MuriloFP|
|
elianiva|
d-oit|
punkpeye|
wkordalski|
sachasayan|
Smartsheet-JB-Brown|
-|
monotykamary|
cannuri|
feifei325|
zhangtony239|
qdaxb|
xyOz-dev|
-|
dtrugman|
vigneshsubbiah16|
lloydchang|
shariqriazz|
pugazhendhi-m|
Szpadel|
+|
monotykamary|
cannuri|
xyOz-dev|
feifei325|
zhangtony239|
qdaxb|
+|
shariqriazz|
pugazhendhi-m|
vigneshsubbiah16|
lloydchang|
dtrugman|
Szpadel|
|
chrarnoldus|
diarmidmackenzie|
olweraltuve|
psv2522|
Premshay|
lupuletic|
-|
PeterDaveHello|
aheizi|
kiwina|
afshawnlotfi|
RaySinner|
nbihan-mediware|
+|
kiwina|
aheizi|
PeterDaveHello|
afshawnlotfi|
RaySinner|
nbihan-mediware|
|
ChuKhaLi|
hassoncs|
emshvac|
kyle-apex|
noritaka1166|
pdecat|
-|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
dleffel|
-|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
SannidhyaSah|
sammcj|
+|
SannidhyaSah|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
+|
dleffel|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
sammcj|
|
Ruakij|
p12tic|
gtaylor|
aitoroses|
mr-ryan-james|
heyseth|
|
taisukeoe|
avtc|
dlab-anton|
eonghk|
kcwhite|
ronyblum|
|
teddyOOXX|
vincentsong|
yongjer|
zeozeozeo|
ashktn|
franekp|
-|
yt3trees|
anton-otee|
benzntech|
axkirillov|
bramburn|
olearycrew|
-|
snoyiatk|
GitlyHallows|
ross|
philfung|
napter|
mdp|
-|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
kohii|
kinandan|
-|
jwcraig|
shoopapa|
im47cn|
hongzio|
hatsu38|
GOODBOY008|
-|
forestyoo|
dqroid|
dairui1|
bannzai|
tgfjt|
axmo|
-|
asychin|
amittell|
tmsjngx0|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
-|
vladstudio|
nevermorec|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
-|
student20880|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
roomote|
-|
robertheadley|
refactorthis|
qingyuan1109|
pokutuna|
philipnext|
oprstchn|
-|
nobu007|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
-|
lightrabbit|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
-|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
bogdan0083|
-|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
HadesArchitect|
-|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
AMHesch|
-|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
SECKainersdorfer|
-|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
marvijo-code|
-|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
KanTakahiro|
ksze|
-|
Jdo300|
hesara|
DeXtroTip|
pfitz|
celestial-vault| |
+|
yt3trees|
benzntech|
axkirillov|
anton-otee|
bramburn|
olearycrew|
+|
snoyiatk|
GitlyHallows|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
+|
mdp|
napter|
ross|
philfung|
dairui1|
dqroid|
+|
forestyoo|
GOODBOY008|
hatsu38|
hongzio|
im47cn|
shoopapa|
+|
jwcraig|
kinandan|
nevermorec|
bannzai|
axmo|
asychin|
+|
amittell|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
vladstudio|
tmsjngx0|
+|
tgfjt|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
student20880|
+|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
robertheadley|
refactorthis|
+|
qingyuan1109|
pokutuna|
philipnext|
village-way|
oprstchn|
nobu007|
+|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
lightrabbit|
+|
kohii|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
+|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
thecolorblue|
+|
bogdan0083|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
+|
HadesArchitect|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
+|
AMHesch|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
+|
SECKainersdorfer|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
+|
marvijo-code|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
Rexarrior|
+|
KanTakahiro|
ksze|
Jdo300|
hesara|
DeXtroTip|
pfitz|
+|
celestial-vault| | | | | |
## Giấy Phép
diff --git a/locales/zh-CN/README.md b/locales/zh-CN/README.md
index 0c276b40c80..dc8b3389418 100644
--- a/locales/zh-CN/README.md
+++ b/locales/zh-CN/README.md
@@ -186,34 +186,35 @@ code --install-extension bin/roo-cline-.vsix
|
KJ7LNW|
a8trejo|
ColemanRoo|
canrobins13|
stea9499|
joemanley201|
|
System233|
jquanton|
nissa-seru|
NyxJae|
jr|
MuriloFP|
|
elianiva|
d-oit|
punkpeye|
wkordalski|
sachasayan|
Smartsheet-JB-Brown|
-|
monotykamary|
cannuri|
feifei325|
zhangtony239|
qdaxb|
xyOz-dev|
-|
dtrugman|
vigneshsubbiah16|
lloydchang|
shariqriazz|
pugazhendhi-m|
Szpadel|
+|
monotykamary|
cannuri|
xyOz-dev|
feifei325|
zhangtony239|
qdaxb|
+|
shariqriazz|
pugazhendhi-m|
vigneshsubbiah16|
lloydchang|
dtrugman|
Szpadel|
|
chrarnoldus|
diarmidmackenzie|
olweraltuve|
psv2522|
Premshay|
lupuletic|
-|
PeterDaveHello|
aheizi|
kiwina|
afshawnlotfi|
RaySinner|
nbihan-mediware|
+|
kiwina|
aheizi|
PeterDaveHello|
afshawnlotfi|
RaySinner|
nbihan-mediware|
|
ChuKhaLi|
hassoncs|
emshvac|
kyle-apex|
noritaka1166|
pdecat|
-|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
dleffel|
-|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
SannidhyaSah|
sammcj|
+|
SannidhyaSah|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
+|
dleffel|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
sammcj|
|
Ruakij|
p12tic|
gtaylor|
aitoroses|
mr-ryan-james|
heyseth|
|
taisukeoe|
avtc|
dlab-anton|
eonghk|
kcwhite|
ronyblum|
|
teddyOOXX|
vincentsong|
yongjer|
zeozeozeo|
ashktn|
franekp|
-|
yt3trees|
anton-otee|
benzntech|
axkirillov|
bramburn|
olearycrew|
-|
snoyiatk|
GitlyHallows|
ross|
philfung|
napter|
mdp|
-|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
kohii|
kinandan|
-|
jwcraig|
shoopapa|
im47cn|
hongzio|
hatsu38|
GOODBOY008|
-|
forestyoo|
dqroid|
dairui1|
bannzai|
tgfjt|
axmo|
-|
asychin|
amittell|
tmsjngx0|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
-|
vladstudio|
nevermorec|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
-|
student20880|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
roomote|
-|
robertheadley|
refactorthis|
qingyuan1109|
pokutuna|
philipnext|
oprstchn|
-|
nobu007|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
-|
lightrabbit|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
-|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
bogdan0083|
-|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
HadesArchitect|
-|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
AMHesch|
-|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
SECKainersdorfer|
-|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
marvijo-code|
-|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
KanTakahiro|
ksze|
-|
Jdo300|
hesara|
DeXtroTip|
pfitz|
celestial-vault| |
+|
yt3trees|
benzntech|
axkirillov|
anton-otee|
bramburn|
olearycrew|
+|
snoyiatk|
GitlyHallows|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
+|
mdp|
napter|
ross|
philfung|
dairui1|
dqroid|
+|
forestyoo|
GOODBOY008|
hatsu38|
hongzio|
im47cn|
shoopapa|
+|
jwcraig|
kinandan|
nevermorec|
bannzai|
axmo|
asychin|
+|
amittell|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
vladstudio|
tmsjngx0|
+|
tgfjt|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
student20880|
+|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
robertheadley|
refactorthis|
+|
qingyuan1109|
pokutuna|
philipnext|
village-way|
oprstchn|
nobu007|
+|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
lightrabbit|
+|
kohii|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
+|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
thecolorblue|
+|
bogdan0083|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
+|
HadesArchitect|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
+|
AMHesch|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
+|
SECKainersdorfer|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
+|
marvijo-code|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
Rexarrior|
+|
KanTakahiro|
ksze|
Jdo300|
hesara|
DeXtroTip|
pfitz|
+|
celestial-vault| | | | | |
## 许可证
diff --git a/locales/zh-TW/README.md b/locales/zh-TW/README.md
index db52894d671..85dfce876b5 100644
--- a/locales/zh-TW/README.md
+++ b/locales/zh-TW/README.md
@@ -187,34 +187,35 @@ code --install-extension bin/roo-cline-.vsix
|
KJ7LNW|
a8trejo|
ColemanRoo|
canrobins13|
stea9499|
joemanley201|
|
System233|
jquanton|
nissa-seru|
NyxJae|
jr|
MuriloFP|
|
elianiva|
d-oit|
punkpeye|
wkordalski|
sachasayan|
Smartsheet-JB-Brown|
-|
monotykamary|
cannuri|
feifei325|
zhangtony239|
qdaxb|
xyOz-dev|
-|
dtrugman|
vigneshsubbiah16|
lloydchang|
shariqriazz|
pugazhendhi-m|
Szpadel|
+|
monotykamary|
cannuri|
xyOz-dev|
feifei325|
zhangtony239|
qdaxb|
+|
shariqriazz|
pugazhendhi-m|
vigneshsubbiah16|
lloydchang|
dtrugman|
Szpadel|
|
chrarnoldus|
diarmidmackenzie|
olweraltuve|
psv2522|
Premshay|
lupuletic|
-|
PeterDaveHello|
aheizi|
kiwina|
afshawnlotfi|
RaySinner|
nbihan-mediware|
+|
kiwina|
aheizi|
PeterDaveHello|
afshawnlotfi|
RaySinner|
nbihan-mediware|
|
ChuKhaLi|
hassoncs|
emshvac|
kyle-apex|
noritaka1166|
pdecat|
-|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
dleffel|
-|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
SannidhyaSah|
sammcj|
+|
SannidhyaSah|
StevenTCramer|
Lunchb0ne|
SmartManoj|
vagadiya|
slytechnical|
+|
dleffel|
arthurauffray|
upamune|
NamesMT|
taylorwilsdon|
sammcj|
|
Ruakij|
p12tic|
gtaylor|
aitoroses|
mr-ryan-james|
heyseth|
|
taisukeoe|
avtc|
dlab-anton|
eonghk|
kcwhite|
ronyblum|
|
teddyOOXX|
vincentsong|
yongjer|
zeozeozeo|
ashktn|
franekp|
-|
yt3trees|
anton-otee|
benzntech|
axkirillov|
bramburn|
olearycrew|
-|
snoyiatk|
GitlyHallows|
ross|
philfung|
napter|
mdp|
-|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
kohii|
kinandan|
-|
jwcraig|
shoopapa|
im47cn|
hongzio|
hatsu38|
GOODBOY008|
-|
forestyoo|
dqroid|
dairui1|
bannzai|
tgfjt|
axmo|
-|
asychin|
amittell|
tmsjngx0|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
-|
vladstudio|
nevermorec|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
-|
student20880|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
roomote|
-|
robertheadley|
refactorthis|
qingyuan1109|
pokutuna|
philipnext|
oprstchn|
-|
nobu007|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
-|
lightrabbit|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
-|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
bogdan0083|
-|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
HadesArchitect|
-|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
AMHesch|
-|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
SECKainersdorfer|
-|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
marvijo-code|
-|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
KanTakahiro|
ksze|
-|
Jdo300|
hesara|
DeXtroTip|
pfitz|
celestial-vault| |
+|
yt3trees|
benzntech|
axkirillov|
anton-otee|
bramburn|
olearycrew|
+|
snoyiatk|
GitlyHallows|
jcbdev|
Chenjiayuan195|
julionav|
SplittyDev|
+|
mdp|
napter|
ross|
philfung|
dairui1|
dqroid|
+|
forestyoo|
GOODBOY008|
hatsu38|
hongzio|
im47cn|
shoopapa|
+|
jwcraig|
kinandan|
nevermorec|
bannzai|
axmo|
asychin|
+|
amittell|
Yoshino-Yukitaro|
Yikai-Liao|
zxdvd|
vladstudio|
tmsjngx0|
+|
tgfjt|
PretzelVector|
zetaloop|
cdlliuy|
user202729|
student20880|
+|
shohei-ihaya|
shaybc|
seedlord|
samir-nimbly|
robertheadley|
refactorthis|
+|
qingyuan1109|
pokutuna|
philipnext|
village-way|
oprstchn|
nobu007|
+|
mosleyit|
moqimoqidea|
mlopezr|
mecab|
olup|
lightrabbit|
+|
kohii|
linegel|
edwin-truthsearch-io|
EamonNerbonne|
dbasclpy|
dflatline|
+|
Deon588|
dleen|
devxpain|
chadgauth|
brunobergher|
thecolorblue|
+|
bogdan0083|
Atlogit|
atlasgong|
andreastempsch|
alasano|
QuinsZouls|
+|
HadesArchitect|
alarno|
nexon33|
adilhafeez|
adamwlarson|
adamhill|
+|
AMHesch|
maekawataiki|
AlexandruSmirnov|
samsilveira|
01Rian|
RSO|
+|
SECKainersdorfer|
R-omk|
Sarke|
kvokka|
ecmasx|
mollux|
+|
marvijo-code|
mamertofabian|
monkeyDluffy6017|
libertyteeth|
shtse8|
Rexarrior|
+|
KanTakahiro|
ksze|
Jdo300|
hesara|
DeXtroTip|
pfitz|
+|
celestial-vault| | | | | |
## 授權
diff --git a/packages/types/src/api.ts b/packages/types/src/api.ts
index 6fb181b5734..7e58c158024 100644
--- a/packages/types/src/api.ts
+++ b/packages/types/src/api.ts
@@ -21,6 +21,10 @@ export interface RooCodeAPIEvents {
taskCompleted: [taskId: string, tokenUsage: TokenUsage, toolUsage: ToolUsage, isSubtask: IsSubtask]
taskTokenUsageUpdated: [taskId: string, tokenUsage: TokenUsage]
taskToolFailed: [taskId: string, toolName: ToolName, error: string]
+ taskTimeoutWarning: [taskId: string, remainingMs: number]
+ taskTimedOut: [taskId: string]
+ taskTimeoutExtended: [taskId: string, newTimeoutMs: number]
+ taskTimeoutCleared: [taskId: string]
}
export interface RooCodeAPI extends EventEmitter {
diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts
index 5b729a125fb..3fe25787d43 100644
--- a/packages/types/src/global-settings.ts
+++ b/packages/types/src/global-settings.ts
@@ -49,6 +49,11 @@ export const globalSettingsSchema = z.object({
allowedMaxRequests: z.number().nullish(),
autoCondenseContext: z.boolean().optional(),
autoCondenseContextPercent: z.number().optional(),
+
+ // Subtask timeout settings
+ defaultSubtaskTimeoutMs: z.number().optional(),
+ subtaskTimeoutWarningPercent: z.number().optional(),
+ maxSubtaskTimeoutExtensions: z.number().optional(),
maxConcurrentFileReads: z.number().optional(),
browserToolEnabled: z.boolean().optional(),
@@ -103,6 +108,7 @@ export const globalSettingsSchema = z.object({
customSupportPrompts: customSupportPromptsSchema.optional(),
enhancementApiConfigId: z.string().optional(),
historyPreviewCollapsed: z.boolean().optional(),
+ profileThresholds: z.record(z.string(), z.number()).optional(),
})
export type GlobalSettings = z.infer
diff --git a/packages/types/src/ipc.ts b/packages/types/src/ipc.ts
index 28accde9de5..1852ba7a380 100644
--- a/packages/types/src/ipc.ts
+++ b/packages/types/src/ipc.ts
@@ -29,6 +29,10 @@ export enum RooCodeEventName {
TaskCompleted = "taskCompleted",
TaskTokenUsageUpdated = "taskTokenUsageUpdated",
TaskToolFailed = "taskToolFailed",
+ TaskTimeoutWarning = "taskTimeoutWarning",
+ TaskTimedOut = "taskTimedOut",
+ TaskTimeoutExtended = "taskTimeoutExtended",
+ TaskTimeoutCleared = "taskTimeoutCleared",
EvalPass = "evalPass",
EvalFail = "evalFail",
}
@@ -52,6 +56,10 @@ export const rooCodeEventsSchema = z.object({
[RooCodeEventName.TaskCompleted]: z.tuple([z.string(), tokenUsageSchema, toolUsageSchema, isSubtaskSchema]),
[RooCodeEventName.TaskTokenUsageUpdated]: z.tuple([z.string(), tokenUsageSchema]),
[RooCodeEventName.TaskToolFailed]: z.tuple([z.string(), toolNamesSchema, z.string()]),
+ [RooCodeEventName.TaskTimeoutWarning]: z.tuple([z.string(), z.number()]),
+ [RooCodeEventName.TaskTimedOut]: z.tuple([z.string()]),
+ [RooCodeEventName.TaskTimeoutExtended]: z.tuple([z.string(), z.number()]),
+ [RooCodeEventName.TaskTimeoutCleared]: z.tuple([z.string()]),
})
export type RooCodeEvents = z.infer
@@ -165,6 +173,26 @@ export const taskEventSchema = z.discriminatedUnion("eventName", [
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskToolFailed],
taskId: z.number().optional(),
}),
+ z.object({
+ eventName: z.literal(RooCodeEventName.TaskTimeoutWarning),
+ payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskTimeoutWarning],
+ taskId: z.number().optional(),
+ }),
+ z.object({
+ eventName: z.literal(RooCodeEventName.TaskTimedOut),
+ payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskTimedOut],
+ taskId: z.number().optional(),
+ }),
+ z.object({
+ eventName: z.literal(RooCodeEventName.TaskTimeoutExtended),
+ payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskTimeoutExtended],
+ taskId: z.number().optional(),
+ }),
+ z.object({
+ eventName: z.literal(RooCodeEventName.TaskTimeoutCleared),
+ payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskTimeoutCleared],
+ taskId: z.number().optional(),
+ }),
z.object({
eventName: z.literal(RooCodeEventName.EvalPass),
payload: z.undefined(),
diff --git a/packages/types/src/providers/index.ts b/packages/types/src/providers/index.ts
index 5f1c08041f7..8b0624ef2af 100644
--- a/packages/types/src/providers/index.ts
+++ b/packages/types/src/providers/index.ts
@@ -8,6 +8,7 @@ export * from "./groq.js"
export * from "./lite-llm.js"
export * from "./lm-studio.js"
export * from "./mistral.js"
+export * from "./ollama.js"
export * from "./openai.js"
export * from "./openrouter.js"
export * from "./requesty.js"
diff --git a/packages/types/src/providers/lm-studio.ts b/packages/types/src/providers/lm-studio.ts
index f83bbc10391..9e39ae56081 100644
--- a/packages/types/src/providers/lm-studio.ts
+++ b/packages/types/src/providers/lm-studio.ts
@@ -1 +1,19 @@
+import type { ModelInfo } from "../model.js"
+
export const LMSTUDIO_DEFAULT_TEMPERATURE = 0
+
+// LM Studio
+// https://lmstudio.ai/docs/cli/ls
+export const lMStudioDefaultModelId = "mistralai/devstral-small-2505"
+export const lMStudioDefaultModelInfo: ModelInfo = {
+ maxTokens: 8192,
+ contextWindow: 200_000,
+ supportsImages: true,
+ supportsComputerUse: true,
+ supportsPromptCache: true,
+ inputPrice: 0,
+ outputPrice: 0,
+ cacheWritesPrice: 0,
+ cacheReadsPrice: 0,
+ description: "LM Studio hosted models",
+}
diff --git a/packages/types/src/providers/ollama.ts b/packages/types/src/providers/ollama.ts
new file mode 100644
index 00000000000..d269da8f4d0
--- /dev/null
+++ b/packages/types/src/providers/ollama.ts
@@ -0,0 +1,17 @@
+import type { ModelInfo } from "../model.js"
+
+// Ollama
+// https://ollama.com/models
+export const ollamaDefaultModelId = "devstral:24b"
+export const ollamaDefaultModelInfo: ModelInfo = {
+ maxTokens: 4096,
+ contextWindow: 200_000,
+ supportsImages: true,
+ supportsComputerUse: true,
+ supportsPromptCache: true,
+ inputPrice: 0,
+ outputPrice: 0,
+ cacheWritesPrice: 0,
+ cacheReadsPrice: 0,
+ description: "Ollama hosted models",
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 878c571f7a7..7ab6e2821f4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -579,6 +579,9 @@ importers:
'@google/genai':
specifier: ^1.0.0
version: 1.3.0(@modelcontextprotocol/sdk@1.12.0)
+ '@lmstudio/sdk':
+ specifier: ^1.1.1
+ version: 1.2.0
'@mistralai/mistralai':
specifier: ^1.3.6
version: 1.6.1(zod@3.25.61)
@@ -697,8 +700,8 @@ importers:
specifier: ^5.0.0
version: 5.0.0
pretty-bytes:
- specifier: ^6.1.1
- version: 6.1.1
+ specifier: ^7.0.0
+ version: 7.0.0
ps-tree:
specifier: ^1.2.0
version: 1.2.0
@@ -718,8 +721,8 @@ importers:
specifier: ^0.16.0
version: 0.16.0
serialize-error:
- specifier: ^11.0.3
- version: 11.0.3
+ specifier: ^12.0.0
+ version: 12.0.0
simple-git:
specifier: ^3.27.0
version: 3.27.0
@@ -971,8 +974,8 @@ importers:
specifier: ^1.227.2
version: 1.242.1
pretty-bytes:
- specifier: ^6.1.1
- version: 6.1.1
+ specifier: ^7.0.0
+ version: 7.0.0
react:
specifier: ^18.3.1
version: 18.3.1
@@ -1025,8 +1028,8 @@ importers:
specifier: ^6.1.13
version: 6.1.18(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
tailwind-merge:
- specifier: ^2.6.0
- version: 2.6.0
+ specifier: ^3.0.0
+ version: 3.3.0
tailwindcss:
specifier: ^4.0.0
version: 4.1.6
@@ -2024,6 +2027,12 @@ packages:
cpu: [x64]
os: [win32]
+ '@lmstudio/lms-isomorphic@0.4.5':
+ resolution: {integrity: sha512-Or9KS1Iz3LC7D7WMe4zbqAqKOlDsVcrvMoQFBhmydzzxOg+eYBM5gtfgMMjcwjM0BuUVPhYOjTWEyfXpqfVJzg==}
+
+ '@lmstudio/sdk@1.2.0':
+ resolution: {integrity: sha512-Eoolmi1cSuGXmLYwtn6pD9eOwjMTb+bQ4iv+i/EYz/hCc+HtbfJamoKfyyw4FogRc03RHsXHe1X18voR40D+2g==}
+
'@manypkg/find-root@1.1.0':
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
@@ -3835,8 +3844,8 @@ packages:
'@types/node@20.17.57':
resolution: {integrity: sha512-f3T4y6VU4fVQDKVqJV4Uppy8c1p/sVvS3peyqxyWnzkqXFJLRU7Y1Bl7rMS1Qe9z0v4M6McY0Fp9yBsgHJUsWQ==}
- '@types/node@20.19.0':
- resolution: {integrity: sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==}
+ '@types/node@20.19.1':
+ resolution: {integrity: sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==}
'@types/node@22.15.29':
resolution: {integrity: sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==}
@@ -6587,6 +6596,9 @@ packages:
jsonfile@4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+ jsonschema@1.5.0:
+ resolution: {integrity: sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==}
+
jsonwebtoken@9.0.2:
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
engines: {node: '>=12', npm: '>=6'}
@@ -7901,9 +7913,9 @@ packages:
engines: {node: '>=14'}
hasBin: true
- pretty-bytes@6.1.1:
- resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==}
- engines: {node: ^14.13.1 || >=16.0.0}
+ pretty-bytes@7.0.0:
+ resolution: {integrity: sha512-U5otLYPR3L0SVjHGrkEUx5mf7MxV2ceXeE7VwWPk+hyzC5drNohsOGNPDZqxCqyX1lkbEN4kl1LiI8QFd7r0ZA==}
+ engines: {node: '>=20'}
pretty-format@27.5.1:
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
@@ -8383,9 +8395,9 @@ packages:
resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
engines: {node: '>= 18'}
- serialize-error@11.0.3:
- resolution: {integrity: sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==}
- engines: {node: '>=14.16'}
+ serialize-error@12.0.0:
+ resolution: {integrity: sha512-ZYkZLAvKTKQXWuh5XpBw7CdbSzagarX39WyZ2H07CDLC5/KfsRGlIXV8d4+tfqX1M7916mRqR1QfNHSij+c9Pw==}
+ engines: {node: '>=18'}
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
@@ -8788,9 +8800,6 @@ packages:
tabbable@5.3.3:
resolution: {integrity: sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==}
- tailwind-merge@2.6.0:
- resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
-
tailwind-merge@3.3.0:
resolution: {integrity: sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==}
@@ -9054,9 +9063,9 @@ packages:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
- type-fest@2.19.0:
- resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
- engines: {node: '>=12.20'}
+ type-fest@4.41.0:
+ resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
+ engines: {node: '>=16'}
type-is@2.0.1:
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
@@ -11054,6 +11063,24 @@ snapshots:
'@libsql/win32-x64-msvc@0.5.13':
optional: true
+ '@lmstudio/lms-isomorphic@0.4.5':
+ dependencies:
+ ws: 8.18.2
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ '@lmstudio/sdk@1.2.0':
+ dependencies:
+ '@lmstudio/lms-isomorphic': 0.4.5
+ chalk: 4.1.2
+ jsonschema: 1.5.0
+ zod: 3.25.61
+ zod-to-json-schema: 3.24.5(zod@3.25.61)
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
'@manypkg/find-root@1.1.0':
dependencies:
'@babel/runtime': 7.27.4
@@ -12995,7 +13022,7 @@ snapshots:
dependencies:
undici-types: 6.19.8
- '@types/node@20.19.0':
+ '@types/node@20.19.1':
dependencies:
undici-types: 6.21.0
optional: true
@@ -13044,7 +13071,7 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
- '@types/node': 20.19.0
+ '@types/node': 20.19.1
optional: true
'@types/yargs-parser@21.0.3': {}
@@ -13217,7 +13244,7 @@ snapshots:
sirv: 3.0.1
tinyglobby: 0.2.14
tinyrainbow: 2.0.0
- vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.17.57)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)
+ vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.29)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)
'@vitest/utils@3.2.4':
dependencies:
@@ -16106,6 +16133,8 @@ snapshots:
optionalDependencies:
graceful-fs: 4.2.11
+ jsonschema@1.5.0: {}
+
jsonwebtoken@9.0.2:
dependencies:
jws: 3.2.2
@@ -17698,7 +17727,7 @@ snapshots:
prettier@3.5.3: {}
- pretty-bytes@6.1.1: {}
+ pretty-bytes@7.0.0: {}
pretty-format@27.5.1:
dependencies:
@@ -18350,9 +18379,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- serialize-error@11.0.3:
+ serialize-error@12.0.0:
dependencies:
- type-fest: 2.19.0
+ type-fest: 4.41.0
serialize-javascript@6.0.2:
dependencies:
@@ -18812,8 +18841,6 @@ snapshots:
tabbable@5.3.3: {}
- tailwind-merge@2.6.0: {}
-
tailwind-merge@3.3.0: {}
tailwindcss-animate@1.0.7(tailwindcss@3.4.17):
@@ -19090,7 +19117,7 @@ snapshots:
dependencies:
prelude-ls: 1.2.1
- type-fest@2.19.0: {}
+ type-fest@4.41.0: {}
type-is@2.0.1:
dependencies:
diff --git a/scripts/update-contributors.js b/scripts/update-contributors.js
old mode 100755
new mode 100644
index 32fd645f68e..6bd4c35f0ca
--- a/scripts/update-contributors.js
+++ b/scripts/update-contributors.js
@@ -183,9 +183,15 @@ async function readReadme() {
* @param {Array} contributors Array of contributor objects from GitHub API
* @returns {string} HTML for contributors section
*/
+const EXCLUDED_LOGIN_SUBSTRINGS = ['[bot]', 'R00-B0T'];
+const EXCLUDED_LOGIN_EXACTS = ['cursor', 'roomote'];
+
function formatContributorsSection(contributors) {
- // Filter out GitHub Actions bot
- const filteredContributors = contributors.filter((c) => !c.login.includes("[bot]") && !c.login.includes("R00-B0T"))
+ // Filter out GitHub Actions bot, cursor, and roomote
+ const filteredContributors = contributors.filter((c) =>
+ !EXCLUDED_LOGIN_SUBSTRINGS.some(sub => c.login.includes(sub)) &&
+ !EXCLUDED_LOGIN_EXACTS.includes(c.login)
+ )
// Start building with Markdown table format
let markdown = `${START_MARKER}
diff --git a/src/api/providers/fetchers/__tests__/fixtures/lmstudio-model-details.json b/src/api/providers/fetchers/__tests__/fixtures/lmstudio-model-details.json
new file mode 100644
index 00000000000..43f5505710a
--- /dev/null
+++ b/src/api/providers/fetchers/__tests__/fixtures/lmstudio-model-details.json
@@ -0,0 +1,14 @@
+{
+ "mistralai/devstral-small-2505": {
+ "type": "llm",
+ "modelKey": "mistralai/devstral-small-2505",
+ "format": "safetensors",
+ "displayName": "Devstral Small 2505",
+ "path": "mistralai/devstral-small-2505",
+ "sizeBytes": 13277565112,
+ "architecture": "mistral",
+ "vision": false,
+ "trainedForToolUse": false,
+ "maxContextLength": 131072
+ }
+}
diff --git a/src/api/providers/fetchers/__tests__/fixtures/ollama-model-details.json b/src/api/providers/fetchers/__tests__/fixtures/ollama-model-details.json
new file mode 100644
index 00000000000..e0b6ac03d9b
--- /dev/null
+++ b/src/api/providers/fetchers/__tests__/fixtures/ollama-model-details.json
@@ -0,0 +1,58 @@
+{
+ "qwen3-2to16:latest": {
+ "license": " Apache License\\n Version 2.0, January 2004\\n...",
+ "modelfile": "model.modelfile,# To build a new Modelfile based on this, replace FROM with:...",
+ "parameters": "repeat_penalty 1\\nstop \\\\nstop...",
+ "template": "{{- if .Messages }}\\n{{- if or .System .Tools }}<|im_start|>system...",
+ "details": {
+ "parent_model": "/Users/brad/.ollama/models/blobs/sha256-3291abe70f16ee9682de7bfae08db5373ea9d6497e614aaad63340ad421d6312",
+ "format": "gguf",
+ "family": "qwen3",
+ "families": ["qwen3"],
+ "parameter_size": "32.8B",
+ "quantization_level": "Q4_K_M"
+ },
+ "model_info": {
+ "general.architecture": "qwen3",
+ "general.basename": "Qwen3",
+ "general.file_type": 15,
+ "general.parameter_count": 32762123264,
+ "general.quantization_version": 2,
+ "general.size_label": "32B",
+ "general.type": "model",
+ "qwen3.attention.head_count": 64,
+ "qwen3.attention.head_count_kv": 8,
+ "qwen3.attention.key_length": 128,
+ "qwen3.attention.layer_norm_rms_epsilon": 0.000001,
+ "qwen3.attention.value_length": 128,
+ "qwen3.block_count": 64,
+ "qwen3.context_length": 40960,
+ "qwen3.embedding_length": 5120,
+ "qwen3.feed_forward_length": 25600,
+ "qwen3.rope.freq_base": 1000000,
+ "tokenizer.ggml.add_bos_token": false,
+ "tokenizer.ggml.bos_token_id": 151643,
+ "tokenizer.ggml.eos_token_id": 151645,
+ "tokenizer.ggml.merges": null,
+ "tokenizer.ggml.model": "gpt2",
+ "tokenizer.ggml.padding_token_id": 151643,
+ "tokenizer.ggml.pre": "qwen2",
+ "tokenizer.ggml.token_type": null,
+ "tokenizer.ggml.tokens": null
+ },
+ "tensors": [
+ {
+ "name": "output.weight",
+ "type": "Q6_K",
+ "shape": [5120, 151936]
+ },
+ {
+ "name": "output_norm.weight",
+ "type": "F32",
+ "shape": [5120]
+ }
+ ],
+ "capabilities": ["completion", "tools"],
+ "modified_at": "2025-06-02T22:16:13.644123606-04:00"
+ }
+}
diff --git a/src/api/providers/fetchers/__tests__/lmstudio.test.ts b/src/api/providers/fetchers/__tests__/lmstudio.test.ts
new file mode 100644
index 00000000000..59b43887852
--- /dev/null
+++ b/src/api/providers/fetchers/__tests__/lmstudio.test.ts
@@ -0,0 +1,197 @@
+import axios from "axios"
+import { vi, describe, it, expect, beforeEach } from "vitest"
+import { LMStudioClient, LLM, LLMInstanceInfo } from "@lmstudio/sdk" // LLMInfo is a type
+import { getLMStudioModels, parseLMStudioModel } from "../lmstudio"
+import { ModelInfo, lMStudioDefaultModelInfo } from "@roo-code/types" // ModelInfo is a type
+
+// Mock axios
+vi.mock("axios")
+const mockedAxios = axios as any
+
+// Mock @lmstudio/sdk
+const mockGetModelInfo = vi.fn()
+const mockListLoaded = vi.fn()
+vi.mock("@lmstudio/sdk", () => {
+ return {
+ LMStudioClient: vi.fn().mockImplementation(() => ({
+ llm: {
+ listLoaded: mockListLoaded,
+ },
+ })),
+ }
+})
+const MockedLMStudioClientConstructor = LMStudioClient as any
+
+describe("LMStudio Fetcher", () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ MockedLMStudioClientConstructor.mockClear()
+ mockListLoaded.mockClear()
+ mockGetModelInfo.mockClear()
+ })
+
+ describe("parseLMStudioModel", () => {
+ it("should correctly parse raw LLMInfo to ModelInfo", () => {
+ const rawModel: LLMInstanceInfo = {
+ type: "llm",
+ modelKey: "mistralai/devstral-small-2505",
+ format: "safetensors",
+ displayName: "Devstral Small 2505",
+ path: "mistralai/devstral-small-2505",
+ sizeBytes: 13277565112,
+ architecture: "mistral",
+ identifier: "mistralai/devstral-small-2505",
+ instanceReference: "RAP5qbeHVjJgBiGFQ6STCuTJ",
+ vision: false,
+ trainedForToolUse: false,
+ maxContextLength: 131072,
+ contextLength: 7161,
+ }
+
+ const expectedModelInfo: ModelInfo = {
+ ...lMStudioDefaultModelInfo,
+ description: `${rawModel.displayName} - ${rawModel.path}`,
+ contextWindow: rawModel.contextLength,
+ supportsPromptCache: true,
+ supportsImages: rawModel.vision,
+ supportsComputerUse: false,
+ maxTokens: rawModel.contextLength,
+ inputPrice: 0,
+ outputPrice: 0,
+ cacheWritesPrice: 0,
+ cacheReadsPrice: 0,
+ }
+
+ const result = parseLMStudioModel(rawModel)
+ expect(result).toEqual(expectedModelInfo)
+ })
+ })
+
+ describe("getLMStudioModels", () => {
+ const baseUrl = "http://localhost:1234"
+ const lmsUrl = "ws://localhost:1234"
+
+ const mockRawModel: LLMInstanceInfo = {
+ architecture: "test-arch",
+ identifier: "mistralai/devstral-small-2505",
+ instanceReference: "RAP5qbeHVjJgBiGFQ6STCuTJ",
+ modelKey: "test-model-key-1",
+ path: "/path/to/test-model-1",
+ type: "llm",
+ displayName: "Test Model One",
+ maxContextLength: 2048,
+ contextLength: 7161,
+ paramsString: "1B params, 2k context",
+ vision: true,
+ format: "gguf",
+ sizeBytes: 1000000000,
+ trainedForToolUse: false, // Added
+ }
+
+ it("should fetch and parse models successfully", async () => {
+ mockedAxios.get.mockResolvedValueOnce({ data: { status: "ok" } })
+ mockListLoaded.mockResolvedValueOnce([{ getModelInfo: mockGetModelInfo }])
+ mockGetModelInfo.mockResolvedValueOnce(mockRawModel)
+
+ const result = await getLMStudioModels(baseUrl)
+
+ expect(mockedAxios.get).toHaveBeenCalledTimes(1)
+ expect(mockedAxios.get).toHaveBeenCalledWith(`${baseUrl}/v1/models`)
+ expect(MockedLMStudioClientConstructor).toHaveBeenCalledTimes(1)
+ expect(MockedLMStudioClientConstructor).toHaveBeenCalledWith({ baseUrl: lmsUrl })
+ expect(mockListLoaded).toHaveBeenCalledTimes(1)
+
+ const expectedParsedModel = parseLMStudioModel(mockRawModel)
+ expect(result).toEqual({ [mockRawModel.modelKey]: expectedParsedModel })
+ })
+
+ it("should use default baseUrl if an empty string is provided", async () => {
+ const defaultBaseUrl = "http://localhost:1234"
+ const defaultLmsUrl = "ws://localhost:1234"
+ mockedAxios.get.mockResolvedValueOnce({ data: {} })
+ mockListLoaded.mockResolvedValueOnce([])
+
+ await getLMStudioModels("")
+
+ expect(mockedAxios.get).toHaveBeenCalledWith(`${defaultBaseUrl}/v1/models`)
+ expect(MockedLMStudioClientConstructor).toHaveBeenCalledWith({ baseUrl: defaultLmsUrl })
+ })
+
+ it("should transform https baseUrl to wss for LMStudioClient", async () => {
+ const httpsBaseUrl = "https://securehost:4321"
+ const wssLmsUrl = "wss://securehost:4321"
+ mockedAxios.get.mockResolvedValueOnce({ data: {} })
+ mockListLoaded.mockResolvedValueOnce([])
+
+ await getLMStudioModels(httpsBaseUrl)
+
+ expect(mockedAxios.get).toHaveBeenCalledWith(`${httpsBaseUrl}/v1/models`)
+ expect(MockedLMStudioClientConstructor).toHaveBeenCalledWith({ baseUrl: wssLmsUrl })
+ })
+
+ it("should return an empty object if lmsUrl is unparsable", async () => {
+ const unparsableBaseUrl = "http://localhost:invalid:port" // Leads to ws://localhost:invalid:port
+
+ const result = await getLMStudioModels(unparsableBaseUrl)
+
+ expect(result).toEqual({})
+ expect(mockedAxios.get).not.toHaveBeenCalled()
+ expect(MockedLMStudioClientConstructor).not.toHaveBeenCalled()
+ })
+
+ it("should return an empty object and log error if axios.get fails with a generic error", async () => {
+ const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {})
+ const networkError = new Error("Network connection failed")
+ mockedAxios.get.mockRejectedValueOnce(networkError)
+
+ const result = await getLMStudioModels(baseUrl)
+
+ expect(mockedAxios.get).toHaveBeenCalledTimes(1)
+ expect(mockedAxios.get).toHaveBeenCalledWith(`${baseUrl}/v1/models`)
+ expect(MockedLMStudioClientConstructor).not.toHaveBeenCalled()
+ expect(mockListLoaded).not.toHaveBeenCalled()
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
+ `Error fetching LMStudio models: ${JSON.stringify(networkError, Object.getOwnPropertyNames(networkError), 2)}`,
+ )
+ expect(result).toEqual({})
+ consoleErrorSpy.mockRestore()
+ })
+
+ it("should return an empty object and log info if axios.get fails with ECONNREFUSED", async () => {
+ const consoleInfoSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
+ const econnrefusedError = new Error("Connection refused")
+ ;(econnrefusedError as any).code = "ECONNREFUSED"
+ mockedAxios.get.mockRejectedValueOnce(econnrefusedError)
+
+ const result = await getLMStudioModels(baseUrl)
+
+ expect(mockedAxios.get).toHaveBeenCalledTimes(1)
+ expect(mockedAxios.get).toHaveBeenCalledWith(`${baseUrl}/v1/models`)
+ expect(MockedLMStudioClientConstructor).not.toHaveBeenCalled()
+ expect(mockListLoaded).not.toHaveBeenCalled()
+ expect(consoleInfoSpy).toHaveBeenCalledWith(`Error connecting to LMStudio at ${baseUrl}`)
+ expect(result).toEqual({})
+ consoleInfoSpy.mockRestore()
+ })
+
+ it("should return an empty object and log error if listDownloadedModels fails", async () => {
+ const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {})
+ const listError = new Error("LMStudio SDK internal error")
+
+ mockedAxios.get.mockResolvedValueOnce({ data: {} })
+ mockListLoaded.mockRejectedValueOnce(listError)
+
+ const result = await getLMStudioModels(baseUrl)
+
+ expect(mockedAxios.get).toHaveBeenCalledTimes(1)
+ expect(MockedLMStudioClientConstructor).toHaveBeenCalledTimes(1)
+ expect(MockedLMStudioClientConstructor).toHaveBeenCalledWith({ baseUrl: lmsUrl })
+ expect(mockListLoaded).toHaveBeenCalledTimes(1)
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
+ `Error fetching LMStudio models: ${JSON.stringify(listError, Object.getOwnPropertyNames(listError), 2)}`,
+ )
+ expect(result).toEqual({})
+ consoleErrorSpy.mockRestore()
+ })
+ })
+})
diff --git a/src/api/providers/fetchers/__tests__/ollama.test.ts b/src/api/providers/fetchers/__tests__/ollama.test.ts
new file mode 100644
index 00000000000..cada0a4b603
--- /dev/null
+++ b/src/api/providers/fetchers/__tests__/ollama.test.ts
@@ -0,0 +1,133 @@
+import axios from "axios"
+import path from "path"
+import { vi, describe, it, expect, beforeEach } from "vitest"
+import { getOllamaModels, parseOllamaModel } from "../ollama"
+import ollamaModelsData from "./fixtures/ollama-model-details.json"
+
+// Mock axios
+vi.mock("axios")
+const mockedAxios = axios as any
+
+describe("Ollama Fetcher", () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ })
+
+ describe("parseOllamaModel", () => {
+ it("should correctly parse Ollama model info", () => {
+ const modelData = ollamaModelsData["qwen3-2to16:latest"]
+ const parsedModel = parseOllamaModel(modelData)
+
+ expect(parsedModel).toEqual({
+ maxTokens: 40960,
+ contextWindow: 40960,
+ supportsImages: false,
+ supportsComputerUse: false,
+ supportsPromptCache: true,
+ inputPrice: 0,
+ outputPrice: 0,
+ cacheWritesPrice: 0,
+ cacheReadsPrice: 0,
+ description: "Family: qwen3, Context: 40960, Size: 32.8B",
+ })
+ })
+ })
+
+ describe("getOllamaModels", () => {
+ it("should fetch model list from /api/tags and details for each model from /api/show", async () => {
+ const baseUrl = "http://localhost:11434"
+ const modelName = "devstral2to16:latest"
+
+ const mockApiTagsResponse = {
+ models: [
+ {
+ name: modelName,
+ model: modelName,
+ modified_at: "2025-06-03T09:23:22.610222878-04:00",
+ size: 14333928010,
+ digest: "6a5f0c01d2c96c687d79e32fdd25b87087feb376bf9838f854d10be8cf3c10a5",
+ details: {
+ family: "llama",
+ families: ["llama"],
+ format: "gguf",
+ parameter_size: "23.6B",
+ parent_model: "",
+ quantization_level: "Q4_K_M",
+ },
+ },
+ ],
+ }
+ const mockApiShowResponse = {
+ license: "Mock License",
+ modelfile: "FROM /path/to/blob\nTEMPLATE {{ .Prompt }}",
+ parameters: "num_ctx 4096\nstop_token ",
+ template: "{{ .System }}USER: {{ .Prompt }}ASSISTANT:",
+ modified_at: "2025-06-03T09:23:22.610222878-04:00",
+ details: {
+ parent_model: "",
+ format: "gguf",
+ family: "llama",
+ families: ["llama"],
+ parameter_size: "23.6B",
+ quantization_level: "Q4_K_M",
+ },
+ model_info: {
+ "ollama.context_length": 4096,
+ "some.other.info": "value",
+ },
+ capabilities: ["completion"],
+ }
+
+ mockedAxios.get.mockResolvedValueOnce({ data: mockApiTagsResponse })
+ mockedAxios.post.mockResolvedValueOnce({ data: mockApiShowResponse })
+
+ const result = await getOllamaModels(baseUrl)
+
+ expect(mockedAxios.get).toHaveBeenCalledTimes(1)
+ expect(mockedAxios.get).toHaveBeenCalledWith(`${baseUrl}/api/tags`)
+
+ expect(mockedAxios.post).toHaveBeenCalledTimes(1)
+ expect(mockedAxios.post).toHaveBeenCalledWith(`${baseUrl}/api/show`, { model: modelName })
+
+ expect(typeof result).toBe("object")
+ expect(result).not.toBeInstanceOf(Array)
+ expect(Object.keys(result).length).toBe(1)
+ expect(result[modelName]).toBeDefined()
+
+ const expectedParsedDetails = parseOllamaModel(mockApiShowResponse as any)
+ expect(result[modelName]).toEqual(expectedParsedDetails)
+ })
+
+ it("should return an empty list if the initial /api/tags call fails", async () => {
+ const baseUrl = "http://localhost:11434"
+ mockedAxios.get.mockRejectedValueOnce(new Error("Network error"))
+ const consoleInfoSpy = vi.spyOn(console, "error").mockImplementation(() => {}) // Spy and suppress output
+
+ const result = await getOllamaModels(baseUrl)
+
+ expect(mockedAxios.get).toHaveBeenCalledTimes(1)
+ expect(mockedAxios.get).toHaveBeenCalledWith(`${baseUrl}/api/tags`)
+ expect(mockedAxios.post).not.toHaveBeenCalled()
+ expect(result).toEqual({})
+ })
+
+ it("should log an info message and return an empty object on ECONNREFUSED", async () => {
+ const baseUrl = "http://localhost:11434"
+ const consoleInfoSpy = vi.spyOn(console, "warn").mockImplementation(() => {}) // Spy and suppress output
+
+ const econnrefusedError = new Error("Connection refused") as any
+ econnrefusedError.code = "ECONNREFUSED"
+ mockedAxios.get.mockRejectedValueOnce(econnrefusedError)
+
+ const result = await getOllamaModels(baseUrl)
+
+ expect(mockedAxios.get).toHaveBeenCalledTimes(1)
+ expect(mockedAxios.get).toHaveBeenCalledWith(`${baseUrl}/api/tags`)
+ expect(mockedAxios.post).not.toHaveBeenCalled()
+ expect(consoleInfoSpy).toHaveBeenCalledWith(`Failed connecting to Ollama at ${baseUrl}`)
+ expect(result).toEqual({})
+
+ consoleInfoSpy.mockRestore() // Restore original console.info
+ })
+ })
+})
diff --git a/src/api/providers/fetchers/lmstudio.ts b/src/api/providers/fetchers/lmstudio.ts
new file mode 100644
index 00000000000..ea1a590f1e2
--- /dev/null
+++ b/src/api/providers/fetchers/lmstudio.ts
@@ -0,0 +1,54 @@
+import { ModelInfo, lMStudioDefaultModelInfo } from "@roo-code/types"
+import { LLM, LLMInfo, LLMInstanceInfo, LMStudioClient } from "@lmstudio/sdk"
+import axios from "axios"
+
+export const parseLMStudioModel = (rawModel: LLMInstanceInfo): ModelInfo => {
+ const modelInfo: ModelInfo = Object.assign({}, lMStudioDefaultModelInfo, {
+ description: `${rawModel.displayName} - ${rawModel.path}`,
+ contextWindow: rawModel.contextLength,
+ supportsPromptCache: true,
+ supportsImages: rawModel.vision,
+ supportsComputerUse: false,
+ maxTokens: rawModel.contextLength,
+ })
+
+ return modelInfo
+}
+
+export async function getLMStudioModels(baseUrl = "http://localhost:1234"): Promise> {
+ // clearing the input can leave an empty string; use the default in that case
+ baseUrl = baseUrl === "" ? "http://localhost:1234" : baseUrl
+
+ const models: Record = {}
+ // ws is required to connect using the LMStudio library
+ const lmsUrl = baseUrl.replace(/^http:\/\//, "ws://").replace(/^https:\/\//, "wss://")
+
+ try {
+ if (!URL.canParse(lmsUrl)) {
+ return models
+ }
+
+ // test the connection to LM Studio first
+ // errors will be caught further down
+ await axios.get(`${baseUrl}/v1/models`)
+
+ const client = new LMStudioClient({ baseUrl: lmsUrl })
+ const response = (await client.llm.listLoaded().then((models: LLM[]) => {
+ return Promise.all(models.map((m) => m.getModelInfo()))
+ })) as Array
+
+ for (const lmstudioModel of response) {
+ models[lmstudioModel.modelKey] = parseLMStudioModel(lmstudioModel)
+ }
+ } catch (error) {
+ if (error.code === "ECONNREFUSED") {
+ console.warn(`Error connecting to LMStudio at ${baseUrl}`)
+ } else {
+ console.error(
+ `Error fetching LMStudio models: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
+ )
+ }
+ }
+
+ return models
+}
diff --git a/src/api/providers/fetchers/modelCache.ts b/src/api/providers/fetchers/modelCache.ts
index 12d636bc46c..5956187e417 100644
--- a/src/api/providers/fetchers/modelCache.ts
+++ b/src/api/providers/fetchers/modelCache.ts
@@ -14,6 +14,9 @@ import { getGlamaModels } from "./glama"
import { getUnboundModels } from "./unbound"
import { getLiteLLMModels } from "./litellm"
import { GetModelsOptions } from "../../../shared/api"
+import { getOllamaModels } from "./ollama"
+import { getLMStudioModels } from "./lmstudio"
+
const memoryCache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 5 * 60 })
async function writeModels(router: RouterName, data: ModelRecord) {
@@ -68,6 +71,12 @@ export const getModels = async (options: GetModelsOptions): Promise
// Type safety ensures apiKey and baseUrl are always provided for litellm
models = await getLiteLLMModels(options.apiKey, options.baseUrl)
break
+ case "ollama":
+ models = await getOllamaModels(options.baseUrl)
+ break
+ case "lmstudio":
+ models = await getLMStudioModels(options.baseUrl)
+ break
default: {
// Ensures router is exhaustively checked if RouterName is a strict union
const exhaustiveCheck: never = provider
diff --git a/src/api/providers/fetchers/ollama.ts b/src/api/providers/fetchers/ollama.ts
new file mode 100644
index 00000000000..8de2c1a918c
--- /dev/null
+++ b/src/api/providers/fetchers/ollama.ts
@@ -0,0 +1,100 @@
+import axios from "axios"
+import { ModelInfo, ollamaDefaultModelInfo } from "@roo-code/types"
+import { z } from "zod"
+
+const OllamaModelDetailsSchema = z.object({
+ family: z.string(),
+ families: z.array(z.string()),
+ format: z.string(),
+ parameter_size: z.string(),
+ parent_model: z.string(),
+ quantization_level: z.string(),
+})
+
+const OllamaModelSchema = z.object({
+ details: OllamaModelDetailsSchema,
+ digest: z.string(),
+ model: z.string(),
+ modified_at: z.string(),
+ name: z.string(),
+ size: z.number(),
+})
+
+const OllamaModelInfoResponseSchema = z.object({
+ modelfile: z.string(),
+ parameters: z.string(),
+ template: z.string(),
+ details: OllamaModelDetailsSchema,
+ model_info: z.record(z.string(), z.any()),
+ capabilities: z.array(z.string()).optional(),
+})
+
+const OllamaModelsResponseSchema = z.object({
+ models: z.array(OllamaModelSchema),
+})
+
+type OllamaModelsResponse = z.infer
+
+type OllamaModelInfoResponse = z.infer
+
+export const parseOllamaModel = (rawModel: OllamaModelInfoResponse): ModelInfo => {
+ const contextKey = Object.keys(rawModel.model_info).find((k) => k.includes("context_length"))
+ const contextWindow =
+ contextKey && typeof rawModel.model_info[contextKey] === "number" ? rawModel.model_info[contextKey] : undefined
+
+ const modelInfo: ModelInfo = Object.assign({}, ollamaDefaultModelInfo, {
+ description: `Family: ${rawModel.details.family}, Context: ${contextWindow}, Size: ${rawModel.details.parameter_size}`,
+ contextWindow: contextWindow || ollamaDefaultModelInfo.contextWindow,
+ supportsPromptCache: true,
+ supportsImages: rawModel.capabilities?.includes("vision"),
+ supportsComputerUse: false,
+ maxTokens: contextWindow || ollamaDefaultModelInfo.contextWindow,
+ })
+
+ return modelInfo
+}
+
+export async function getOllamaModels(baseUrl = "http://localhost:11434"): Promise> {
+ const models: Record = {}
+
+ // clearing the input can leave an empty string; use the default in that case
+ baseUrl = baseUrl === "" ? "http://localhost:11434" : baseUrl
+
+ try {
+ if (!URL.canParse(baseUrl)) {
+ return models
+ }
+
+ const response = await axios.get(`${baseUrl}/api/tags`)
+ const parsedResponse = OllamaModelsResponseSchema.safeParse(response.data)
+ let modelInfoPromises = []
+
+ if (parsedResponse.success) {
+ for (const ollamaModel of parsedResponse.data.models) {
+ modelInfoPromises.push(
+ axios
+ .post(`${baseUrl}/api/show`, {
+ model: ollamaModel.model,
+ })
+ .then((ollamaModelInfo) => {
+ models[ollamaModel.name] = parseOllamaModel(ollamaModelInfo.data)
+ }),
+ )
+ }
+
+ await Promise.all(modelInfoPromises)
+ } else {
+ console.error(`Error parsing Ollama models response: ${JSON.stringify(parsedResponse.error, null, 2)}`)
+ }
+ } catch (error) {
+ if (error.code === "ECONNREFUSED") {
+ console.warn(`Failed connecting to Ollama at ${baseUrl}`)
+ } else {
+ console.error(
+ `Error fetching Ollama models: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
+ )
+ }
+ }
+
+ return models
+}
diff --git a/src/api/providers/ollama.ts b/src/api/providers/ollama.ts
index 7f384e9a989..a7713ba4214 100644
--- a/src/api/providers/ollama.ts
+++ b/src/api/providers/ollama.ts
@@ -1,6 +1,5 @@
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
-import axios from "axios"
import { type ModelInfo, openAiModelInfoSaneDefaults, DEEP_SEEK_DEFAULT_TEMPERATURE } from "@roo-code/types"
@@ -111,17 +110,3 @@ export class OllamaHandler extends BaseProvider implements SingleCompletionHandl
}
}
}
-
-export async function getOllamaModels(baseUrl = "http://localhost:11434") {
- try {
- if (!URL.canParse(baseUrl)) {
- return []
- }
-
- const response = await axios.get(`${baseUrl}/api/tags`)
- const modelsArray = response.data?.models?.map((model: any) => model.name) || []
- return [...new Set(modelsArray)]
- } catch (error) {
- return []
- }
-}
diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts
index 8a8b57bb0ca..3b73b1915c6 100644
--- a/src/core/condense/index.ts
+++ b/src/core/condense/index.ts
@@ -8,6 +8,8 @@ import { ApiMessage } from "../task-persistence/apiMessages"
import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
export const N_MESSAGES_TO_KEEP = 3
+export const MIN_CONDENSE_THRESHOLD = 5 // Minimum percentage of context window to trigger condensing
+export const MAX_CONDENSE_THRESHOLD = 100 // Maximum percentage of context window to trigger condensing
const SUMMARY_PROMPT = `\
Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions.
diff --git a/src/core/prompts/tools/new-task.ts b/src/core/prompts/tools/new-task.ts
index 7301b7b422d..a652f4aa7ce 100644
--- a/src/core/prompts/tools/new-task.ts
+++ b/src/core/prompts/tools/new-task.ts
@@ -7,17 +7,20 @@ Description: This will let you create a new task instance in the chosen mode usi
Parameters:
- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
+- timeout_seconds: (optional) Number of seconds after which the subtask will automatically timeout and be aborted. If not specified, uses default timeout setting.
Usage:
your-mode-slug-here
Your initial instructions here
+300
Example:
code
Implement a new feature for the application.
+600
`
}
diff --git a/src/core/sliding-window/__tests__/sliding-window.spec.ts b/src/core/sliding-window/__tests__/sliding-window.spec.ts
index 0f41942547f..3bda5351d42 100644
--- a/src/core/sliding-window/__tests__/sliding-window.spec.ts
+++ b/src/core/sliding-window/__tests__/sliding-window.spec.ts
@@ -19,7 +19,14 @@ import {
// Create a mock ApiHandler for testing
class MockApiHandler extends BaseProvider {
createMessage(): any {
- throw new Error("Method not implemented.")
+ // Mock implementation for testing - returns an async iterable stream
+ const mockStream = {
+ async *[Symbol.asyncIterator]() {
+ yield { type: "text", text: "Mock summary content" }
+ yield { type: "usage", inputTokens: 100, outputTokens: 50 }
+ },
+ }
+ return mockStream
}
getModel(): { id: string; info: ModelInfo } {
@@ -265,6 +272,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
// Check the new return type
@@ -304,6 +313,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result).toEqual({
@@ -337,6 +348,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
const result2 = await truncateConversationIfNeeded({
@@ -349,6 +362,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result1.messages).toEqual(result2.messages)
@@ -368,6 +383,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
const result4 = await truncateConversationIfNeeded({
@@ -380,6 +397,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result3.messages).toEqual(result4.messages)
@@ -414,6 +433,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(resultWithSmall).toEqual({
messages: messagesWithSmallContent,
@@ -447,6 +468,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(resultWithLarge.messages).not.toEqual(messagesWithLargeContent) // Should truncate
expect(resultWithLarge.summary).toBe("")
@@ -473,6 +496,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(resultWithVeryLarge.messages).not.toEqual(messagesWithVeryLargeContent) // Should truncate
expect(resultWithVeryLarge.summary).toBe("")
@@ -509,6 +534,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result).toEqual({
messages: expectedResult,
@@ -554,6 +581,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
// Verify summarizeConversation was called with the right parameters
@@ -619,6 +648,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
// Verify summarizeConversation was called
@@ -664,6 +695,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 50, // This shouldn't matter since autoCondenseContext is false
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
// Verify summarizeConversation was not called
@@ -719,6 +752,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 60%
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
// Verify summarizeConversation was called with the right parameters
@@ -769,6 +804,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 40%
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
// Verify summarizeConversation was not called
@@ -787,6 +824,212 @@ describe("Sliding Window", () => {
})
})
+ /**
+ * Tests for profile-specific thresholds functionality
+ */
+ describe("profile-specific thresholds", () => {
+ const createModelInfo = (contextWindow: number, maxTokens?: number): ModelInfo => ({
+ contextWindow,
+ supportsPromptCache: true,
+ maxTokens,
+ })
+
+ const messages: ApiMessage[] = [
+ { role: "user", content: "First message" },
+ { role: "assistant", content: "Second message" },
+ { role: "user", content: "Third message" },
+ { role: "assistant", content: "Fourth message" },
+ { role: "user", content: "Fifth message" },
+ ]
+
+ /**
+ * Test that a profile's specific threshold is correctly used instead of the global threshold
+ * when defined in profileThresholds
+ */
+ it("should use profile-specific threshold when enabled and profile has specific threshold", async () => {
+ const modelInfo = createModelInfo(100000, 30000)
+ const profileThresholds = {
+ "test-profile": 60, // Profile-specific threshold of 60%
+ }
+ const currentProfileId = "test-profile"
+ const contextWindow = modelInfo.contextWindow
+
+ // Set tokens to 65% of context window - above profile threshold (60%) but below global default (100%)
+ const totalTokens = Math.floor(contextWindow * 0.65) // 65000 tokens
+
+ // Create messages with very small content in the last one to avoid token overflow
+ const messagesWithSmallContent = [
+ ...messages.slice(0, -1),
+ { ...messages[messages.length - 1], content: "" },
+ ]
+
+ // Mock the summarizeConversation function
+ const mockSummary = "Profile-specific threshold summary"
+ const mockCost = 0.03
+ const mockSummarizeResponse: condenseModule.SummarizeResponse = {
+ messages: [
+ { role: "user", content: "First message" },
+ { role: "assistant", content: mockSummary, isSummary: true },
+ { role: "user", content: "Last message" },
+ ],
+ summary: mockSummary,
+ cost: mockCost,
+ newContextTokens: 100,
+ }
+
+ const summarizeSpy = vi
+ .spyOn(condenseModule, "summarizeConversation")
+ .mockResolvedValue(mockSummarizeResponse)
+
+ const result = await truncateConversationIfNeeded({
+ messages: messagesWithSmallContent,
+ totalTokens,
+ contextWindow,
+ maxTokens: modelInfo.maxTokens,
+ apiHandler: mockApiHandler,
+ autoCondenseContext: true,
+ autoCondenseContextPercent: 100, // Global threshold of 100%
+ systemPrompt: "System prompt",
+ taskId,
+ profileThresholds,
+ currentProfileId,
+ })
+
+ // Should use summarization because 65% > 60% (profile threshold)
+ expect(summarizeSpy).toHaveBeenCalled()
+ expect(result).toMatchObject({
+ messages: mockSummarizeResponse.messages,
+ summary: mockSummary,
+ cost: mockCost,
+ prevContextTokens: totalTokens,
+ })
+
+ // Clean up
+ summarizeSpy.mockRestore()
+ })
+
+ /**
+ * Test that when a profile's threshold is set to -1,
+ * the function correctly falls back to using the global autoCondenseContextPercent
+ */
+ it("should fall back to global threshold when profile threshold is -1", async () => {
+ const modelInfo = createModelInfo(100000, 30000)
+ const profileThresholds = {
+ "test-profile": -1, // Profile threshold set to -1 (use global)
+ }
+ const currentProfileId = "test-profile"
+ const contextWindow = modelInfo.contextWindow
+
+ // Set tokens to 80% of context window - above global threshold (75%) but would be below if profile had its own
+ const totalTokens = Math.floor(contextWindow * 0.8) // 80000 tokens
+
+ // Create messages with very small content in the last one to avoid token overflow
+ const messagesWithSmallContent = [
+ ...messages.slice(0, -1),
+ { ...messages[messages.length - 1], content: "" },
+ ]
+
+ // Mock the summarizeConversation function
+ const mockSummary = "Global threshold fallback summary"
+ const mockCost = 0.04
+ const mockSummarizeResponse: condenseModule.SummarizeResponse = {
+ messages: [
+ { role: "user", content: "First message" },
+ { role: "assistant", content: mockSummary, isSummary: true },
+ { role: "user", content: "Last message" },
+ ],
+ summary: mockSummary,
+ cost: mockCost,
+ newContextTokens: 120,
+ }
+
+ const summarizeSpy = vi
+ .spyOn(condenseModule, "summarizeConversation")
+ .mockResolvedValue(mockSummarizeResponse)
+
+ const result = await truncateConversationIfNeeded({
+ messages: messagesWithSmallContent,
+ totalTokens,
+ contextWindow,
+ maxTokens: modelInfo.maxTokens,
+ apiHandler: mockApiHandler,
+ autoCondenseContext: true,
+ autoCondenseContextPercent: 75, // Global threshold of 75%
+ systemPrompt: "System prompt",
+ taskId,
+ profileThresholds,
+ currentProfileId,
+ })
+
+ // Should use summarization because 80% > 75% (global threshold, since profile is -1)
+ expect(summarizeSpy).toHaveBeenCalled()
+ expect(result).toMatchObject({
+ messages: mockSummarizeResponse.messages,
+ summary: mockSummary,
+ cost: mockCost,
+ prevContextTokens: totalTokens,
+ })
+
+ // Clean up
+ summarizeSpy.mockRestore()
+ })
+
+ /**
+ * Test that when a profile does not have a specific threshold defined,
+ * the function correctly falls back to the global default
+ */
+ it("should fall back to global threshold when profile has no specific threshold", async () => {
+ const modelInfo = createModelInfo(100000, 30000)
+ const profileThresholds = {
+ "other-profile": 50, // Different profile has a threshold
+ }
+ const currentProfileId = "test-profile" // This profile is not in profileThresholds
+ const contextWindow = modelInfo.contextWindow
+
+ // Calculate allowedTokens: contextWindow * (1 - TOKEN_BUFFER_PERCENTAGE) - reservedTokens
+ // allowedTokens = 100000 * 0.9 - 30000 = 60000
+ // Set tokens to be below both the global threshold (80%) and allowedTokens
+ const totalTokens = 50000 // 50% of context window, well below 60000 allowedTokens and 80% threshold
+
+ // Create messages with very small content in the last one to avoid token overflow
+ const messagesWithSmallContent = [
+ ...messages.slice(0, -1),
+ { ...messages[messages.length - 1], content: "" },
+ ]
+
+ // Reset any previous mock calls
+ vi.clearAllMocks()
+ const summarizeSpy = vi.spyOn(condenseModule, "summarizeConversation")
+
+ const result = await truncateConversationIfNeeded({
+ messages: messagesWithSmallContent,
+ totalTokens,
+ contextWindow,
+ maxTokens: modelInfo.maxTokens,
+ apiHandler: mockApiHandler,
+ autoCondenseContext: true,
+ autoCondenseContextPercent: 80, // Global threshold of 80%
+ systemPrompt: "System prompt",
+ taskId,
+ profileThresholds,
+ currentProfileId,
+ })
+
+ // Should NOT use summarization because 50% < 80% (global threshold, since profile has no specific threshold)
+ // and totalTokens (50000) < allowedTokens (60000)
+ expect(summarizeSpy).not.toHaveBeenCalled()
+ expect(result).toEqual({
+ messages: messagesWithSmallContent,
+ summary: "",
+ cost: 0,
+ prevContextTokens: totalTokens,
+ })
+
+ // Clean up
+ summarizeSpy.mockRestore()
+ })
+ })
+
/**
* Tests for the getMaxTokens function (private but tested through truncateConversationIfNeeded)
*/
@@ -829,6 +1072,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result1).toEqual({
messages: messagesWithSmallContent,
@@ -848,6 +1093,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result2.messages).not.toEqual(messagesWithSmallContent)
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
@@ -878,6 +1125,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result1).toEqual({
messages: messagesWithSmallContent,
@@ -897,6 +1146,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result2.messages).not.toEqual(messagesWithSmallContent)
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
@@ -926,6 +1177,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result1.messages).toEqual(messagesWithSmallContent)
@@ -940,6 +1193,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result2).not.toEqual(messagesWithSmallContent)
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
@@ -967,6 +1222,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result1.messages).toEqual(messagesWithSmallContent)
@@ -981,6 +1238,8 @@ describe("Sliding Window", () => {
autoCondenseContextPercent: 100,
systemPrompt: "System prompt",
taskId,
+ profileThresholds: {},
+ currentProfileId: "default",
})
expect(result2).not.toEqual(messagesWithSmallContent)
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts
index dc9eaf718d5..ae26f51a520 100644
--- a/src/core/sliding-window/index.ts
+++ b/src/core/sliding-window/index.ts
@@ -3,7 +3,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
import { TelemetryService } from "@roo-code/telemetry"
import { ApiHandler } from "../../api"
-import { summarizeConversation, SummarizeResponse } from "../condense"
+import { MAX_CONDENSE_THRESHOLD, MIN_CONDENSE_THRESHOLD, summarizeConversation, SummarizeResponse } from "../condense"
import { ApiMessage } from "../task-persistence/apiMessages"
/**
@@ -74,6 +74,8 @@ type TruncateOptions = {
taskId: string
customCondensingPrompt?: string
condensingApiHandler?: ApiHandler
+ profileThresholds: Record
+ currentProfileId: string
}
type TruncateResponse = SummarizeResponse & { prevContextTokens: number }
@@ -97,6 +99,8 @@ export async function truncateConversationIfNeeded({
taskId,
customCondensingPrompt,
condensingApiHandler,
+ profileThresholds,
+ currentProfileId,
}: TruncateOptions): Promise {
let error: string | undefined
let cost = 0
@@ -117,9 +121,29 @@ export async function truncateConversationIfNeeded({
// Truncate if we're within TOKEN_BUFFER_PERCENTAGE of the context window
const allowedTokens = contextWindow * (1 - TOKEN_BUFFER_PERCENTAGE) - reservedTokens
+ // Determine the effective threshold to use
+ let effectiveThreshold = autoCondenseContextPercent
+ const profileThreshold = profileThresholds[currentProfileId]
+ if (profileThreshold !== undefined) {
+ if (profileThreshold === -1) {
+ // Special case: -1 means inherit from global setting
+ effectiveThreshold = autoCondenseContextPercent
+ } else if (profileThreshold >= MIN_CONDENSE_THRESHOLD && profileThreshold <= MAX_CONDENSE_THRESHOLD) {
+ // Valid custom threshold
+ effectiveThreshold = profileThreshold
+ } else {
+ // Invalid threshold value, fall back to global setting
+ console.warn(
+ `Invalid profile threshold ${profileThreshold} for profile "${currentProfileId}". Using global default of ${autoCondenseContextPercent}%`,
+ )
+ effectiveThreshold = autoCondenseContextPercent
+ }
+ }
+ // If no specific threshold is found for the profile, fall back to global setting
+
if (autoCondenseContext) {
const contextPercent = (100 * prevContextTokens) / contextWindow
- if (contextPercent >= autoCondenseContextPercent || prevContextTokens > allowedTokens) {
+ if (contextPercent >= effectiveThreshold || prevContextTokens > allowedTokens) {
// Attempt to intelligently condense the context
const result = await summarizeConversation(
messages,
diff --git a/src/core/task/SubtaskTimeoutManager.ts b/src/core/task/SubtaskTimeoutManager.ts
new file mode 100644
index 00000000000..13e7459afa0
--- /dev/null
+++ b/src/core/task/SubtaskTimeoutManager.ts
@@ -0,0 +1,157 @@
+export interface TimeoutConfig {
+ timeoutMs: number
+ warningMs?: number
+ onTimeout: (taskId: string) => void
+ onWarning?: (taskId: string, remainingMs: number) => void
+ onExtended?: (taskId: string, newTimeoutMs: number) => void
+ onCleared?: (taskId: string) => void
+}
+
+export interface TimeoutStatus {
+ taskId: string
+ startTime: number
+ timeoutMs: number
+ warningMs?: number
+ hasWarned: boolean
+ isActive: boolean
+}
+
+export class SubtaskTimeoutManager {
+ private timeouts: Map = new Map()
+ private warnings: Map = new Map()
+ private statuses: Map = new Map()
+
+ startTimeout(taskId: string, config: TimeoutConfig): void {
+ this.clearTimeout(taskId)
+
+ const status: TimeoutStatus = {
+ taskId,
+ startTime: Date.now(),
+ timeoutMs: config.timeoutMs,
+ warningMs: config.warningMs,
+ hasWarned: false,
+ isActive: true,
+ }
+
+ this.statuses.set(taskId, status)
+
+ if (config.warningMs && config.onWarning) {
+ const warningTimeout = setTimeout(() => {
+ const currentStatus = this.statuses.get(taskId)
+ if (currentStatus && currentStatus.isActive && !currentStatus.hasWarned) {
+ currentStatus.hasWarned = true
+ const remainingMs = Math.max(0, config.timeoutMs - config.warningMs!)
+ config.onWarning!(taskId, remainingMs)
+ }
+ }, config.warningMs)
+
+ this.warnings.set(taskId, warningTimeout)
+ }
+
+ const timeoutHandle = setTimeout(() => {
+ const currentStatus = this.statuses.get(taskId)
+ if (currentStatus && currentStatus.isActive) {
+ currentStatus.isActive = false
+ this.clearTimeout(taskId)
+ config.onTimeout(taskId)
+ }
+ }, config.timeoutMs)
+
+ this.timeouts.set(taskId, timeoutHandle)
+ }
+
+ extendTimeout(taskId: string, extensionMs: number, config?: TimeoutConfig): boolean {
+ const status = this.statuses.get(taskId)
+ if (!status || !status.isActive) {
+ return false
+ }
+
+ this.clearTimeout(taskId)
+
+ const elapsed = Date.now() - status.startTime
+ const newTimeoutMs = status.timeoutMs + extensionMs
+ const remainingWarningMs = status.warningMs ? Math.max(0, status.warningMs - elapsed) : undefined
+
+ const newStatus: TimeoutStatus = {
+ ...status,
+ timeoutMs: newTimeoutMs,
+ hasWarned: elapsed >= (status.warningMs || Infinity),
+ }
+
+ this.statuses.set(taskId, newStatus)
+
+ if (config) {
+ const adjustedConfig: TimeoutConfig = {
+ ...config,
+ timeoutMs: newTimeoutMs - elapsed,
+ warningMs: remainingWarningMs,
+ }
+
+ this.startTimeout(taskId, adjustedConfig)
+ config.onExtended?.(taskId, newTimeoutMs)
+ }
+
+ return true
+ }
+
+ clearTimeout(taskId: string): boolean {
+ const timeoutHandle = this.timeouts.get(taskId)
+ const warningHandle = this.warnings.get(taskId)
+ const status = this.statuses.get(taskId)
+
+ if (timeoutHandle) {
+ clearTimeout(timeoutHandle)
+ this.timeouts.delete(taskId)
+ }
+
+ if (warningHandle) {
+ clearTimeout(warningHandle)
+ this.warnings.delete(taskId)
+ }
+
+ if (status) {
+ status.isActive = false
+ this.statuses.delete(taskId)
+ return true
+ }
+
+ return false
+ }
+
+ getTimeRemaining(taskId: string): number {
+ const status = this.statuses.get(taskId)
+ if (!status || !status.isActive) {
+ return 0
+ }
+
+ const elapsed = Date.now() - status.startTime
+ return Math.max(0, status.timeoutMs - elapsed)
+ }
+
+ getStatus(taskId: string): TimeoutStatus | undefined {
+ return this.statuses.get(taskId)
+ }
+
+ isActive(taskId: string): boolean {
+ const status = this.statuses.get(taskId)
+ return status?.isActive ?? false
+ }
+
+ getActiveTimeouts(): string[] {
+ return Array.from(this.statuses.entries())
+ .filter(([, status]) => status.isActive)
+ .map(([taskId]) => taskId)
+ }
+
+ dispose(): void {
+ for (const timeoutHandle of this.timeouts.values()) {
+ clearTimeout(timeoutHandle)
+ }
+ for (const warningHandle of this.warnings.values()) {
+ clearTimeout(warningHandle)
+ }
+ this.timeouts.clear()
+ this.warnings.clear()
+ this.statuses.clear()
+ }
+}
diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts
index 6f6f2d684af..0a0121c5163 100644
--- a/src/core/task/Task.ts
+++ b/src/core/task/Task.ts
@@ -85,6 +85,7 @@ import { processUserContentMentions } from "../mentions/processUserContentMentio
import { ApiMessage } from "../task-persistence/apiMessages"
import { getMessagesSinceLastSummary, summarizeConversation } from "../condense"
import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
+import { SubtaskTimeoutManager, type TimeoutConfig, type TimeoutStatus } from "./SubtaskTimeoutManager"
export type ClineEvents = {
message: [{ action: "created" | "updated"; message: ClineMessage }]
@@ -98,6 +99,10 @@ export type ClineEvents = {
taskCompleted: [taskId: string, tokenUsage: TokenUsage, toolUsage: ToolUsage]
taskTokenUsageUpdated: [taskId: string, tokenUsage: TokenUsage]
taskToolFailed: [taskId: string, tool: ToolName, error: string]
+ taskTimeoutWarning: [taskId: string, remainingMs: number]
+ taskTimedOut: [taskId: string]
+ taskTimeoutExtended: [taskId: string, newTimeoutMs: number]
+ taskTimeoutCleared: [taskId: string]
}
export type TaskOptions = {
@@ -116,6 +121,7 @@ export type TaskOptions = {
parentTask?: Task
taskNumber?: number
onCreated?: (cline: Task) => void
+ subtaskTimeoutMs?: number
}
export class Task extends EventEmitter {
@@ -137,6 +143,10 @@ export class Task extends EventEmitter {
pausedModeSlug: string = defaultModeSlug
private pauseInterval: NodeJS.Timeout | undefined
+ // Timeout Management
+ private timeoutManager: SubtaskTimeoutManager
+ private subtaskTimeoutMs?: number
+
// API
readonly apiConfiguration: ProviderSettings
api: ApiHandler
@@ -217,6 +227,7 @@ export class Task extends EventEmitter {
parentTask,
taskNumber = -1,
onCreated,
+ subtaskTimeoutMs,
}: TaskOptions) {
super()
@@ -282,6 +293,8 @@ export class Task extends EventEmitter {
}
this.toolRepetitionDetector = new ToolRepetitionDetector(this.consecutiveMistakeLimit)
+ this.timeoutManager = new SubtaskTimeoutManager()
+ this.subtaskTimeoutMs = subtaskTimeoutMs
onCreated?.(this)
@@ -1017,6 +1030,13 @@ export class Task extends EventEmitter {
this.pauseInterval = undefined
}
+ // Clean up timeout manager
+ try {
+ this.timeoutManager.dispose()
+ } catch (error) {
+ console.error("Error disposing timeout manager:", error)
+ }
+
// Release any terminals associated with this task.
try {
// Release any terminals associated with this task.
@@ -1643,6 +1663,7 @@ export class Task extends EventEmitter {
mode,
autoCondenseContext = true,
autoCondenseContextPercent = 100,
+ profileThresholds = {},
} = state ?? {}
// Get condensing configuration for automatic triggers
@@ -1719,6 +1740,8 @@ export class Task extends EventEmitter {
taskId: this.taskId,
customCondensingPrompt,
condensingApiHandler,
+ profileThresholds,
+ currentProfileId: state?.currentApiConfigName || "default",
})
if (truncateResult.messages !== this.apiConversationHistory) {
await this.overwriteApiConversationHistory(truncateResult.messages)
@@ -1905,6 +1928,82 @@ export class Task extends EventEmitter {
}
}
+ // Timeout Management
+
+ public startSubtaskTimeout(subtaskId: string, timeoutMs?: number): void {
+ const actualTimeoutMs = timeoutMs || this.subtaskTimeoutMs
+ if (!actualTimeoutMs) {
+ return
+ }
+
+ const config: TimeoutConfig = {
+ timeoutMs: actualTimeoutMs,
+ warningMs: Math.floor(actualTimeoutMs * 0.8), // Warn at 80%
+ onTimeout: (taskId: string) => {
+ this.emit("taskTimedOut", taskId)
+ const provider = this.providerRef.deref()
+ if (provider) {
+ provider.abortSubtask(taskId)
+ }
+ },
+ onWarning: (taskId: string, remainingMs: number) => {
+ this.emit("taskTimeoutWarning", taskId, remainingMs)
+ },
+ onExtended: (taskId: string, newTimeoutMs: number) => {
+ this.emit("taskTimeoutExtended", taskId, newTimeoutMs)
+ },
+ onCleared: (taskId: string) => {
+ this.emit("taskTimeoutCleared", taskId)
+ },
+ }
+
+ this.timeoutManager.startTimeout(subtaskId, config)
+ }
+
+ public extendSubtaskTimeout(subtaskId: string, extensionMs: number): boolean {
+ const config: TimeoutConfig = {
+ timeoutMs: 0, // Will be calculated by manager
+ onTimeout: (taskId: string) => {
+ this.emit("taskTimedOut", taskId)
+ const provider = this.providerRef.deref()
+ if (provider) {
+ provider.abortSubtask(taskId)
+ }
+ },
+ onWarning: (taskId: string, remainingMs: number) => {
+ this.emit("taskTimeoutWarning", taskId, remainingMs)
+ },
+ onExtended: (taskId: string, newTimeoutMs: number) => {
+ this.emit("taskTimeoutExtended", taskId, newTimeoutMs)
+ },
+ onCleared: (taskId: string) => {
+ this.emit("taskTimeoutCleared", taskId)
+ },
+ }
+
+ return this.timeoutManager.extendTimeout(subtaskId, extensionMs, config)
+ }
+
+ public clearSubtaskTimeout(subtaskId: string): boolean {
+ const result = this.timeoutManager.clearTimeout(subtaskId)
+ if (result) {
+ this.emit("taskTimeoutCleared", subtaskId)
+ }
+ return result
+ }
+
+ public getSubtaskTimeoutStatus(subtaskId: string): TimeoutStatus | undefined {
+ return this.timeoutManager.getStatus(subtaskId)
+ }
+
+ public getSubtaskTimeRemaining(subtaskId: string): number {
+ return this.timeoutManager.getTimeRemaining(subtaskId)
+ }
+
+ public isSubtaskTimeoutActive(subtaskId: string): boolean {
+ return this.timeoutManager.isActive(subtaskId)
+ }
+
// Getters
public get cwd() {
diff --git a/src/core/task/__tests__/SubtaskTimeoutManager.test.ts b/src/core/task/__tests__/SubtaskTimeoutManager.test.ts
new file mode 100644
index 00000000000..c33265d1538
--- /dev/null
+++ b/src/core/task/__tests__/SubtaskTimeoutManager.test.ts
@@ -0,0 +1,222 @@
+import { jest } from "@jest/globals"
+import { SubtaskTimeoutManager, type TimeoutConfig } from "../SubtaskTimeoutManager"
+
+describe("SubtaskTimeoutManager", () => {
+ let manager: SubtaskTimeoutManager
+ let mockOnTimeout: jest.MockedFunction<(taskId: string) => void>
+ let mockOnWarning: jest.MockedFunction<(taskId: string, remainingMs: number) => void>
+
+ beforeEach(() => {
+ jest.useFakeTimers()
+ manager = new SubtaskTimeoutManager()
+ mockOnTimeout = jest.fn()
+ mockOnWarning = jest.fn()
+ })
+
+ afterEach(() => {
+ manager.dispose()
+ jest.useRealTimers()
+ })
+
+ describe("startTimeout", () => {
+ it("should start a timeout for a task", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ }
+
+ manager.startTimeout("task1", config)
+
+ expect(manager.isActive("task1")).toBe(true)
+ expect(manager.getTimeRemaining("task1")).toBe(5000)
+ })
+
+ it("should call onTimeout when timeout expires", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ }
+
+ manager.startTimeout("task1", config)
+
+ jest.advanceTimersByTime(5000)
+
+ expect(mockOnTimeout).toHaveBeenCalledWith("task1")
+ expect(manager.isActive("task1")).toBe(false)
+ })
+
+ it("should call onWarning at the specified time", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ warningMs: 2000,
+ onTimeout: mockOnTimeout,
+ onWarning: mockOnWarning,
+ }
+
+ manager.startTimeout("task1", config)
+
+ jest.advanceTimersByTime(2000)
+
+ expect(mockOnWarning).toHaveBeenCalledWith("task1", 3000)
+ expect(mockOnTimeout).not.toHaveBeenCalled()
+ })
+
+ it("should replace existing timeout when starting new one for same task", () => {
+ const config1: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ }
+ const config2: TimeoutConfig = {
+ timeoutMs: 10000,
+ onTimeout: mockOnTimeout,
+ }
+
+ manager.startTimeout("task1", config1)
+ manager.startTimeout("task1", config2)
+
+ jest.advanceTimersByTime(5000)
+ expect(mockOnTimeout).not.toHaveBeenCalled()
+ expect(manager.isActive("task1")).toBe(true)
+
+ jest.advanceTimersByTime(5000)
+ expect(mockOnTimeout).toHaveBeenCalledWith("task1")
+ })
+ })
+
+ describe("extendTimeout", () => {
+ it("should extend an active timeout", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ onExtended: jest.fn(),
+ }
+
+ manager.startTimeout("task1", config)
+ jest.advanceTimersByTime(2000)
+
+ const result = manager.extendTimeout("task1", 3000, config)
+
+ expect(result).toBe(true)
+ expect(manager.getTimeRemaining("task1")).toBeGreaterThan(5000)
+ })
+
+ it("should return false for inactive task", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ }
+
+ const result = manager.extendTimeout("nonexistent", 3000, config)
+ expect(result).toBe(false)
+ })
+
+ it("should call onExtended callback when extending", () => {
+ const mockOnExtended = jest.fn()
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ onExtended: mockOnExtended,
+ }
+
+ manager.startTimeout("task1", config)
+ manager.extendTimeout("task1", 3000, config)
+
+ expect(mockOnExtended).toHaveBeenCalledWith("task1", 8000)
+ })
+ })
+
+ describe("clearTimeout", () => {
+ it("should clear an active timeout", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ }
+
+ manager.startTimeout("task1", config)
+ const result = manager.clearTimeout("task1")
+
+ expect(result).toBe(true)
+ expect(manager.isActive("task1")).toBe(false)
+
+ jest.advanceTimersByTime(5000)
+ expect(mockOnTimeout).not.toHaveBeenCalled()
+ })
+
+ it("should return false for non-existent task", () => {
+ const result = manager.clearTimeout("nonexistent")
+ expect(result).toBe(false)
+ })
+ })
+
+ describe("getTimeRemaining", () => {
+ it("should return correct remaining time", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ }
+
+ manager.startTimeout("task1", config)
+ jest.advanceTimersByTime(2000)
+
+ const remaining = manager.getTimeRemaining("task1")
+ expect(remaining).toBe(3000)
+ })
+
+ it("should return 0 for inactive task", () => {
+ const remaining = manager.getTimeRemaining("nonexistent")
+ expect(remaining).toBe(0)
+ })
+ })
+
+ describe("getActiveTimeouts", () => {
+ it("should return list of active timeout task IDs", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ }
+
+ manager.startTimeout("task1", config)
+ manager.startTimeout("task2", config)
+
+ const active = manager.getActiveTimeouts()
+ expect(active).toEqual(expect.arrayContaining(["task1", "task2"]))
+ expect(active).toHaveLength(2)
+ })
+
+ it("should not include expired timeouts", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ }
+
+ manager.startTimeout("task1", config)
+ manager.startTimeout("task2", config)
+
+ jest.advanceTimersByTime(5000)
+
+ const active = manager.getActiveTimeouts()
+ expect(active).toHaveLength(0)
+ })
+ })
+
+ describe("dispose", () => {
+ it("should clear all timeouts and statuses", () => {
+ const config: TimeoutConfig = {
+ timeoutMs: 5000,
+ onTimeout: mockOnTimeout,
+ }
+
+ manager.startTimeout("task1", config)
+ manager.startTimeout("task2", config)
+
+ manager.dispose()
+
+ expect(manager.getActiveTimeouts()).toHaveLength(0)
+ expect(manager.isActive("task1")).toBe(false)
+ expect(manager.isActive("task2")).toBe(false)
+
+ jest.advanceTimersByTime(5000)
+ expect(mockOnTimeout).not.toHaveBeenCalled()
+ })
+ })
+})
diff --git a/src/core/tools/newTaskTool.ts b/src/core/tools/newTaskTool.ts
index ab2519e9b46..98c140820df 100644
--- a/src/core/tools/newTaskTool.ts
+++ b/src/core/tools/newTaskTool.ts
@@ -16,6 +16,7 @@ export async function newTaskTool(
) {
const mode: string | undefined = block.params.mode
const message: string | undefined = block.params.message
+ const timeoutSeconds: string | undefined = block.params.timeout_seconds
try {
if (block.partial) {
@@ -23,6 +24,7 @@ export async function newTaskTool(
tool: "newTask",
mode: removeClosingTag("mode", mode),
message: removeClosingTag("message", message),
+ timeout_seconds: removeClosingTag("timeout_seconds", timeoutSeconds),
})
await cline.ask("tool", partialMessage, block.partial).catch(() => {})
@@ -47,6 +49,17 @@ export async function newTaskTool(
// Un-escape one level: \\@ -> \@ (removes one backslash for hierarchical subtasks)
const unescapedMessage = message.replace(/\\\\@/g, "\\@")
+ // Parse timeout if provided
+ let timeoutMs: number | undefined
+ if (timeoutSeconds) {
+ const parsedTimeout = parseInt(timeoutSeconds, 10)
+ if (isNaN(parsedTimeout) || parsedTimeout <= 0) {
+ pushToolResult(formatResponse.toolError("Invalid timeout_seconds: must be a positive number"))
+ return
+ }
+ timeoutMs = parsedTimeout * 1000 // Convert to milliseconds
+ }
+
// Verify the mode exists
const targetMode = getModeBySlug(mode, (await cline.providerRef.deref()?.getState())?.customModes)
@@ -59,6 +72,7 @@ export async function newTaskTool(
tool: "newTask",
mode: targetMode.name,
content: message,
+ timeout_seconds: timeoutSeconds ? parseInt(timeoutSeconds, 10) : undefined,
})
const didApprove = await askApproval("tool", toolMessage)
@@ -86,13 +100,18 @@ export async function newTaskTool(
// Delay to allow mode change to take effect before next tool is executed.
await delay(500)
- const newCline = await provider.initClineWithTask(unescapedMessage, undefined, cline)
+ const newCline = await provider.initClineWithTask(unescapedMessage, undefined, cline, timeoutMs)
if (!newCline) {
pushToolResult(t("tools:newTask.errors.policy_restriction"))
return
}
cline.emit("taskSpawned", newCline.taskId)
+ // Start timeout if specified
+ if (timeoutMs) {
+ cline.startSubtaskTimeout(newCline.taskId, timeoutMs)
+ }
+
pushToolResult(`Successfully created new task in ${targetMode.name} mode with message: ${unescapedMessage}`)
// Set the isPaused flag to true so the parent
diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts
index 49fd0c1258f..8bf8f901a2e 100644
--- a/src/core/webview/ClineProvider.ts
+++ b/src/core/webview/ClineProvider.ts
@@ -239,6 +239,32 @@ export class ClineProvider
await this.removeClineFromStack()
}
+ // Abort a specific subtask by taskId (used for timeout scenarios)
+ async abortSubtask(taskId: string) {
+ console.log(`[subtasks] aborting subtask ${taskId} due to timeout`)
+
+ // Find the task in the stack
+ const taskIndex = this.clineStack.findIndex((task) => task.taskId === taskId)
+ if (taskIndex === -1) {
+ console.warn(`[subtasks] task ${taskId} not found in stack for abortion`)
+ return
+ }
+
+ const task = this.clineStack[taskIndex]
+
+ // If it's the current task (top of stack), handle like normal cancellation
+ if (taskIndex === this.clineStack.length - 1) {
+ await this.finishSubTask("Task timed out and was aborted")
+ } else {
+ // If it's not the current task, abort it directly and remove from stack
+ await task.abortTask(true)
+ this.clineStack.splice(taskIndex, 1)
+
+ // Update UI to reflect stack change
+ await this.postStateToWebview()
+ }
+ }
+
/*
VSCode extensions use the disposable pattern to clean up resources when the sidebar/editor tab is closed by the user or system. This applies to event listening, commands, interacting with the UI, etc.
- https://vscode-docs.readthedocs.io/en/stable/extensions/patterns-and-principles/
@@ -526,6 +552,7 @@ export class ClineProvider
task?: string,
images?: string[],
parentTask?: Task,
+ subtaskTimeoutMs?: number,
options: Partial<
Pick<
TaskOptions,
@@ -559,6 +586,7 @@ export class ClineProvider
parentTask,
taskNumber: this.clineStack.length + 1,
onCreated: (cline) => this.emit("clineCreated", cline),
+ subtaskTimeoutMs,
...options,
})
@@ -1376,6 +1404,7 @@ export class ClineProvider
customCondensingPrompt,
codebaseIndexConfig,
codebaseIndexModels,
+ profileThresholds,
} = await this.getState()
const telemetryKey = process.env.POSTHOG_API_KEY
@@ -1483,6 +1512,7 @@ export class ClineProvider
codebaseIndexEmbedderModelId: "",
},
mdmCompliant: this.checkMdmCompliance(),
+ profileThresholds: profileThresholds ?? {},
}
}
@@ -1632,6 +1662,7 @@ export class ClineProvider
codebaseIndexEmbedderBaseUrl: "",
codebaseIndexEmbedderModelId: "",
},
+ profileThresholds: stateValues.profileThresholds ?? {},
}
}
diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts
index de4f34a12ac..fabb0aae60b 100644
--- a/src/core/webview/__tests__/ClineProvider.spec.ts
+++ b/src/core/webview/__tests__/ClineProvider.spec.ts
@@ -522,6 +522,7 @@ describe("ClineProvider", () => {
autoCondenseContextPercent: 100,
cloudIsAuthenticated: false,
sharingEnabled: false,
+ profileThresholds: {},
}
const message: ExtensionMessage = {
@@ -2266,6 +2267,8 @@ describe("ClineProvider - Router Models", () => {
glama: mockModels,
unbound: mockModels,
litellm: mockModels,
+ ollama: {},
+ lmstudio: {},
},
})
})
@@ -2308,6 +2311,8 @@ describe("ClineProvider - Router Models", () => {
requesty: {},
glama: mockModels,
unbound: {},
+ ollama: {},
+ lmstudio: {},
litellm: {},
},
})
@@ -2327,6 +2332,13 @@ describe("ClineProvider - Router Models", () => {
values: { provider: "unbound" },
})
+ expect(mockPostMessage).toHaveBeenCalledWith({
+ type: "singleRouterModelFetchResponse",
+ success: false,
+ error: "Unbound API error",
+ values: { provider: "unbound" },
+ })
+
expect(mockPostMessage).toHaveBeenCalledWith({
type: "singleRouterModelFetchResponse",
success: false,
@@ -2410,6 +2422,8 @@ describe("ClineProvider - Router Models", () => {
glama: mockModels,
unbound: mockModels,
litellm: {},
+ ollama: {},
+ lmstudio: {},
},
})
})
diff --git a/src/core/webview/__tests__/webviewMessageHandler.spec.ts b/src/core/webview/__tests__/webviewMessageHandler.spec.ts
index e15b18ccdb4..46ace3ce85b 100644
--- a/src/core/webview/__tests__/webviewMessageHandler.spec.ts
+++ b/src/core/webview/__tests__/webviewMessageHandler.spec.ts
@@ -73,6 +73,8 @@ describe("webviewMessageHandler - requestRouterModels", () => {
glama: mockModels,
unbound: mockModels,
litellm: mockModels,
+ ollama: {},
+ lmstudio: {},
},
})
})
@@ -158,6 +160,8 @@ describe("webviewMessageHandler - requestRouterModels", () => {
glama: mockModels,
unbound: mockModels,
litellm: {},
+ ollama: {},
+ lmstudio: {},
},
})
})
@@ -193,6 +197,8 @@ describe("webviewMessageHandler - requestRouterModels", () => {
glama: mockModels,
unbound: {},
litellm: {},
+ ollama: {},
+ lmstudio: {},
},
})
@@ -222,11 +228,11 @@ describe("webviewMessageHandler - requestRouterModels", () => {
it("handles Error objects and string errors correctly", async () => {
// Mock providers to fail with different error types
mockGetModels
- .mockRejectedValueOnce(new Error("Structured error message")) // Error object
- .mockRejectedValueOnce("String error message") // String error
- .mockRejectedValueOnce({ message: "Object with message" }) // Object error
- .mockResolvedValueOnce({}) // Success
- .mockResolvedValueOnce({}) // Success
+ .mockRejectedValueOnce(new Error("Structured error message")) // openrouter
+ .mockRejectedValueOnce(new Error("Requesty API error")) // requesty
+ .mockRejectedValueOnce(new Error("Glama API error")) // glama
+ .mockRejectedValueOnce(new Error("Unbound API error")) // unbound
+ .mockRejectedValueOnce(new Error("LiteLLM connection failed")) // litellm
await webviewMessageHandler(mockClineProvider, {
type: "requestRouterModels",
@@ -243,16 +249,30 @@ describe("webviewMessageHandler - requestRouterModels", () => {
expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({
type: "singleRouterModelFetchResponse",
success: false,
- error: "String error message",
+ error: "Requesty API error",
values: { provider: "requesty" },
})
expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({
type: "singleRouterModelFetchResponse",
success: false,
- error: "[object Object]",
+ error: "Glama API error",
values: { provider: "glama" },
})
+
+ expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({
+ type: "singleRouterModelFetchResponse",
+ success: false,
+ error: "Unbound API error",
+ values: { provider: "unbound" },
+ })
+
+ expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({
+ type: "singleRouterModelFetchResponse",
+ success: false,
+ error: "LiteLLM connection failed",
+ values: { provider: "litellm" },
+ })
})
it("prefers config values over message values for LiteLLM", async () => {
diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts
index 3e988a0c797..ee7796ed24f 100644
--- a/src/core/webview/webviewMessageHandler.ts
+++ b/src/core/webview/webviewMessageHandler.ts
@@ -29,9 +29,7 @@ import { singleCompletionHandler } from "../../utils/single-completion-handler"
import { searchCommits } from "../../utils/git"
import { exportSettings, importSettings } from "../config/importExport"
import { getOpenAiModels } from "../../api/providers/openai"
-import { getOllamaModels } from "../../api/providers/ollama"
import { getVsCodeLmModels } from "../../api/providers/vscode-lm"
-import { getLmStudioModels } from "../../api/providers/lm-studio"
import { openMention } from "../mentions"
import { TelemetrySetting } from "../../shared/TelemetrySetting"
import { getWorkspacePath } from "../../utils/path"
@@ -357,6 +355,8 @@ export const webviewMessageHandler = async (
glama: {},
unbound: {},
litellm: {},
+ ollama: {},
+ lmstudio: {},
}
const safeGetModels = async (options: GetModelsOptions): Promise => {
@@ -378,6 +378,9 @@ export const webviewMessageHandler = async (
{ key: "unbound", options: { provider: "unbound", apiKey: apiConfiguration.unboundApiKey } },
]
+ // Don't fetch Ollama and LM Studio models by default anymore
+ // They have their own specific handlers: requestOllamaModels and requestLmStudioModels
+
const litellmApiKey = apiConfiguration.litellmApiKey || message?.values?.litellmApiKey
const litellmBaseUrl = apiConfiguration.litellmBaseUrl || message?.values?.litellmBaseUrl
if (litellmApiKey && litellmBaseUrl) {
@@ -394,13 +397,31 @@ export const webviewMessageHandler = async (
}),
)
- const fetchedRouterModels: Partial> = { ...routerModels }
+ const fetchedRouterModels: Partial> = {
+ ...routerModels,
+ // Initialize ollama and lmstudio with empty objects since they use separate handlers
+ ollama: {},
+ lmstudio: {},
+ }
results.forEach((result, index) => {
const routerName = modelFetchPromises[index].key // Get RouterName using index
if (result.status === "fulfilled") {
fetchedRouterModels[routerName] = result.value.models
+
+ // Ollama and LM Studio settings pages still need these events
+ if (routerName === "ollama" && Object.keys(result.value.models).length > 0) {
+ provider.postMessageToWebview({
+ type: "ollamaModels",
+ ollamaModels: Object.keys(result.value.models),
+ })
+ } else if (routerName === "lmstudio" && Object.keys(result.value.models).length > 0) {
+ provider.postMessageToWebview({
+ type: "lmStudioModels",
+ lmStudioModels: Object.keys(result.value.models),
+ })
+ }
} else {
// Handle rejection: Post a specific error message for this provider
const errorMessage = result.reason instanceof Error ? result.reason.message : String(result.reason)
@@ -421,7 +442,50 @@ export const webviewMessageHandler = async (
type: "routerModels",
routerModels: fetchedRouterModels as Record,
})
+
break
+ case "requestOllamaModels": {
+ // Specific handler for Ollama models only
+ const { apiConfiguration: ollamaApiConfig } = await provider.getState()
+ try {
+ const ollamaModels = await getModels({
+ provider: "ollama",
+ baseUrl: ollamaApiConfig.ollamaBaseUrl,
+ })
+
+ if (Object.keys(ollamaModels).length > 0) {
+ provider.postMessageToWebview({
+ type: "ollamaModels",
+ ollamaModels: Object.keys(ollamaModels),
+ })
+ }
+ } catch (error) {
+ // Silently fail - user hasn't configured Ollama yet
+ console.debug("Ollama models fetch failed:", error)
+ }
+ break
+ }
+ case "requestLmStudioModels": {
+ // Specific handler for LM Studio models only
+ const { apiConfiguration: lmStudioApiConfig } = await provider.getState()
+ try {
+ const lmStudioModels = await getModels({
+ provider: "lmstudio",
+ baseUrl: lmStudioApiConfig.lmStudioBaseUrl,
+ })
+
+ if (Object.keys(lmStudioModels).length > 0) {
+ provider.postMessageToWebview({
+ type: "lmStudioModels",
+ lmStudioModels: Object.keys(lmStudioModels),
+ })
+ }
+ } catch (error) {
+ // Silently fail - user hasn't configured LM Studio yet
+ console.debug("LM Studio models fetch failed:", error)
+ }
+ break
+ }
case "requestOpenAiModels":
if (message?.values?.baseUrl && message?.values?.apiKey) {
const openAiModels = await getOpenAiModels(
@@ -433,16 +497,6 @@ export const webviewMessageHandler = async (
provider.postMessageToWebview({ type: "openAiModels", openAiModels })
}
- break
- case "requestOllamaModels":
- const ollamaModels = await getOllamaModels(message.text)
- // TODO: Cache like we do for OpenRouter, etc?
- provider.postMessageToWebview({ type: "ollamaModels", ollamaModels })
- break
- case "requestLmStudioModels":
- const lmStudioModels = await getLmStudioModels(message.text)
- // TODO: Cache like we do for OpenRouter, etc?
- provider.postMessageToWebview({ type: "lmStudioModels", lmStudioModels })
break
case "requestVsCodeLmModels":
const vsCodeLmModels = await getVsCodeLmModels()
@@ -1065,6 +1119,10 @@ export const webviewMessageHandler = async (
await updateGlobalState("customCondensingPrompt", message.text)
await provider.postStateToWebview()
break
+ case "profileThresholds":
+ await updateGlobalState("profileThresholds", message.values)
+ await provider.postStateToWebview()
+ break
case "autoApprovalEnabled":
await updateGlobalState("autoApprovalEnabled", message.bool ?? false)
await provider.postStateToWebview()
@@ -1597,5 +1655,57 @@ export const webviewMessageHandler = async (
}
break
}
+ case "extendSubtaskTimeout":
+ if (message.taskId && typeof message.extensionMs === "number") {
+ try {
+ const currentTask = provider.getCurrentCline()
+ if (currentTask) {
+ const success = currentTask.extendSubtaskTimeout(message.taskId, message.extensionMs)
+ if (!success) {
+ provider.log(
+ `Failed to extend subtask timeout: task ${message.taskId} not found or inactive`,
+ )
+ vscode.window.showWarningMessage(t("common:errors.subtask_timeout_not_found"))
+ }
+ }
+ } catch (error) {
+ provider.log(
+ `Failed to extend subtask timeout: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
+ )
+ vscode.window.showErrorMessage(t("common:errors.extend_subtask_timeout"))
+ }
+ }
+ break
+ case "clearSubtaskTimeout":
+ if (message.taskId) {
+ try {
+ const currentTask = provider.getCurrentCline()
+ if (currentTask) {
+ const success = currentTask.clearSubtaskTimeout(message.taskId)
+ if (!success) {
+ provider.log(`Failed to clear subtask timeout: task ${message.taskId} not found`)
+ vscode.window.showWarningMessage(t("common:errors.subtask_timeout_not_found"))
+ }
+ }
+ } catch (error) {
+ provider.log(
+ `Failed to clear subtask timeout: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
+ )
+ vscode.window.showErrorMessage(t("common:errors.clear_subtask_timeout"))
+ }
+ }
+ break
+ case "defaultSubtaskTimeoutMs":
+ await updateGlobalState("defaultSubtaskTimeoutMs", message.value)
+ await provider.postStateToWebview()
+ break
+ case "subtaskTimeoutWarningPercent":
+ await updateGlobalState("subtaskTimeoutWarningPercent", message.value)
+ await provider.postStateToWebview()
+ break
+ case "maxSubtaskTimeoutExtensions":
+ await updateGlobalState("maxSubtaskTimeoutExtensions", message.value)
+ await provider.postStateToWebview()
+ break
}
}
diff --git a/src/extension/api.ts b/src/extension/api.ts
index 3bb538dcb30..c9d31c784ed 100644
--- a/src/extension/api.ts
+++ b/src/extension/api.ts
@@ -132,7 +132,7 @@ export class API extends EventEmitter implements RooCodeAPI {
await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
await provider.postMessageToWebview({ type: "invoke", invoke: "newChat", text, images })
- const cline = await provider.initClineWithTask(text, images, undefined, {
+ const cline = await provider.initClineWithTask(text, images, undefined, undefined, {
consecutiveMistakeLimit: Number.MAX_SAFE_INTEGER,
})
diff --git a/src/package.json b/src/package.json
index 4c2df62ddd2..bb4a784173b 100644
--- a/src/package.json
+++ b/src/package.json
@@ -3,7 +3,7 @@
"displayName": "%extension.displayName%",
"description": "%extension.description%",
"publisher": "RooVeterinaryInc",
- "version": "3.21.1",
+ "version": "3.21.3",
"icon": "assets/icons/icon.png",
"galleryBanner": {
"color": "#617A91",
@@ -369,6 +369,7 @@
"@aws-sdk/client-bedrock-runtime": "^3.779.0",
"@aws-sdk/credential-providers": "^3.806.0",
"@google/genai": "^1.0.0",
+ "@lmstudio/sdk": "^1.1.1",
"@mistralai/mistralai": "^1.3.6",
"@modelcontextprotocol/sdk": "^1.9.0",
"@qdrant/js-client-rest": "^1.14.0",
@@ -408,14 +409,14 @@
"p-wait-for": "^5.0.2",
"pdf-parse": "^1.1.1",
"pkce-challenge": "^5.0.0",
- "pretty-bytes": "^6.1.1",
+ "pretty-bytes": "^7.0.0",
"ps-tree": "^1.2.0",
"puppeteer-chromium-resolver": "^24.0.0",
"puppeteer-core": "^23.4.0",
"reconnecting-eventsource": "^1.6.4",
"sanitize-filename": "^1.6.3",
"say": "^0.16.0",
- "serialize-error": "^11.0.3",
+ "serialize-error": "^12.0.0",
"simple-git": "^3.27.0",
"sound-play": "^1.1.0",
"string-similarity": "^4.0.4",
diff --git a/src/services/checkpoints/__tests__/ShadowCheckpointService.spec.ts b/src/services/checkpoints/__tests__/ShadowCheckpointService.spec.ts
index a403c4e8518..cba8eee8ba4 100644
--- a/src/services/checkpoints/__tests__/ShadowCheckpointService.spec.ts
+++ b/src/services/checkpoints/__tests__/ShadowCheckpointService.spec.ts
@@ -12,8 +12,6 @@ import * as fileSearch from "../../../services/search/file-search"
import { RepoPerTaskCheckpointService } from "../RepoPerTaskCheckpointService"
-vitest.setConfig({ testTimeout: 20_000 })
-
const tmpDir = path.join(os.tmpdir(), "CheckpointService")
const initWorkspaceRepo = async ({
diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts
index a515a2d0040..9a2c9230bd7 100644
--- a/src/shared/ExtensionMessage.ts
+++ b/src/shared/ExtensionMessage.ts
@@ -213,6 +213,7 @@ export type ExtensionState = Pick<
| "customCondensingPrompt"
| "codebaseIndexConfig"
| "codebaseIndexModels"
+ | "profileThresholds"
> & {
version: string
clineMessages: ClineMessage[]
@@ -259,6 +260,7 @@ export type ExtensionState = Pick<
autoCondenseContextPercent: number
marketplaceItems?: MarketplaceItem[]
marketplaceInstalledMetadata?: { project: Record; global: Record }
+ profileThresholds: Record
}
export interface ClineSayTool {
diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts
index 0f2342cbd08..cd2775e4012 100644
--- a/src/shared/WebviewMessage.ts
+++ b/src/shared/WebviewMessage.ts
@@ -72,6 +72,11 @@ export interface WebviewMessage {
| "alwaysAllowSubtasks"
| "autoCondenseContext"
| "autoCondenseContextPercent"
+ | "extendSubtaskTimeout"
+ | "clearSubtaskTimeout"
+ | "defaultSubtaskTimeoutMs"
+ | "subtaskTimeoutWarningPercent"
+ | "maxSubtaskTimeoutExtensions"
| "condensingApiConfigId"
| "updateCondensingPrompt"
| "playSound"
@@ -159,6 +164,7 @@ export interface WebviewMessage {
| "indexCleared"
| "focusPanelRequest"
| "codebaseIndexConfig"
+ | "profileThresholds"
| "setHistoryPreviewCollapsed"
| "openExternal"
| "filterMarketplaceItems"
@@ -170,6 +176,7 @@ export interface WebviewMessage {
| "marketplaceInstallResult"
| "fetchMarketplaceData"
| "switchTab"
+ | "profileThresholds"
text?: string
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"
disabled?: boolean
@@ -199,6 +206,8 @@ export interface WebviewMessage {
source?: "global" | "project"
requestId?: string
ids?: string[]
+ taskId?: string
+ extensionMs?: number
hasSystemPromptOverride?: boolean
terminalOperation?: "continue" | "abort"
historyPreviewCollapsed?: boolean
diff --git a/src/shared/api.ts b/src/shared/api.ts
index 8ad88286589..d1bfa2794b1 100644
--- a/src/shared/api.ts
+++ b/src/shared/api.ts
@@ -6,7 +6,7 @@ export type ApiHandlerOptions = Omit
// RouterName
-const routerNames = ["openrouter", "requesty", "glama", "unbound", "litellm"] as const
+const routerNames = ["openrouter", "requesty", "glama", "unbound", "litellm", "ollama", "lmstudio"] as const
export type RouterName = (typeof routerNames)[number]
@@ -82,3 +82,5 @@ export type GetModelsOptions =
| { provider: "requesty"; apiKey?: string }
| { provider: "unbound"; apiKey?: string }
| { provider: "litellm"; apiKey: string; baseUrl: string }
+ | { provider: "ollama"; baseUrl?: string }
+ | { provider: "lmstudio"; baseUrl?: string }
diff --git a/src/shared/tools.ts b/src/shared/tools.ts
index 0725e2e4d64..59f07ce76fa 100644
--- a/src/shared/tools.ts
+++ b/src/shared/tools.ts
@@ -64,6 +64,7 @@ export const toolParamNames = [
"end_line",
"query",
"args",
+ "timeout_seconds",
] as const
export type ToolParamName = (typeof toolParamNames)[number]
diff --git a/src/vitest.config.ts b/src/vitest.config.ts
index e20e40c655c..4e576f3c96c 100644
--- a/src/vitest.config.ts
+++ b/src/vitest.config.ts
@@ -8,6 +8,8 @@ export default defineConfig({
watch: false,
reporters: ["dot"],
silent: true,
+ testTimeout: 20_000,
+ hookTimeout: 20_000,
},
resolve: {
alias: {
diff --git a/webview-ui/package.json b/webview-ui/package.json
index 859c7315515..4d5e5cc821c 100644
--- a/webview-ui/package.json
+++ b/webview-ui/package.json
@@ -50,7 +50,7 @@
"lucide-react": "^0.518.0",
"mermaid": "^11.4.1",
"posthog-js": "^1.227.2",
- "pretty-bytes": "^6.1.1",
+ "pretty-bytes": "^7.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^15.4.1",
@@ -68,7 +68,7 @@
"shiki": "^3.2.1",
"source-map": "^0.7.4",
"styled-components": "^6.1.13",
- "tailwind-merge": "^2.6.0",
+ "tailwind-merge": "^3.0.0",
"tailwindcss": "^4.0.0",
"tailwindcss-animate": "^1.0.7",
"unist-util-visit": "^5.0.0",
diff --git a/webview-ui/src/components/chat/McpExecution.tsx b/webview-ui/src/components/chat/McpExecution.tsx
index 8e0882340b9..a96f368a17e 100644
--- a/webview-ui/src/components/chat/McpExecution.tsx
+++ b/webview-ui/src/components/chat/McpExecution.tsx
@@ -242,6 +242,7 @@ export const McpExecution = ({
serverName={useMcpServer.serverName}
serverSource={server?.source}
alwaysAllowMcp={alwaysAllowMcp}
+ isInChatContext={true}
/>
)}
@@ -256,6 +257,7 @@ export const McpExecution = ({
serverName={serverName}
serverSource={undefined}
alwaysAllowMcp={alwaysAllowMcp}
+ isInChatContext={true}
/>
)}
diff --git a/webview-ui/src/components/chat/SubtaskTimeoutProgress.tsx b/webview-ui/src/components/chat/SubtaskTimeoutProgress.tsx
new file mode 100644
index 00000000000..cd03273e9f3
--- /dev/null
+++ b/webview-ui/src/components/chat/SubtaskTimeoutProgress.tsx
@@ -0,0 +1,155 @@
+import { memo, useState, useEffect } from "react"
+import { useTranslation } from "react-i18next"
+import { Clock, Plus, X } from "lucide-react"
+
+import { Button } from "@src/components/ui"
+import { cn } from "@src/lib/utils"
+import { vscode } from "@src/utils/vscode"
+
+export interface SubtaskTimeoutProgressProps {
+ taskId: string
+ timeoutMs: number
+ startTime: number
+ warningPercent?: number
+ onExtend?: (taskId: string, extensionMs: number) => void
+ onClear?: (taskId: string) => void
+ className?: string
+}
+
+const SubtaskTimeoutProgress = ({
+ taskId,
+ timeoutMs,
+ startTime,
+ warningPercent = 80,
+ onExtend,
+ onClear,
+ className,
+}: SubtaskTimeoutProgressProps) => {
+ const { t } = useTranslation()
+ const [currentTime, setCurrentTime] = useState(Date.now())
+ const [isExpired, setIsExpired] = useState(false)
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setCurrentTime(Date.now())
+ }, 1000)
+
+ return () => clearInterval(interval)
+ }, [])
+
+ const elapsed = currentTime - startTime
+ const remaining = Math.max(0, timeoutMs - elapsed)
+ const progress = Math.min(100, (elapsed / timeoutMs) * 100)
+ const isWarning = progress >= warningPercent
+ const isUrgent = progress >= 95
+
+ useEffect(() => {
+ if (remaining <= 0 && !isExpired) {
+ setIsExpired(true)
+ }
+ }, [remaining, isExpired])
+
+ const formatTime = (ms: number): string => {
+ const seconds = Math.ceil(ms / 1000)
+ const minutes = Math.floor(seconds / 60)
+ const remainingSeconds = seconds % 60
+
+ if (minutes > 0) {
+ return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`
+ }
+ return `${seconds}s`
+ }
+
+ const handleExtend = () => {
+ const extensionMs = Math.max(60000, Math.floor(timeoutMs * 0.5)) // Extend by 50% or 1 minute, whichever is larger
+ onExtend?.(taskId, extensionMs)
+ vscode.postMessage({
+ type: "extendSubtaskTimeout",
+ taskId,
+ extensionMs,
+ })
+ }
+
+ const handleClear = () => {
+ onClear?.(taskId)
+ vscode.postMessage({
+ type: "clearSubtaskTimeout",
+ taskId,
+ })
+ }
+
+ if (remaining <= 0 && isExpired) {
+ return (
+