leerness 1.9.204 → 1.9.205
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 +40 -0
- package/README.md +3 -3
- package/bin/harness.js +97 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.205 — 2026-05-22
|
|
4
|
+
|
|
5
|
+
**⚡ ScheduleWakeup 사용자 요청 우선 갱신 (사용자 명시) — wakeup miss 실측 후속.**
|
|
6
|
+
|
|
7
|
+
### 사용자 명시 + 보고
|
|
8
|
+
> *"자동모드로 진행할때, 예정된 알람이전에 사용자 요청이 들어오면 백그라운드의 알람을 종료후 다시 갱신하는 방식은 어떤지"*
|
|
9
|
+
> *"ScheduleWakeup 10:51 KST 가 지났어"* — 사용자가 명시 보고한 wakeup miss 사례
|
|
10
|
+
|
|
11
|
+
### 1. `.harness/active-wakeups.json` 등록 추적
|
|
12
|
+
- release sync-main 마무리 시 다음 wakeup 자동 등록
|
|
13
|
+
- 구조: `{ expectedFireAt, intervalMin, source, status: pending/fired/missed/superseded, registeredAt }`
|
|
14
|
+
- 최근 20개 유지
|
|
15
|
+
- 함수: `_loadActiveWakeups` / `_writeActiveWakeups` / `_recordWakeup` / `_activeWakeupsPath`
|
|
16
|
+
|
|
17
|
+
### 2. handoff 진입 시 wakeup 상태 분석 (`_analyzeWakeupStatus`)
|
|
18
|
+
| kind | 조건 | 알림 |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `early` | delta < -5min (미래) | `## ⚡ 사용자 조기 진입 감지` |
|
|
21
|
+
| `on-time` | -5 ~ +5min | `✓ ScheduleWakeup 정시 진입` |
|
|
22
|
+
| `late` | +5 ~ +30min | (조용히) |
|
|
23
|
+
| `missed` | > +30min | `## ⏰ ScheduleWakeup miss 확정` + status 자동 갱신 |
|
|
24
|
+
|
|
25
|
+
### 3. 조기 진입 시 자동 갱신 가이드
|
|
26
|
+
사용자가 wakeup 예정 시각 이전 진입 → "라운드 마무리 시 새 wakeup 등록 (이전 자동 superseded)" 안내
|
|
27
|
+
|
|
28
|
+
### 4. miss 자동 status 갱신
|
|
29
|
+
fire 시간 지남 + 30분 이상 → status `pending` → `missed` 자동 업데이트 → 통계 누적
|
|
30
|
+
|
|
31
|
+
### 5. 누적 회귀 (1.9.199~204) — 모두 유지
|
|
32
|
+
|
|
33
|
+
### 6. stress-v150 — 16/16 PASS
|
|
34
|
+
- 등록 추적 (3) + 상태 분석 (2) + 임시 워크스페이스 통합 (2) + 성능 (2) + 누적 (7)
|
|
35
|
+
- 성능: --version cold start avg **464 ms** · MCP 54 도구 **460 ms**
|
|
36
|
+
|
|
37
|
+
### 7. 자동 release
|
|
38
|
+
- main 자동 push **67 라운드 연속** (1.9.140~205)
|
|
39
|
+
- npm publish 자동 (1.9.178~)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
3
43
|
## 1.9.204 — 2026-05-22
|
|
4
44
|
|
|
5
45
|
**⏰ timezone 보강 + 🔄 auto-loop 활성 라벨 (사용자 명시 2종).**
|
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) []() []() []() []() []() []() [-brightgreen)]() []() []() []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
|
|
13
13
|
║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
|
|
14
14
|
║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
|
|
15
|
-
║ v1.9.
|
|
15
|
+
║ v1.9.205 AI Agent Reliability Harness + Sandbox ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · sandbox · drift ║
|
|
17
|
-
║
|
|
17
|
+
║ ⚡ wakeup early/missed 감지 + 자동 갱신 가이드 (사용자 명시) ║
|
|
18
18
|
╚══════════════════════════════════════════════════════════════╝
|
|
19
19
|
```
|
|
20
20
|
|
package/bin/harness.js
CHANGED
|
@@ -7,7 +7,7 @@ const cp = require('child_process');
|
|
|
7
7
|
const os = require('os'); // 1.9.178: _publishToNpm 에서 os.tmpdir() 사용 (전역 import)
|
|
8
8
|
const readline = require('readline');
|
|
9
9
|
|
|
10
|
-
const VERSION = '1.9.
|
|
10
|
+
const VERSION = '1.9.205';
|
|
11
11
|
|
|
12
12
|
// 1.9.184: DEP0190 (child_process shell: true) deprecation warning 억제 (사용자 명시).
|
|
13
13
|
// leerness 는 cross-platform PATH resolution 을 위해 shell: true 를 의도적으로 사용 (claude.cmd / ollama.cmd 등 Windows .cmd 처리).
|
|
@@ -1779,6 +1779,60 @@ async function skillAutoCacheCmd(root, sub) {
|
|
|
1779
1779
|
if (c.expired) log(` → 갱신: leerness skill auto-cache refresh`);
|
|
1780
1780
|
}
|
|
1781
1781
|
|
|
1782
|
+
// 1.9.205: ScheduleWakeup 등록 추적 (사용자 명시)
|
|
1783
|
+
// "예정된 알람이전에 사용자 요청이 들어오면 백그라운드의 알람을 종료후 다시 갱신"
|
|
1784
|
+
// 라운드 마무리 시 등록된 wakeup id/expectedFireAt 을 .harness/active-wakeups.json 에 기록 →
|
|
1785
|
+
// handoff/resume 진입 시 expected fire 가 과거이면 "wakeup miss 또는 사용자 조기 진입" 감지 →
|
|
1786
|
+
// 메인 에이전트가 새 wakeup 호출하라는 명확한 안내
|
|
1787
|
+
function _activeWakeupsPath(root) { return path.join(root, '.harness', 'active-wakeups.json'); }
|
|
1788
|
+
function _loadActiveWakeups(root) {
|
|
1789
|
+
try {
|
|
1790
|
+
const fp = _activeWakeupsPath(root);
|
|
1791
|
+
if (!exists(fp)) return { wakeups: [] };
|
|
1792
|
+
const j = JSON.parse(read(fp));
|
|
1793
|
+
return { wakeups: Array.isArray(j.wakeups) ? j.wakeups : [] };
|
|
1794
|
+
} catch { return { wakeups: [] }; }
|
|
1795
|
+
}
|
|
1796
|
+
function _writeActiveWakeups(root, wakeups) {
|
|
1797
|
+
try {
|
|
1798
|
+
mkdirp(path.join(root, '.harness'));
|
|
1799
|
+
writeUtf8(_activeWakeupsPath(root), JSON.stringify({ wakeups, updatedAt: new Date().toISOString() }, null, 2));
|
|
1800
|
+
return true;
|
|
1801
|
+
} catch { return false; }
|
|
1802
|
+
}
|
|
1803
|
+
function _recordWakeup(root, entry) {
|
|
1804
|
+
const state = _loadActiveWakeups(root);
|
|
1805
|
+
// entry: { expectedFireAt, intervalMin, source, status: 'pending'|'fired'|'missed'|'superseded' }
|
|
1806
|
+
const e = { ...entry, registeredAt: new Date().toISOString() };
|
|
1807
|
+
// 같은 expectedFireAt 중복 시 replace
|
|
1808
|
+
state.wakeups = state.wakeups.filter(w => w.expectedFireAt !== e.expectedFireAt);
|
|
1809
|
+
state.wakeups.push(e);
|
|
1810
|
+
// 최근 20개만 유지
|
|
1811
|
+
if (state.wakeups.length > 20) state.wakeups = state.wakeups.slice(-20);
|
|
1812
|
+
_writeActiveWakeups(root, state.wakeups);
|
|
1813
|
+
return e;
|
|
1814
|
+
}
|
|
1815
|
+
// wakeup miss / 사용자 조기 진입 감지 — 활성 wakeup 중 가장 가까운 미래/과거 fire 분석
|
|
1816
|
+
function _analyzeWakeupStatus(root) {
|
|
1817
|
+
const state = _loadActiveWakeups(root);
|
|
1818
|
+
const pending = state.wakeups.filter(w => w.status === 'pending');
|
|
1819
|
+
if (pending.length === 0) return { hasWakeup: false };
|
|
1820
|
+
// 가장 최근 등록된 pending wakeup
|
|
1821
|
+
const latest = pending.sort((a, b) => new Date(b.registeredAt).getTime() - new Date(a.registeredAt).getTime())[0];
|
|
1822
|
+
const now = Date.now();
|
|
1823
|
+
const expectedMs = new Date(latest.expectedFireAt).getTime();
|
|
1824
|
+
const deltaMin = Math.floor((now - expectedMs) / 60000);
|
|
1825
|
+
// deltaMin < 0 → 아직 fire 시간 전 (사용자 조기 진입)
|
|
1826
|
+
// deltaMin >= 0 → fire 시간 지남 (정시 또는 miss)
|
|
1827
|
+
// |deltaMin| < 5 → 정시 부근
|
|
1828
|
+
let kind;
|
|
1829
|
+
if (deltaMin < -5) kind = 'early'; // 사용자 조기 진입
|
|
1830
|
+
else if (deltaMin > 30) kind = 'missed'; // wakeup miss
|
|
1831
|
+
else if (deltaMin > 5) kind = 'late'; // 약간 지연
|
|
1832
|
+
else kind = 'on-time';
|
|
1833
|
+
return { hasWakeup: true, latest, deltaMin, kind, pending: pending.length };
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1782
1836
|
// 1.9.203: 자동 라운드 plan 정리 — 사용자 명시
|
|
1783
1837
|
// "백그라운드에 다음 작업이 시작가능한 예상 시간 + 일어났을때 해야하는 일을 정리"
|
|
1784
1838
|
// 라운드 마무리 시 .harness/auto-resume-plan.json 자동 저장 → 다음 wakeup 시 즉시 실행 가능
|
|
@@ -4224,6 +4278,40 @@ function handoff(root) {
|
|
|
4224
4278
|
}
|
|
4225
4279
|
} catch {}
|
|
4226
4280
|
}
|
|
4281
|
+
// 1.9.205: ScheduleWakeup 상태 분석 + 사용자 조기 진입 / miss 감지 (사용자 명시)
|
|
4282
|
+
if (process.env.LEERNESS_NO_WAKEUP_STATUS !== '1' && !has('--quiet')) {
|
|
4283
|
+
try {
|
|
4284
|
+
const wakeup = _analyzeWakeupStatus(root);
|
|
4285
|
+
if (wakeup.hasWakeup) {
|
|
4286
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
4287
|
+
const yel = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
4288
|
+
const grn = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
|
|
4289
|
+
const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
4290
|
+
const expectedLocal = _formatLocal(wakeup.latest.expectedFireAt);
|
|
4291
|
+
if (wakeup.kind === 'early') {
|
|
4292
|
+
// 사용자 조기 진입 — 기존 wakeup 갱신 권장
|
|
4293
|
+
log(yel(`## ⚡ 사용자 조기 진입 감지 (1.9.205) — 등록된 wakeup ${Math.abs(wakeup.deltaMin)}분 후 fire 예정`));
|
|
4294
|
+
log(dim(` 예정 fire: ${expectedLocal} (${wakeup.latest.source})`));
|
|
4295
|
+
log(dim(` → 권장: 라운드 마무리 시 새 wakeup 등록 (이전 자동 superseded)`));
|
|
4296
|
+
log('');
|
|
4297
|
+
} else if (wakeup.kind === 'missed') {
|
|
4298
|
+
// wakeup miss — fire 시간 지남
|
|
4299
|
+
log(yel(`## ⏰ ScheduleWakeup miss 확정 (1.9.205) — 예정 fire ${wakeup.deltaMin}분 전`));
|
|
4300
|
+
log(dim(` 예정 fire: ${expectedLocal} (${wakeup.latest.source})`));
|
|
4301
|
+
log(dim(` → 회복: 라운드 마무리 시 새 wakeup 자동 등록`));
|
|
4302
|
+
// 자동으로 status 를 'missed' 로 업데이트
|
|
4303
|
+
try {
|
|
4304
|
+
const s = _loadActiveWakeups(root);
|
|
4305
|
+
const idx = s.wakeups.findIndex(w => w.expectedFireAt === wakeup.latest.expectedFireAt);
|
|
4306
|
+
if (idx >= 0) { s.wakeups[idx].status = 'missed'; _writeActiveWakeups(root, s.wakeups); }
|
|
4307
|
+
} catch {}
|
|
4308
|
+
log('');
|
|
4309
|
+
} else if (wakeup.kind === 'on-time') {
|
|
4310
|
+
log(grn(` ✓ ScheduleWakeup 정시 진입 (1.9.205, delta ${wakeup.deltaMin}분)`));
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
} catch {}
|
|
4314
|
+
}
|
|
4227
4315
|
// 1.9.203: auto-resume-plan 알림 — 라운드 마무리 시 저장된 plan 자동 노출
|
|
4228
4316
|
// 사용자 명시: "백그라운드에 다음 작업이 시작가능한 예상 시간 + 일어났을때 해야하는 일을 정리"
|
|
4229
4317
|
if (_autoResumePlan && !has('--quiet') && process.env.LEERNESS_NO_RESUME_PLAN !== '1') {
|
|
@@ -9334,6 +9422,14 @@ function _publishToNpm(root, opts = {}) {
|
|
|
9334
9422
|
note: `다음 wakeup 시: 1) leerness resume . 으로 plan 확인 2) leerness handoff . 실행 3) leerness next-action take 로 즉시 task 진입`
|
|
9335
9423
|
});
|
|
9336
9424
|
_writeAutoResumePlan(root, plan);
|
|
9425
|
+
// 1.9.205: 다음 wakeup 등록 추적 (사용자 명시 갱신용)
|
|
9426
|
+
_recordWakeup(root, {
|
|
9427
|
+
expectedFireAt: plan.expectedFireAt,
|
|
9428
|
+
intervalMin: plan.intervalMin || 25,
|
|
9429
|
+
source: 'release-sync-main',
|
|
9430
|
+
status: 'pending',
|
|
9431
|
+
fromVersion: pkgVersion
|
|
9432
|
+
});
|
|
9337
9433
|
} catch {}
|
|
9338
9434
|
} else {
|
|
9339
9435
|
const errOut = (pubR.stderr || pubR.stdout || '').slice(-400);
|