leerness 1.9.153 → 1.9.154
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/CHANGELOG.md +31 -0
- package/README.md +2 -2
- package/bin/harness.js +60 -24
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.154 — 2026-05-20
|
|
4
|
+
|
|
5
|
+
**agent 1-shot multi-provider + REPL `:provider` 활성 검증 (1.9.153 후속, 일관성 강화).**
|
|
6
|
+
|
|
7
|
+
자율 모드 84 라운드.
|
|
8
|
+
|
|
9
|
+
### Added — `leerness agent "<task>" --provider <p>` 1-shot multi-provider
|
|
10
|
+
- 기존: 1-shot 모드는 Ollama 만 호출, 다른 CLI 는 `agents dispatch` 안내만
|
|
11
|
+
- 변경: **claude / codex / gemini / copilot 도 직접 호출** (1.9.153 `_cliChat` 재사용)
|
|
12
|
+
- `_recordRun` observability — provider/model 필드 동적 (`agent_one_shot` kind)
|
|
13
|
+
- task-log 기록도 provider 동적 (`leerness agent (claude:claude, role=actor)` 형식)
|
|
14
|
+
- 실패 시 provider 별 friendly 안내 (Ollama: BASE_URL 확인 / 외부 CLI: `LEERNESS_ENABLE_<X>=1` + 설치)
|
|
15
|
+
|
|
16
|
+
### Added — REPL `:provider <p>` 전환 시 활성 사전 검증
|
|
17
|
+
- validProviders 화이트리스트 5종 — 알 수 없는 provider 거부
|
|
18
|
+
- **비활성 (`ready` 아님) provider 전환 시 즉시 거부** — 실제 호출 시 실패 방지
|
|
19
|
+
- `_checkAgent` 결과로 status/installed/enabled 종합 판정
|
|
20
|
+
- Ollama 는 `LEERNESS_OLLAMA_BASE_URL` 미설정 시 친절한 안내 (블록 아님 — fallback URL 시도)
|
|
21
|
+
- 전환 성공 시 `rl.setPrompt(prompt())` 으로 프롬프트 즉시 갱신
|
|
22
|
+
|
|
23
|
+
### Verified — setup-agents `_selectMany` 일관성 (회귀 방지)
|
|
24
|
+
- 1.9.34 이래 setup-agents 이미 `_selectMany` 사용 중 — 1.9.151 install 흐름과 일관
|
|
25
|
+
- stress-v99 회귀 테스트 추가 (사용자 명시 요청 일관성 보장)
|
|
26
|
+
|
|
27
|
+
### Verified
|
|
28
|
+
- e2e 217/217 ✓
|
|
29
|
+
- stress-v99: 18/18 (1-shot multi-provider 6종 + REPL :provider 검증 4종 + setup-agents 1종 + 누적 회귀 7종)
|
|
30
|
+
- VERSION = 1.9.154 / autonomous-rounds = 84
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
3
34
|
## 1.9.153 — 2026-05-20
|
|
4
35
|
|
|
5
36
|
**`.env` 직접 생성/마이그레이션 + REPL 배너 leerness 고유 문구 + multi-provider REPL (사용자 명시 3종).**
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []() []() []() []() []() []() []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
|
|
13
13
|
║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
|
|
14
14
|
║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
|
|
15
|
-
║ v1.9.
|
|
15
|
+
║ v1.9.154 AI Agent Reliability Harness + Sandbox ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · sandbox · drift ║
|
|
17
17
|
╚══════════════════════════════════════════════════════════════╝
|
|
18
18
|
```
|
package/bin/harness.js
CHANGED
|
@@ -6,7 +6,7 @@ const path = require('path');
|
|
|
6
6
|
const cp = require('child_process');
|
|
7
7
|
const readline = require('readline');
|
|
8
8
|
|
|
9
|
-
const VERSION = '1.9.
|
|
9
|
+
const VERSION = '1.9.154';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -10425,7 +10425,36 @@ async function _agentRepl(root, opts) {
|
|
|
10425
10425
|
if (!['planner', 'reviewer', 'actor'].includes(r)) { log(C.yel(` ⚠ role 은 planner/reviewer/actor`)); return false; }
|
|
10426
10426
|
state.role = r; rl.setPrompt(prompt()); log(C.green(` role = ${r}`)); return false;
|
|
10427
10427
|
}
|
|
10428
|
-
if (op === 'provider') {
|
|
10428
|
+
if (op === 'provider') {
|
|
10429
|
+
const newProv = rest[0] || state.provider;
|
|
10430
|
+
const validProviders = ['ollama', 'claude', 'codex', 'gemini', 'copilot'];
|
|
10431
|
+
if (!validProviders.includes(newProv)) {
|
|
10432
|
+
log(C.yel(` ⚠ provider 는 ${validProviders.join(' / ')} (받음: ${newProv})`));
|
|
10433
|
+
return false;
|
|
10434
|
+
}
|
|
10435
|
+
// 1.9.154: provider 전환 시 활성 ready 사전 검증 — 비활성/미설치이면 친절한 안내 후 거부 (실제 호출 시 실패 방지)
|
|
10436
|
+
if (newProv === 'ollama') {
|
|
10437
|
+
// Ollama 는 HTTP 기반 — 단순히 LEERNESS_OLLAMA_BASE_URL 확인
|
|
10438
|
+
const url = process.env.LEERNESS_OLLAMA_BASE_URL || '';
|
|
10439
|
+
if (!url) {
|
|
10440
|
+
log(C.yel(` ⚠ ollama base URL 미설정 (LEERNESS_OLLAMA_BASE_URL) — 기본 http://localhost:11434 시도`));
|
|
10441
|
+
}
|
|
10442
|
+
} else {
|
|
10443
|
+
const agent = EXTERNAL_AGENTS.find(a => a.id === newProv);
|
|
10444
|
+
if (agent) {
|
|
10445
|
+
const st = _checkAgent(agent);
|
|
10446
|
+
if (st.status !== 'ready') {
|
|
10447
|
+
log(C.yel(` ⚠ ${newProv} 비활성 (${st.status}) — .env 에서 LEERNESS_ENABLE_${newProv.toUpperCase()}=1 + CLI 설치 필요`));
|
|
10448
|
+
log(C.dim(` (leerness agents list 로 상태 확인) — provider 전환 취소`));
|
|
10449
|
+
return false;
|
|
10450
|
+
}
|
|
10451
|
+
}
|
|
10452
|
+
}
|
|
10453
|
+
state.provider = newProv;
|
|
10454
|
+
rl.setPrompt(prompt());
|
|
10455
|
+
log(C.green(` provider = ${state.provider}`));
|
|
10456
|
+
return false;
|
|
10457
|
+
}
|
|
10429
10458
|
if (op === 'clear') { process.stdout.write('\x1b[2J\x1b[H'); return false; }
|
|
10430
10459
|
if (op === 'reset') { state.history = []; log(C.dim(' history 초기화됨')); return false; }
|
|
10431
10460
|
if (op === 'history') {
|
|
@@ -10558,32 +10587,39 @@ async function agentCmd(root, taskArg) {
|
|
|
10558
10587
|
} catch {}
|
|
10559
10588
|
if (dryRun) { log('\n(dry-run) LLM 호출 스킵 — provider/권한/컨텍스트만 출력'); return; }
|
|
10560
10589
|
if (!provider) { fail('활성 provider 없음 — .env 에서 LEERNESS_ENABLE_OLLAMA=1 또는 LEERNESS_ENABLE_CLAUDE=1 활성화'); process.exitCode = 1; return; }
|
|
10561
|
-
//
|
|
10590
|
+
// 1.9.148: role prompt 자동 prepend (모든 provider 공통)
|
|
10591
|
+
const finalPrompt = `${rolePrompt}\n\nTask: ${task}`;
|
|
10592
|
+
const t0 = Date.now();
|
|
10593
|
+
// 1.9.154: 1-shot 모드도 multi-provider — Ollama 외 claude/codex/gemini/copilot 직접 호출 (1.9.153 _cliChat 재사용)
|
|
10594
|
+
let r;
|
|
10562
10595
|
if (provider === 'ollama') {
|
|
10563
10596
|
log('\n[ollama 호출 중...]');
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
log('\n[response (model=' + r.model + ', role=' + role + ', ' + dt + 'ms)]\n' + r.response);
|
|
10573
|
-
try {
|
|
10574
|
-
const tlp = taskLogPath(root);
|
|
10575
|
-
const block = `\n## ${today()} leerness agent (ollama:${r.model}, role=${role})\n- task: ${task.slice(0, 200)}\n- response (preview): ${r.response.slice(0, 240).replace(/\n+/g, ' ')}\n`;
|
|
10576
|
-
append(tlp, block);
|
|
10577
|
-
} catch {}
|
|
10578
|
-
} else {
|
|
10579
|
-
fail(`ollama 호출 실패: ${r.error || 'unknown'}`);
|
|
10580
|
-
log(` → ollama serve 실행 + LEERNESS_OLLAMA_BASE_URL 확인`);
|
|
10581
|
-
process.exitCode = 1;
|
|
10582
|
-
}
|
|
10597
|
+
r = await _ollamaChat(finalPrompt);
|
|
10598
|
+
} else if (['claude', 'codex', 'gemini', 'copilot'].includes(provider)) {
|
|
10599
|
+
log(`\n[${provider} CLI 호출 중...]`);
|
|
10600
|
+
r = await _cliChat(root, provider, finalPrompt, { timeout: 90000 });
|
|
10601
|
+
if (r.ok && !r.model) r.model = provider; // _cliChat 결과 보강
|
|
10602
|
+
} else {
|
|
10603
|
+
fail(`알 수 없는 provider: ${provider} (ollama/claude/codex/gemini/copilot)`);
|
|
10604
|
+
process.exitCode = 1;
|
|
10583
10605
|
return;
|
|
10584
10606
|
}
|
|
10585
|
-
|
|
10586
|
-
|
|
10607
|
+
const dt = Date.now() - t0;
|
|
10608
|
+
// 1.9.149: observability 기록
|
|
10609
|
+
_recordRun(root, { kind: 'agent_one_shot', provider, model: r.model || provider, role, durationMs: dt, ok: r.ok, error: r.error, task: task.slice(0, 200), responseChars: (r.response || '').length });
|
|
10610
|
+
if (r.ok) {
|
|
10611
|
+
log(`\n[response (provider=${provider}, model=${r.model || provider}, role=${role}, ${dt}ms)]\n${r.response}`);
|
|
10612
|
+
try {
|
|
10613
|
+
const tlp = taskLogPath(root);
|
|
10614
|
+
const block = `\n## ${today()} leerness agent (${provider}:${r.model || provider}, role=${role})\n- task: ${task.slice(0, 200)}\n- response (preview): ${(r.response || '').slice(0, 240).replace(/\n+/g, ' ')}\n`;
|
|
10615
|
+
append(tlp, block);
|
|
10616
|
+
} catch {}
|
|
10617
|
+
} else {
|
|
10618
|
+
fail(`${provider} 호출 실패: ${r.error || 'unknown'}`);
|
|
10619
|
+
if (provider === 'ollama') log(` → ollama serve 실행 + LEERNESS_OLLAMA_BASE_URL 확인`);
|
|
10620
|
+
else log(` → .env 에서 LEERNESS_ENABLE_${provider.toUpperCase()}=1 + CLI 설치 확인 (leerness agents list)`);
|
|
10621
|
+
process.exitCode = 1;
|
|
10622
|
+
}
|
|
10587
10623
|
}
|
|
10588
10624
|
|
|
10589
10625
|
// ===== 1.9.147: 자동 유지보수 시스템 (사용자 명시 요청) =====
|