deel-local-cli 2.0.3 → 2.1.0
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 +26 -7
- package/README.md +27 -7
- package/bin/deel.js +34 -1
- package/package.json +1 -1
- package/src/acp/serve.js +34 -5
- package/src/agent/donecheck.js +97 -0
- package/src/agent/loop.js +174 -11
- package/src/agent/modes.js +3 -0
- package/src/agent/session.js +15 -0
- package/src/commands.js +20 -0
- package/src/completion.js +3 -0
- package/src/doctor.js +11 -1
- package/src/eval/run.js +432 -0
- package/src/eval/starter.js +95 -0
- package/src/i18n/en.js +20 -0
- package/src/i18n/ja.js +20 -0
- package/src/i18n/ko.js +20 -0
- package/src/i18n/zh.js +20 -0
- package/src/oneshot.js +63 -3
- package/src/repl.js +85 -11
- package/src/safety/policy.js +38 -0
- package/src/tools/index.js +101 -9
- package/src/ui/approve.js +57 -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)
|
|
@@ -127,6 +127,7 @@
|
|
|
127
127
|
| [늘려 쓰기](docs/ko/extend.md) | 스킬 · 플러그인 · MCP · 에이전트 · 훅 · ACP |
|
|
128
128
|
| [속도와 씀씀이](docs/ko/tuning.md) | 단계별 추론 강도 · 프리픽스 캐시 · 컨텍스트 길이 |
|
|
129
129
|
| [안전망과 사내 반입](docs/ko/safety.md) | 되돌리기 · 작업 범위 · 감사기록 · 심사 서류 |
|
|
130
|
+
| [사내 배포 안내](docs/ko/company.md) | 처음 쓰는 사람 · 관리 정책 한 장 · 어디로 나가나 · 막는 것의 한계 |
|
|
130
131
|
| [설정](docs/ko/config.md) · [개발](docs/ko/develop.md) | 환경변수 · 실행 옵션 · 검사 돌리기 · 폴더 구조 |
|
|
131
132
|
| [릴리스 노트](docs/ko/releases.md) | [1.16.x](docs/ko/releases/1.x.md#116x) · [1.15.x](docs/ko/releases/1.x.md#115x) · [1.14.x](docs/ko/releases/1.x.md#114x) · [1.13.x](docs/ko/releases/1.x.md#113x) · [1.12.x](docs/ko/releases/1.x.md#112x) · [1.10.x](docs/ko/releases/1.x.md#110x) · [1.9.x](docs/ko/releases/1.x.md#19x) · [그 앞](docs/ko/releases.md) |
|
|
132
133
|
|
|
@@ -322,7 +323,7 @@ deel --offline
|
|
|
322
323
|
무엇이 어디로 갈 수 있는지는 켤 때 화면 맨 위에 늘 적혀 있습니다.
|
|
323
324
|
|
|
324
325
|
```
|
|
325
|
-
deel 2.0
|
|
326
|
+
deel 2.1.0 ⌂ 이 안
|
|
326
327
|
보냄 이 컴퓨터 안 127.0.0.1:11434 ← 여기 말고는 어디로도 안 갑니다
|
|
327
328
|
```
|
|
328
329
|
|
|
@@ -1140,9 +1141,26 @@ AWS 키 · Google 키 · JWT · 주소에 박힌 열쇠 · `Authorization` 계
|
|
|
1140
1141
|
|
|
1141
1142
|
| 모드 | 언제 물어보나 |
|
|
1142
1143
|
|---|---|
|
|
1143
|
-
| `auto` (기본) | 안
|
|
1144
|
+
| `auto` (기본) | 안 물어봄 — **맡기는 모드**. 되돌리기가 안전망. 모델이 되묻거나 계획부터 내도 60초 안에 답이 없으면 알아서 진행(`askWait`) |
|
|
1144
1145
|
| `confirm` | 되돌릴 수 없는 명령만 |
|
|
1145
|
-
| `strict` | 파일 변경·명령
|
|
1146
|
+
| `strict` | 파일 변경·명령 전부. **바뀔 내용(diff)을 먼저 보여 주고** 묻습니다 |
|
|
1147
|
+
|
|
1148
|
+
회사에서 「바꾸기 전에 늘 사람이 본다」 를 정해야 하면 관리 정책에 `"approval": "strict"` 한 줄을
|
|
1149
|
+
적습니다. 그러면 `/mode` · Shift+Tab · `--yes` · 「앞으로 묻지 않기」 어느 것으로도 그 아래로 못
|
|
1150
|
+
내립니다 — [사내 배포 안내](docs/ko/company.md).
|
|
1151
|
+
|
|
1152
|
+
**「다 됐다」 는 검사가 정합니다.** 설정에 `"check": "npm test"` 를 적거나 `deel run --check` 로 주면,
|
|
1153
|
+
모델이 무엇을 바꾸고 끝내려 할 때 deel 이 그 검사를 **직접** 돌립니다. 실패하면 출력을 돌려주고 이어서
|
|
1154
|
+
고치게 하고(기본 3판), 끝내 실패면 실패로 끝냅니다 — `deel run` 은 종료코드 8. 검사 명령도 모델의
|
|
1155
|
+
명령과 같은 관문(규칙·훅·승인·위험 명령 차단)을 지납니다 — [완료 검사](docs/ko/company.md#완료-검사).
|
|
1156
|
+
|
|
1157
|
+
**얼마나 해내나는 골든셋으로 잽니다.** `deel eval --init` 이 예제 과제 다섯을 만들고, `deel eval` 이
|
|
1158
|
+
과제마다 임시 폴더에서 deel 을 돌린 뒤 **모델이 못 본** 정답 검사로 채점합니다. 결과를 남겨 두고
|
|
1159
|
+
지난번보다 나빠진 과제를 짚습니다 — [골든셋](docs/ko/company.md#골든셋).
|
|
1160
|
+
|
|
1161
|
+
위험 명령 차단은 **금지 목록 방식**입니다. 목록에 없는 꼴은 새어 나갈 수 있고, 실제로 그렇게
|
|
1162
|
+
찾아 막아 온 기록이 릴리스 노트에 있습니다. 그래서 하나에 기대지 않고 겹쳐 씁니다 — 작업 범위,
|
|
1163
|
+
정책 금지 규칙, 승인, 되돌리기, 감사 로그.
|
|
1146
1164
|
|
|
1147
1165
|
되돌리기 이력은 파일 내용을 통째로 담기 때문에 큰 파일을 여러 번 고치면 금방 커집니다.
|
|
1148
1166
|
32MB 를 넘으면 **최근 50턴만 남기고** 오래된 것을 버립니다. 방금 한 일은 언제나
|
|
@@ -1246,7 +1264,7 @@ deel stats # 이 폴더에서 실제로 무엇을 했나 (.de
|
|
|
1246
1264
|
## 개발
|
|
1247
1265
|
|
|
1248
1266
|
```bash
|
|
1249
|
-
npm test 전체 검증 (14,
|
|
1267
|
+
npm test 전체 검증 (14,322항목 — 몇몇은 터미널에 따라 갈립니다)
|
|
1250
1268
|
npm run coverage 검사가 소스의 어디를 밟았는지
|
|
1251
1269
|
npm run verify 반입·통신 검증만
|
|
1252
1270
|
npm run bench 편집 성공률 측정
|
|
@@ -1303,7 +1321,7 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
|
|
|
1303
1321
|
| `exitcode` · `doorparity` | 7 · 19 | 화면에 적은 종료코드 표가 진짜인가 · 문 **네 개**가 같은 것을 주는가 |
|
|
1304
1322
|
| `no-bundle` | 28 | 배포 묶음에 남의 것이 안 섞였는가 · 검사 파일 위생 |
|
|
1305
1323
|
| `edit-bench` | 15건 | 편집 성공률 |
|
|
1306
|
-
| `mutate` | 어긋 1,
|
|
1324
|
+
| `mutate` | 어긋 1,785개 | **검사가 정말 지키는가** — 지켜야 할 줄을 일부러 어긋내고 빨개지는지 본다 |
|
|
1307
1325
|
|
|
1308
1326
|
> **자세히** — 어디를 밟았는지 · 폴더 구조
|
|
1309
1327
|
>
|
|
@@ -1315,7 +1333,8 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
|
|
|
1315
1333
|
|
|
1316
1334
|
| 판 | 무엇이 바뀌었나 |
|
|
1317
1335
|
|---|---|
|
|
1318
|
-
| **[2.0
|
|
1336
|
+
| **[2.1.0](docs/ko/releases/2.1.md#210)** | 맡기면 멈추지 않고, 묻게 하면 diff 를 보여 줍니다 — 「다 됐다」 는 검사가 정합니다(`check`) · 얼마나 해내나는 골든셋으로 잽니다(`deel eval`) · 관리 정책 `"approval"` 로 회사가 승인 바닥을 겁니다 |
|
|
1337
|
+
| [2.0.3](docs/ko/releases/2.0.md#203) | 2.0.2 는 npm 에 못 나갔습니다 — 이제 GitHub 릴리스는 npm 에 올라간 뒤에만 생깁니다 |
|
|
1319
1338
|
| [2.0.2](docs/ko/releases/2.0.md#202) | 2.0.1 까지 넘겨 둔 것을 다 잡았습니다 — `deel run` 에만 없던 MCP 도구, 끝난 명령의 번호로 가던 `taskkill`, 한 번 넘어지면 세션 내내 못 쓰던 MCP 서버 |
|
|
1320
1339
|
| [2.0.1](docs/ko/releases/2.0.md#201) | 울타리 하나가 안 걸렸습니다 — `sudo -u root bash` 가 `curl … \| bash` 막이를 지나갔습니다 — 그리고 관문 넷이 빨개질 수 없었습니다 |
|
|
1321
1340
|
| [2.0.0](docs/ko/releases/2.0.md#200) | 있다고 적힌 울타리마다 실제로 걸리는지 다시 봤습니다 — 바뀐 것 전부·어디를 어떻게 고쳤나·어떻게 찾았나(CHA) |
|
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)
|
|
@@ -162,6 +162,7 @@ This page is the **summary**. Each section links to the detail behind it.
|
|
|
162
162
|
| [Extending](docs/en/extend.md) | Skills · plugins · MCP · subagents · hooks · ACP |
|
|
163
163
|
| [Speed and spend](docs/en/tuning.md) | Per-stage effort · the prefix cache · context length |
|
|
164
164
|
| [Safety and corporate review](docs/en/safety.md) | Undo · working scope · audit log · the review package |
|
|
165
|
+
| [Rolling it out at work](docs/en/company.md) | First-time users · one policy file · where requests go · what the guards cannot do |
|
|
165
166
|
| [Configuration](docs/en/config.md) · [Development](docs/en/develop.md) | Env vars · run flags · running the tests · folder layout |
|
|
166
167
|
| [Release notes](docs/en/releases.md) | [1.16.x](docs/en/releases/1.x.md#116x) · [1.15.x](docs/en/releases/1.x.md#115x) · [1.14.x](docs/en/releases/1.x.md#114x) · [1.13.x](docs/en/releases/1.x.md#113x) · [1.12.x](docs/en/releases/1.x.md#112x) · [1.10.x](docs/en/releases/1.x.md#110x) · [1.9.x](docs/en/releases/1.x.md#19x) · [older](docs/en/releases.md) |
|
|
167
168
|
|
|
@@ -395,7 +396,7 @@ deel --offline
|
|
|
395
396
|
The destination is printed at the top of every session:
|
|
396
397
|
|
|
397
398
|
```
|
|
398
|
-
deel 2.0
|
|
399
|
+
deel 2.1.0 ⌂ inside
|
|
399
400
|
Sends to this machine 127.0.0.1:11434 ← nowhere else
|
|
400
401
|
```
|
|
401
402
|
|
|
@@ -1242,9 +1243,27 @@ does not ask.
|
|
|
1242
1243
|
|
|
1243
1244
|
| Mode | Asks when |
|
|
1244
1245
|
|---|---|
|
|
1245
|
-
| `auto` (default) | Never — undo is the safety net |
|
|
1246
|
+
| `auto` (default) | Never — **the hand-off mode**; undo is the safety net. If the model asks a question or offers a plan first, it carries on by itself after 60 seconds with no answer (`askWait`) |
|
|
1246
1247
|
| `confirm` | Irreversible commands only |
|
|
1247
|
-
| `strict` | All file changes and commands |
|
|
1248
|
+
| `strict` | All file changes and commands. **Shows the change (a diff) first**, then asks |
|
|
1249
|
+
|
|
1250
|
+
If your organisation needs "a person sees every change before it lands", put `"approval": "strict"`
|
|
1251
|
+
in the managed policy. Then `/mode`, Shift+Tab, `--yes` and "don't ask again" all stop at that
|
|
1252
|
+
floor — see [Rolling it out at work](docs/en/company.md).
|
|
1253
|
+
|
|
1254
|
+
**A check, not the model, says when it is done.** Put `"check": "npm test"` in the config or pass
|
|
1255
|
+
`deel run --check`, and when the model has changed something and tries to finish, deel runs that check
|
|
1256
|
+
**itself**. A failure goes back to the model to keep fixing (3 rounds by default); still failing ends as
|
|
1257
|
+
failed — `deel run` exits with 8. The check goes through the same gates as the model's own commands
|
|
1258
|
+
(rules, hooks, approval, the dangerous-command guard) — see [Completion check](docs/en/company.md#completion-check).
|
|
1259
|
+
|
|
1260
|
+
**How much it gets done is measured with a golden set.** `deel eval --init` creates five example tasks,
|
|
1261
|
+
and `deel eval` runs deel on each one in a temp folder, then grades it with checks **the model never saw**.
|
|
1262
|
+
Results are kept, and tasks that got worse since last time are pointed out — see [Golden set](docs/en/company.md#golden-set).
|
|
1263
|
+
|
|
1264
|
+
Blocked commands are a **deny list**. Shapes the list does not know can get through, and the
|
|
1265
|
+
release notes record the ones found and closed so far. So no single layer is relied on: working
|
|
1266
|
+
scope, policy deny rules, approval, undo and the audit log are stacked.
|
|
1248
1267
|
|
|
1249
1268
|
Undo history stores whole file contents, so repeated edits to large files add up. Past 32MB
|
|
1250
1269
|
it keeps the **most recent 50 turns** and drops the rest. What you just did is always
|
|
@@ -1340,7 +1359,7 @@ Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder tak
|
|
|
1340
1359
|
## Development
|
|
1341
1360
|
|
|
1342
1361
|
```bash
|
|
1343
|
-
npm test Full suite (14,
|
|
1362
|
+
npm test Full suite (14,322 checks; a few are TTY-dependent)
|
|
1344
1363
|
npm run coverage Which lines the tests actually execute
|
|
1345
1364
|
npm run verify Import + network checks only
|
|
1346
1365
|
npm run bench Edit success rate
|
|
@@ -1384,7 +1403,7 @@ so one run tells you everything.
|
|
|
1384
1403
|
| `exitcode` · `doorparity` | 7 · 19 | The printed exit-code table is real · all **four** doors hand out the same thing |
|
|
1385
1404
|
| `no-bundle` | 28 | Nothing foreign in the published package; test-file hygiene |
|
|
1386
1405
|
| `edit-bench` | 15 cases | Edit success rate |
|
|
1387
|
-
| `mutate` | 1,
|
|
1406
|
+
| `mutate` | 1,785 mutants | **Whether the tests actually guard** — break the line on purpose, check it turns red |
|
|
1388
1407
|
|
|
1389
1408
|
> **More** — Coverage · Layout
|
|
1390
1409
|
>
|
|
@@ -1396,7 +1415,8 @@ so one run tells you everything.
|
|
|
1396
1415
|
|
|
1397
1416
|
| Version | What changed |
|
|
1398
1417
|
|---|---|
|
|
1399
|
-
| **[2.0
|
|
1418
|
+
| **[2.1.0](docs/en/releases/2.1.md#210)** | Hand it off and it keeps going; make it ask and it shows the diff — a check decides when it is done (`check`), a golden set measures how much gets done (`deel eval`), and `"approval"` in the managed policy lets a company set an approval floor |
|
|
1419
|
+
| [2.0.3](docs/en/releases/2.0.md#203) | 2.0.2 never reached npm — GitHub releases now appear only after npm has the version |
|
|
1400
1420
|
| [2.0.2](docs/en/releases/2.0.md#202) | Everything 2.0.1 left open, closed — MCP tools missing only from `deel run`, `taskkill` sent to an exited command's pid, MCP servers that stayed down for the session after one crash |
|
|
1401
1421
|
| [2.0.1](docs/en/releases/2.0.md#201) | One fence in 2.0.0 did not hold — `sudo -u root bash` walked through the `curl … \| bash` guard — and four gates could not go red |
|
|
1402
1422
|
| [2.0.0](docs/en/releases/2.0.md#200) | Every fence we said was there, checked for whether it actually holds — everything that changed, where and how it was fixed, and how it was found (CHA) |
|
package/bin/deel.js
CHANGED
|
@@ -23,6 +23,7 @@ import { 규칙모으기, 어떻게할까, 확인목록, 확인인자, 확인돌
|
|
|
23
23
|
import { 기록자리, 세기, 도구차례, 막힘차례, 셈JSON } from '../src/stats.js';
|
|
24
24
|
import { 진찰 } from '../src/doctor.js';
|
|
25
25
|
import { 설명, 설명줄들 } from '../src/configexplain.js';
|
|
26
|
+
import { runEval } from '../src/eval/run.js';
|
|
26
27
|
|
|
27
28
|
const MIN_NODE = 20;
|
|
28
29
|
|
|
@@ -625,6 +626,8 @@ const BOOL = new Set([
|
|
|
625
626
|
'hard',
|
|
626
627
|
// trust --off · --list, scan --save · --pick. 여태 목록에 없어 뒤의 낱말을 삼킬 수 있었다.
|
|
627
628
|
'off', 'list', 'save', 'pick',
|
|
629
|
+
// eval --init · --keep (src/eval/run.js).
|
|
630
|
+
'init', 'keep',
|
|
628
631
|
]);
|
|
629
632
|
|
|
630
633
|
/*
|
|
@@ -646,6 +649,8 @@ const BOOL = new Set([
|
|
|
646
649
|
const 값깃발 = new Set([
|
|
647
650
|
'root', 'mode', 'work', 'level', 'ctx', 'max-tokens', 'think', 'effort', 'output-schema',
|
|
648
651
|
'out', 'only', 'days', 'tool', 'url', 'key', 'model', 'host', 'ports', 'timeout', 'rm', 'delete',
|
|
652
|
+
// run --check <명령> (agent/donecheck.js) · eval --repeat <수> (src/eval/run.js).
|
|
653
|
+
'check', 'repeat',
|
|
649
654
|
]);
|
|
650
655
|
// 값을 줘도 되고 안 줘도 되는 깃발. `--resume` 만 치면 이어 할 대화를 고른다.
|
|
651
656
|
const 값골라깃발 = new Set(['resume']);
|
|
@@ -902,6 +907,8 @@ function help() {
|
|
|
902
907
|
say(` ${c.gray('--json')} 결과를 JSON 한 덩이로 (답·도구 횟수·토큰·끝난 까닭)`);
|
|
903
908
|
say(` ${c.gray('--quiet')} 도구가 무엇을 했는지 안 적음 (오류는 그래도 적음)`);
|
|
904
909
|
say(` ${c.gray('--yes')} 승인이 필요한 것도 그냥 실행. ${c.yellow('기본은 거부입니다')}`);
|
|
910
|
+
// 새로 넣는 도움말 줄은 말() 로 적는다 — 영어로 켠 사람에게 한국어 줄을 더 새게 하지 않는다(test/langleak.test.js 의 래칫).
|
|
911
|
+
say(` ${c.gray('--check <cmd>')} ${말('cli.checkFlag')}`);
|
|
905
912
|
/*
|
|
906
913
|
* 답의 모양을 못 박는 자리 (src/agent/outschema.js).
|
|
907
914
|
*
|
|
@@ -914,7 +921,13 @@ function help() {
|
|
|
914
921
|
// 이 줄은 src/oneshot.js 의 EXIT 와 짝이다. test/exitcode.test.js 가 둘이
|
|
915
922
|
// 어긋나면 빨개진다 — 한 판 동안 refusal(6) 이 여기서 빠져 있었고, 그
|
|
916
923
|
// 사실을 말해 주는 자리가 아무 데도 없었다.
|
|
917
|
-
say(` ${c.gray('끝난 까닭이 종료코드에 담깁니다:')} ${c.gray('0 끝냄 · 1 오류 · 2 걸음수상한 · 3 헛돎 · 4 중단 · 5 말없이끊김 · 6 거절 · 7 모양안맞음 · 64 사용법틀림')}`);
|
|
924
|
+
say(` ${c.gray('끝난 까닭이 종료코드에 담깁니다:')} ${c.gray('0 끝냄 · 1 오류 · 2 걸음수상한 · 3 헛돎 · 4 중단 · 5 말없이끊김 · 6 거절 · 7 모양안맞음 · 8 검사실패 · 64 사용법틀림')}`);
|
|
925
|
+
say('');
|
|
926
|
+
say(` ${c.bold(말('cli.evalTitle'))} ${c.gray(말('cli.evalWhy'))}`);
|
|
927
|
+
say('');
|
|
928
|
+
say(` ${c.cyan('deel eval --init')} ${말('cli.evalInit')}`);
|
|
929
|
+
say(` ${c.cyan('deel eval [dir]')} ${말('cli.evalRun')}`);
|
|
930
|
+
say(` ${c.gray('--repeat <n> · --only <name> · --keep · --json')}`);
|
|
918
931
|
say('');
|
|
919
932
|
say(` ${c.bold('진단 직접 지정')} ${c.gray('— 설정을 남기지 않고 확인만 할 때')}`);
|
|
920
933
|
say('');
|
|
@@ -1113,6 +1126,8 @@ async function main() {
|
|
|
1113
1126
|
quiet: flags.quiet === true || flags.quiet === 'true',
|
|
1114
1127
|
// 답의 모양을 못 박는다. 이게 있으면 표준출력은 그 JSON 하나다.
|
|
1115
1128
|
outputSchema: flags['output-schema'] ? String(flags['output-schema']) : undefined,
|
|
1129
|
+
// 끝내려는 자리에서 돌릴 검사 (agent/donecheck.js). 설정의 check 를 이긴다.
|
|
1130
|
+
check: flags.check !== undefined ? String(flags.check) : undefined,
|
|
1116
1131
|
});
|
|
1117
1132
|
case '':
|
|
1118
1133
|
case 'chat':
|
|
@@ -1186,6 +1201,24 @@ async function main() {
|
|
|
1186
1201
|
return runRules(args, flags);
|
|
1187
1202
|
case 'doc2md':
|
|
1188
1203
|
return runDoc2md(args, flags);
|
|
1204
|
+
// 과제 모음(골든셋)을 실제 모델로 돌려 성공률을 잰다 (src/eval/run.js).
|
|
1205
|
+
case 'eval':
|
|
1206
|
+
return runEval({
|
|
1207
|
+
폴더: args[0] ?? null,
|
|
1208
|
+
init: flags.init === true || flags.init === 'true',
|
|
1209
|
+
repeat: flags.repeat ?? 1,
|
|
1210
|
+
keep: flags.keep === true || flags.keep === 'true',
|
|
1211
|
+
json: flags.json === true || flags.json === 'true',
|
|
1212
|
+
only: flags.only !== undefined ? String(flags.only) : null,
|
|
1213
|
+
시간초: flags.timeout !== undefined ? Number(flags.timeout) : null,
|
|
1214
|
+
// 과제마다 띄우는 `deel run` 에 그대로 넘길 것 — 바깥 게이트웨이면 --online 이 있어야 돈다.
|
|
1215
|
+
깃발들: [
|
|
1216
|
+
...(flags.online === true || flags.online === 'true' ? ['--online'] : []),
|
|
1217
|
+
...(flags.offline === true || flags.offline === 'true' ? ['--offline'] : []),
|
|
1218
|
+
...(flags.think ? ['--think', String(flags.think)] : []),
|
|
1219
|
+
...(flags.effort ? ['--effort', String(flags.effort)] : []),
|
|
1220
|
+
],
|
|
1221
|
+
});
|
|
1189
1222
|
case 'sbom':
|
|
1190
1223
|
return runSbom(flags);
|
|
1191
1224
|
case 'scan':
|
package/package.json
CHANGED
package/src/acp/serve.js
CHANGED
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
import { existsSync, statSync, realpathSync } from 'node:fs';
|
|
31
31
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
32
32
|
import { VERSION } from '../version.js';
|
|
33
|
-
import { 규칙모으기, 늘허락,
|
|
33
|
+
import { 규칙모으기, 늘허락, 정책읽기, 승인바닥 } from '../safety/policy.js';
|
|
34
|
+
import { 검사설정 } from '../agent/donecheck.js';
|
|
34
35
|
import { 훅읽기 } from '../safety/hooks.js';
|
|
35
36
|
import { 에이전트읽기 } from '../agent/agents.js';
|
|
36
37
|
import { 남길것읽기 } from '../safety/shellenv.js';
|
|
@@ -546,6 +547,8 @@ export async function acp(opts = {}) {
|
|
|
546
547
|
get 눈있나() { return !!conn.vision; },
|
|
547
548
|
// 적어 둔 허락·금지 규칙 (safety/policy.js). 승인 모드보다 먼저 본다.
|
|
548
549
|
규칙들: 규칙모으기(cfg),
|
|
550
|
+
// 끝내려는 자리에서 돌릴 검사 (agent/donecheck.js). 터미널과 같은 설정을 읽는다.
|
|
551
|
+
완료검사: 검사설정(cfg),
|
|
549
552
|
// 사람이 적어 둔 훅 (safety/hooks.js). 에디터 안에서도 같은 것이 돌아야 한다 —
|
|
550
553
|
// 여기만 빠지면 「터미널에서는 막히고 에디터에서는 안 막힌다」 가 된다.
|
|
551
554
|
훅들: 훅정보.훅들,
|
|
@@ -569,7 +572,7 @@ export async function acp(opts = {}) {
|
|
|
569
572
|
*/
|
|
570
573
|
ask: async (_라벨, o = {}) => o?.def ?? '',
|
|
571
574
|
askPassword: async () => null,
|
|
572
|
-
confirm: (이름,
|
|
575
|
+
confirm: (이름, 인자, 곁것 = {}) => 승인묻기(방, 이름, 인자, 곁것?.미리보기),
|
|
573
576
|
};
|
|
574
577
|
|
|
575
578
|
방.ctx.배움 = new 배움(root, homeDir());
|
|
@@ -616,7 +619,7 @@ export async function acp(opts = {}) {
|
|
|
616
619
|
// deel 의 안전장치를 에디터의 승인 창으로 그대로 내보낸다. 이게 붙는 것과
|
|
617
620
|
// 안 붙는 것의 차이가 크다 — 안 붙으면 위험한 명령을 물어볼 데가 없어서
|
|
618
621
|
// 무조건 거부하게 되고, 그러면 에디터 안에서는 아무 일도 못 하는 도구가 된다.
|
|
619
|
-
async function 승인묻기(방, 이름,
|
|
622
|
+
async function 승인묻기(방, 이름, 인자, 미리보기 = []) {
|
|
620
623
|
/*
|
|
621
624
|
* ── 끊긴 턴의 승인은 없다 (사냥5 H5-3) ─────────────────────────────────
|
|
622
625
|
*
|
|
@@ -631,7 +634,13 @@ export async function acp(opts = {}) {
|
|
|
631
634
|
*/
|
|
632
635
|
const 신호 = 방.도는신호 ?? null;
|
|
633
636
|
if (신호?.aborted) return false;
|
|
634
|
-
|
|
637
|
+
/*
|
|
638
|
+
* 관리 정책이 승인 바닥을 걸었으면 「앞으로 묻지 않기」 는 없다 (safety/policy.js 의 승인바닥).
|
|
639
|
+
* 바닥은 바꾸기 전에 사람이 본다는 약속이라, 버튼 한 번으로 그 사람을 빼는 길을 주면
|
|
640
|
+
* 안 된다. 전에 눌러 둔 늘 허락도 이 판에서는 안 먹는다.
|
|
641
|
+
*/
|
|
642
|
+
const 바닥걸림 = 승인바닥().바닥 !== 'auto';
|
|
643
|
+
if (방.늘허락.has(이름) && !바닥걸림) return true;
|
|
635
644
|
|
|
636
645
|
const 아이디 = `t${++방.도구번호}`;
|
|
637
646
|
try {
|
|
@@ -644,10 +653,18 @@ export async function acp(opts = {}) {
|
|
|
644
653
|
status: 'pending',
|
|
645
654
|
locations: 도구자리(이름, 인자, null, { 뿌리: 방.root }),
|
|
646
655
|
rawInput: 인자 ?? {},
|
|
656
|
+
/*
|
|
657
|
+
* 무엇이 바뀌는지를 같이 보낸다 (tools/index.js 의 바뀔내용). 편집기는 이걸 승인 창에
|
|
658
|
+
* diff 로 그린다. 여태는 제목 한 줄과 날것 인자만 보내서, 사람은 old_string·new_string
|
|
659
|
+
* JSON 을 읽고 허락해야 했다. 새 파일이면 oldText 가 null 이다 (ACP 의 Diff 꼴).
|
|
660
|
+
*/
|
|
661
|
+
...(Array.isArray(미리보기) && 미리보기.length
|
|
662
|
+
? { content: 미리보기.map((것) => ({ type: 'diff', path: 것.경로, oldText: 것.전, newText: 것.후 })) }
|
|
663
|
+
: {}),
|
|
647
664
|
},
|
|
648
665
|
options: [
|
|
649
666
|
{ optionId: 'allow_once', name: '이번만 실행', kind: 'allow_once' },
|
|
650
|
-
{ optionId: 'allow_always', name: `${이름} 은 앞으로 묻지 않기`, kind: 'allow_always' },
|
|
667
|
+
...(바닥걸림 ? [] : [{ optionId: 'allow_always', name: `${이름} 은 앞으로 묻지 않기`, kind: 'allow_always' }]),
|
|
651
668
|
{ optionId: 'reject_once', name: '하지 않기', kind: 'reject_once' },
|
|
652
669
|
],
|
|
653
670
|
}, { signal: 신호 });
|
|
@@ -656,6 +673,7 @@ export async function acp(opts = {}) {
|
|
|
656
673
|
if (신호?.aborted) return false;
|
|
657
674
|
const 결과 = 답?.outcome ?? {};
|
|
658
675
|
if (결과.outcome !== 'selected') return false; // cancelled 도 여기로 온다
|
|
676
|
+
if (결과.optionId === 'allow_always' && 바닥걸림) return true;
|
|
659
677
|
if (결과.optionId === 'allow_always') {
|
|
660
678
|
방.늘허락.add(이름);
|
|
661
679
|
/*
|
|
@@ -882,6 +900,17 @@ export async function acp(opts = {}) {
|
|
|
882
900
|
말하기(`\n\n_(${옮긴말('ev.hookBlock')})_\n\n`);
|
|
883
901
|
break;
|
|
884
902
|
|
|
903
|
+
// 완료 검사 (agent/donecheck.js). 에디터에도 무엇을 돌렸고 어떻게 됐는지 적는다.
|
|
904
|
+
case 'check_start':
|
|
905
|
+
말하기(`\n\n_(${옮긴말('check.start', { 판: ev.판, 최대: ev.최대, 명령: ev.명령 })})_\n\n`);
|
|
906
|
+
break;
|
|
907
|
+
|
|
908
|
+
case 'check':
|
|
909
|
+
말하기(`\n\n_(${ev.ok === true ? 옮긴말('check.pass', { 명령: ev.명령 })
|
|
910
|
+
: ev.ok === false ? `${옮긴말('check.fail', { 판: ev.판, 최대: ev.최대, 요약: ev.요약 })} — ${ev.판 < ev.최대 ? 옮긴말('check.retry') : 옮긴말('check.gaveUp', { 판: ev.판 })}`
|
|
911
|
+
: 옮긴말('check.skipped', { 까닭: ev.까닭 })})_\n\n`);
|
|
912
|
+
break;
|
|
913
|
+
|
|
885
914
|
case 'nudge':
|
|
886
915
|
말하기(`\n\n_(${ev.why === '요청누락'
|
|
887
916
|
? 옮긴말('ev.nudgeMissed', { n: ev.빠진?.length ?? 0 })
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// 완료 검사 — 「다 됐습니다」 를 모델의 말이 아니라 **검사 결과**로 가른다 (2.1.0).
|
|
2
|
+
//
|
|
3
|
+
// ── 왜 만드나 ───────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// 사내 검토에서 나온 말이 둘이었다. 「검증을 정적 검사에 기대면 한계가 있다」 와
|
|
6
|
+
// 「루프를 더 다듬어야 한다」. 재 보니 맞았다. 턴이 끝나는 자리는 **모델이 도구를
|
|
7
|
+
// 안 부르고 말만 한 걸음** 하나였다. 모델이 「고쳤습니다」 라고 하면 그걸로 끝이었다.
|
|
8
|
+
//
|
|
9
|
+
// 확인할 길은 있었다. Verify 는 문법·짝을 읽어서 보고(tools/verify.js), checkmethods 는
|
|
10
|
+
// 이 프로젝트의 검사 명령을 찾아 알려 주고(tools/checkmethods.js), 증거는 「고친 뒤에
|
|
11
|
+
// 돌려서 통과한 검사가 있나」 를 사람에게 보여 준다(agent/evidence.js). 셋 다 **권하거나
|
|
12
|
+
// 보여 주기만** 한다. 돌릴지는 모델이 정했고, 안 돌리고 끝내도 막는 자리가 없었다.
|
|
13
|
+
//
|
|
14
|
+
// 그래서 사람이 검사 명령 하나를 정해 두면(`"check": "npm test"`), 모델이 끝내려는
|
|
15
|
+
// 자리에서 **deel 이 직접** 그 명령을 돌린다.
|
|
16
|
+
//
|
|
17
|
+
// 통과 그대로 끝낸다. 끝맺음에 「완료 검사 통과」 가 붙는다
|
|
18
|
+
// 실패 실패 출력을 모델에게 돌려주고 이어서 고치게 한다 — 정해 둔 판수까지
|
|
19
|
+
// 다 써도 실패 끝내되 **실패로** 끝낸다. `deel run` 은 종료코드 8 을 낸다
|
|
20
|
+
//
|
|
21
|
+
// ── 안 하는 것 ──────────────────────────────────────────────────────────
|
|
22
|
+
//
|
|
23
|
+
// 명령을 **지어내지 않는다.** 정해 둔 것이 없으면 이 고리는 아예 안 돈다. 있을 법한
|
|
24
|
+
// 검사(npm test)를 짐작해 돌리면, 그 프로젝트에서 그게 20분짜리이거나 DB 를 비우는
|
|
25
|
+
// 스크립트일 때 사람이 모르는 채로 돈다.
|
|
26
|
+
//
|
|
27
|
+
// 따로 도는 길도 **안 만든다.** 이 명령은 모델이 부른 Bash 와 똑같은 관문을 지난다 —
|
|
28
|
+
// 적어 둔 금지 규칙, 사람이 건 훅, 승인 모드(strict 면 묻는다), 위험 명령 막이, 열쇠 뺀
|
|
29
|
+
// 환경. 설정에 적힌 명령이라고 관문을 건너뛰면 저장소 설정 한 줄이 strict 를 뚫는다.
|
|
30
|
+
//
|
|
31
|
+
// 아무것도 안 바꾼 턴에는 안 돈다. 물어본 것에 답만 한 턴에 테스트가 도는 것은 소음이다.
|
|
32
|
+
// 고친 뒤 실패를 받고 **다시 아무것도 안 바꾼 채** 끝내려 하면 다시 돌리지 않는다 —
|
|
33
|
+
// 같은 결과가 나올 것을 또 기다리게 하지 않고, 실패로 끝낸다.
|
|
34
|
+
|
|
35
|
+
/** 한 턴에 몇 번까지 「고치고 → 다시 검사」 를 돌까. 기본과 상한. */
|
|
36
|
+
export const 기본판수 = 3;
|
|
37
|
+
export const 최대판수 = 10;
|
|
38
|
+
/** 검사 한 번의 제한 시간(ms). Bash 도구의 상한(10분)과 같다 — 넘겨 봐야 거기서 잘린다. */
|
|
39
|
+
export const 기본시간 = 600_000;
|
|
40
|
+
const 최소시간 = 1_000;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 설정과 실행 깃발에서 완료 검사를 읽는다. 정해 둔 명령이 없으면 null — 고리를 안 돈다.
|
|
44
|
+
*
|
|
45
|
+
* @param {object} cfg 설정 (`check` · `checkRounds` · `checkTimeout`)
|
|
46
|
+
* @param {string} [덮을명령] `deel run --check` 로 준 명령. 설정보다 이긴다
|
|
47
|
+
* @returns {{명령:string, 판수:number, 시간:number} | null}
|
|
48
|
+
*/
|
|
49
|
+
export function 검사설정(cfg = {}, 덮을명령 = undefined) {
|
|
50
|
+
const 날것 = 덮을명령 !== undefined && 덮을명령 !== null ? 덮을명령 : cfg?.check;
|
|
51
|
+
if (typeof 날것 !== 'string') return null;
|
|
52
|
+
const 명령 = 날것.trim();
|
|
53
|
+
if (!명령) return null;
|
|
54
|
+
/*
|
|
55
|
+
* 판수는 1 ~ 10 에서만 받는다. 0 이나 음수를 「끈다」 로 읽으면 명령을 적어 둔 사람이
|
|
56
|
+
* 왜 안 도는지 모른다. 끄려면 check 를 지운다. 숫자가 아니면 기본값이다.
|
|
57
|
+
*/
|
|
58
|
+
const n = Number(cfg?.checkRounds);
|
|
59
|
+
const 판수 = Number.isInteger(n) && n >= 1 ? Math.min(n, 최대판수) : 기본판수;
|
|
60
|
+
const 초 = Number(cfg?.checkTimeout);
|
|
61
|
+
const 시간 = Number.isFinite(초) && 초 > 0 ? Math.min(기본시간, Math.max(최소시간, Math.round(초 * 1000))) : 기본시간;
|
|
62
|
+
return { 명령, 판수, 시간 };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 긴 출력에서 **뒤를** 남긴다. 테스트가 무엇이 틀렸는지 말하는 자리는 대개 끝이다 —
|
|
67
|
+
* 앞을 남기면 모델은 「테스트 시작」 줄만 받고 무엇이 틀렸는지 모른다.
|
|
68
|
+
*/
|
|
69
|
+
export function 출력꼬리(글, 한도 = 6000) {
|
|
70
|
+
const s = String(글 ?? '');
|
|
71
|
+
if (s.length <= 한도) return s;
|
|
72
|
+
return `…(앞 ${s.length - 한도}자 줄임)\n${s.slice(s.length - 한도)}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 도구 결과 하나를 통과·실패로 가른다. 종료코드 0 으로 끝난 것만 통과다 —
|
|
77
|
+
* 시그널로 죽은 것·시간 초과·못 돌린 것은 다 실패다 (tools/index.js 의 Bash 결과 머리말).
|
|
78
|
+
*/
|
|
79
|
+
export function 통과했나(result) {
|
|
80
|
+
return !!result && !result.error && result.failed !== true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** 실패한 검사를 모델에게 돌려줄 말. 무엇을 돌렸고, 몇 번째이고, 무엇이 나왔는지. */
|
|
84
|
+
export function 실패말({ 명령, 출력, 판, 최대 }, { 영어 = false } = {}) {
|
|
85
|
+
const 남은 = Math.max(0, 최대 - 판);
|
|
86
|
+
const 꼬리 = 출력꼬리(출력);
|
|
87
|
+
if (영어) {
|
|
88
|
+
return `The completion check \`${명령}\` failed (${판}/${최대}). You said the work was done, but the check does not pass.\n\n`
|
|
89
|
+
+ `${꼬리}\n\n`
|
|
90
|
+
+ 'Read the failure, fix the cause in the code, and finish again. Do not edit or delete the check itself to make it pass.'
|
|
91
|
+
+ (남은 ? ` The check runs again when you finish (${남은} more ${남은 === 1 ? 'try' : 'tries'}).` : '');
|
|
92
|
+
}
|
|
93
|
+
return `완료 검사 \`${명령}\` 가 실패했습니다 (${판}/${최대}). 다 됐다고 했는데 검사가 통과하지 않습니다.\n\n`
|
|
94
|
+
+ `${꼬리}\n\n`
|
|
95
|
+
+ '실패 내용을 읽고 코드에서 원인을 고친 뒤 다시 끝내세요. 검사를 통과시키려고 검사 자체를 고치거나 지우지 마세요.'
|
|
96
|
+
+ (남은 ? ` 끝내면 검사가 다시 돕니다 (${남은}번 남음).` : '');
|
|
97
|
+
}
|