deel-local-cli 1.20.7 → 1.20.9
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.ko.md +4 -2
- package/README.md +4 -2
- package/bin/deel.js +27 -0
- package/package.json +1 -1
- package/src/agent/compact.js +103 -8
- package/src/agent/evolve.js +18 -1
- package/src/agent/models.js +22 -0
- package/src/agent/modes.js +67 -7
- package/src/agent/session.js +12 -1
- package/src/backend/adapter.js +93 -9
- package/src/backend/mcp.js +108 -13
- package/src/backend/probe.js +108 -14
- package/src/backend/wire.js +18 -0
- package/src/commands.js +203 -20
- package/src/i18n/en.js +3 -1
- package/src/i18n/ko.js +3 -1
- package/src/repl.js +13 -2
- package/src/tools/jobs.js +85 -7
- package/src/tools/pdf.js +168 -44
- package/src/ui/ansi.js +30 -8
- package/src/ui/inputbox.js +105 -25
- package/src/ui/level.js +9 -1
- package/src/ui/office.js +16 -2
- package/src/ui/status.js +26 -3
package/README.ko.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
|
|
22
22
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
|
|
23
|
-
[](docs/ko/develop.md)
|
|
24
24
|
|
|
25
25
|
[](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
|
|
26
26
|
[](package.json)
|
|
@@ -301,7 +301,7 @@ deel --offline
|
|
|
301
301
|
무엇이 어디로 갈 수 있는지는 켤 때 화면 맨 위에 늘 적혀 있습니다.
|
|
302
302
|
|
|
303
303
|
```
|
|
304
|
-
deel 1.20.
|
|
304
|
+
deel 1.20.9 ⌂ 이 안
|
|
305
305
|
보냄 이 컴퓨터 안 127.0.0.1:11434 ← 여기 말고는 어디로도 안 갑니다
|
|
306
306
|
```
|
|
307
307
|
|
|
@@ -1289,6 +1289,8 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
|
|
|
1289
1289
|
|
|
1290
1290
|
| 판 | 무엇이 바뀌었나 |
|
|
1291
1291
|
|---|---|
|
|
1292
|
+
| [1.20.9](docs/ko/releases/1.20.md#1209) | 안 보이는 것이 아니라 다른 것이 보이고 있었습니다 |
|
|
1293
|
+
| [1.20.8](docs/ko/releases/1.20.md#1208) | 못 쟀다는 것을 안 된다고 적고 있었습니다 |
|
|
1292
1294
|
| [1.20.7](docs/ko/releases/1.20.md#1207) | 서버가 말해 준 것과 반대를 배우고 있었습니다 |
|
|
1293
1295
|
| [1.20.6](docs/ko/releases/1.20.md#1206) | 2차 리뷰가 판을 조용히 잃고 있었습니다 |
|
|
1294
1296
|
| [1.20.5](docs/ko/releases/1.20.md#1205) | `aws_access_token` 이 안 잡히고 있었습니다 |
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Vendor APIs connect too — **only when you say so**
|
|
|
20
20
|
|
|
21
21
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
|
|
22
22
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
|
|
23
|
-
[](docs/en/develop.md)
|
|
24
24
|
|
|
25
25
|
[](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
|
|
26
26
|
[](package.json)
|
|
@@ -374,7 +374,7 @@ deel --offline
|
|
|
374
374
|
The destination is printed at the top of every session:
|
|
375
375
|
|
|
376
376
|
```
|
|
377
|
-
deel 1.20.
|
|
377
|
+
deel 1.20.9 ⌂ inside
|
|
378
378
|
Sends to this machine 127.0.0.1:11434 ← nowhere else
|
|
379
379
|
```
|
|
380
380
|
|
|
@@ -1350,6 +1350,8 @@ so one run tells you everything.
|
|
|
1350
1350
|
|
|
1351
1351
|
| Version | What changed |
|
|
1352
1352
|
|---|---|
|
|
1353
|
+
| [1.20.9](docs/en/releases/1.20.md#1209) | Nothing was missing from the screen — something else was on it |
|
|
1354
|
+
| [1.20.8](docs/en/releases/1.20.md#1208) | We were writing down "can't" where the truth was "couldn't measure" |
|
|
1353
1355
|
| [1.20.7](docs/en/releases/1.20.md#1207) | We were learning the opposite of what the server said |
|
|
1354
1356
|
| [1.20.6](docs/en/releases/1.20.md#1206) | The second review was losing rounds silently |
|
|
1355
1357
|
| [1.20.5](docs/en/releases/1.20.md#1205) | `aws_access_token` was not being caught |
|
package/bin/deel.js
CHANGED
|
@@ -701,6 +701,33 @@ async function main() {
|
|
|
701
701
|
|
|
702
702
|
if (flags.help || cmd === 'help') { help(); return 0; }
|
|
703
703
|
|
|
704
|
+
/*
|
|
705
|
+
* `--work` 에 오타가 났으면 **여기서 멈춘다.**
|
|
706
|
+
*
|
|
707
|
+
* 모르는 이름은 조용히 기본값(종합)으로 떨어진다. 그런데 종합은 Write·Edit·
|
|
708
|
+
* Bash 를 다 가진 모드다 — `deel --work architcet` 한 번으로, 파일을 안
|
|
709
|
+
* 건드리는 모드로 켠 줄 알고 **고칠 수 있는 상태**로 돌게 된다. 화면에
|
|
710
|
+
* 남는 단서는 상태줄 글자 하나뿐이다.
|
|
711
|
+
*
|
|
712
|
+
* 대화 중에 치는 `/work` 는 관대하게 받아도 된다(틀리면 바로 화면에 뜨고
|
|
713
|
+
* 다시 치면 된다). 깃발은 한 번 켜면 그 세션이 끝까지 그걸로 간다.
|
|
714
|
+
*
|
|
715
|
+
* 판 번호·도움말보다 뒤에 둔다 — 그 둘은 무엇을 잘못 쳤든 답해야 한다.
|
|
716
|
+
*/
|
|
717
|
+
if (flags.work !== undefined && flags.work !== true) {
|
|
718
|
+
const { normalize: 모드이름, MODES: 모드들, 보일이름: 모드보임 } = await import('../src/agent/modes.js');
|
|
719
|
+
if (!모드이름(String(flags.work))) {
|
|
720
|
+
const 있는것 = Object.keys(모드들).map((k) => `${모드보임(k)}(${k})`).join(' · ');
|
|
721
|
+
say('');
|
|
722
|
+
say(` 그런 작업 모드가 없습니다: ${String(flags.work)}`);
|
|
723
|
+
say(` 있는 것: ${있는것}`);
|
|
724
|
+
say('');
|
|
725
|
+
say(' 그냥 켜면 「종합」 으로 돕니다 — 그건 파일을 고칠 수 있는 모드입니다.');
|
|
726
|
+
say('');
|
|
727
|
+
return 2;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
704
731
|
switch (cmd) {
|
|
705
732
|
// 한 번만 돌고 끝난다. 스크립트·배치에서 부르는 자리다.
|
|
706
733
|
//
|
package/package.json
CHANGED
package/src/agent/compact.js
CHANGED
|
@@ -105,7 +105,9 @@ export const 캐시일때비울몫 = 0.10;
|
|
|
105
105
|
* 늦어질 뿐이고, 있는데 없다고 치면 방금 만든 캐시를 깬다. 그래서 모르는
|
|
106
106
|
* 쪽은 덜 잃는 쪽으로 기울인다.
|
|
107
107
|
*
|
|
108
|
-
*
|
|
108
|
+
* **두 번** 불렀는데도 읽힌 것이 하나도 없으면, 그때는 정말 없는 것이다.
|
|
109
|
+
* (여기가 「두 번 넘게」 라고 적혀 있었다. 아래 셈은 두 번째에 이미 단정한다 —
|
|
110
|
+
* 한 번의 차이지만, 적힌 것과 하는 것이 다르면 다음 사람이 그 값을 못 믿는다.)
|
|
109
111
|
*/
|
|
110
112
|
export const 재볼기회 = 2;
|
|
111
113
|
|
|
@@ -163,7 +165,37 @@ export function foldImages(session, { keep = KEEP_IMAGES } = {}) {
|
|
|
163
165
|
const 자리 = [];
|
|
164
166
|
ms.forEach((m, i) => { if (그림장수(m)) 자리.push(i); });
|
|
165
167
|
|
|
166
|
-
|
|
168
|
+
/*
|
|
169
|
+
* ── 남기는 것은 **장수**다, 메시지 수가 아니다 ─────────────────────────
|
|
170
|
+
*
|
|
171
|
+
* `KEEP_IMAGES` 는 위 머리말대로 「최근 몇 **장**」 이다. 그런데 자리를
|
|
172
|
+
* 메시지 단위로 세고 거기서 keep 을 빼고 있었다. 그래서 한 메시지에 여러
|
|
173
|
+
* 장이 실리면(`@a.png @b.png @c.png` 를 한 줄로 붙이면 그렇게 된다)
|
|
174
|
+
* **남는 장수가 keep 을 훌쩍 넘었다.**
|
|
175
|
+
*
|
|
176
|
+
* 석 장 실린 메시지 + 한 장 실린 메시지 → 자리가 둘이라 한 장도 안 뺀다
|
|
177
|
+
* (남는 것은 넉 장. keep 은 둘)
|
|
178
|
+
*
|
|
179
|
+
* 몇 MB 짜리 사진 셋이 「최근 둘만 남긴다」 는 이름 아래 그대로 남는다.
|
|
180
|
+
*
|
|
181
|
+
* ── 그러면서도 한 덩이는 쪼개지 않는다 ────────────────────────────────
|
|
182
|
+
*
|
|
183
|
+
* 사람이 한 번에 붙인 사진들은 한 이야기다. 그 안에서 어느 장이 더 오래된
|
|
184
|
+
* 것인지 알 길도 없다. 그래서 **마지막 덩이는 몇 장이든 남기고**, 그보다
|
|
185
|
+
* 앞엣것은 장수를 더해 keep 을 안 넘을 때만 남긴다. 마지막 덩이를 빼지
|
|
186
|
+
* 않는 까닭: 방금 보여 준 사진을 그 자리에서 없애면 그 턴이 헛턴이 된다.
|
|
187
|
+
*/
|
|
188
|
+
let 남긴장수 = 0;
|
|
189
|
+
let 첫남길자리 = 자리.length;
|
|
190
|
+
for (let k = 자리.length - 1; k >= 0; k--) {
|
|
191
|
+
const 이번장수 = 그림장수(ms[자리[k]]);
|
|
192
|
+
// 마지막 덩이는 무조건 남긴다. 그 뒤로는 넘치면 거기서 끊는다.
|
|
193
|
+
if (k < 자리.length - 1 && 남긴장수 + 이번장수 > keep) break;
|
|
194
|
+
남긴장수 += 이번장수;
|
|
195
|
+
첫남길자리 = k;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const 뺄것 = 자리.slice(0, 첫남길자리);
|
|
167
199
|
const 뺀것들 = [];
|
|
168
200
|
for (const i of 뺄것) {
|
|
169
201
|
const m = ms[i];
|
|
@@ -174,7 +206,14 @@ ${접힘표} 그림 ${장수}장은 자리를 비우려고 뺐습니다. 필요
|
|
|
174
206
|
delete ms[i].images;
|
|
175
207
|
뺀것들.push({ 장수 });
|
|
176
208
|
}
|
|
177
|
-
|
|
209
|
+
/*
|
|
210
|
+
* `뺀것` 은 **장수**다.
|
|
211
|
+
*
|
|
212
|
+
* 여기가 `뺄것.length` — 곧 **메시지 수**였다. 화면은 이 값을 「그림 N장을
|
|
213
|
+
* 뺐습니다」 로 적으므로, 한 메시지에서 넉 장을 뺀 판에 「1장」 이라고
|
|
214
|
+
* 떴다. 사람이 세어 볼 수 있는 숫자라 틀리면 바로 거짓이 된다.
|
|
215
|
+
*/
|
|
216
|
+
return { 뺀것: 뺀것들.reduce((a, x) => a + x.장수, 0), 뺀메시지: 뺄것.length, 뺀것들 };
|
|
178
217
|
}
|
|
179
218
|
|
|
180
219
|
export function shouldFold(session, at = null) {
|
|
@@ -241,8 +280,21 @@ export function foldToolResults(session, { keep = KEEP_RECENT, min = FOLD_MIN,
|
|
|
241
280
|
* 같은 것을 두 번 뽑을 까닭이 없다 — 한 번 뽑아서 같이 들고 간다.
|
|
242
281
|
*/
|
|
243
282
|
const 아는것 = 이름표.get(첫?.id) ?? null;
|
|
283
|
+
/*
|
|
284
|
+
* 이름은 **한 메시지에 실린 것 전부**를 들고 간다.
|
|
285
|
+
*
|
|
286
|
+
* 아래 「마지막 할 일 목록은 안 접는다」 가 이 이름을 본다. 그런데
|
|
287
|
+
* 첫 것만 들고 가면, 모델이 한 턴에 `Read` 와 `TodoWrite` 를 같이 부른
|
|
288
|
+
* 판에서 — Anthropic 꼴은 그 결과 둘을 **한 메시지**에 싣는다 —
|
|
289
|
+
* 이름이 'Read' 로 잡혀 할 일 목록이 지킴 목록에서 빠진다. 그러면
|
|
290
|
+
* 그 자리가 접히면서 남은 항목이 어디에도 없어진다.
|
|
291
|
+
*
|
|
292
|
+
* 아래 머리말이 「접는 순간 남은 항목이 어디에도 없어진다」 고 적어 둔
|
|
293
|
+
* 바로 그 일이, 도구를 둘 같이 부른 판에서만 조용히 일어났다 (35차 리뷰).
|
|
294
|
+
*/
|
|
295
|
+
const 이름들 = 것들.map((x) => 이름표.get(x?.id)?.name ?? x?.name ?? '').filter(Boolean);
|
|
244
296
|
자리.push({
|
|
245
|
-
i, 글,
|
|
297
|
+
i, 글, 이름들,
|
|
246
298
|
이름: 아는것?.name ?? 첫?.name ?? '',
|
|
247
299
|
args: 아는것?.args ?? {},
|
|
248
300
|
});
|
|
@@ -262,7 +314,7 @@ export function foldToolResults(session, { keep = KEEP_RECENT, min = FOLD_MIN,
|
|
|
262
314
|
* 남겨 둘수록 해롭다. 지키는 것은 **마지막 하나**뿐이고, 목록 하나는
|
|
263
315
|
* 몇백 글자라 자리도 거의 안 먹는다.
|
|
264
316
|
*/
|
|
265
|
-
const 지킬할일 = 자리.filter((x) => x
|
|
317
|
+
const 지킬할일 = 자리.filter((x) => x.이름들.includes('TodoWrite')).at(-1)?.i ?? -1;
|
|
266
318
|
|
|
267
319
|
const 접을것 = 자리
|
|
268
320
|
.slice(0, Math.max(0, 자리.length - keep))
|
|
@@ -296,7 +348,31 @@ export function foldToolResults(session, { keep = KEEP_RECENT, min = FOLD_MIN,
|
|
|
296
348
|
* 100자도 안 된다). 그래서 이 수는 언제나 **새로 접을 것**의 수다.
|
|
297
349
|
*/
|
|
298
350
|
const 예상이득 = 접을것.reduce((a, x) => a + estimateTokens(x.글), 0);
|
|
299
|
-
|
|
351
|
+
/*
|
|
352
|
+
* ── 개수를 기다리는 것이 손해가 되는 판 ────────────────────────────────
|
|
353
|
+
*
|
|
354
|
+
* 위 개수 문턱은 「3천 토큰 아끼려고 앞머리를 깨지 말라」 는 말이다. 맞는
|
|
355
|
+
* 말인데, **아낄 것이 3만 토큰일 때도** 똑같이 기다렸다. 큰 파일 읽기 셋이
|
|
356
|
+
* 쌓인 판에서 접기를 미루면 그 다음 안전망은 80% 요약 압축이고, 그쪽은
|
|
357
|
+
* 대화를 **통째로 요약으로 갈아 치우는** 훨씬 비싼 일이다. 개수를 기다리다
|
|
358
|
+
* 더 큰 것을 치르는 셈이다.
|
|
359
|
+
*
|
|
360
|
+
* 그래서 기다림을 **값으로** 판단한다. 앞머리를 깨는 값은 **접은 뒤에**
|
|
361
|
+
* 그 자리부터 다시 보내야 하는 토큰이다 — 캐시에서 싸게 읽던 것을 정가로
|
|
362
|
+
* 다시 쓴다. 아끼는 것이 그보다 크면 지금 접는 것이 이득이라, 개수를 더
|
|
363
|
+
* 기다리지 않는다.
|
|
364
|
+
*
|
|
365
|
+
* 접기 전 양에서 아끼는 양을 뺀다. 접을 글 자체를 대가로 세면 안 된다 —
|
|
366
|
+
* 그건 접은 뒤에 사라지는 것이라, 세는 순간 늘 「기다려라」 가 나온다.
|
|
367
|
+
*/
|
|
368
|
+
const 깰자리 = 접을것[0]?.i ?? -1;
|
|
369
|
+
const 깨는값 = 깰자리 < 0 ? Infinity
|
|
370
|
+
: Math.max(0, ms.slice(깰자리).reduce((a, m) => a + estimateTokens(
|
|
371
|
+
도구결과인가(m) ? 결과들(m).map((x) => x.글).join('\n')
|
|
372
|
+
: (typeof m.content === 'string' ? m.content : JSON.stringify(m.content ?? '')),
|
|
373
|
+
), 0) - 예상이득);
|
|
374
|
+
const 기다릴까 = 접을것.length < keep && 예상이득 <= 깨는값;
|
|
375
|
+
if (예상이득 < 넘을것 || 기다릴까) {
|
|
300
376
|
return {
|
|
301
377
|
접은것: 0, 아낀토큰: 0, 접은것들: [], 미룸: true,
|
|
302
378
|
모인것: 예상이득, 모인개수: 접을것.length, 넘을것,
|
|
@@ -352,7 +428,18 @@ export function foldToolResults(session, { keep = KEEP_RECENT, min = FOLD_MIN,
|
|
|
352
428
|
*/
|
|
353
429
|
return {
|
|
354
430
|
접은것: 접을것.length, 아낀토큰: Math.max(0, 아낀토큰), 접은것들,
|
|
355
|
-
|
|
431
|
+
/*
|
|
432
|
+
* 화면에 「캐시를 다시 씁니다」 를 적을까.
|
|
433
|
+
*
|
|
434
|
+
* `캐시가도나()` 를 그대로 쓰고 있었다. 그 함수는 **결정**을 위해 모르는
|
|
435
|
+
* 쪽을 「있다」 로 기울인다(그 머리말) — 그래서 첫 턴에는, 캐시가 아예
|
|
436
|
+
* 없는 창구에서도 늘 참이었다. 그러면 화면은 「값과 지연이 한 번 튑니다」
|
|
437
|
+
* 라고 겁을 주는데 잃을 캐시가 애초에 없다.
|
|
438
|
+
*
|
|
439
|
+
* 결정은 기울여도 되지만 **화면은 아는 것만 적는다.** 캐시를 정말 읽어
|
|
440
|
+
* 본 적이 있을 때만 말한다 (35차 리뷰).
|
|
441
|
+
*/
|
|
442
|
+
캐시다시씀: (session?.usage?.cacheRead ?? 0) > 0,
|
|
356
443
|
};
|
|
357
444
|
}
|
|
358
445
|
|
|
@@ -525,7 +612,15 @@ export async function compact(session, { auto = false, signal = null, onBackoff
|
|
|
525
612
|
* 「앞에서 읽은 그대로입니다」 를 내밀면 모델은 대화에 없는 글을 가리키는
|
|
526
613
|
* 쪽지만 받는다. 통째로 다시 싣는 것보다 훨씬 나쁘다.
|
|
527
614
|
*/
|
|
528
|
-
|
|
615
|
+
/*
|
|
616
|
+
* 읽은 파일 수도 같이 내린다.
|
|
617
|
+
*
|
|
618
|
+
* 아래 성공한 길은 `filesRead.clear()` 도 하는데 이쪽은 안 했다. 그 표는
|
|
619
|
+
* `/context` 가 「도구 결과 N개」 로 세는 자리라(agent/session.js), 물러선
|
|
620
|
+
* 뒤에는 **대화에 없는 파일까지 세어 보여 줬다.** 사람이 자리를 왜 이만큼
|
|
621
|
+
* 쓰는지 그 화면으로 판단하는데, 거기 없는 것이 섞여 있으면 안 된다.
|
|
622
|
+
*/
|
|
623
|
+
if (folded > 0) { session.파일기억?.잊기(); session.filesRead?.clear?.(); }
|
|
529
624
|
const after = session.breakdown().used;
|
|
530
625
|
return {
|
|
531
626
|
ok: folded > 0, folded, before, after, fallback: true,
|
package/src/agent/evolve.js
CHANGED
|
@@ -298,10 +298,27 @@ export class 배움 {
|
|
|
298
298
|
return out.length > 1 ? out.join('\n') : null;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
/**
|
|
301
|
+
/**
|
|
302
|
+
* 사람이 지우라고 한 것. 파일째 비운다.
|
|
303
|
+
*
|
|
304
|
+
* ── `'전선'` 은 왜 따로 있나 ───────────────────────────────────────────
|
|
305
|
+
*
|
|
306
|
+
* 전선 모양은 **틀리게 배울 수 있는 유일한 것**이다 (backend/wire.js 의
|
|
307
|
+
* 배울전선: "잘못 배우는 것은 못 배우는 것보다 나쁘다"). 게이트웨이가
|
|
308
|
+
* 한동안 잘못 답했거나 우리가 문구를 잘못 읽으면, 멀쩡한 기능이 꺼진 채
|
|
309
|
+
* 디스크에 굳는다 — 그리고 그 창구에서는 영영 그대로다.
|
|
310
|
+
*
|
|
311
|
+
* 그런데 되돌릴 길이 **「쌓아 둔 것 전부 비우기」 하나**였다. 그러면 몇 주
|
|
312
|
+
* 걸려 쌓은 명령 겪음과 토큰 보정까지 같이 날아간다. 그 값이 아까워서
|
|
313
|
+
* 사람은 안 비우고, 안 비우니 꺼진 기능을 그냥 안고 쓴다.
|
|
314
|
+
*
|
|
315
|
+
* 그래서 전선만 지운다. 다음 요청에서 짐작으로 다시 시작하고, 서버가
|
|
316
|
+
* 여전히 거절하면 같은 것을 다시 배운다 — 잃을 것이 없다.
|
|
317
|
+
*/
|
|
302
318
|
지우기(어디 = '전부') {
|
|
303
319
|
if (어디 === '전부' || 어디 === '폴더') { this.폴더 = 빈것(); this.#저장폴더(); }
|
|
304
320
|
if (어디 === '전부' || 어디 === '모델') { this.집 = 빈것(); this.#저장집(); }
|
|
321
|
+
if (어디 === '전선') { this.집.전선 = {}; this.#저장집(); }
|
|
305
322
|
return this;
|
|
306
323
|
}
|
|
307
324
|
|
package/src/agent/models.js
CHANGED
|
@@ -36,6 +36,9 @@ import { load, activeProfile, resolveKey } from '../config.js';
|
|
|
36
36
|
import { isLocalHost, isOffline } from '../safety/network.js';
|
|
37
37
|
import { 잠잠기본, 무소식기본, 연결기본 } from '../backend/http.js';
|
|
38
38
|
import { 인증서설정 } from '../backend/clientcert.js';
|
|
39
|
+
import { 받기설정 } from '../safety/authcmd.js';
|
|
40
|
+
// 열쇠받기 명령을 정책이 못박아 뒀을 수 있다 — 받기설정 이 그 값을 같이 본다.
|
|
41
|
+
import { 정책읽기 } from '../safety/policy.js';
|
|
39
42
|
|
|
40
43
|
// 컨텍스트를 못 알아냈을 때 쓰는 값. repl.js 와 같은 값을 봐야 한다.
|
|
41
44
|
export const CTX_DEFAULT = 32768;
|
|
@@ -67,6 +70,25 @@ export function 연결만들기(prof, { ctx = null, maxTokens = null } = {}) {
|
|
|
67
70
|
tools: prof.tools ?? false,
|
|
68
71
|
json: prof.json ?? false,
|
|
69
72
|
think: prof.think ?? false,
|
|
73
|
+
/*
|
|
74
|
+
* ── 이 두 줄을 빼면 회사 프로필로는 물어볼 수가 없다 ────────────────
|
|
75
|
+
*
|
|
76
|
+
* conn 을 짓는 자리는 다섯이다 — repl.js · oneshot.js · acp/serve.js ·
|
|
77
|
+
* commands.js 의 연결적용, 그리고 여기. 앞의 넷은 이 둘을 넣는데 여기만
|
|
78
|
+
* 빠져 있었다. 이 함수를 쓰는 자리가 둘인데 **둘 다 남에게 넘기는
|
|
79
|
+
* 자리**다 — /consult 로 한 번 물어보기, Task 로 하위에 떼어 주기.
|
|
80
|
+
*
|
|
81
|
+
* 열쇠를 명령으로 받아 오는 프로필(SSO 토큰)은 prof.key 가 비어 있다.
|
|
82
|
+
* 그래서 열쇠받기 없이 지으면 Authorization 이 안 실려 401 이 나고,
|
|
83
|
+
* 열쇠를 다시 받는 길도 잠긴다(adapter.js 의 열쇠다시받을까 는
|
|
84
|
+
* conn.열쇠받기 를 본다). `/model 회사창구` 는 되는데 `/consult
|
|
85
|
+
* 회사창구` 만 안 되는, 까닭이 화면에 안 적히는 꼴이 된다.
|
|
86
|
+
*
|
|
87
|
+
* vision 도 같다 — 안 넣으면 그림을 볼 수 있는 모델에 하위 작업을
|
|
88
|
+
* 떼어 주면서 그림을 못 보는 것으로 친다.
|
|
89
|
+
*/
|
|
90
|
+
열쇠받기: 받기설정(prof, { 정책값: 정책읽기().값 }),
|
|
91
|
+
vision: prof.vision ?? false,
|
|
70
92
|
};
|
|
71
93
|
}
|
|
72
94
|
|
package/src/agent/modes.js
CHANGED
|
@@ -115,7 +115,7 @@ export const MODES = {
|
|
|
115
115
|
id: 'auto',
|
|
116
116
|
hintEn: "picks the right mode for what you ask",
|
|
117
117
|
sayEn: "You are in **Auto** mode. What comes next is not fixed.\n\n- Work out what kind of job this is first, then do it that way.\n If it is an edit, read before editing. If it is a diagnosis, confirm before concluding.\n If it is an explanation, back it with the files.\n- For a large job, break it into steps with TodoWrite and **finish everything you wrote down.**\n Do not write the list and then ask. If there are several chunks, hand them off with Task —\n a subtask runs in its own window, so yours does not fill up.\n- For code you did not write, get the shape with Outline. Use Grep to gather every place it\n touches, then Read **only around the lines you found** (offset/limit). Do not read whole\n files - pour it all in at once and the earlier turns fold away, and you answer having\n forgotten them.\n- Fire unrelated reads **together in one message.** Read tools run side by side; one call at\n a time adds that many round trips, and that is the waiting the person feels.\n- Making several files: one Write call (files array). Do not call it once per file.\n Several places to edit: one Edit call the same way (edits array).\n- Commands that never end (dev servers, watch) need background: true on Bash. Called plainly\n they die on timeout. After starting one, read its output with Jobs, and end it with Jobs when done.\n- Verify what can be verified — call Verify. Do not call something done that you did not check.\n- Do what the job needs. Do not start work the job did not ask for.",
|
|
118
|
-
say짧게En: "**Auto** mode. What comes next is not fixed.\n- Work out what kind of job this is, then do it that way.\n- Large job: break it up with TodoWrite and **finish it all.** Several chunks: hand off with Task.\n- Code you did not write: Outline -> Grep -> Read **only around what you found** (offset/limit).\n- Fire unrelated reads together in one message - they run side by side.\n- Several files: one Write (files array). Several edits: one Edit (edits array).\n- Commands that never end (dev server, watch): background: true on Bash. Read with Jobs, end with Jobs.\n- Verify before you finish. Do not call something done that you did not check.",
|
|
118
|
+
say짧게En: "**Auto** mode. What comes next is not fixed.\n- Work out what kind of job this is, then do it that way.\n- Large job: break it up with TodoWrite and **finish it all.** Several chunks: hand off with Task.\n- Code you did not write: Outline -> Grep -> Read **only around what you found** (offset/limit).\n- Fire unrelated reads together in one message - they run side by side.\n- Several files: one Write (files array). Several edits: one Edit (edits array).\n- Commands that never end (dev server, watch): background: true on Bash. Read with Jobs, end with Jobs.\n- Verify before you finish. Do not call something done that you did not check.\n- Do what the job needs. Do not start work the job did not ask for.",
|
|
119
119
|
name: '종합',
|
|
120
120
|
en: 'Auto',
|
|
121
121
|
glyph: '◎',
|
|
@@ -132,6 +132,9 @@ export const MODES = {
|
|
|
132
132
|
'- 파일 여러 개는 Write 한 번에 (files 배열), 고칠 자리 여럿은 Edit 한 번에 (edits 배열).',
|
|
133
133
|
'- 끝내지 않는 명령(dev 서버·watch)은 Bash 에 background: true. Jobs 로 읽고 끝낸다.',
|
|
134
134
|
'- 끝내기 전에 Verify. 확인 못 한 것을 됐다고 하지 마라.',
|
|
135
|
+
// 「빠진 규칙은 없다」 는 위 주석대로 되려면 이 줄이 있어야 한다.
|
|
136
|
+
// 시키지 않은 일을 벌이는 것은 작은 창에서 더 비싸다.
|
|
137
|
+
'- 시키는 일을 해라. 시키지 않은 일을 벌이지 마라.',
|
|
135
138
|
].join('\n'),
|
|
136
139
|
say: [
|
|
137
140
|
'지금은 **종합** 모드다. 무슨 일이 올지 정해져 있지 않다.',
|
|
@@ -206,6 +209,7 @@ export const MODES = {
|
|
|
206
209
|
id: 'architect',
|
|
207
210
|
hintEn: "shapes the structure · touches no files",
|
|
208
211
|
sayEn: "This is **design**. You have not been given the tools that change files.\n\nRead first. Designing without knowing the current structure is imagining, not designing.\n - Start with Outline for the shape of the folder. Narrow with Glob/Grep, then Read\n **only around the lines you found** (offset/limit). Read whole files and the earlier\n ones fold away after a handful - then you have not seen the structure, only the last file\n - Fire unrelated reads together in one message - they run side by side\n - Work out what depends on what, and in which direction\n\nThen answer in this order.\n 1. Current structure — files, their roles, where the boundaries are (point with path:line)\n 2. What is wrong — why the current shape does not hold\n 3. Two or three options — what each gains, what each costs, how much work it is\n 4. One recommendation with the reason, and why you dropped the others\n 5. The files this affects\n\nFind the answer inside the conventions this code already uses. Bringing in a new framework is\nthe last resort, and if you go there, first say why the existing conventions cannot do it.",
|
|
212
|
+
say짧게En: "**Design** mode. You have no tools that change files.\n- Read first — designing without the current structure is imagining, not designing.\n Outline -> Glob/Grep -> Read **only around what you found** (offset/limit). Never whole files.\n Fire unrelated reads together in one message. Work out what depends on what, and which way.\n- Answer in this order: 1) current structure (path:line) 2) what is wrong 3) two or three options\n (what each gains, costs, how much work) 4) one recommendation with the reason, and why you dropped\n the others 5) the files this affects.\n- Find it inside the conventions this code already uses. A new framework is the last resort — if you\n go there, first say why the existing conventions cannot do it.",
|
|
209
213
|
name: '설계',
|
|
210
214
|
en: 'Architect',
|
|
211
215
|
glyph: '◈',
|
|
@@ -213,6 +217,16 @@ export const MODES = {
|
|
|
213
217
|
tools: [...읽기, ...계획],
|
|
214
218
|
effort: 'deep',
|
|
215
219
|
think: 'high',
|
|
220
|
+
say짧게: [
|
|
221
|
+
'**설계** 모드다. 파일을 바꾸는 도구는 없다.',
|
|
222
|
+
'- 먼저 읽어라 — 구조를 모르면 설계가 아니라 상상이다.',
|
|
223
|
+
' Outline → Glob/Grep → **짚은 자리 앞뒤만** Read (offset·limit). 통째로 읽지 마라.',
|
|
224
|
+
' 상관없는 읽기는 한 번에 같이 부른다. 무엇이 무엇에 기대는지(의존 방향)를 본다.',
|
|
225
|
+
'- 이 차례로 답한다: 1) 지금 구조(경로:줄) 2) 무엇이 문제인가 3) 선택지 2~3개(얻는 것·잃는',
|
|
226
|
+
' 것·드는 손) 4) 추천 하나와 버린 이유 5) 영향받는 파일.',
|
|
227
|
+
'- 이 코드가 이미 쓰는 관례 안에서 찾아라. 새 틀은 마지막 수단이고, 그때는 기존 관례로',
|
|
228
|
+
' 왜 안 되는지 먼저 밝혀라.',
|
|
229
|
+
].join('\n'),
|
|
216
230
|
say: [
|
|
217
231
|
"지금 하는 일은 **설계**다. 파일을 바꾸는 도구는 주어지지 않았다.",
|
|
218
232
|
"",
|
|
@@ -238,7 +252,11 @@ export const MODES = {
|
|
|
238
252
|
ask: {
|
|
239
253
|
id: 'ask',
|
|
240
254
|
hintEn: "explains only · changes nothing",
|
|
241
|
-
|
|
255
|
+
// 영어 쪽이 「Keep it short」 로 남아 있었다. 한국어는 그 말 때문에 여러
|
|
256
|
+
// 가지를 물으면 몇 가지가 빠지던 것을 고쳐 「물음의 크기에 맞춰라」 로
|
|
257
|
+
// 바꿨는데, 영어는 안 바꿔서 **영어로 쓰는 사람에게만 옛 결함이 남아
|
|
258
|
+
// 있었다.** 읽는 절차도 빠져 있어 읽어야 나오는 답을 짐작으로 냈다.
|
|
259
|
+
sayEn: "This is **explanation**. You change nothing.\n\n- Back it with the files. Give the path and line number (src/a.js:42).\n- If you do not know, say so. Do not invent a plausible answer.\n- **Match the size of the answer to the size of the question.** One line asked, one line back;\n several things asked, every one of them answered. Short is not the goal.\n- If it takes reading to answer, read — Outline -> Grep -> Read **only around what you found**.\n Fire unrelated reads together in one message.\n- Do not propose fixes unless asked to fix something. Do not start work you were not asked for.",
|
|
242
260
|
name: '묻기',
|
|
243
261
|
en: 'Ask',
|
|
244
262
|
glyph: '◇',
|
|
@@ -364,6 +382,7 @@ export const MODES = {
|
|
|
364
382
|
id: 'debug',
|
|
365
383
|
hintEn: "finds the cause",
|
|
366
384
|
sayEn: "This is **finding the cause**. Do not fix by guessing.\n\nFollow this order.\n 1. Restate the symptom in one sentence — what happens when you do what.\n 2. Get a reproduction. If there is none, build one. Without it you cannot tell whether you fixed it.\n 3. Form two or three hypotheses. For each, write down what you should see if it is true.\n 4. Check them one at a time, for real — read the logs, run something small, print the value.\n Only what you checked is fact. What you did not check is still a hypothesis.\n If several hypotheses turn on reading alone, read for all of them in one message.\n 5. When you name the cause, bring the evidence. A cause that starts with \"probably\" is not a cause.\n 6. After fixing, run the reproduction from step 2 again. If it is not fixed, go back to step 3.\n\nDo not change several places at once. You will not know which one fixed it.",
|
|
385
|
+
say짧게En: "**Finding the cause.** Do not fix by guessing.\n1. Restate the symptom in one sentence — what happens when you do what.\n2. Get a reproduction. If there is none, build one — without it you cannot tell whether you fixed it.\n3. Form two or three hypotheses. For each, write what you should see if it is true.\n4. Check them one at a time, for real. Only what you checked is fact.\n If several turn on reading alone, read for all of them in one message.\n5. Name the cause with the evidence. A cause that starts with \"probably\" is not a cause.\n6. After fixing, run the reproduction from step 2 again. Not fixed — back to step 3.\nDo not change several places at once. You will not know which one fixed it.",
|
|
367
386
|
name: '디버그',
|
|
368
387
|
en: 'Debug',
|
|
369
388
|
glyph: '◉',
|
|
@@ -371,6 +390,17 @@ export const MODES = {
|
|
|
371
390
|
tools: [...읽기, ...계획, ...쓰기, ...확인, ...쪼개기],
|
|
372
391
|
effort: 'deep',
|
|
373
392
|
think: 'high',
|
|
393
|
+
say짧게: [
|
|
394
|
+
'**원인 찾기** 모드다. 짐작으로 고치지 마라.',
|
|
395
|
+
'1. 증상을 한 문장으로 다시 적는다 — 무엇을 했을 때 무엇이 일어나는가.',
|
|
396
|
+
'2. 재현 방법을 확보한다. 없으면 만든다 — 재현 못 하면 고쳤는지도 알 수 없다.',
|
|
397
|
+
'3. 가설 2~3개. 각각 「맞다면 무엇이 보여야 하는가」 를 같이 적는다.',
|
|
398
|
+
'4. 하나씩 실제로 확인한다. 확인한 것만 사실이다.',
|
|
399
|
+
' 읽기로 가릴 가설이 여럿이면 한 번에 같이 읽는다 — 나란히 돈다.',
|
|
400
|
+
'5. 원인에는 증거를 같이 댄다. 「아마」 로 시작하는 원인은 원인이 아니다.',
|
|
401
|
+
'6. 고친 뒤 2번 절차로 다시 확인한다. 안 고쳐졌으면 3번으로 돌아간다.',
|
|
402
|
+
'한 번에 여러 곳을 고치지 마라 — 무엇이 고쳤는지 알 수 없게 된다.',
|
|
403
|
+
].join('\n'),
|
|
374
404
|
say: [
|
|
375
405
|
"지금 하는 일은 **원인 찾기**다. 짐작으로 고치지 마라.",
|
|
376
406
|
"",
|
|
@@ -391,7 +421,8 @@ export const MODES = {
|
|
|
391
421
|
plan: {
|
|
392
422
|
id: 'plan',
|
|
393
423
|
hintEn: "plan first · run it after approval",
|
|
394
|
-
sayEn: "This is **planning**. You have not been given the tools that change files.\nDo not try to edit code. Produce a plan and stop.\n\nConfirm first — a plan built without knowing the current state is a wish, not a plan.\n Start with Outline for the shape, narrow with Glob/Grep, then Read **only around the lines\n you found** (offset/limit). Fire unrelated reads together in one message.\n\nThen write it in this order.\n 1. Goal — what does \"done\" look like (as a sentence you can check)\n 2. Current state — the files involved and what they do now (point with path:line)\n 3. What changes — per file, what and why\n 4. Order — step by step. Each step small enough to check on its own\n 5. Risks — what could break, and how to get back if it does\n 6. How to check — what do you run to know it worked\n\nWrite the steps into TodoWrite as well. After approval you continue straight from them.\n The number of steps is not fixed — match it to the size of the job. Do not force it to three.\n A small job ends in two or three; a large one lists all ten or more.\nIf something is unknown, do not invent it — write \"this needs to be confirmed\".\n\nEnd with \"Shall I go ahead with this?\". Once approved
|
|
424
|
+
sayEn: "This is **planning**. You have not been given the tools that change files.\nDo not try to edit code. Produce a plan and stop.\n\nConfirm first — a plan built without knowing the current state is a wish, not a plan.\n Start with Outline for the shape, narrow with Glob/Grep, then Read **only around the lines\n you found** (offset/limit). Fire unrelated reads together in one message.\n\nThen write it in this order.\n 1. Goal — what does \"done\" look like (as a sentence you can check)\n 2. Current state — the files involved and what they do now (point with path:line)\n 3. What changes — per file, what and why\n 4. Order — step by step. Each step small enough to check on its own\n 5. Risks — what could break, and how to get back if it does\n 6. How to check — what do you run to know it worked\n\nWrite the steps into TodoWrite as well. After approval you continue straight from them.\n The number of steps is not fixed — match it to the size of the job. Do not force it to three.\n A small job ends in two or three; a large one lists all ten or more.\nIf something is unknown, do not invent it — write \"this needs to be confirmed\".\n\nEnd with \"Shall I go ahead with this?\".\n You cannot make the edits in this mode — you have no tools for it. Once approved,\n **tell the person to type `/code`** — you have no way to switch modes yourself.",
|
|
425
|
+
say짧게En: "**Planning.** You have no tools that change files. Produce a plan and stop.\n- Confirm first — a plan built without the current state is a wish, not a plan.\n Outline -> Glob/Grep -> Read **only around what you found** (offset/limit). Unrelated reads together.\n- Write it in this order: 1) goal (a sentence you can check) 2) current state (path:line)\n 3) what changes, per file, and why 4) order — each step small enough to check on its own\n 5) risks and how to get back 6) how to check it worked.\n- Put the steps in TodoWrite too. Match the number to the size of the job — do not force it to three.\n- If something is unknown, do not invent it — write \"this needs to be confirmed\".\n- End with \"Shall I go ahead with this?\". You have no tools to make the edits, so once approved,\n **tell the person to type `/code`** — you cannot switch modes yourself.",
|
|
395
426
|
name: '계획',
|
|
396
427
|
en: 'Plan',
|
|
397
428
|
glyph: '☰',
|
|
@@ -399,6 +430,17 @@ export const MODES = {
|
|
|
399
430
|
tools: [...읽기, ...계획],
|
|
400
431
|
effort: 'deep',
|
|
401
432
|
think: 'high',
|
|
433
|
+
say짧게: [
|
|
434
|
+
'**계획** 모드다. 파일을 바꾸는 도구는 없다. 계획을 내고 멈춘다.',
|
|
435
|
+
'- 먼저 확인하라 — 지금 상태를 모르면 계획이 아니라 희망이다.',
|
|
436
|
+
' Outline → Glob/Grep → **짚은 자리 앞뒤만** Read (offset·limit). 상관없는 읽기는 한 번에.',
|
|
437
|
+
'- 이 차례로 적어라: 1) 목표(확인할 수 있는 문장) 2) 지금 상태(경로:줄) 3) 바꿀 것(파일별로',
|
|
438
|
+
' 무엇을 왜) 4) 순서(각 단계는 따로 확인할 수 있는 크기로) 5) 위험과 되돌리는 길 6) 확인 방법.',
|
|
439
|
+
'- 단계는 TodoWrite 로도 적어라. 수는 일의 크기에 맞춘다 — 세 개로 맞추지 마라.',
|
|
440
|
+
'- 모르는 것은 지어내지 말고 「이건 확인이 필요하다」 고 적어라.',
|
|
441
|
+
'- 「이대로 진행할까요?」 로 끝내라. 고치는 도구가 없으니, 승인을 받으면',
|
|
442
|
+
' **사람에게 `/code` 를 치라고 적어 줘라** — 네가 모드를 바꿀 길은 없다.',
|
|
443
|
+
].join('\n'),
|
|
402
444
|
say: [
|
|
403
445
|
"지금 하는 일은 **계획 세우기**다. 파일을 바꾸는 도구는 주어지지 않았다.",
|
|
404
446
|
"코드를 고치려 들지 마라. 계획을 내고 멈춘다.",
|
|
@@ -420,7 +462,12 @@ export const MODES = {
|
|
|
420
462
|
" 작은 일이면 두세 개로 끝내고, 큰 일이면 열 개가 넘어도 그대로 다 적어라.",
|
|
421
463
|
"모르는 것이 있으면 지어내지 말고 \"이건 확인이 필요하다\" 고 적어라.",
|
|
422
464
|
"",
|
|
423
|
-
|
|
465
|
+
// 모드를 바꾸는 도구는 모델에게 없다. `/code` 는 사람이 치는 명령이다.
|
|
466
|
+
// 「바꿔 실행한다」 고 적어 두면, 승인을 받은 모델이 고칠 도구도 없이
|
|
467
|
+
// 고치려 들다 아무것도 못 하고 멈춘다 — 사람은 기다리고만 있게 된다.
|
|
468
|
+
"마지막에 \"이대로 진행할까요?\" 로 끝내라.",
|
|
469
|
+
" 실제로 고치는 것은 이 모드에서 안 된다(고치는 도구가 없다). 승인을 받으면",
|
|
470
|
+
" **사람에게 `/code` 를 치라고 적어 줘라** — 네가 모드를 바꿀 길은 없다.",
|
|
424
471
|
].join('\n'),
|
|
425
472
|
},
|
|
426
473
|
|
|
@@ -473,7 +520,16 @@ export const DEFAULT = 'auto';
|
|
|
473
520
|
export function normalize(v) {
|
|
474
521
|
const s = String(v ?? '').trim().toLowerCase();
|
|
475
522
|
if (!s) return null;
|
|
476
|
-
|
|
523
|
+
/*
|
|
524
|
+
* 열쇠가 **제 것인지**를 본다.
|
|
525
|
+
*
|
|
526
|
+
* 그냥 `MODES[s]` 로 보면 `constructor`·`__proto__` 같은 이름에 물려받은
|
|
527
|
+
* 것이 잡혀 참이 된다. 그러면 그 이름이 모드로 통과하고, `get()` 이
|
|
528
|
+
* 돌려준 것에는 `tools` 가 없어 **터진다** — 하필 터지는 자리가
|
|
529
|
+
* `canWrite()` 이고, 그것은 하위 작업이 부모보다 셀 수 없게 막는
|
|
530
|
+
* 자리(tools/task.js 의 하위모드)다. 아래 별명 표도 같은 이유로 같이 막는다.
|
|
531
|
+
*/
|
|
532
|
+
if (Object.hasOwn(MODES, s)) return s;
|
|
477
533
|
const 별명 = {
|
|
478
534
|
'종합': 'auto', '자동': 'auto', '기본': 'auto', 'auto': 'auto', '맡김': 'auto',
|
|
479
535
|
'코드': 'code', 'c': 'code',
|
|
@@ -488,7 +544,7 @@ export function normalize(v) {
|
|
|
488
544
|
'inspect': 'inspect', 'audit': 'inspect', 'review': 'inspect', 'i': 'inspect',
|
|
489
545
|
'총괄': 'orchestrator', '오케': 'orchestrator', 'orch': 'orchestrator', 'o': 'orchestrator',
|
|
490
546
|
};
|
|
491
|
-
return 별명[s]
|
|
547
|
+
return Object.hasOwn(별명, s) ? 별명[s] : null;
|
|
492
548
|
}
|
|
493
549
|
|
|
494
550
|
export function get(id) {
|
|
@@ -502,7 +558,11 @@ export function get(id) {
|
|
|
502
558
|
* 규칙(session.js)이 줄어드는 자리와 같은 경계다. 셋이 같이 움직여야
|
|
503
559
|
* '작은 창에서는 고정 몫을 줄인다' 가 흩어진 세 결정이 아니라 한 결정이 된다.
|
|
504
560
|
*
|
|
505
|
-
* 짧은 판이 없는
|
|
561
|
+
* 짧은 판이 없는 것은 **묻기 하나**다(340자 — 원래 짧다). 그냥 그대로 쓴다.
|
|
562
|
+
*
|
|
563
|
+
* 앞서는 설계·디버그·계획에도 짧은 판이 없었다. 「원래 짧은 것들」 이라고
|
|
564
|
+
* 적어 두었지만 계획은 761자였다 — 작은 창에서 줄이자고 만든 자리가 정작 큰
|
|
565
|
+
* 모드에서 안 줄었다. 그래서 지금은 검사가 그 짝을 지킨다(test/modes.test.js).
|
|
506
566
|
*/
|
|
507
567
|
export function 말(id, ctx) {
|
|
508
568
|
const m = get(id);
|
package/src/agent/session.js
CHANGED
|
@@ -899,8 +899,18 @@ export class Session {
|
|
|
899
899
|
{ label: 옮긴말('ctx.toolResults', { n: this.filesRead.size }), n: files },
|
|
900
900
|
];
|
|
901
901
|
const used = rows.reduce((a, r) => a + r.n, 0);
|
|
902
|
+
/*
|
|
903
|
+
* 창 크기를 **알아낸 것인지**도 같이 내놓는다.
|
|
904
|
+
*
|
|
905
|
+
* 안 알려 주는 창구에서는 32k 로 둔다(그래야 무엇이든 돌아간다). 그런데
|
|
906
|
+
* 화면이 그 값을 잰 것과 똑같은 낯으로 `5.6k/32k · 17%` 라고 적으면,
|
|
907
|
+
* 실제 창이 8k 인 자리에서 답이 잘리는 동안 게이지는 초록이다. 같은
|
|
908
|
+
* 상태줄이 모델 급은 짐작일 때 `◈ 보통?` 으로 흐리게 적는다 — 창 크기도
|
|
909
|
+
* 같은 규칙을 따라야 한다(ui/status.js).
|
|
910
|
+
*/
|
|
911
|
+
const 잰것 = this.conn.ctx != null;
|
|
902
912
|
const total = this.conn.ctx ?? 32768;
|
|
903
|
-
return { rows, used, total, left: Math.max(0, total - used) };
|
|
913
|
+
return { rows, used, total, 총잰것: 잰것, left: Math.max(0, total - used) };
|
|
904
914
|
}
|
|
905
915
|
|
|
906
916
|
/**
|
|
@@ -918,6 +928,7 @@ export class Session {
|
|
|
918
928
|
rows,
|
|
919
929
|
used,
|
|
920
930
|
total: 날것.total,
|
|
931
|
+
총잰것: 날것.총잰것,
|
|
921
932
|
left: Math.max(0, 날것.total - used),
|
|
922
933
|
보정: this.보정,
|
|
923
934
|
보정잰것: this.보정잰것,
|