genetics-mcp 2.2.0 → 2.2.1
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/genetics.mjs +66 -4
- package/package.json +1 -1
package/genetics.mjs
CHANGED
|
@@ -763,7 +763,20 @@ const SECRET_SHAPES = [
|
|
|
763
763
|
['slack-token', /\bxox[baprs]-[A-Za-z0-9-]{10,}/g],
|
|
764
764
|
];
|
|
765
765
|
const MASK = '[가림: 서버에 올리며 지움]';
|
|
766
|
-
|
|
766
|
+
/*
|
|
767
|
+
* 안 올리는 레코드 종류. **서버의 `AGENT_SESSION_DROP_TYPES` 와 같은 집합이어야 한다**
|
|
768
|
+
* (`www/portal/lib/agent_session.php:59-60`). 갈라지면 클라이언트가 보낸 것을 서버가 또
|
|
769
|
+
* 버리거나 그 반대가 되어, 줄 수·바이트가 안 맞고 동치 시험이 조용히 어긋난다.
|
|
770
|
+
*
|
|
771
|
+
* 뒤의 셋은 2026-08-21 에 더했다. **순서가 중요했다** — 서버가 먼저 버리게 배포한 뒤에
|
|
772
|
+
* 여기를 맞췄다. 반대로 했으면 그 종류가 서버에 **도착하지 않아** 서버 쪽 동치 시험이
|
|
773
|
+
* 「입력이 빈 채로 초록」이 됐을 것이다. 운영 반영을 눈으로 확인하고 넣었다(05:44:43Z).
|
|
774
|
+
*
|
|
775
|
+
* ⚠ `cwd` 는 **이 집합으로 버리기 전에** 센다(scrubTranscript). `attachment` 와 `system` 이
|
|
776
|
+
* cwd 를 많이 싣기 때문이다(902b54cd 에서 684 + 152줄) — 버린 뒤에 세면 표본이 조용히 준다.
|
|
777
|
+
*/
|
|
778
|
+
const DROP_TYPES = new Set(['file-history-snapshot', 'file-history-delta', 'attachment',
|
|
779
|
+
'last-prompt', 'queue-operation', 'system']);
|
|
767
780
|
|
|
768
781
|
function maskSecrets(text) {
|
|
769
782
|
let n = 0;
|
|
@@ -802,8 +815,34 @@ function maskDeep(v) {
|
|
|
802
815
|
return [v, 0];
|
|
803
816
|
}
|
|
804
817
|
|
|
818
|
+
/**
|
|
819
|
+
* 집 경로를 `~` 로 접는다. **본문과 origin_cwd 가 같은 함수를 써야 한다** —
|
|
820
|
+
* 다르게 접으면 서버에서 둘을 맞춰 볼 때 조용히 안 맞는다.
|
|
821
|
+
*/
|
|
822
|
+
function relHome(p) { return p.replace(/^\/(Users|home)\/[^/]+/, '~'); }
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* 이 대화가 **실제로 돈 자리**. 줄마다 실린 `cwd` 의 **최빈값**이다.
|
|
826
|
+
*
|
|
827
|
+
* 왜 최빈값인가 — 실측(트랜스크립트 3개 전수, 2026-08-21):
|
|
828
|
+
* 902b54cd cwd 있는 줄 6,328 최빈 6,295 (99.5%)
|
|
829
|
+
* a0f154a7 cwd 있는 줄 5,129 최빈 4,829 (94.2%)
|
|
830
|
+
* 6aead92f cwd 있는 줄 627 최빈 611 (97.4%)
|
|
831
|
+
* 소수값은 전부 최빈값의 **하위 폴더**였다(`editor/` · `www/portal/` · `mcp/` · `memory/`).
|
|
832
|
+
* 그래서 마지막 값이나 첫 값은 약하다 — 하위 폴더 한 번에 끌려간다.
|
|
833
|
+
* 같은 수면 **짧은 쪽**을 고른다(하위 폴더가 아니라 뿌리를 고르려는 것).
|
|
834
|
+
*/
|
|
835
|
+
function cwdMode(counts) {
|
|
836
|
+
let best = '', n = 0;
|
|
837
|
+
for (const [k, v] of counts) {
|
|
838
|
+
if (v > n || (v === n && k.length < best.length)) { best = k; n = v; }
|
|
839
|
+
}
|
|
840
|
+
return best;
|
|
841
|
+
}
|
|
842
|
+
|
|
805
843
|
function scrubTranscript(raw) {
|
|
806
844
|
const out = [];
|
|
845
|
+
const cwds = new Map();
|
|
807
846
|
let redacted = 0, dropped = 0, no = 0;
|
|
808
847
|
for (const line of raw.split('\n')) {
|
|
809
848
|
no++;
|
|
@@ -813,9 +852,18 @@ function scrubTranscript(raw) {
|
|
|
813
852
|
// 검열을 못 거친 줄은 안 보낸다. 조용히 흘리면 원문이 그대로 나간다.
|
|
814
853
|
throw new Error(`${no}번째 줄을 읽을 수 없습니다 — 검열을 못 거친 줄은 안 올립니다.`);
|
|
815
854
|
}
|
|
855
|
+
/*
|
|
856
|
+
* ★ **cwd 는 버리기 전에 센다.** 지금도 `attachment` 가 cwd 를 많이 싣고(902b54cd 에서 684줄)
|
|
857
|
+
* 그 종류는 바로 아래에서 버려진다. 형제 세션이 `system` 등 셋을 DROP_TYPES 에 더할 예정이라
|
|
858
|
+
* (그것도 152줄을 싣는다) 버린 뒤에 세면 표본이 **조용히** 줄어든다. 순서가 곧 방어다.
|
|
859
|
+
*/
|
|
860
|
+
if (typeof r.cwd === 'string' && r.cwd) {
|
|
861
|
+
const k = relHome(r.cwd);
|
|
862
|
+
cwds.set(k, (cwds.get(k) || 0) + 1);
|
|
863
|
+
}
|
|
816
864
|
if (DROP_TYPES.has(r.type)) { dropped++; continue; }
|
|
817
865
|
if ('toolUseResult' in r) { r.toolUseResult = MASK; redacted++; }
|
|
818
|
-
if (typeof r.cwd === 'string') r.cwd = r.cwd
|
|
866
|
+
if (typeof r.cwd === 'string') r.cwd = relHome(r.cwd);
|
|
819
867
|
const c = r?.message?.content;
|
|
820
868
|
if (typeof c === 'string') {
|
|
821
869
|
const [t, n] = maskSecrets(c); r.message.content = t; redacted += n;
|
|
@@ -839,7 +887,7 @@ function scrubTranscript(raw) {
|
|
|
839
887
|
out.push(JSON.stringify(safe));
|
|
840
888
|
}
|
|
841
889
|
if (!out.length) throw new Error('빈 트랜스크립트입니다.');
|
|
842
|
-
return { jsonl: out.join('\n') + '\n', lines: out.length, dropped, redacted };
|
|
890
|
+
return { jsonl: out.join('\n') + '\n', lines: out.length, dropped, redacted, cwd: cwdMode(cwds) };
|
|
843
891
|
}
|
|
844
892
|
|
|
845
893
|
/** cwd → 트랜스크립트 디렉터리. **해석된 경로**로 슬러그를 만든다(심볼릭 링크 함정). */
|
|
@@ -938,9 +986,23 @@ async function pushSession(tenant, uuid, title, dryRun) {
|
|
|
938
986
|
* 운영에서 이미 두 세션이 그렇게 잃었다(둘 다 updated_at 05:05:21, 같은 초).
|
|
939
987
|
* 없는 값을 「빈 값」으로 말하지 않는다 — 그 둘은 다른 뜻이다.
|
|
940
988
|
*/
|
|
989
|
+
/*
|
|
990
|
+
* ── origin_cwd 는 「일한 곳」이지 「올린 곳」이 아니다 ──────────────────
|
|
991
|
+
*
|
|
992
|
+
* 예전에는 `process.cwd()` 였다. 그건 **올릴 때 이 프로세스가 서 있던 폴더**일 뿐이라,
|
|
993
|
+
* 다른 자리에서 한 번만 올려도 출처가 통째로 바뀐다. 그리고 서버는 재업로드 때
|
|
994
|
+
* `origin_cwd=VALUES(origin_cwd)` 로 **조건 없이 덮는다**(api.php:1008) — 되돌아오지 않는다.
|
|
995
|
+
*
|
|
996
|
+
* 실제로 났다(2026-08-21): 형제 세션이 검열을 시험하려고 `/private/tmp/scrubcheck` 에서
|
|
997
|
+
* 올렸더니, 두뇌가 읽는 명부(`purpose.md`)의 「자리」 칸에 **`scrubcheck`** 가 실렸다.
|
|
998
|
+
* 묶음 판정 점수기의 place 축도 같은 값을 먹는다 — 가장 강한 축이 오염된 것이다.
|
|
999
|
+
*
|
|
1000
|
+
* 진짜 출처는 이미 손에 있었다: **본문 각 줄의 `cwd`**(scrubTranscript 가 센다).
|
|
1001
|
+
* 없을 때만 옛 방식으로 물러선다 — 그래야 퇴보가 없다.
|
|
1002
|
+
*/
|
|
941
1003
|
const body = {
|
|
942
1004
|
tenant, uuid,
|
|
943
|
-
origin_cwd:
|
|
1005
|
+
origin_cwd: sc.cwd || relHome(process.cwd()),
|
|
944
1006
|
origin_git: gitHere(), tools: toolsStamp(),
|
|
945
1007
|
jsonl: sc.jsonl,
|
|
946
1008
|
};
|