flavor-code 1.3.11 → 1.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +426 -425
- package/README.zh-CN.md +423 -422
- package/dist/agent/types.d.ts +3 -0
- package/dist/{app-L52CTVNR.js → app-2SBDQ6NS.js} +76 -201
- package/dist/{chunk-KKIBYDJI.js → chunk-2BF7CVWZ.js} +5 -7
- package/dist/{chunk-JZ5FVBET.js → chunk-EYCTP5JB.js} +1 -1
- package/dist/{chunk-IG4Z23CL.js → chunk-JFKRX2KG.js} +7 -0
- package/dist/{chunk-IGFN2DM5.js → chunk-W2D2MLY2.js} +705 -107
- package/dist/cli.js +6 -6
- package/dist/config/schema.d.ts +12 -0
- package/dist/desktop/astgraph-service.d.ts +34 -0
- package/dist/desktop/main.js +590 -282
- package/dist/desktop-renderer/assets/index-BiDVkW9t.js +154 -0
- package/dist/desktop-renderer/assets/{index-CDxzMJ9C.css → index-DHTr_Acb.css} +1 -1
- package/dist/desktop-renderer/assets/{interactive-terminal-CNgUWG7b.js → interactive-terminal-CKHHiGNG.js} +1 -1
- package/dist/desktop-renderer/index.html +2 -2
- package/dist/explain/service.d.ts +114 -0
- package/dist/{load-GKA7P6RJ.js → load-LTTKVPJZ.js} +1 -1
- package/dist/models/anthropic.d.ts +7 -0
- package/dist/models/openai.d.ts +3 -0
- package/dist/models/types.d.ts +19 -0
- package/dist/sdk/index.js +3 -3
- package/dist/ui/char-width.d.ts +12 -0
- package/dist/ui/commands.d.ts +6 -2
- package/dist/ui/session.d.ts +1 -0
- package/dist/ui/thinking-line.d.ts +42 -0
- package/dist/ui/transcript.d.ts +2 -0
- package/package.json +1 -1
- package/dist/desktop-renderer/assets/index-Bl5dmm02.js +0 -154
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
redactConfig,
|
|
13
13
|
setProjectMcpServerDisabled,
|
|
14
14
|
updateProtectedFile
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-JFKRX2KG.js";
|
|
16
16
|
import {
|
|
17
17
|
AuditLogger,
|
|
18
18
|
appendUsageLog,
|
|
@@ -66,12 +66,12 @@ function normalizeProviderError(error) {
|
|
|
66
66
|
|
|
67
67
|
// src/utils/async.ts
|
|
68
68
|
function withTimeout(promise, ms) {
|
|
69
|
-
return new Promise((
|
|
69
|
+
return new Promise((resolve29, reject) => {
|
|
70
70
|
let settled = false;
|
|
71
71
|
const timer = setTimeout(() => {
|
|
72
72
|
if (settled) return;
|
|
73
73
|
settled = true;
|
|
74
|
-
|
|
74
|
+
resolve29({ timedOut: true, value: void 0 });
|
|
75
75
|
}, ms);
|
|
76
76
|
timer.unref?.();
|
|
77
77
|
promise.then(
|
|
@@ -79,7 +79,7 @@ function withTimeout(promise, ms) {
|
|
|
79
79
|
if (!settled) {
|
|
80
80
|
settled = true;
|
|
81
81
|
clearTimeout(timer);
|
|
82
|
-
|
|
82
|
+
resolve29({ timedOut: false, value });
|
|
83
83
|
}
|
|
84
84
|
},
|
|
85
85
|
(error) => {
|
|
@@ -94,13 +94,13 @@ function withTimeout(promise, ms) {
|
|
|
94
94
|
}
|
|
95
95
|
function awaitWithSignal(promise, signal) {
|
|
96
96
|
signal.throwIfAborted();
|
|
97
|
-
return new Promise((
|
|
97
|
+
return new Promise((resolve29, reject) => {
|
|
98
98
|
const onAbort = () => reject(signal.reason);
|
|
99
99
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
100
100
|
promise.then(
|
|
101
101
|
(value) => {
|
|
102
102
|
signal.removeEventListener("abort", onAbort);
|
|
103
|
-
|
|
103
|
+
resolve29(value);
|
|
104
104
|
},
|
|
105
105
|
(error) => {
|
|
106
106
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -1082,6 +1082,231 @@ function packageVersion() {
|
|
|
1082
1082
|
throw new Error("package.json with a version field was not found");
|
|
1083
1083
|
}
|
|
1084
1084
|
|
|
1085
|
+
// src/ui/char-width.ts
|
|
1086
|
+
function charWidth(codePoint) {
|
|
1087
|
+
if (codePoint === 8203 || // ZERO WIDTH SPACE
|
|
1088
|
+
codePoint === 8204 || // ZERO WIDTH NON-JOINER
|
|
1089
|
+
codePoint === 8205 || // ZERO WIDTH JOINER
|
|
1090
|
+
codePoint === 65279 || // BOM / ZERO WIDTH NO-BREAK SPACE
|
|
1091
|
+
codePoint >= 768 && codePoint <= 879 || // Combining Diacritical Marks
|
|
1092
|
+
codePoint >= 1155 && codePoint <= 1161 || // Cyrillic combining marks
|
|
1093
|
+
codePoint >= 1425 && codePoint <= 1469 || // Hebrew combining marks
|
|
1094
|
+
codePoint >= 1552 && codePoint <= 1562 || // Arabic combining marks
|
|
1095
|
+
codePoint >= 1611 && codePoint <= 1631 || // Arabic combining marks
|
|
1096
|
+
codePoint >= 1648 && codePoint <= 1648 || // Arabic combining marks
|
|
1097
|
+
codePoint >= 1750 && codePoint <= 1756 || // Arabic combining marks
|
|
1098
|
+
codePoint >= 1759 && codePoint <= 1764 || // Arabic combining marks
|
|
1099
|
+
codePoint >= 1767 && codePoint <= 1768 || // Arabic combining marks
|
|
1100
|
+
codePoint >= 1770 && codePoint <= 1773 || // Arabic combining marks
|
|
1101
|
+
codePoint >= 1809 && codePoint <= 1809 || // Syriac combining marks
|
|
1102
|
+
codePoint >= 1840 && codePoint <= 1866 || // Syriac combining marks
|
|
1103
|
+
codePoint >= 1958 && codePoint <= 1968 || // Thaana combining marks
|
|
1104
|
+
codePoint >= 2304 && codePoint <= 2306 || // Devanagari combining marks
|
|
1105
|
+
codePoint >= 2362 && codePoint <= 2362 || // Devanagari combining marks
|
|
1106
|
+
codePoint >= 2364 && codePoint <= 2364 || // Devanagari combining marks
|
|
1107
|
+
codePoint >= 2369 && codePoint <= 2376 || // Devanagari combining marks
|
|
1108
|
+
codePoint >= 2381 && codePoint <= 2381 || // Devanagari combining marks
|
|
1109
|
+
codePoint >= 2385 && codePoint <= 2391 || // Devanagari combining marks
|
|
1110
|
+
codePoint >= 2402 && codePoint <= 2403 || // Devanagari combining marks
|
|
1111
|
+
codePoint >= 2433 && codePoint <= 2433 || // Bengali combining marks
|
|
1112
|
+
codePoint >= 2492 && codePoint <= 2492 || // Bengali combining marks
|
|
1113
|
+
codePoint >= 2497 && codePoint <= 2500 || // Bengali combining marks
|
|
1114
|
+
codePoint >= 2509 && codePoint <= 2509 || // Bengali combining marks
|
|
1115
|
+
codePoint >= 2530 && codePoint <= 2531 || // Bengali combining marks
|
|
1116
|
+
codePoint >= 2561 && codePoint <= 2562 || // Gurmukhi combining marks
|
|
1117
|
+
codePoint >= 2620 && codePoint <= 2620 || // Gurmukhi combining marks
|
|
1118
|
+
codePoint >= 2625 && codePoint <= 2626 || // Gurmukhi combining marks
|
|
1119
|
+
codePoint >= 2631 && codePoint <= 2632 || // Gurmukhi combining marks
|
|
1120
|
+
codePoint >= 2635 && codePoint <= 2637 || // Gurmukhi combining marks
|
|
1121
|
+
codePoint >= 2672 && codePoint <= 2673 || // Gurmukhi combining marks
|
|
1122
|
+
codePoint >= 2689 && codePoint <= 2690 || // Gujarati combining marks
|
|
1123
|
+
codePoint >= 2748 && codePoint <= 2748 || // Gujarati combining marks
|
|
1124
|
+
codePoint >= 2753 && codePoint <= 2757 || // Gujarati combining marks
|
|
1125
|
+
codePoint >= 2759 && codePoint <= 2760 || // Gujarati combining marks
|
|
1126
|
+
codePoint >= 2765 && codePoint <= 2765 || // Gujarati combining marks
|
|
1127
|
+
codePoint >= 2786 && codePoint <= 2787 || // Gujarati combining marks
|
|
1128
|
+
codePoint >= 2817 && codePoint <= 2817 || // Oriya combining marks
|
|
1129
|
+
codePoint >= 2876 && codePoint <= 2876 || // Oriya combining marks
|
|
1130
|
+
codePoint >= 2879 && codePoint <= 2879 || // Oriya combining marks
|
|
1131
|
+
codePoint >= 2881 && codePoint <= 2883 || // Oriya combining marks
|
|
1132
|
+
codePoint >= 2893 && codePoint <= 2893 || // Oriya combining marks
|
|
1133
|
+
codePoint >= 2902 && codePoint <= 2902 || // Oriya combining marks
|
|
1134
|
+
codePoint >= 2946 && codePoint <= 2946 || // Tamil combining marks
|
|
1135
|
+
codePoint >= 3008 && codePoint <= 3008 || // Tamil combining marks
|
|
1136
|
+
codePoint >= 3021 && codePoint <= 3021 || // Tamil combining marks
|
|
1137
|
+
codePoint >= 3134 && codePoint <= 3136 || // Telugu combining marks
|
|
1138
|
+
codePoint >= 3142 && codePoint <= 3144 || // Telugu combining marks
|
|
1139
|
+
codePoint >= 3146 && codePoint <= 3149 || // Telugu combining marks
|
|
1140
|
+
codePoint >= 3157 && codePoint <= 3158 || // Telugu combining marks
|
|
1141
|
+
codePoint >= 3260 && codePoint <= 3260 || // Kannada combining marks
|
|
1142
|
+
codePoint >= 3263 && codePoint <= 3263 || // Kannada combining marks
|
|
1143
|
+
codePoint >= 3270 && codePoint <= 3270 || // Kannada combining marks
|
|
1144
|
+
codePoint >= 3276 && codePoint <= 3277 || // Kannada combining marks
|
|
1145
|
+
codePoint >= 3298 && codePoint <= 3299 || // Kannada combining marks
|
|
1146
|
+
codePoint >= 3393 && codePoint <= 3395 || // Malayalam combining marks
|
|
1147
|
+
codePoint >= 3405 && codePoint <= 3405 || // Malayalam combining marks
|
|
1148
|
+
codePoint >= 3530 && codePoint <= 3530 || // Sinhala combining marks
|
|
1149
|
+
codePoint >= 3538 && codePoint <= 3540 || // Sinhala combining marks
|
|
1150
|
+
codePoint >= 3542 && codePoint <= 3542 || // Sinhala combining marks
|
|
1151
|
+
codePoint >= 3633 && codePoint <= 3633 || // Thai combining marks
|
|
1152
|
+
codePoint >= 3636 && codePoint <= 3642 || // Thai combining marks
|
|
1153
|
+
codePoint >= 3655 && codePoint <= 3662 || // Thai combining marks
|
|
1154
|
+
codePoint >= 3761 && codePoint <= 3761 || // Lao combining marks
|
|
1155
|
+
codePoint >= 3764 && codePoint <= 3769 || // Lao combining marks
|
|
1156
|
+
codePoint >= 3771 && codePoint <= 3772 || // Lao combining marks
|
|
1157
|
+
codePoint >= 3784 && codePoint <= 3789 || // Lao combining marks
|
|
1158
|
+
codePoint >= 3864 && codePoint <= 3865 || // Tibetan combining marks
|
|
1159
|
+
codePoint >= 3893 && codePoint <= 3893 || // Tibetan combining marks
|
|
1160
|
+
codePoint >= 3895 && codePoint <= 3895 || // Tibetan combining marks
|
|
1161
|
+
codePoint >= 3897 && codePoint <= 3897 || // Tibetan combining marks
|
|
1162
|
+
codePoint >= 3953 && codePoint <= 3966 || // Tibetan combining marks
|
|
1163
|
+
codePoint >= 3968 && codePoint <= 3972 || // Tibetan combining marks
|
|
1164
|
+
codePoint >= 3974 && codePoint <= 3975 || // Tibetan combining marks
|
|
1165
|
+
codePoint >= 3984 && codePoint <= 3991 || // Tibetan combining marks
|
|
1166
|
+
codePoint >= 3993 && codePoint <= 4028 || // Tibetan combining marks
|
|
1167
|
+
codePoint >= 4038 && codePoint <= 4038 || // Tibetan combining marks
|
|
1168
|
+
codePoint >= 4146 && codePoint <= 4151 || // Myanmar combining marks
|
|
1169
|
+
codePoint >= 4153 && codePoint <= 4154 || // Myanmar combining marks
|
|
1170
|
+
codePoint >= 4157 && codePoint <= 4158 || // Myanmar combining marks
|
|
1171
|
+
codePoint >= 4184 && codePoint <= 4185 || // Myanmar combining marks
|
|
1172
|
+
codePoint >= 4190 && codePoint <= 4192 || // Myanmar combining marks
|
|
1173
|
+
codePoint >= 4209 && codePoint <= 4212 || // Myanmar combining marks
|
|
1174
|
+
codePoint >= 4226 && codePoint <= 4226 || // Myanmar combining marks
|
|
1175
|
+
codePoint >= 4229 && codePoint <= 4230 || // Myanmar combining marks
|
|
1176
|
+
codePoint >= 4237 && codePoint <= 4237 || // Myanmar combining marks
|
|
1177
|
+
codePoint >= 4957 && codePoint <= 4959 || // Ethiopic combining marks
|
|
1178
|
+
codePoint >= 5906 && codePoint <= 5908 || // Tagalog combining marks
|
|
1179
|
+
codePoint >= 5938 && codePoint <= 5940 || // Hanunoo combining marks
|
|
1180
|
+
codePoint >= 5970 && codePoint <= 5971 || // Buhid combining marks
|
|
1181
|
+
codePoint >= 6002 && codePoint <= 6003 || // Tagbanwa combining marks
|
|
1182
|
+
codePoint >= 6068 && codePoint <= 6069 || // Khmer combining marks
|
|
1183
|
+
codePoint >= 6071 && codePoint <= 6077 || // Khmer combining marks
|
|
1184
|
+
codePoint >= 6086 && codePoint <= 6086 || // Khmer combining marks
|
|
1185
|
+
codePoint >= 6089 && codePoint <= 6099 || // Khmer combining marks
|
|
1186
|
+
codePoint >= 6109 && codePoint <= 6109 || // Khmer combining marks
|
|
1187
|
+
codePoint >= 6155 && codePoint <= 6157 || // Mongolian combining marks
|
|
1188
|
+
codePoint >= 6313 && codePoint <= 6313 || // Mongolian combining marks
|
|
1189
|
+
codePoint >= 6432 && codePoint <= 6434 || // Limbu combining marks
|
|
1190
|
+
codePoint >= 6439 && codePoint <= 6440 || // Limbu combining marks
|
|
1191
|
+
codePoint >= 6450 && codePoint <= 6450 || // Limbu combining marks
|
|
1192
|
+
codePoint >= 6457 && codePoint <= 6459 || // Limbu combining marks
|
|
1193
|
+
codePoint >= 6679 && codePoint <= 6680 || // Buginese combining marks
|
|
1194
|
+
codePoint >= 6912 && codePoint <= 6915 || // Balinese combining marks
|
|
1195
|
+
codePoint >= 6964 && codePoint <= 6964 || // Balinese combining marks
|
|
1196
|
+
codePoint >= 6966 && codePoint <= 6970 || // Balinese combining marks
|
|
1197
|
+
codePoint >= 6972 && codePoint <= 6972 || // Balinese combining marks
|
|
1198
|
+
codePoint >= 6978 && codePoint <= 6978 || // Balinese combining marks
|
|
1199
|
+
codePoint >= 7019 && codePoint <= 7027 || // Balinese combining marks
|
|
1200
|
+
codePoint >= 7616 && codePoint <= 7679 || // Combining Diacritical Marks Supplement
|
|
1201
|
+
codePoint >= 8400 && codePoint <= 8432 || // Combining Diacritical Marks for Symbols
|
|
1202
|
+
codePoint >= 11503 && codePoint <= 11505 || // Coptic combining marks
|
|
1203
|
+
codePoint >= 11647 && codePoint <= 11647 || // Tifinagh combining marks
|
|
1204
|
+
codePoint >= 11744 && codePoint <= 11775 || // Cyrillic Extended-A combining marks
|
|
1205
|
+
codePoint >= 42607 && codePoint <= 42610 || // Cyrillic Extended-B combining marks
|
|
1206
|
+
codePoint >= 42612 && codePoint <= 42621 || // Cyrillic Extended-B combining marks
|
|
1207
|
+
codePoint >= 42654 && codePoint <= 42655 || // Cyrillic Extended-B combining marks
|
|
1208
|
+
codePoint >= 42736 && codePoint <= 42737 || // Bamum combining marks
|
|
1209
|
+
codePoint >= 43010 && codePoint <= 43010 || // Syloti Nagri combining marks
|
|
1210
|
+
codePoint >= 43014 && codePoint <= 43014 || // Syloti Nagri combining marks
|
|
1211
|
+
codePoint >= 43019 && codePoint <= 43019 || // Syloti Nagri combining marks
|
|
1212
|
+
codePoint >= 43045 && codePoint <= 43046 || // Syloti Nagri combining marks
|
|
1213
|
+
codePoint >= 43204 && codePoint <= 43204 || // Saurashtra combining marks
|
|
1214
|
+
codePoint >= 43232 && codePoint <= 43249 || // Devanagari Extended combining marks
|
|
1215
|
+
codePoint >= 43302 && codePoint <= 43309 || // Kayah Li combining marks
|
|
1216
|
+
codePoint >= 43335 && codePoint <= 43345 || // Rejang combining marks
|
|
1217
|
+
codePoint >= 43392 && codePoint <= 43394 || // Javanese combining marks
|
|
1218
|
+
codePoint >= 43443 && codePoint <= 43443 || // Javanese combining marks
|
|
1219
|
+
codePoint >= 43446 && codePoint <= 43449 || // Javanese combining marks
|
|
1220
|
+
codePoint >= 43452 && codePoint <= 43452 || // Javanese combining marks
|
|
1221
|
+
codePoint >= 43561 && codePoint <= 43566 || // Cham combining marks
|
|
1222
|
+
codePoint >= 43569 && codePoint <= 43570 || // Cham combining marks
|
|
1223
|
+
codePoint >= 43573 && codePoint <= 43574 || // Cham combining marks
|
|
1224
|
+
codePoint >= 43587 && codePoint <= 43587 || // Cham combining marks
|
|
1225
|
+
codePoint >= 43596 && codePoint <= 43596 || // Cham combining marks
|
|
1226
|
+
codePoint >= 43696 && codePoint <= 43696 || // Tai Viet combining marks
|
|
1227
|
+
codePoint >= 43698 && codePoint <= 43700 || // Tai Viet combining marks
|
|
1228
|
+
codePoint >= 43703 && codePoint <= 43704 || // Tai Viet combining marks
|
|
1229
|
+
codePoint >= 43710 && codePoint <= 43711 || // Tai Viet combining marks
|
|
1230
|
+
codePoint >= 43713 && codePoint <= 43713 || // Tai Viet combining marks
|
|
1231
|
+
codePoint >= 44005 && codePoint <= 44005 || // Meetei Mayek combining marks
|
|
1232
|
+
codePoint >= 44008 && codePoint <= 44008 || // Meetei Mayek combining marks
|
|
1233
|
+
codePoint >= 44013 && codePoint <= 44013 || // Meetei Mayek combining marks
|
|
1234
|
+
codePoint >= 64286 && codePoint <= 64286 || // Hebrew combining marks
|
|
1235
|
+
codePoint >= 65024 && codePoint <= 65039 || // Variation Selectors
|
|
1236
|
+
codePoint >= 65056 && codePoint <= 65071 || // Combining Half Marks
|
|
1237
|
+
codePoint >= 917760 && codePoint <= 917999) {
|
|
1238
|
+
return 0;
|
|
1239
|
+
}
|
|
1240
|
+
if (codePoint >= 4352 && codePoint <= 4447 || // Hangul Jamo
|
|
1241
|
+
codePoint >= 9001 && codePoint <= 9002 || // Left/Right-pointing angle bracket
|
|
1242
|
+
codePoint >= 11904 && codePoint <= 12350 || // CJK Radicals Supplement, Kangxi Radicals, CJK Symbols
|
|
1243
|
+
codePoint >= 12352 && codePoint <= 13247 || // Hiragana, Katakana, Bopomofo, Hangul Compatibility, Kanbun, CJK Strokes, Enclosed CJK
|
|
1244
|
+
codePoint >= 13312 && codePoint <= 19903 || // CJK Unified Ideographs Extension A
|
|
1245
|
+
codePoint >= 19968 && codePoint <= 42191 || // CJK Unified Ideographs, Yi Syllables, Yi Radicals
|
|
1246
|
+
codePoint >= 43360 && codePoint <= 43388 || // Hangul Jamo Extended-A
|
|
1247
|
+
codePoint >= 44032 && codePoint <= 55203 || // Hangul Syllables
|
|
1248
|
+
codePoint >= 63744 && codePoint <= 64255 || // CJK Compatibility Ideographs
|
|
1249
|
+
codePoint >= 65040 && codePoint <= 65049 || // Vertical forms
|
|
1250
|
+
codePoint >= 65072 && codePoint <= 65135 || // CJK Compatibility Forms, Small Form Variants
|
|
1251
|
+
codePoint >= 65281 && codePoint <= 65376 || // Fullwidth Forms
|
|
1252
|
+
codePoint >= 65504 && codePoint <= 65510 || // Fullwidth Signs
|
|
1253
|
+
codePoint >= 110592 && codePoint <= 111359 || // Kana Supplement, Kana Extended-A, Small Kana Extension
|
|
1254
|
+
codePoint >= 126980 && codePoint <= 126980 || // Mahjong tile
|
|
1255
|
+
codePoint >= 127183 && codePoint <= 127183 || // Playing card
|
|
1256
|
+
codePoint >= 127232 && codePoint <= 129535 || // Enclosed Alphanumeric Supplement, Emoticons, Transport, etc.
|
|
1257
|
+
codePoint >= 129536 && codePoint <= 129647 || // Chess Symbols
|
|
1258
|
+
codePoint >= 129648 && codePoint <= 129791 || // Symbols and Pictographs Extended-A
|
|
1259
|
+
codePoint >= 131072 && codePoint <= 196605 || // CJK Unified Ideographs Extension B–G, CJK Compatibility Supplement
|
|
1260
|
+
codePoint >= 196608 && codePoint <= 262141) {
|
|
1261
|
+
return 2;
|
|
1262
|
+
}
|
|
1263
|
+
return 1;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// src/ui/thinking-line.ts
|
|
1267
|
+
var THINKING_SCROLL_CELLS_PER_SEC = 16;
|
|
1268
|
+
var THINKING_EASE = 0.18;
|
|
1269
|
+
var THINKING_MAX_STEP_MS = 250;
|
|
1270
|
+
var THINKING_MAX_STORED_CHARS = 4e3;
|
|
1271
|
+
function normalizeThinkingText(text) {
|
|
1272
|
+
return text.replace(/\s+/gu, " ").trim();
|
|
1273
|
+
}
|
|
1274
|
+
function thinkingDisplayWidth(text) {
|
|
1275
|
+
let width = 0;
|
|
1276
|
+
for (const character of text) width += charWidth(character.codePointAt(0) ?? 32);
|
|
1277
|
+
return width;
|
|
1278
|
+
}
|
|
1279
|
+
function sliceThinkingByWidth(text, startCell, cellCount) {
|
|
1280
|
+
let out = "";
|
|
1281
|
+
let cell = 0;
|
|
1282
|
+
for (const character of text) {
|
|
1283
|
+
const w = charWidth(character.codePointAt(0) ?? 32);
|
|
1284
|
+
if (cell + w > startCell && cell < startCell + cellCount) out += character;
|
|
1285
|
+
cell += w;
|
|
1286
|
+
if (cell >= startCell + cellCount) break;
|
|
1287
|
+
}
|
|
1288
|
+
return out;
|
|
1289
|
+
}
|
|
1290
|
+
function thinkingWindow(text, previousStart, options) {
|
|
1291
|
+
const normalized = normalizeThinkingText(text);
|
|
1292
|
+
const width = Math.max(1, Math.floor(options.width));
|
|
1293
|
+
const total = thinkingDisplayWidth(normalized);
|
|
1294
|
+
if (total <= width) return { text: normalized, start: 0 };
|
|
1295
|
+
const maxStart = total - width;
|
|
1296
|
+
const from = Math.min(Math.max(0, previousStart), maxStart);
|
|
1297
|
+
const cellsPerSecond = options.cellsPerSecond ?? THINKING_SCROLL_CELLS_PER_SEC;
|
|
1298
|
+
const ease = options.ease ?? THINKING_EASE;
|
|
1299
|
+
const dt = Math.max(0, Math.min(options.dtMs, THINKING_MAX_STEP_MS));
|
|
1300
|
+
const lag = maxStart - from;
|
|
1301
|
+
const step = Math.max(cellsPerSecond * dt / 1e3, lag * ease);
|
|
1302
|
+
const start = Math.min(maxStart, Math.floor(from + step));
|
|
1303
|
+
return { text: sliceThinkingByWidth(normalized, start, width), start };
|
|
1304
|
+
}
|
|
1305
|
+
function appendThinkingText(previous, delta) {
|
|
1306
|
+
const next = (previous ?? "") + delta;
|
|
1307
|
+
return next.length > THINKING_MAX_STORED_CHARS ? next.slice(-THINKING_MAX_STORED_CHARS) : next;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1085
1310
|
// src/ui/transcript.ts
|
|
1086
1311
|
function createTranscriptState() {
|
|
1087
1312
|
return { completed: [], nextId: 1 };
|
|
@@ -1178,6 +1403,9 @@ function transcriptReducer(state, action) {
|
|
|
1178
1403
|
if (event.type === "text") {
|
|
1179
1404
|
return { ...state, active: addText(withoutModelActivity(state.active), event.text) };
|
|
1180
1405
|
}
|
|
1406
|
+
if (event.type === "thinking") {
|
|
1407
|
+
return { ...state, active: attachThinking(state.active, event.text) };
|
|
1408
|
+
}
|
|
1181
1409
|
if (event.type === "tool-start") return upsertStatus({ ...state, active: withoutModelActivity(state.active) }, {
|
|
1182
1410
|
kind: "status",
|
|
1183
1411
|
id: `tool:${event.id}`,
|
|
@@ -1478,6 +1706,18 @@ function finishActive(state) {
|
|
|
1478
1706
|
...state.taskSnapshot === void 0 ? {} : { taskSnapshot: state.taskSnapshot }
|
|
1479
1707
|
};
|
|
1480
1708
|
}
|
|
1709
|
+
function attachThinking(turn, text) {
|
|
1710
|
+
const blocks = [...turn.blocks];
|
|
1711
|
+
const index = blocks.findIndex((block2) => block2.kind === "status" && block2.activity === "model" && block2.state === "running");
|
|
1712
|
+
if (index < 0) return turn;
|
|
1713
|
+
const block = blocks[index];
|
|
1714
|
+
if (block === void 0 || block.kind !== "status") return turn;
|
|
1715
|
+
blocks[index] = {
|
|
1716
|
+
...block,
|
|
1717
|
+
thinkingText: appendThinkingText(block.thinkingText, text)
|
|
1718
|
+
};
|
|
1719
|
+
return { ...turn, blocks };
|
|
1720
|
+
}
|
|
1481
1721
|
function withoutModelActivity(turn, id) {
|
|
1482
1722
|
const blocks = turn.blocks.filter((block) => block.kind !== "status" || block.activity !== "model" || id !== void 0 && block.id !== id);
|
|
1483
1723
|
if (blocks.length === turn.blocks.length) return turn;
|
|
@@ -2984,6 +3224,7 @@ var MVP_COMMANDS = [
|
|
|
2984
3224
|
"mcp",
|
|
2985
3225
|
"commit",
|
|
2986
3226
|
"review",
|
|
3227
|
+
"explain",
|
|
2987
3228
|
"ide",
|
|
2988
3229
|
"memory",
|
|
2989
3230
|
"remember",
|
|
@@ -3022,6 +3263,7 @@ var COMMAND_DESCRIPTIONS = {
|
|
|
3022
3263
|
evolve: "Self-improvement loop: capture failures, suggest fixes, verify with tests",
|
|
3023
3264
|
commit: "Generate a commit message for staged changes and commit after confirmation",
|
|
3024
3265
|
review: "Review uncommitted changes for bugs and risks before committing",
|
|
3266
|
+
explain: "Explain a symbol for newcomers (AST graph + source + git history)",
|
|
3025
3267
|
mcp: "Manage MCP servers",
|
|
3026
3268
|
ide: "Show the connected IDE and editor context",
|
|
3027
3269
|
memory: "Show long-term project memory",
|
|
@@ -3098,6 +3340,12 @@ function parseSlashCommand(input, dynamicCommands = [], skillCommands = [], mana
|
|
|
3098
3340
|
const focus = args.join(" ").trim();
|
|
3099
3341
|
return focus.length === 0 ? { name } : { name, focus };
|
|
3100
3342
|
}
|
|
3343
|
+
if (name === "explain") {
|
|
3344
|
+
const [query, ...focusParts] = args;
|
|
3345
|
+
if (query === void 0) return { name };
|
|
3346
|
+
const focus = focusParts.join(" ").trim();
|
|
3347
|
+
return focus.length === 0 ? { name, query } : { name, query, focus };
|
|
3348
|
+
}
|
|
3101
3349
|
if (name === "loop") {
|
|
3102
3350
|
const goal = args.join(" ").trim();
|
|
3103
3351
|
if (!goal) return { name: "invalid", command: name, message: "Use /loop <goal>." };
|
|
@@ -3701,6 +3949,14 @@ var FlavorSession = class {
|
|
|
3701
3949
|
this.#notice(await required(this.#services.gitCommit, "commit")(command.hint, signal));
|
|
3702
3950
|
} else if (command.name === "review") {
|
|
3703
3951
|
this.#notice(await required(this.#services.gitReview, "review")(command.focus, signal));
|
|
3952
|
+
} else if (command.name === "explain") {
|
|
3953
|
+
const result = await required(this.#services.explain, "explain")(
|
|
3954
|
+
command.query,
|
|
3955
|
+
command.focus,
|
|
3956
|
+
signal,
|
|
3957
|
+
(chunk) => this.#services.output({ type: "text", text: chunk })
|
|
3958
|
+
);
|
|
3959
|
+
if (result !== "") this.#notice(result);
|
|
3704
3960
|
} else if (command.name === "mcp") {
|
|
3705
3961
|
this.#notice(await this.#services.mcp(command, signal));
|
|
3706
3962
|
} else if (command.name === "pals") {
|
|
@@ -3867,9 +4123,9 @@ var FlavorSession = class {
|
|
|
3867
4123
|
}
|
|
3868
4124
|
#waitForPlanningIdle(key) {
|
|
3869
4125
|
if (!this.#activePlanning.has(key)) return Promise.resolve();
|
|
3870
|
-
return new Promise((
|
|
4126
|
+
return new Promise((resolve29) => {
|
|
3871
4127
|
const check = () => {
|
|
3872
|
-
if (!this.#activePlanning.has(key))
|
|
4128
|
+
if (!this.#activePlanning.has(key)) resolve29();
|
|
3873
4129
|
else this.#planningIdleWaiters.add(check);
|
|
3874
4130
|
};
|
|
3875
4131
|
this.#planningIdleWaiters.add(check);
|
|
@@ -4217,12 +4473,12 @@ var BrokerStateError = class extends Error {
|
|
|
4217
4473
|
};
|
|
4218
4474
|
var WINDOWS_PIPE_PREFIX = "\\\\.\\pipe\\";
|
|
4219
4475
|
function canConnect(address) {
|
|
4220
|
-
return new Promise((
|
|
4476
|
+
return new Promise((resolve29) => {
|
|
4221
4477
|
const socket = new Socket();
|
|
4222
4478
|
const finish = (connected) => {
|
|
4223
4479
|
socket.removeAllListeners();
|
|
4224
4480
|
socket.destroy();
|
|
4225
|
-
|
|
4481
|
+
resolve29(connected);
|
|
4226
4482
|
};
|
|
4227
4483
|
socket.once("connect", () => finish(true));
|
|
4228
4484
|
socket.once("error", () => finish(false));
|
|
@@ -4271,7 +4527,7 @@ var PalBrokerServer = class {
|
|
|
4271
4527
|
this.authToken ??= await loadOrCreatePalAuthToken(this.authHome === void 0 ? {} : { home: this.authHome });
|
|
4272
4528
|
try {
|
|
4273
4529
|
await this.prepareAddress();
|
|
4274
|
-
await new Promise((
|
|
4530
|
+
await new Promise((resolve29, reject) => {
|
|
4275
4531
|
const onError = (error) => {
|
|
4276
4532
|
this.server.off("listening", onListening);
|
|
4277
4533
|
reject(error);
|
|
@@ -4280,7 +4536,7 @@ var PalBrokerServer = class {
|
|
|
4280
4536
|
this.server.off("error", onError);
|
|
4281
4537
|
this.started = true;
|
|
4282
4538
|
this.ownsAddress = true;
|
|
4283
|
-
|
|
4539
|
+
resolve29();
|
|
4284
4540
|
};
|
|
4285
4541
|
this.server.once("error", onError);
|
|
4286
4542
|
this.server.once("listening", onListening);
|
|
@@ -4308,7 +4564,7 @@ var PalBrokerServer = class {
|
|
|
4308
4564
|
const ownsAddress = this.ownsAddress;
|
|
4309
4565
|
for (const connection of this.connections) connection.socket.destroy();
|
|
4310
4566
|
if (this.started) {
|
|
4311
|
-
await new Promise((
|
|
4567
|
+
await new Promise((resolve29) => this.server.close(() => resolve29()));
|
|
4312
4568
|
this.started = false;
|
|
4313
4569
|
}
|
|
4314
4570
|
this.ownsAddress = false;
|
|
@@ -4327,8 +4583,8 @@ var PalBrokerServer = class {
|
|
|
4327
4583
|
});
|
|
4328
4584
|
if (this.endpointLock === void 0) throw new Error(`Pals endpoint '${this.address}' is active or owned`);
|
|
4329
4585
|
try {
|
|
4330
|
-
const
|
|
4331
|
-
if (!
|
|
4586
|
+
const stat10 = await lstat3(this.address);
|
|
4587
|
+
if (!stat10.isSocket()) throw new Error(`Refusing to unlink '${this.address}' because it is not a socket`);
|
|
4332
4588
|
await unlink3(this.address);
|
|
4333
4589
|
} catch (error) {
|
|
4334
4590
|
if (error.code !== "ENOENT") throw error;
|
|
@@ -4341,8 +4597,8 @@ var PalBrokerServer = class {
|
|
|
4341
4597
|
}
|
|
4342
4598
|
async unlinkKnownSocket() {
|
|
4343
4599
|
try {
|
|
4344
|
-
const
|
|
4345
|
-
if (
|
|
4600
|
+
const stat10 = await lstat3(this.address);
|
|
4601
|
+
if (stat10.isSocket()) await unlink3(this.address);
|
|
4346
4602
|
} catch (error) {
|
|
4347
4603
|
if (error.code !== "ENOENT") throw error;
|
|
4348
4604
|
}
|
|
@@ -4977,15 +5233,15 @@ var PalBrokerState = class {
|
|
|
4977
5233
|
// src/pals/broker-cli.ts
|
|
4978
5234
|
var startupAttempts = /* @__PURE__ */ new Map();
|
|
4979
5235
|
function delay(milliseconds) {
|
|
4980
|
-
return new Promise((
|
|
5236
|
+
return new Promise((resolve29) => setTimeout(resolve29, milliseconds));
|
|
4981
5237
|
}
|
|
4982
5238
|
function canConnect2(address) {
|
|
4983
|
-
return new Promise((
|
|
5239
|
+
return new Promise((resolve29) => {
|
|
4984
5240
|
const socket = new Socket2();
|
|
4985
5241
|
const finish = (connected) => {
|
|
4986
5242
|
socket.removeAllListeners();
|
|
4987
5243
|
socket.destroy();
|
|
4988
|
-
|
|
5244
|
+
resolve29(connected);
|
|
4989
5245
|
};
|
|
4990
5246
|
socket.once("connect", () => finish(true));
|
|
4991
5247
|
socket.once("error", () => finish(false));
|
|
@@ -5028,8 +5284,8 @@ async function runPalBroker(options) {
|
|
|
5028
5284
|
const sweepIntervalMs = options.sweepIntervalMs ?? 5e3;
|
|
5029
5285
|
const idleShutdownMs = options.idleShutdownMs ?? 3e4;
|
|
5030
5286
|
let resolveClosed;
|
|
5031
|
-
const closed = new Promise((
|
|
5032
|
-
resolveClosed =
|
|
5287
|
+
const closed = new Promise((resolve29) => {
|
|
5288
|
+
resolveClosed = resolve29;
|
|
5033
5289
|
});
|
|
5034
5290
|
let closing;
|
|
5035
5291
|
const timer = setInterval(() => {
|
|
@@ -5072,7 +5328,7 @@ async function ensureOnce(options) {
|
|
|
5072
5328
|
import { readFile as readFile22 } from "fs/promises";
|
|
5073
5329
|
import { createHash as createHash18, randomUUID as randomUUID20 } from "crypto";
|
|
5074
5330
|
import { homedir as homedir2, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
|
|
5075
|
-
import { basename as basename6, join as
|
|
5331
|
+
import { basename as basename6, join as join26, relative as relative20, resolve as resolve28, sep as sep20 } from "path";
|
|
5076
5332
|
|
|
5077
5333
|
// src/agent/subagents.ts
|
|
5078
5334
|
import { z as z5 } from "zod";
|
|
@@ -6341,7 +6597,12 @@ function providerValidMessages(input) {
|
|
|
6341
6597
|
const calls = original.toolCalls.filter((call) => call.id && call.name && availableResults.has(call.id));
|
|
6342
6598
|
calls.forEach((call) => announced.add(call.id));
|
|
6343
6599
|
if (!original.content && calls.length === 0) continue;
|
|
6344
|
-
output.push({
|
|
6600
|
+
output.push({
|
|
6601
|
+
role: "assistant",
|
|
6602
|
+
content: original.content,
|
|
6603
|
+
...calls.length === 0 ? {} : { toolCalls: calls.map((call) => ({ ...call })) },
|
|
6604
|
+
...original.thinkingBlocks === void 0 ? {} : { thinkingBlocks: original.thinkingBlocks.map((block) => ({ ...block })) }
|
|
6605
|
+
});
|
|
6345
6606
|
continue;
|
|
6346
6607
|
}
|
|
6347
6608
|
if (original.role === "tool") {
|
|
@@ -6919,10 +7180,10 @@ function sanitize(message2, secrets) {
|
|
|
6919
7180
|
}
|
|
6920
7181
|
function waitForRetry(delayMs, signal) {
|
|
6921
7182
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
6922
|
-
return new Promise((
|
|
7183
|
+
return new Promise((resolve29, reject) => {
|
|
6923
7184
|
const timer = setTimeout(() => {
|
|
6924
7185
|
signal?.removeEventListener("abort", onAbort);
|
|
6925
|
-
|
|
7186
|
+
resolve29();
|
|
6926
7187
|
}, delayMs);
|
|
6927
7188
|
const onAbort = () => {
|
|
6928
7189
|
clearTimeout(timer);
|
|
@@ -7064,6 +7325,7 @@ var AgentLoop = class {
|
|
|
7064
7325
|
}
|
|
7065
7326
|
let assistantText = "";
|
|
7066
7327
|
const collectedToolCalls = [];
|
|
7328
|
+
const collectedThinking = [];
|
|
7067
7329
|
let completed = false;
|
|
7068
7330
|
let reactiveRetried = false;
|
|
7069
7331
|
let attempt = 1;
|
|
@@ -7101,6 +7363,7 @@ var AgentLoop = class {
|
|
|
7101
7363
|
}
|
|
7102
7364
|
assistantText = "";
|
|
7103
7365
|
collectedToolCalls.length = 0;
|
|
7366
|
+
collectedThinking.length = 0;
|
|
7104
7367
|
completed = false;
|
|
7105
7368
|
let terminalError;
|
|
7106
7369
|
let providerError = false;
|
|
@@ -7121,6 +7384,10 @@ var AgentLoop = class {
|
|
|
7121
7384
|
assistantText += event.text;
|
|
7122
7385
|
accumulatedText += event.text;
|
|
7123
7386
|
yield event;
|
|
7387
|
+
} else if (event.type === "thinking") {
|
|
7388
|
+
yield event;
|
|
7389
|
+
} else if (event.type === "thinking-block") {
|
|
7390
|
+
collectedThinking.push({ text: event.text, ...event.signature === void 0 ? {} : { signature: event.signature } });
|
|
7124
7391
|
} else if (event.type === "tool-call") {
|
|
7125
7392
|
collectedToolCalls.push({ kind: "valid", id: event.id, name: event.name, input: event.input });
|
|
7126
7393
|
} else if (event.type === "invalid-tool-call") {
|
|
@@ -7468,7 +7735,8 @@ var AgentLoop = class {
|
|
|
7468
7735
|
const assistantMessage = {
|
|
7469
7736
|
role: "assistant",
|
|
7470
7737
|
content: assistantText,
|
|
7471
|
-
toolCalls: toolCalls.map(({ id, name, input }) => ({ id, name, input }))
|
|
7738
|
+
toolCalls: toolCalls.map(({ id, name, input }) => ({ id, name, input })),
|
|
7739
|
+
...collectedThinking.length === 0 ? {} : { thinkingBlocks: collectedThinking.map((block) => ({ ...block })) }
|
|
7472
7740
|
};
|
|
7473
7741
|
this.#options.context.appendMany([assistantMessage, ...stagedMessages]);
|
|
7474
7742
|
for (const { call, result } of stagedResults) {
|
|
@@ -7526,10 +7794,10 @@ function serializeToolInput(input) {
|
|
|
7526
7794
|
}
|
|
7527
7795
|
function waitForRetry2(delayMs, signal) {
|
|
7528
7796
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
7529
|
-
return new Promise((
|
|
7797
|
+
return new Promise((resolve29, reject) => {
|
|
7530
7798
|
const timer = setTimeout(() => {
|
|
7531
7799
|
signal?.removeEventListener("abort", onAbort);
|
|
7532
|
-
|
|
7800
|
+
resolve29();
|
|
7533
7801
|
}, delayMs);
|
|
7534
7802
|
const onAbort = () => {
|
|
7535
7803
|
clearTimeout(timer);
|
|
@@ -8735,7 +9003,7 @@ function decoratePayload(payload, envelope) {
|
|
|
8735
9003
|
return envelope === void 0 ? payload : { ...payload, ...envelope };
|
|
8736
9004
|
}
|
|
8737
9005
|
function runShellHandler(descriptor, event, signal) {
|
|
8738
|
-
return new Promise((
|
|
9006
|
+
return new Promise((resolve29, reject) => {
|
|
8739
9007
|
const child = spawn4(descriptor.command, [...descriptor.args ?? []], {
|
|
8740
9008
|
env: { ...process.env, ...descriptor.env },
|
|
8741
9009
|
shell: false,
|
|
@@ -8778,7 +9046,7 @@ function runShellHandler(descriptor, event, signal) {
|
|
|
8778
9046
|
return;
|
|
8779
9047
|
}
|
|
8780
9048
|
try {
|
|
8781
|
-
|
|
9049
|
+
resolve29(HookDecisionSchema.parse(JSON.parse(stdout)));
|
|
8782
9050
|
} catch (error) {
|
|
8783
9051
|
reject(error);
|
|
8784
9052
|
}
|
|
@@ -8975,14 +9243,14 @@ async function createIslandControlServer(options) {
|
|
|
8975
9243
|
void dispatch(line, socket, options, token);
|
|
8976
9244
|
});
|
|
8977
9245
|
});
|
|
8978
|
-
await new Promise((
|
|
9246
|
+
await new Promise((resolve29, reject) => {
|
|
8979
9247
|
const onError = (error) => {
|
|
8980
9248
|
server.removeListener("listening", onListening);
|
|
8981
9249
|
reject(error);
|
|
8982
9250
|
};
|
|
8983
9251
|
const onListening = () => {
|
|
8984
9252
|
server.removeListener("error", onError);
|
|
8985
|
-
|
|
9253
|
+
resolve29();
|
|
8986
9254
|
};
|
|
8987
9255
|
server.once("error", onError);
|
|
8988
9256
|
server.once("listening", onListening);
|
|
@@ -9053,11 +9321,11 @@ function write(socket, payload) {
|
|
|
9053
9321
|
}
|
|
9054
9322
|
}
|
|
9055
9323
|
function closeServer(server) {
|
|
9056
|
-
return new Promise((
|
|
9324
|
+
return new Promise((resolve29) => {
|
|
9057
9325
|
try {
|
|
9058
|
-
server.close(() =>
|
|
9326
|
+
server.close(() => resolve29());
|
|
9059
9327
|
} catch {
|
|
9060
|
-
|
|
9328
|
+
resolve29();
|
|
9061
9329
|
}
|
|
9062
9330
|
});
|
|
9063
9331
|
}
|
|
@@ -10242,7 +10510,7 @@ var USAGE = [
|
|
|
10242
10510
|
" clear reset signals, done markers, and verified markers"
|
|
10243
10511
|
].join("\n");
|
|
10244
10512
|
function runCommand(command, cwd, timeoutMs) {
|
|
10245
|
-
return new Promise((
|
|
10513
|
+
return new Promise((resolve29) => {
|
|
10246
10514
|
const isWindows = process.platform === "win32";
|
|
10247
10515
|
const child = spawn5(isWindows ? "cmd.exe" : "/bin/sh", isWindows ? ["/d", "/s", "/c", command] : ["-c", command], {
|
|
10248
10516
|
cwd,
|
|
@@ -10252,7 +10520,7 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
10252
10520
|
let stderr = "";
|
|
10253
10521
|
const timer = setTimeout(() => {
|
|
10254
10522
|
child.kill();
|
|
10255
|
-
|
|
10523
|
+
resolve29({ ok: false, stdout, stderr: `${stderr}
|
|
10256
10524
|
[killed: exceeded ${timeoutMs}ms]`, code: "timeout" });
|
|
10257
10525
|
}, timeoutMs);
|
|
10258
10526
|
child.stdout.on("data", (chunk) => {
|
|
@@ -10263,12 +10531,12 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
10263
10531
|
});
|
|
10264
10532
|
child.on("error", (error) => {
|
|
10265
10533
|
clearTimeout(timer);
|
|
10266
|
-
|
|
10534
|
+
resolve29({ ok: false, stdout, stderr: `${stderr}
|
|
10267
10535
|
${error.message}`, code: "spawn" });
|
|
10268
10536
|
});
|
|
10269
10537
|
child.on("close", (code) => {
|
|
10270
10538
|
clearTimeout(timer);
|
|
10271
|
-
|
|
10539
|
+
resolve29({ ok: code === 0, stdout, stderr, code: code ?? "unknown" });
|
|
10272
10540
|
});
|
|
10273
10541
|
});
|
|
10274
10542
|
}
|
|
@@ -12725,6 +12993,7 @@ var CLAUDE_CLIENT_HEADERS = {
|
|
|
12725
12993
|
"x-app": "cli"
|
|
12726
12994
|
};
|
|
12727
12995
|
var DEFAULT_MAX_OUTPUT_TOKENS = 32768;
|
|
12996
|
+
var DEFAULT_THINKING_BUDGET = 8192;
|
|
12728
12997
|
var MAX_CACHE_MARKERS = 4;
|
|
12729
12998
|
function collectCacheMarkers(system, messages) {
|
|
12730
12999
|
const markers = [];
|
|
@@ -12783,9 +13052,26 @@ function formatCacheUsage(model, snapshot4, shape) {
|
|
|
12783
13052
|
...shape === void 0 ? {} : { requestMessages: shape.messages, requestMarkers: shape.markers }
|
|
12784
13053
|
});
|
|
12785
13054
|
}
|
|
13055
|
+
function stripThinkingBlocks(messages) {
|
|
13056
|
+
for (const message2 of messages) {
|
|
13057
|
+
if (message2.role !== "assistant" || !Array.isArray(message2.content)) continue;
|
|
13058
|
+
const blocks = message2.content;
|
|
13059
|
+
if (!blocks.some((block) => block?.type === "thinking")) continue;
|
|
13060
|
+
message2.content = blocks.filter((block) => block?.type !== "thinking");
|
|
13061
|
+
}
|
|
13062
|
+
}
|
|
13063
|
+
function isThinkingParamRejected(error) {
|
|
13064
|
+
const status = error?.status;
|
|
13065
|
+
if (status !== void 0 && status !== 400) return false;
|
|
13066
|
+
const message2 = error instanceof Error ? error.message : String(error ?? "");
|
|
13067
|
+
return /\bthinking\b/iu.test(message2) && /(budget_tokens|expected|not supported|unsupported|unknown|invalid|extra inputs|unrecognized)/iu.test(message2);
|
|
13068
|
+
}
|
|
12786
13069
|
var AnthropicModelAdapter = class {
|
|
12787
13070
|
client;
|
|
12788
13071
|
maxOutputTokens;
|
|
13072
|
+
thinkingBudget;
|
|
13073
|
+
/** Set once an endpoint rejects the `thinking` parameter; later requests stop sending it. */
|
|
13074
|
+
#thinkingRejected = false;
|
|
12789
13075
|
debugUsage;
|
|
12790
13076
|
constructor(options) {
|
|
12791
13077
|
const maxOutputTokens = options.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;
|
|
@@ -12793,6 +13079,7 @@ var AnthropicModelAdapter = class {
|
|
|
12793
13079
|
throw new Error("maxOutputTokens must be a positive integer");
|
|
12794
13080
|
}
|
|
12795
13081
|
this.maxOutputTokens = maxOutputTokens;
|
|
13082
|
+
this.thinkingBudget = options.thinkingBudget ?? DEFAULT_THINKING_BUDGET;
|
|
12796
13083
|
this.debugUsage = options.debugUsage ?? isEnvTruthy(process.env.FLAVOR_DEBUG_USAGE);
|
|
12797
13084
|
this.client = options.client ?? new Anthropic({
|
|
12798
13085
|
...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
|
|
@@ -12800,6 +13087,16 @@ var AnthropicModelAdapter = class {
|
|
|
12800
13087
|
...options.headers === void 0 ? {} : { defaultHeaders: options.headers }
|
|
12801
13088
|
});
|
|
12802
13089
|
}
|
|
13090
|
+
/**
|
|
13091
|
+
* Extended-thinking request parameter when enabled, otherwise undefined.
|
|
13092
|
+
* Anthropic requires the budget to fit inside max_tokens with a 1024 floor.
|
|
13093
|
+
*/
|
|
13094
|
+
#thinkingParam() {
|
|
13095
|
+
if (this.#thinkingRejected || this.thinkingBudget <= 0) return void 0;
|
|
13096
|
+
const budget = Math.min(this.thinkingBudget, this.maxOutputTokens - 1);
|
|
13097
|
+
if (budget < 1024) return void 0;
|
|
13098
|
+
return { type: "enabled", budget_tokens: budget };
|
|
13099
|
+
}
|
|
12803
13100
|
#logUsage(request, snapshot4, shape) {
|
|
12804
13101
|
const line = formatCacheUsage(request.model, snapshot4, shape);
|
|
12805
13102
|
if (this.debugUsage) {
|
|
@@ -12855,7 +13152,13 @@ var AnthropicModelAdapter = class {
|
|
|
12855
13152
|
}
|
|
12856
13153
|
messages.push({ role: "user", content: results });
|
|
12857
13154
|
} else if (message2.role === "assistant" && message2.toolCalls?.length) {
|
|
13155
|
+
const echoThinking = this.#thinkingRejected ? [] : (message2.thinkingBlocks ?? []).filter((block) => block.signature !== void 0);
|
|
12858
13156
|
const content = [
|
|
13157
|
+
...echoThinking.map((block) => ({
|
|
13158
|
+
type: "thinking",
|
|
13159
|
+
thinking: block.text,
|
|
13160
|
+
signature: block.signature
|
|
13161
|
+
})),
|
|
12859
13162
|
...modelContentText(message2.content) ? [{ type: "text", text: modelContentText(message2.content) }] : [],
|
|
12860
13163
|
...message2.toolCalls.map((call) => ({
|
|
12861
13164
|
type: "tool_use",
|
|
@@ -12917,18 +13220,56 @@ var AnthropicModelAdapter = class {
|
|
|
12917
13220
|
stream: true,
|
|
12918
13221
|
messages,
|
|
12919
13222
|
...system ? { system } : {},
|
|
13223
|
+
...(() => {
|
|
13224
|
+
const thinking = this.#thinkingParam();
|
|
13225
|
+
return thinking === void 0 ? {} : { thinking };
|
|
13226
|
+
})(),
|
|
12920
13227
|
tools: [...request.tools].sort((a, b) => a.name.localeCompare(b.name)).map((tool) => ({
|
|
12921
13228
|
name: tool.name,
|
|
12922
13229
|
description: tool.description,
|
|
12923
13230
|
input_schema: { ...tool.inputSchema, type: "object" }
|
|
12924
13231
|
}))
|
|
12925
13232
|
};
|
|
12926
|
-
|
|
13233
|
+
let stream;
|
|
13234
|
+
try {
|
|
13235
|
+
stream = await this.client.messages.create(body, { signal: request.signal });
|
|
13236
|
+
} catch (error) {
|
|
13237
|
+
if (this.thinkingBudget > 0 && !this.#thinkingRejected && isThinkingParamRejected(error)) {
|
|
13238
|
+
this.#thinkingRejected = true;
|
|
13239
|
+
delete body.thinking;
|
|
13240
|
+
stripThinkingBlocks(body.messages);
|
|
13241
|
+
stream = await this.client.messages.create(body, { signal: request.signal });
|
|
13242
|
+
} else {
|
|
13243
|
+
throw error;
|
|
13244
|
+
}
|
|
13245
|
+
}
|
|
13246
|
+
const thinkingBuffers = /* @__PURE__ */ new Map();
|
|
12927
13247
|
for await (const event of stream) {
|
|
12928
13248
|
if (event.type === "message_start") {
|
|
12929
13249
|
hasUsage = event.message?.usage !== void 0;
|
|
12930
13250
|
inputTokens = updateInputUsage(inputUsage, event.message?.usage);
|
|
12931
13251
|
outputTokens = event.message?.usage?.output_tokens ?? outputTokens;
|
|
13252
|
+
} else if (event.type === "content_block_start" && event.content_block?.type === "thinking" && event.index !== void 0) {
|
|
13253
|
+
thinkingBuffers.set(event.index, { text: "" });
|
|
13254
|
+
} else if (event.type === "content_block_delta" && event.delta?.type === "thinking_delta") {
|
|
13255
|
+
if (event.index !== void 0 && event.delta.thinking) {
|
|
13256
|
+
const pending = thinkingBuffers.get(event.index);
|
|
13257
|
+
if (pending) pending.text += event.delta.thinking;
|
|
13258
|
+
}
|
|
13259
|
+
if (event.delta.thinking) yield { type: "thinking", text: event.delta.thinking };
|
|
13260
|
+
} else if (event.type === "content_block_delta" && event.delta?.type === "signature_delta" && event.index !== void 0) {
|
|
13261
|
+
const pending = thinkingBuffers.get(event.index);
|
|
13262
|
+
if (pending) pending.signature = (pending.signature ?? "") + (event.delta.signature ?? "");
|
|
13263
|
+
} else if (event.type === "content_block_stop" && event.index !== void 0 && thinkingBuffers.has(event.index)) {
|
|
13264
|
+
const pending = thinkingBuffers.get(event.index);
|
|
13265
|
+
if (pending.text.length > 0 || pending.signature !== void 0) {
|
|
13266
|
+
yield {
|
|
13267
|
+
type: "thinking-block",
|
|
13268
|
+
text: pending.text,
|
|
13269
|
+
...pending.signature === void 0 ? {} : { signature: pending.signature }
|
|
13270
|
+
};
|
|
13271
|
+
}
|
|
13272
|
+
thinkingBuffers.delete(event.index);
|
|
12932
13273
|
} else if (event.type === "content_block_start" && event.content_block?.type === "tool_use" && event.index !== void 0 && event.content_block.id && event.content_block.name) {
|
|
12933
13274
|
pendingTools.set(event.index, {
|
|
12934
13275
|
id: event.content_block.id,
|
|
@@ -13131,12 +13472,14 @@ function formatOpenAIUsage(model, breakdown) {
|
|
|
13131
13472
|
var OpenAIModelAdapter = class {
|
|
13132
13473
|
client;
|
|
13133
13474
|
debugUsage;
|
|
13475
|
+
thinkingEffort;
|
|
13134
13476
|
constructor(options) {
|
|
13135
13477
|
this.client = options.client ?? new OpenAI({
|
|
13136
13478
|
...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
|
|
13137
13479
|
...options.baseURL === void 0 ? {} : { baseURL: options.baseURL }
|
|
13138
13480
|
});
|
|
13139
13481
|
this.debugUsage = options.debugUsage ?? isEnvTruthy(process.env.FLAVOR_DEBUG_USAGE);
|
|
13482
|
+
this.thinkingEffort = options.thinkingEffort;
|
|
13140
13483
|
}
|
|
13141
13484
|
#logUsage(model, breakdown) {
|
|
13142
13485
|
if (breakdown === void 0) return;
|
|
@@ -13158,6 +13501,7 @@ var OpenAIModelAdapter = class {
|
|
|
13158
13501
|
const body = {
|
|
13159
13502
|
model: request.model,
|
|
13160
13503
|
input: (await Promise.all(request.messages.map(toInput))).flat(),
|
|
13504
|
+
...this.thinkingEffort === void 0 ? {} : { reasoning: { effort: this.thinkingEffort } },
|
|
13161
13505
|
tools: [...request.tools].sort((a, b) => a.name.localeCompare(b.name)).map((tool) => ({
|
|
13162
13506
|
type: "function",
|
|
13163
13507
|
name: tool.name,
|
|
@@ -13190,6 +13534,8 @@ var OpenAIModelAdapter = class {
|
|
|
13190
13534
|
}
|
|
13191
13535
|
} else if (event.type === "response.output_text.delta" && event.delta) {
|
|
13192
13536
|
yield { type: "text", text: event.delta };
|
|
13537
|
+
} else if ((event.type === "response.reasoning_summary_text.delta" || event.type === "response.reasoning_text.delta") && event.delta) {
|
|
13538
|
+
yield { type: "thinking", text: event.delta };
|
|
13193
13539
|
} else if (event.type === "response.function_call_arguments.done" && event.output_index !== void 0 && event.name) {
|
|
13194
13540
|
const callId = callIds.get(event.output_index);
|
|
13195
13541
|
if (callId && !emittedCalls.has(event.output_index)) {
|
|
@@ -13706,8 +14052,8 @@ var OAuthCallbackAuthProvider = class {
|
|
|
13706
14052
|
async #startCallbackServer(expectedState, signal) {
|
|
13707
14053
|
let serverResolve;
|
|
13708
14054
|
let serverReject;
|
|
13709
|
-
const codePromise = new Promise((
|
|
13710
|
-
serverResolve =
|
|
14055
|
+
const codePromise = new Promise((resolve29, reject) => {
|
|
14056
|
+
serverResolve = resolve29;
|
|
13711
14057
|
serverReject = reject;
|
|
13712
14058
|
});
|
|
13713
14059
|
const server = createServer3((req, res) => {
|
|
@@ -13741,7 +14087,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
13741
14087
|
);
|
|
13742
14088
|
serverResolve(code);
|
|
13743
14089
|
});
|
|
13744
|
-
return new Promise((
|
|
14090
|
+
return new Promise((resolve29, reject) => {
|
|
13745
14091
|
server.once("error", (error) => {
|
|
13746
14092
|
reject(new Error(`OAuth callback server failed: ${error.message}`));
|
|
13747
14093
|
});
|
|
@@ -13758,7 +14104,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
13758
14104
|
signal.addEventListener("abort", () => {
|
|
13759
14105
|
serverReject(new Error("OAuth flow aborted"));
|
|
13760
14106
|
}, { once: true });
|
|
13761
|
-
|
|
14107
|
+
resolve29({ redirectUri, promise: codePromise });
|
|
13762
14108
|
});
|
|
13763
14109
|
});
|
|
13764
14110
|
}
|
|
@@ -16897,8 +17243,8 @@ var LspConnection = class {
|
|
|
16897
17243
|
const id = this.#nextId++;
|
|
16898
17244
|
const request = { jsonrpc: "2.0", id, method, params };
|
|
16899
17245
|
const timeout = AbortSignal.timeout(LSP_REQUEST_TIMEOUT_MS);
|
|
16900
|
-
const promise = new Promise((
|
|
16901
|
-
this.#pending.set(id, { resolve:
|
|
17246
|
+
const promise = new Promise((resolve29, reject) => {
|
|
17247
|
+
this.#pending.set(id, { resolve: resolve29, reject });
|
|
16902
17248
|
timeout.addEventListener("abort", () => {
|
|
16903
17249
|
if (this.#pending.has(id)) {
|
|
16904
17250
|
this.#pending.delete(id);
|
|
@@ -17302,19 +17648,19 @@ async function changeSummary(workspace) {
|
|
|
17302
17648
|
};
|
|
17303
17649
|
}
|
|
17304
17650
|
async function stagedDiff(workspace, maxChars = 96e3) {
|
|
17305
|
-
const
|
|
17651
|
+
const stat10 = await git2(workspace, ["diff", "--staged", "--stat"]);
|
|
17306
17652
|
const diff = await git2(workspace, ["diff", "--staged", "--no-ext-diff", "--no-color"]);
|
|
17307
17653
|
if (!diff.ok) throw gitFailure(diff, "diff --staged");
|
|
17308
17654
|
const text = diff.stdout;
|
|
17309
17655
|
return {
|
|
17310
17656
|
diff: text.length > maxChars ? `${text.slice(0, maxChars)}
|
|
17311
17657
|
\u2026[diff truncated]` : text,
|
|
17312
|
-
stat:
|
|
17658
|
+
stat: stat10.stdout.trim(),
|
|
17313
17659
|
truncated: text.length > maxChars
|
|
17314
17660
|
};
|
|
17315
17661
|
}
|
|
17316
17662
|
async function uncommittedDiff(workspace, maxChars = 96e3) {
|
|
17317
|
-
const
|
|
17663
|
+
const stat10 = await git2(workspace, ["diff", "HEAD", "--stat"]);
|
|
17318
17664
|
const diff = await git2(workspace, ["diff", "HEAD", "--no-ext-diff", "--no-color"]);
|
|
17319
17665
|
if (!diff.ok && !diff.stderr.includes("unknown revision")) throw gitFailure(diff, "diff HEAD");
|
|
17320
17666
|
const summary2 = await changeSummary(workspace);
|
|
@@ -17322,7 +17668,7 @@ async function uncommittedDiff(workspace, maxChars = 96e3) {
|
|
|
17322
17668
|
return {
|
|
17323
17669
|
diff: text.length > maxChars ? `${text.slice(0, maxChars)}
|
|
17324
17670
|
\u2026[diff truncated]` : text,
|
|
17325
|
-
stat:
|
|
17671
|
+
stat: stat10.stdout.trim(),
|
|
17326
17672
|
untracked: summary2.untrackedFiles
|
|
17327
17673
|
};
|
|
17328
17674
|
}
|
|
@@ -17463,6 +17809,232 @@ function formatReviewReport(report) {
|
|
|
17463
17809
|
return lines.join("\n");
|
|
17464
17810
|
}
|
|
17465
17811
|
|
|
17812
|
+
// src/explain/service.ts
|
|
17813
|
+
async function resolveExplainTarget(graph, query) {
|
|
17814
|
+
const trimmed = query.trim();
|
|
17815
|
+
if (trimmed.includes("#")) {
|
|
17816
|
+
const hit = await graph.relations(trimmed).catch(() => void 0);
|
|
17817
|
+
if (hit?.origin !== void 0) return { kind: "resolved", node: hit.origin };
|
|
17818
|
+
}
|
|
17819
|
+
const hits = await graph.search(trimmed, 10);
|
|
17820
|
+
if (hits.length === 0) return { kind: "not-found", query: trimmed };
|
|
17821
|
+
if (hits.length === 1) return { kind: "resolved", node: hits[0] };
|
|
17822
|
+
return { kind: "ambiguous", candidates: hits };
|
|
17823
|
+
}
|
|
17824
|
+
function explainCandidateLabel(candidate) {
|
|
17825
|
+
return candidate.id;
|
|
17826
|
+
}
|
|
17827
|
+
async function selectExplainCandidate(candidates, questions, signal) {
|
|
17828
|
+
const shown = candidates.slice(0, 3);
|
|
17829
|
+
const answers = await questions.ask([{
|
|
17830
|
+
header: "Symbol",
|
|
17831
|
+
question: "Multiple symbols match. Which one should /explain cover? (Pick one, or type a more exact name.)",
|
|
17832
|
+
options: [
|
|
17833
|
+
...shown.map((candidate) => ({
|
|
17834
|
+
label: explainCandidateLabel(candidate),
|
|
17835
|
+
description: `${candidate.kind} \xB7 ${candidate.filePath}:${candidate.startLine}-${candidate.endLine}`
|
|
17836
|
+
})),
|
|
17837
|
+
{ label: "Cancel", description: "Abort /explain without choosing." }
|
|
17838
|
+
]
|
|
17839
|
+
}], signal);
|
|
17840
|
+
const answer = (answers[0] ?? "").trim();
|
|
17841
|
+
if (answer === "Cancel" || answer === "") return { kind: "cancelled" };
|
|
17842
|
+
const picked = shown.find((candidate) => explainCandidateLabel(candidate) === answer);
|
|
17843
|
+
if (picked !== void 0) return { kind: "picked", node: picked };
|
|
17844
|
+
return { kind: "typed", query: answer };
|
|
17845
|
+
}
|
|
17846
|
+
var MAX_SOURCE_CHARS = 2e4;
|
|
17847
|
+
function buildExplainPrompt(input) {
|
|
17848
|
+
const source = input.anchorSource.length > MAX_SOURCE_CHARS ? `${input.anchorSource.slice(0, MAX_SOURCE_CHARS)}
|
|
17849
|
+
\u2026 (truncated)` : input.anchorSource;
|
|
17850
|
+
const line = (n) => `- ${n.name} \u2014 ${n.filePath}:${n.startLine}`;
|
|
17851
|
+
return [
|
|
17852
|
+
"You are a senior engineer onboarding a new team member. Explain the following code symbol clearly and concretely; never invent behavior that is not in the shown evidence.",
|
|
17853
|
+
`Answer in ${input.language}.`,
|
|
17854
|
+
"Structure the answer with short markdown sections:",
|
|
17855
|
+
"1. \u5B83\u662F\u505A\u4EC0\u4E48\u7684 \u2014 one paragraph, plain language, beginner first.",
|
|
17856
|
+
"2. \u5173\u952E\u5B9E\u73B0\u70B9 \u2014 2-5 bullets tied to concrete lines/branches in the source.",
|
|
17857
|
+
"3. \u8C01\u8C03\u7528\u5B83 / \u5B83\u8C03\u7528\u8C01 \u2014 use the relation lists; explain the data flow in one or two sentences.",
|
|
17858
|
+
"4. \u4E3A\u4EC0\u4E48\u8FD9\u6837\u5199 \u2014 infer from the recent commit subjects when possible; say '\u4E0D\u786E\u5B9A' instead of guessing.",
|
|
17859
|
+
"5. \u65B0\u4EBA\u6CE8\u610F\u4E8B\u9879 \u2014 pitfalls, guards or invariants worth knowing.",
|
|
17860
|
+
...input.focus === void 0 || input.focus.trim() === "" ? [] : [`The user specifically asks about: ${input.focus.trim()}`],
|
|
17861
|
+
"",
|
|
17862
|
+
`Symbol: ${input.anchor.id} (${input.anchor.kind}${input.anchor.signature ? `, signature: ${input.anchor.signature}` : ""})`,
|
|
17863
|
+
"",
|
|
17864
|
+
`Source (${input.anchor.filePath}:${input.anchor.startLine}-${input.anchor.endLine}):`,
|
|
17865
|
+
source,
|
|
17866
|
+
"",
|
|
17867
|
+
`Callers:
|
|
17868
|
+
${input.callers.map(line).join("\n") || "- (none)"}`,
|
|
17869
|
+
`Callees:
|
|
17870
|
+
${input.callees.map(line).join("\n") || "- (none)"}`,
|
|
17871
|
+
"",
|
|
17872
|
+
`Recent commits touching the file:
|
|
17873
|
+
${input.history.map((h) => `- ${h.date} ${h.author}: ${h.subject}`).join("\n") || "- (no history)"}`
|
|
17874
|
+
].join("\n");
|
|
17875
|
+
}
|
|
17876
|
+
async function explainWithModel(options, prompt, signal, onText) {
|
|
17877
|
+
const { adapter, model } = options.registry.get(options.modelId());
|
|
17878
|
+
let text = "";
|
|
17879
|
+
for await (const event of adapter.stream({
|
|
17880
|
+
model,
|
|
17881
|
+
messages: [{ role: "user", content: prompt }],
|
|
17882
|
+
tools: [],
|
|
17883
|
+
signal
|
|
17884
|
+
})) {
|
|
17885
|
+
if (event.type === "text") {
|
|
17886
|
+
text += event.text;
|
|
17887
|
+
onText?.(event.text);
|
|
17888
|
+
} else if (event.type === "error") throw new Error(`explain generation failed: ${event.error.message}`);
|
|
17889
|
+
else if (event.type === "done") break;
|
|
17890
|
+
}
|
|
17891
|
+
const cleaned = text.trim();
|
|
17892
|
+
if (cleaned === "") throw new Error("explain generation returned no text");
|
|
17893
|
+
return cleaned;
|
|
17894
|
+
}
|
|
17895
|
+
var HISTORY_LIMIT = 8;
|
|
17896
|
+
var MAX_RESOLVE_ROUNDS = 2;
|
|
17897
|
+
async function runExplain(deps, query, focus, signal) {
|
|
17898
|
+
if (query === void 0) {
|
|
17899
|
+
return "Usage: /explain <symbol | file.ts#symbol> [what to focus on]. Example: /explain src/order.ts#cancelOrder error handling";
|
|
17900
|
+
}
|
|
17901
|
+
if (!(await deps.graph.status()).available) {
|
|
17902
|
+
return "The code graph is not built yet \u2014 run /ast init, then try /explain again.";
|
|
17903
|
+
}
|
|
17904
|
+
let target = await resolveExplainTarget(deps.graph, query);
|
|
17905
|
+
for (let round = 1; round <= MAX_RESOLVE_ROUNDS; round += 1) {
|
|
17906
|
+
if (target.kind !== "ambiguous") break;
|
|
17907
|
+
if (deps.questions === void 0) {
|
|
17908
|
+
return `"${query}" is ambiguous. Use the exact node id, e.g. /explain ${target.candidates[0]?.id ?? "path/file.ts#name"}.`;
|
|
17909
|
+
}
|
|
17910
|
+
const selection = await selectExplainCandidate(target.candidates, deps.questions, signal);
|
|
17911
|
+
if (selection.kind === "cancelled") return "/explain cancelled \u2014 no symbol was selected.";
|
|
17912
|
+
if (selection.kind === "picked") {
|
|
17913
|
+
target = { kind: "resolved", node: selection.node };
|
|
17914
|
+
break;
|
|
17915
|
+
}
|
|
17916
|
+
target = await resolveExplainTarget(deps.graph, selection.query);
|
|
17917
|
+
if (round === MAX_RESOLVE_ROUNDS && target.kind === "ambiguous") {
|
|
17918
|
+
return `"${selection.query}" is still ambiguous. Pick one node id explicitly: /explain ${target.candidates[0]?.id ?? "path/file.ts#name"}`;
|
|
17919
|
+
}
|
|
17920
|
+
}
|
|
17921
|
+
if (target.kind === "not-found") {
|
|
17922
|
+
return `No matching symbol for "${target.query}" \u2014 check the name, or refresh the graph with /ast sync.`;
|
|
17923
|
+
}
|
|
17924
|
+
if (target.kind !== "resolved") {
|
|
17925
|
+
return `"${query}" is ambiguous. Pick one node id explicitly: /explain ${target.candidates[0]?.id ?? "path/file.ts#name"}`;
|
|
17926
|
+
}
|
|
17927
|
+
const anchor = target.node;
|
|
17928
|
+
signal.throwIfAborted();
|
|
17929
|
+
deps.notify?.(`Explaining ${anchor.id} \u2026`);
|
|
17930
|
+
const { callers, callees } = await deps.graph.relations(anchor.id, 1);
|
|
17931
|
+
const raw = await deps.readFile(anchor.filePath).catch(() => "");
|
|
17932
|
+
const lines = raw.split("\n");
|
|
17933
|
+
const anchorSource = lines.slice(Math.max(0, anchor.startLine - 1), anchor.endLine).join("\n") || raw;
|
|
17934
|
+
const history = deps.history === void 0 ? [] : await deps.history(anchor.filePath, HISTORY_LIMIT).catch(() => []);
|
|
17935
|
+
try {
|
|
17936
|
+
if (deps.onText !== void 0) deps.onText(`Explain ${anchor.id}
|
|
17937
|
+
|
|
17938
|
+
`);
|
|
17939
|
+
const explanation = await explainWithModel(
|
|
17940
|
+
{ registry: deps.registry, modelId: deps.modelId },
|
|
17941
|
+
buildExplainPrompt({
|
|
17942
|
+
anchor,
|
|
17943
|
+
anchorSource,
|
|
17944
|
+
callers: callers.map((n) => ({ name: n.name, filePath: n.filePath, startLine: n.startLine })),
|
|
17945
|
+
callees: callees.map((n) => ({ name: n.name, filePath: n.filePath, startLine: n.startLine })),
|
|
17946
|
+
history,
|
|
17947
|
+
...focus === void 0 ? {} : { focus },
|
|
17948
|
+
language: deps.language
|
|
17949
|
+
}),
|
|
17950
|
+
signal,
|
|
17951
|
+
deps.onText
|
|
17952
|
+
);
|
|
17953
|
+
return deps.onText === void 0 ? `Explain ${anchor.id}
|
|
17954
|
+
|
|
17955
|
+
${explanation}` : "";
|
|
17956
|
+
} catch (error) {
|
|
17957
|
+
if (signal.aborted) throw error;
|
|
17958
|
+
return `/explain failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
17959
|
+
}
|
|
17960
|
+
}
|
|
17961
|
+
|
|
17962
|
+
// src/desktop/astgraph-service.ts
|
|
17963
|
+
import { stat as stat8 } from "fs/promises";
|
|
17964
|
+
import { createRequire as nodeCreateRequire } from "module";
|
|
17965
|
+
import { join as join22, resolve as resolve22 } from "path";
|
|
17966
|
+
var FIELDS = "id, kind, name, qualified_name, file_path, language, start_line, end_line, signature";
|
|
17967
|
+
var DesktopAstGraphService = class {
|
|
17968
|
+
#path;
|
|
17969
|
+
constructor(workspace) {
|
|
17970
|
+
this.#path = join22(resolve22(workspace), ".flavor", "astgraph", "index.db");
|
|
17971
|
+
}
|
|
17972
|
+
async status() {
|
|
17973
|
+
if ((await stat8(this.#path).catch(() => void 0))?.isFile() !== true) {
|
|
17974
|
+
return { available: false, path: this.#path, files: 0, nodes: 0, edges: 0 };
|
|
17975
|
+
}
|
|
17976
|
+
return this.#read((db) => {
|
|
17977
|
+
const count = (table) => Number(db.prepare(`SELECT COUNT(*) AS count FROM ${table}`).get().count);
|
|
17978
|
+
const metadata = db.prepare("SELECT value FROM project_metadata WHERE key = 'last_index'").get();
|
|
17979
|
+
let indexedAt;
|
|
17980
|
+
try {
|
|
17981
|
+
const at = Number(JSON.parse(metadata?.value ?? "{}").at);
|
|
17982
|
+
if (Number.isFinite(at)) indexedAt = new Date(at).toISOString();
|
|
17983
|
+
} catch {
|
|
17984
|
+
}
|
|
17985
|
+
return { available: true, path: this.#path, files: count("files"), nodes: count("nodes"), edges: count("edges"), ...indexedAt === void 0 ? {} : { indexedAt } };
|
|
17986
|
+
});
|
|
17987
|
+
}
|
|
17988
|
+
async search(query, limit = 40) {
|
|
17989
|
+
const value = query.trim().slice(0, 200);
|
|
17990
|
+
if (value.length === 0 || !(await this.status()).available) return [];
|
|
17991
|
+
return this.#read((db) => db.prepare(`SELECT ${FIELDS} FROM nodes WHERE lower(name) LIKE ? OR lower(qualified_name) LIKE ? ORDER BY CASE WHEN lower(name) = ? THEN 0 ELSE 1 END, name LIMIT ?`).all(`%${value.toLowerCase()}%`, `%${value.toLowerCase()}%`, value.toLowerCase(), Math.min(Math.max(limit, 1), 100)).map(mapNode));
|
|
17992
|
+
}
|
|
17993
|
+
async relations(id, hops = 2) {
|
|
17994
|
+
if (!/^[A-Za-z0-9._:/#@+~-]{1,512}$/.test(id)) throw new Error("Invalid AST node id");
|
|
17995
|
+
if (!(await this.status()).available) return { callers: [], callees: [], impact: [] };
|
|
17996
|
+
return this.#read((db) => {
|
|
17997
|
+
const originRow = db.prepare(`SELECT ${FIELDS} FROM nodes WHERE id = ?`).get(id);
|
|
17998
|
+
const linked = (direction) => {
|
|
17999
|
+
const source = direction === "callers" ? "e.source" : "e.target";
|
|
18000
|
+
const predicate = direction === "callers" ? "e.target = ?" : "e.source = ?";
|
|
18001
|
+
return db.prepare(`SELECT DISTINCT n.${FIELDS.split(", ").join(", n.")} FROM edges e JOIN nodes n ON n.id = ${source} WHERE ${predicate} AND e.kind IN ('calls','imports') LIMIT 100`).all(id).map(mapNode);
|
|
18002
|
+
};
|
|
18003
|
+
const seen = /* @__PURE__ */ new Map();
|
|
18004
|
+
let frontier = [id];
|
|
18005
|
+
for (let hop = 1; hop <= Math.min(Math.max(hops, 1), 4) && frontier.length > 0 && seen.size < 200; hop += 1) {
|
|
18006
|
+
const next = [];
|
|
18007
|
+
for (const current of frontier) {
|
|
18008
|
+
const rows = db.prepare("SELECT source AS other FROM edges WHERE target = ? UNION SELECT target AS other FROM edges WHERE source = ? LIMIT 200").all(current, current);
|
|
18009
|
+
for (const row of rows) if (row.other !== id && !seen.has(row.other)) {
|
|
18010
|
+
seen.set(row.other, hop);
|
|
18011
|
+
next.push(row.other);
|
|
18012
|
+
}
|
|
18013
|
+
}
|
|
18014
|
+
frontier = next;
|
|
18015
|
+
}
|
|
18016
|
+
const impact = [...seen].flatMap(([nodeId, hop]) => {
|
|
18017
|
+
const row = db.prepare(`SELECT ${FIELDS} FROM nodes WHERE id = ?`).get(nodeId);
|
|
18018
|
+
return row === void 0 ? [] : [{ ...mapNode(row), hop }];
|
|
18019
|
+
});
|
|
18020
|
+
return { ...originRow === void 0 ? {} : { origin: mapNode(originRow) }, callers: linked("callers"), callees: linked("callees"), impact };
|
|
18021
|
+
});
|
|
18022
|
+
}
|
|
18023
|
+
#read(run) {
|
|
18024
|
+
const sqlite = nodeCreateRequire(import.meta.url)("node:sqlite");
|
|
18025
|
+
const db = new sqlite.DatabaseSync(this.#path, { readOnly: true });
|
|
18026
|
+
try {
|
|
18027
|
+
return run(db);
|
|
18028
|
+
} finally {
|
|
18029
|
+
db.close();
|
|
18030
|
+
}
|
|
18031
|
+
}
|
|
18032
|
+
};
|
|
18033
|
+
function mapNode(value) {
|
|
18034
|
+
const row = value;
|
|
18035
|
+
return { id: String(row.id), kind: String(row.kind), name: String(row.name), qualifiedName: String(row.qualified_name), filePath: String(row.file_path), language: String(row.language), startLine: Number(row.start_line), endLine: Number(row.end_line), ...row.signature == null ? {} : { signature: String(row.signature) } };
|
|
18036
|
+
}
|
|
18037
|
+
|
|
17466
18038
|
// src/git/tools.ts
|
|
17467
18039
|
import { z as z28 } from "zod";
|
|
17468
18040
|
var GitHistoryInput = z28.object({
|
|
@@ -17557,7 +18129,7 @@ var QuestionBridge = class {
|
|
|
17557
18129
|
}
|
|
17558
18130
|
}
|
|
17559
18131
|
#askLocally(questions, signal) {
|
|
17560
|
-
return new Promise((
|
|
18132
|
+
return new Promise((resolve29, reject) => {
|
|
17561
18133
|
const onAbort = () => {
|
|
17562
18134
|
this.#pending = void 0;
|
|
17563
18135
|
this.#active = false;
|
|
@@ -17571,7 +18143,7 @@ var QuestionBridge = class {
|
|
|
17571
18143
|
this.#pending = void 0;
|
|
17572
18144
|
this.#active = false;
|
|
17573
18145
|
this.#onChange?.();
|
|
17574
|
-
|
|
18146
|
+
resolve29(answers);
|
|
17575
18147
|
},
|
|
17576
18148
|
reject: (error) => {
|
|
17577
18149
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -17664,7 +18236,7 @@ function createTodoWriteTool() {
|
|
|
17664
18236
|
// src/tools/managed.ts
|
|
17665
18237
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
17666
18238
|
import { link as link2, lstat as lstat11, mkdir as mkdir17, readFile as readFile19, readdir as readdir8, unlink as unlink5, writeFile as writeFile10 } from "fs/promises";
|
|
17667
|
-
import { dirname as dirname18, join as
|
|
18239
|
+
import { dirname as dirname18, join as join23, resolve as resolve23 } from "path";
|
|
17668
18240
|
import { z as z31 } from "zod";
|
|
17669
18241
|
var MANAGED_TOOL_VERSION = 1;
|
|
17670
18242
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
@@ -17706,9 +18278,9 @@ var ManagedToolStore = class {
|
|
|
17706
18278
|
#entries = /* @__PURE__ */ new Map();
|
|
17707
18279
|
#diagnostics = [];
|
|
17708
18280
|
constructor(options) {
|
|
17709
|
-
this.#workspace =
|
|
17710
|
-
this.#projectRoot =
|
|
17711
|
-
this.#globalRoot =
|
|
18281
|
+
this.#workspace = resolve23(options.workspace);
|
|
18282
|
+
this.#projectRoot = join23(this.#workspace, ".flavor", "tools");
|
|
18283
|
+
this.#globalRoot = join23(resolve23(options.home), ".flavor-code", "tools");
|
|
17712
18284
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
17713
18285
|
}
|
|
17714
18286
|
get diagnostics() {
|
|
@@ -17755,7 +18327,7 @@ var ManagedToolStore = class {
|
|
|
17755
18327
|
version: MANAGED_TOOL_VERSION,
|
|
17756
18328
|
createdAt: this.#now()
|
|
17757
18329
|
});
|
|
17758
|
-
const path =
|
|
18330
|
+
const path = join23(this.directory(input.scope), filename(input.name));
|
|
17759
18331
|
const definition = compileDefinition(record, input.scope, this.#workspace);
|
|
17760
18332
|
await writeExclusive(path, `${JSON.stringify(record, void 0, 2)}
|
|
17761
18333
|
`);
|
|
@@ -17780,7 +18352,7 @@ var ManagedToolStore = class {
|
|
|
17780
18352
|
if (!parsed.success) return [];
|
|
17781
18353
|
const matches = this.#matching(parsed.data.name).filter((entry2) => parsed.data.scope === void 0 || entry2.scope === parsed.data.scope);
|
|
17782
18354
|
if (matches.length > 0) return matches.map((entry2) => entry2.path);
|
|
17783
|
-
return [
|
|
18355
|
+
return [join23(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
|
|
17784
18356
|
}
|
|
17785
18357
|
async #loadDirectory(scope2) {
|
|
17786
18358
|
const root = this.directory(scope2);
|
|
@@ -17793,7 +18365,7 @@ var ManagedToolStore = class {
|
|
|
17793
18365
|
return;
|
|
17794
18366
|
}
|
|
17795
18367
|
for (const name of names) {
|
|
17796
|
-
const path =
|
|
18368
|
+
const path = join23(root, name);
|
|
17797
18369
|
try {
|
|
17798
18370
|
const info = await lstat11(path);
|
|
17799
18371
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Managed tool record must be a regular file");
|
|
@@ -18712,7 +19284,7 @@ ${total}`;
|
|
|
18712
19284
|
// src/memory/store.ts
|
|
18713
19285
|
import { createHash as createHash16 } from "crypto";
|
|
18714
19286
|
import { readFile as readFile20, rm as rm9 } from "fs/promises";
|
|
18715
|
-
import { resolve as
|
|
19287
|
+
import { resolve as resolve24, join as join24, relative as relative17, sep as sep17 } from "path";
|
|
18716
19288
|
|
|
18717
19289
|
// src/memory/similarity.ts
|
|
18718
19290
|
function normalizeForSimilarity(value) {
|
|
@@ -18814,10 +19386,10 @@ var MemoryStore = class {
|
|
|
18814
19386
|
constructor(options) {
|
|
18815
19387
|
if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
|
|
18816
19388
|
if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
|
|
18817
|
-
this.workspace =
|
|
18818
|
-
this.#memoryRoot =
|
|
18819
|
-
this.path =
|
|
18820
|
-
this.#behaviorPath =
|
|
19389
|
+
this.workspace = resolve24(options.workspace);
|
|
19390
|
+
this.#memoryRoot = join24(this.workspace, ".flavor", "memory");
|
|
19391
|
+
this.path = join24(this.#memoryRoot, "MEMORY.md");
|
|
19392
|
+
this.#behaviorPath = join24(this.#memoryRoot, "behavior.json");
|
|
18821
19393
|
this.#maxEntries = options.maxEntries;
|
|
18822
19394
|
this.#maxEntryChars = options.maxEntryChars;
|
|
18823
19395
|
}
|
|
@@ -19040,7 +19612,7 @@ var MemoryStore = class {
|
|
|
19040
19612
|
}
|
|
19041
19613
|
#resolveContentPath(contentPath) {
|
|
19042
19614
|
if (!/^tasks\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.md$/.test(contentPath)) throw new Error("Invalid memory content path");
|
|
19043
|
-
const path =
|
|
19615
|
+
const path = resolve24(this.#memoryRoot, ...contentPath.split("/"));
|
|
19044
19616
|
const rel = relative17(this.#memoryRoot, path);
|
|
19045
19617
|
if (rel === ".." || rel.startsWith(`..${sep17}`)) throw new Error("Memory content path escapes the memory directory");
|
|
19046
19618
|
return path;
|
|
@@ -19511,8 +20083,8 @@ function createExecutionEnvironment(workspace, config) {
|
|
|
19511
20083
|
}
|
|
19512
20084
|
|
|
19513
20085
|
// src/ide/client.ts
|
|
19514
|
-
import { readFile as readFile21, readdir as readdir9, stat as
|
|
19515
|
-
import { basename as basename5, join as
|
|
20086
|
+
import { readFile as readFile21, readdir as readdir9, stat as stat9 } from "fs/promises";
|
|
20087
|
+
import { basename as basename5, join as join25, relative as relative18, resolve as resolve25, sep as sep18 } from "path";
|
|
19516
20088
|
var FlavorIdeClient = class {
|
|
19517
20089
|
#workspace;
|
|
19518
20090
|
#home;
|
|
@@ -19523,8 +20095,8 @@ var FlavorIdeClient = class {
|
|
|
19523
20095
|
#sessionId;
|
|
19524
20096
|
#eventTail = Promise.resolve();
|
|
19525
20097
|
constructor(options) {
|
|
19526
|
-
this.#workspace =
|
|
19527
|
-
this.#home =
|
|
20098
|
+
this.#workspace = resolve25(options.workspace);
|
|
20099
|
+
this.#home = resolve25(options.home);
|
|
19528
20100
|
this.#environment = options.environment ?? process.env;
|
|
19529
20101
|
this.#fetch = options.fetch ?? fetch;
|
|
19530
20102
|
}
|
|
@@ -19608,7 +20180,7 @@ var FlavorIdeClient = class {
|
|
|
19608
20180
|
};
|
|
19609
20181
|
if (await this.#healthy(configured)) return configured;
|
|
19610
20182
|
}
|
|
19611
|
-
const directory =
|
|
20183
|
+
const directory = join25(this.#home, ".flavor-code", "ide");
|
|
19612
20184
|
let entries;
|
|
19613
20185
|
try {
|
|
19614
20186
|
entries = (await readdir9(directory)).filter((name) => name.endsWith(".lock"));
|
|
@@ -19616,9 +20188,9 @@ var FlavorIdeClient = class {
|
|
|
19616
20188
|
return void 0;
|
|
19617
20189
|
}
|
|
19618
20190
|
const candidates = (await Promise.all(entries.map(async (name) => {
|
|
19619
|
-
const path =
|
|
20191
|
+
const path = join25(directory, name);
|
|
19620
20192
|
try {
|
|
19621
|
-
const [contents, info] = await Promise.all([readFile21(path, "utf8"),
|
|
20193
|
+
const [contents, info] = await Promise.all([readFile21(path, "utf8"), stat9(path)]);
|
|
19622
20194
|
const lock = parseLockfile(contents);
|
|
19623
20195
|
return lock === void 0 ? void 0 : { lock, modified: info.mtimeMs };
|
|
19624
20196
|
} catch {
|
|
@@ -19722,8 +20294,8 @@ function parsePort(value) {
|
|
|
19722
20294
|
return Number.isInteger(port) && port > 0 && port <= 65535 ? port : void 0;
|
|
19723
20295
|
}
|
|
19724
20296
|
function isWithin8(parent, child) {
|
|
19725
|
-
const base = normalize(
|
|
19726
|
-
const target = normalize(
|
|
20297
|
+
const base = normalize(resolve25(parent));
|
|
20298
|
+
const target = normalize(resolve25(child));
|
|
19727
20299
|
return target === base || target.startsWith(`${base}${sep18}`);
|
|
19728
20300
|
}
|
|
19729
20301
|
function normalize(path) {
|
|
@@ -19753,8 +20325,8 @@ var JobRegistry = class {
|
|
|
19753
20325
|
const id = `job-${randomUUID17().slice(0, 12)}`;
|
|
19754
20326
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19755
20327
|
let settle;
|
|
19756
|
-
const done = new Promise((
|
|
19757
|
-
settle =
|
|
20328
|
+
const done = new Promise((resolve29) => {
|
|
20329
|
+
settle = resolve29;
|
|
19758
20330
|
});
|
|
19759
20331
|
const record = {
|
|
19760
20332
|
id,
|
|
@@ -19877,8 +20449,8 @@ function snapshot2(job) {
|
|
|
19877
20449
|
|
|
19878
20450
|
// src/terminal/service.ts
|
|
19879
20451
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
19880
|
-
import { createRequire as
|
|
19881
|
-
import { isAbsolute as isAbsolute11, relative as relative19, resolve as
|
|
20452
|
+
import { createRequire as nodeCreateRequire2 } from "module";
|
|
20453
|
+
import { isAbsolute as isAbsolute11, relative as relative19, resolve as resolve26, sep as sep19 } from "path";
|
|
19882
20454
|
var TerminalService = class {
|
|
19883
20455
|
#root;
|
|
19884
20456
|
#factory;
|
|
@@ -19886,7 +20458,7 @@ var TerminalService = class {
|
|
|
19886
20458
|
#maxOutputChars;
|
|
19887
20459
|
#sessions = /* @__PURE__ */ new Map();
|
|
19888
20460
|
constructor(workspace, options = {}) {
|
|
19889
|
-
this.#root =
|
|
20461
|
+
this.#root = resolve26(workspace);
|
|
19890
20462
|
this.#factory = options.factory ?? defaultFactory;
|
|
19891
20463
|
this.#jobs = options.jobs;
|
|
19892
20464
|
this.#maxOutputChars = options.maxOutputChars ?? 2e5;
|
|
@@ -19991,7 +20563,7 @@ function snapshot3(record) {
|
|
|
19991
20563
|
}
|
|
19992
20564
|
function defaultFactory(shell, args, options) {
|
|
19993
20565
|
try {
|
|
19994
|
-
const pty =
|
|
20566
|
+
const pty = nodeCreateRequire2(import.meta.url)("node-pty");
|
|
19995
20567
|
return pty.spawn(shell, args, { ...options, name: "xterm-256color" });
|
|
19996
20568
|
} catch (error) {
|
|
19997
20569
|
throw new Error(`Pseudo-terminal backend is unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -20004,7 +20576,7 @@ function cleanEnvironment(environment) {
|
|
|
20004
20576
|
return Object.fromEntries(Object.entries(environment).filter((entry2) => entry2[1] !== void 0));
|
|
20005
20577
|
}
|
|
20006
20578
|
function workspacePath(root, input) {
|
|
20007
|
-
const path =
|
|
20579
|
+
const path = resolve26(root, input);
|
|
20008
20580
|
const delta = relative19(root, path);
|
|
20009
20581
|
if (delta === ".." || delta.startsWith(`..${sep19}`) || isAbsolute11(delta)) throw new Error("Terminal cwd is outside the workspace");
|
|
20010
20582
|
return path;
|
|
@@ -20012,7 +20584,7 @@ function workspacePath(root, input) {
|
|
|
20012
20584
|
|
|
20013
20585
|
// src/tools/terminal.ts
|
|
20014
20586
|
import { z as z33 } from "zod";
|
|
20015
|
-
import { resolve as
|
|
20587
|
+
import { resolve as resolve27 } from "path";
|
|
20016
20588
|
var Id = z33.string().regex(/^term-[a-f0-9-]+$/);
|
|
20017
20589
|
var OpenInput = z33.object({ cwd: z33.string().optional(), shell: z33.string().optional(), args: z33.array(z33.string()).optional(), columns: z33.coerce.number().int().min(10).max(500).optional(), rows: z33.coerce.number().int().min(2).max(300).optional() });
|
|
20018
20590
|
var OptionalBoolean = z33.union([z33.boolean(), z33.string().refine((value) => value === "true" || value === "false")]);
|
|
@@ -20026,8 +20598,8 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
|
20026
20598
|
name: "TerminalOpen",
|
|
20027
20599
|
description: "Open a persistent interactive pseudo-terminal in the workspace",
|
|
20028
20600
|
inputSchema: OpenInput,
|
|
20029
|
-
paths: (input) => [
|
|
20030
|
-
permissions: (input) => ({ paths: [
|
|
20601
|
+
paths: (input) => [resolve27(workspace, input.cwd ?? ".")],
|
|
20602
|
+
permissions: (input) => ({ paths: [resolve27(workspace, input.cwd ?? ".")], command: input.shell ?? "system-shell", args: input.args ?? [], cwd: resolve27(workspace, input.cwd ?? ".") }),
|
|
20031
20603
|
execute: async (input, _signal, context) => terminals.open({
|
|
20032
20604
|
owner: owner(context),
|
|
20033
20605
|
...input.cwd === void 0 ? {} : { cwd: input.cwd },
|
|
@@ -20253,8 +20825,8 @@ var PalClient = class {
|
|
|
20253
20825
|
let disconnectTimer;
|
|
20254
20826
|
await Promise.race([
|
|
20255
20827
|
this.request({ version: 1, type: "disconnect", requestId: randomUUID19() }).catch(() => void 0),
|
|
20256
|
-
new Promise((
|
|
20257
|
-
disconnectTimer = setTimeout(
|
|
20828
|
+
new Promise((resolve29) => {
|
|
20829
|
+
disconnectTimer = setTimeout(resolve29, Math.min(this.requestTimeoutMs, 250));
|
|
20258
20830
|
})
|
|
20259
20831
|
]);
|
|
20260
20832
|
if (disconnectTimer !== void 0) clearTimeout(disconnectTimer);
|
|
@@ -20300,7 +20872,7 @@ var PalClient = class {
|
|
|
20300
20872
|
return presence;
|
|
20301
20873
|
}
|
|
20302
20874
|
openSocket() {
|
|
20303
|
-
return new Promise((
|
|
20875
|
+
return new Promise((resolve29, reject) => {
|
|
20304
20876
|
let settled = false;
|
|
20305
20877
|
const rejectConnection = (error) => {
|
|
20306
20878
|
if (settled) return;
|
|
@@ -20323,7 +20895,7 @@ var PalClient = class {
|
|
|
20323
20895
|
settled = true;
|
|
20324
20896
|
if (this.connectingReject === rejectConnection) this.connectingReject = void 0;
|
|
20325
20897
|
socket.off("error", onInitialError);
|
|
20326
|
-
|
|
20898
|
+
resolve29();
|
|
20327
20899
|
};
|
|
20328
20900
|
const onInitialError = (error) => {
|
|
20329
20901
|
socket.off("connect", onConnect);
|
|
@@ -20354,12 +20926,12 @@ var PalClient = class {
|
|
|
20354
20926
|
if (this.closed || socket === void 0 || socket.destroyed || !socket.writable) {
|
|
20355
20927
|
return Promise.reject(new Error(this.closed ? "Pal client is closed" : "Pal client is not connected"));
|
|
20356
20928
|
}
|
|
20357
|
-
return new Promise((
|
|
20929
|
+
return new Promise((resolve29, reject) => {
|
|
20358
20930
|
const timer = setTimeout(() => {
|
|
20359
20931
|
this.pending.delete(request.requestId);
|
|
20360
20932
|
reject(new Error(`Pal request '${request.type}' timed out`));
|
|
20361
20933
|
}, this.requestTimeoutMs);
|
|
20362
|
-
this.pending.set(request.requestId, { resolve:
|
|
20934
|
+
this.pending.set(request.requestId, { resolve: resolve29, reject, timer });
|
|
20363
20935
|
let frame;
|
|
20364
20936
|
try {
|
|
20365
20937
|
frame = encodeControlFrame(BrokerRequestSchema.parse(request));
|
|
@@ -20763,8 +21335,8 @@ var ApprovalBridge = class {
|
|
|
20763
21335
|
}
|
|
20764
21336
|
};
|
|
20765
21337
|
async function createProductionRuntime(options) {
|
|
20766
|
-
const workspace =
|
|
20767
|
-
const home =
|
|
21338
|
+
const workspace = resolve28(options.workspace ?? process.cwd());
|
|
21339
|
+
const home = resolve28(options.home ?? homedir2());
|
|
20768
21340
|
const environment = options.environment ?? process.env;
|
|
20769
21341
|
const ide = new FlavorIdeClient({ workspace, home, environment });
|
|
20770
21342
|
const ideReady = ide.initialize();
|
|
@@ -20988,8 +21560,8 @@ async function createProductionRuntime(options) {
|
|
|
20988
21560
|
const registeredProviders = registration.registered;
|
|
20989
21561
|
let effectiveLlm = registration.effectiveLlm;
|
|
20990
21562
|
const pluginHost = new PluginHost({
|
|
20991
|
-
globalPluginDirs: [
|
|
20992
|
-
projectPluginDirs: [
|
|
21563
|
+
globalPluginDirs: [join26(home, ".flavor-code", "plugins")],
|
|
21564
|
+
projectPluginDirs: [join26(workspace, ".flavor", "plugins")],
|
|
20993
21565
|
config,
|
|
20994
21566
|
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
20995
21567
|
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
@@ -21097,7 +21669,7 @@ async function createProductionRuntime(options) {
|
|
|
21097
21669
|
const address = palSocketAddress({
|
|
21098
21670
|
platform: process.platform,
|
|
21099
21671
|
userScope: home,
|
|
21100
|
-
runtimeDir:
|
|
21672
|
+
runtimeDir: join26(tmpdir4(), `flavor-code-pals-${createHash18("sha256").update(home).digest("hex").slice(0, 16)}`)
|
|
21101
21673
|
});
|
|
21102
21674
|
const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
|
|
21103
21675
|
instanceId: collaboration.instanceId,
|
|
@@ -21208,8 +21780,8 @@ async function createProductionRuntime(options) {
|
|
|
21208
21780
|
diagnostics.push(`MCP servers could not start: ${message(error)}`);
|
|
21209
21781
|
});
|
|
21210
21782
|
const skills = new SkillRegistry({
|
|
21211
|
-
globalRoots: [
|
|
21212
|
-
projectRoots: [
|
|
21783
|
+
globalRoots: [join26(home, ".flavor-code", "skills")],
|
|
21784
|
+
projectRoots: [join26(workspace, ".flavor", "skills"), ...pluginSkillRoots],
|
|
21213
21785
|
authorizeResource: async () => true,
|
|
21214
21786
|
disabledNames: config.skills.disabled
|
|
21215
21787
|
});
|
|
@@ -21217,7 +21789,7 @@ async function createProductionRuntime(options) {
|
|
|
21217
21789
|
skillsReady.catch(() => void 0);
|
|
21218
21790
|
tools.push(createSkillTool(skills), createSkillResourceTool(skills));
|
|
21219
21791
|
if (options.extraTools !== void 0) tools.push(...options.extraTools);
|
|
21220
|
-
const flavor = await optionalText2(
|
|
21792
|
+
const flavor = await optionalText2(join26(workspace, "FLAVOR.md"));
|
|
21221
21793
|
const instructionBaseline = await workspaceInstructions.baseline();
|
|
21222
21794
|
let memoryContext;
|
|
21223
21795
|
if (memoryStore !== void 0) {
|
|
@@ -21795,7 +22367,7 @@ async function createProductionRuntime(options) {
|
|
|
21795
22367
|
evolveService.toolDefinition(),
|
|
21796
22368
|
...mcpTools
|
|
21797
22369
|
];
|
|
21798
|
-
const loopFlavor = await optionalText2(
|
|
22370
|
+
const loopFlavor = await optionalText2(join26(input.workspace, "FLAVOR.md"));
|
|
21799
22371
|
const loopInstructionBaseline = await loopInstructions.baseline();
|
|
21800
22372
|
const loopEnvironment = createPromptEnvironment({
|
|
21801
22373
|
now: /* @__PURE__ */ new Date(),
|
|
@@ -22155,6 +22727,22 @@ async function createProductionRuntime(options) {
|
|
|
22155
22727
|
modelId: () => harness.subagentModelId,
|
|
22156
22728
|
notify: (message2) => emitOutput({ type: "notice", message: message2 })
|
|
22157
22729
|
}, focus, signal),
|
|
22730
|
+
explain: (query, focus, signal, onText) => runExplain({
|
|
22731
|
+
graph: new DesktopAstGraphService(workspace),
|
|
22732
|
+
readFile: async (path) => {
|
|
22733
|
+
const root = resolve28(workspace);
|
|
22734
|
+
const abs = resolve28(root, path);
|
|
22735
|
+
if (abs !== root && !abs.startsWith(root + sep20)) throw new Error(`path outside workspace: ${path}`);
|
|
22736
|
+
return readFile22(abs, "utf8");
|
|
22737
|
+
},
|
|
22738
|
+
history: async (path, limit) => fileHistory(workspace, path, limit),
|
|
22739
|
+
questions,
|
|
22740
|
+
registry,
|
|
22741
|
+
modelId: () => harness.subagentModelId,
|
|
22742
|
+
language,
|
|
22743
|
+
notify: (message2) => emitOutput({ type: "notice", message: message2 }),
|
|
22744
|
+
...onText === void 0 ? {} : { onText }
|
|
22745
|
+
}, query, focus, signal),
|
|
22158
22746
|
audit: async (toolFilter) => {
|
|
22159
22747
|
try {
|
|
22160
22748
|
const raw = await readFile22(auditLogger.path, "utf8");
|
|
@@ -22344,7 +22932,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
22344
22932
|
return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
|
|
22345
22933
|
}
|
|
22346
22934
|
try {
|
|
22347
|
-
const tokenStore = createFileTokenStore(
|
|
22935
|
+
const tokenStore = createFileTokenStore(join26(home, ".flavor-code", "auth.json"));
|
|
22348
22936
|
const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
|
|
22349
22937
|
await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
|
|
22350
22938
|
const oauth = new OAuthCallbackAuthProvider({
|
|
@@ -22367,8 +22955,12 @@ ${renderMemoryDocument(entries)}`;
|
|
|
22367
22955
|
};
|
|
22368
22956
|
registry.register(next.providerId, next.apiType === "anthropic" ? new AnthropicModelAdapter({
|
|
22369
22957
|
...adapterOptions,
|
|
22958
|
+
...providerConfig?.thinkingBudget === void 0 ? {} : { thinkingBudget: providerConfig.thinkingBudget },
|
|
22370
22959
|
...providerConfig?.claudeClient === true ? { headers: CLAUDE_CLIENT_HEADERS } : {}
|
|
22371
|
-
}) : new OpenAIModelAdapter(
|
|
22960
|
+
}) : new OpenAIModelAdapter({
|
|
22961
|
+
...adapterOptions,
|
|
22962
|
+
...providerConfig?.thinkingEffort === void 0 ? {} : { thinkingEffort: providerConfig.thinkingEffort }
|
|
22963
|
+
}));
|
|
22372
22964
|
effectiveLlm = next;
|
|
22373
22965
|
mainModel = `${next.providerId}:${next.defaultModel}`;
|
|
22374
22966
|
childModel = `${next.providerId}:${next.cheapModel}`;
|
|
@@ -22387,7 +22979,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
22387
22979
|
}
|
|
22388
22980
|
},
|
|
22389
22981
|
async logout() {
|
|
22390
|
-
const tokenStore = createFileTokenStore(
|
|
22982
|
+
const tokenStore = createFileTokenStore(join26(home, ".flavor-code", "auth.json"));
|
|
22391
22983
|
const tokens = await tokenStore.load();
|
|
22392
22984
|
const hadCredentials = Object.keys(tokens).length > 0 || effectiveLlm !== void 0;
|
|
22393
22985
|
if (!hadCredentials) {
|
|
@@ -22663,8 +23255,8 @@ async function workspaceFingerprint(workspace) {
|
|
|
22663
23255
|
let remaining = 5 * 1024 * 1024;
|
|
22664
23256
|
for (const name of untracked.stdout.split("\0").filter(Boolean).sort()) {
|
|
22665
23257
|
if (name === ".flavor/loops" || name.startsWith(".flavor/loops/")) continue;
|
|
22666
|
-
const path =
|
|
22667
|
-
const relativePath = relative20(
|
|
23258
|
+
const path = resolve28(workspace, name);
|
|
23259
|
+
const relativePath = relative20(resolve28(workspace), path);
|
|
22668
23260
|
if (relativePath === ".." || relativePath.startsWith(`..${sep20}`)) continue;
|
|
22669
23261
|
hash.update(name);
|
|
22670
23262
|
if (remaining <= 0) continue;
|
|
@@ -22882,7 +23474,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
|
|
|
22882
23474
|
const configured = { ...providers };
|
|
22883
23475
|
if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
|
|
22884
23476
|
if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
|
|
22885
|
-
const oauthTokenStore = createFileTokenStore(
|
|
23477
|
+
const oauthTokenStore = createFileTokenStore(join26(home, ".flavor-code", "auth.json"));
|
|
22886
23478
|
const registered = [];
|
|
22887
23479
|
let effectiveLlm;
|
|
22888
23480
|
for (const [name, provider] of Object.entries(configured)) {
|
|
@@ -22954,8 +23546,12 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
|
|
|
22954
23546
|
};
|
|
22955
23547
|
const adapter = apiProtocol === "anthropic" ? new AnthropicModelAdapter({
|
|
22956
23548
|
...adapterOptions,
|
|
23549
|
+
...runtimeProvider.thinkingBudget === void 0 ? {} : { thinkingBudget: runtimeProvider.thinkingBudget },
|
|
22957
23550
|
...runtimeProvider.claudeClient === true ? { headers: CLAUDE_CLIENT_HEADERS } : {}
|
|
22958
|
-
}) : new OpenAIModelAdapter(
|
|
23551
|
+
}) : new OpenAIModelAdapter({
|
|
23552
|
+
...adapterOptions,
|
|
23553
|
+
...runtimeProvider.thinkingEffort === void 0 ? {} : { thinkingEffort: runtimeProvider.thinkingEffort }
|
|
23554
|
+
});
|
|
22959
23555
|
const cacheProfile = resolveCacheProfile({ apiType: apiProtocol, baseURL: runtimeProvider.baseURL });
|
|
22960
23556
|
if (apiProtocol === "openai" && isDashScopeBaseURL(runtimeProvider.baseURL)) {
|
|
22961
23557
|
diagnostics.push(
|
|
@@ -23320,6 +23916,8 @@ export {
|
|
|
23320
23916
|
executeShell,
|
|
23321
23917
|
packageVersion,
|
|
23322
23918
|
SkillRegistry,
|
|
23919
|
+
charWidth,
|
|
23920
|
+
thinkingWindow,
|
|
23323
23921
|
createTranscriptState,
|
|
23324
23922
|
transcriptReducer,
|
|
23325
23923
|
WorkspaceCheckpointStore,
|