@n2world/orchestrator 1.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/dist/agent-os-rd.d.ts +100 -0
- package/dist/agent-os-rd.js +258 -0
- package/dist/audit-store.d.ts +14 -0
- package/dist/audit-store.js +107 -0
- package/dist/beta-runner.d.ts +95 -0
- package/dist/beta-runner.js +251 -0
- package/dist/beta.d.ts +102 -0
- package/dist/beta.js +180 -0
- package/dist/browser-agent.d.ts +90 -0
- package/dist/browser-agent.js +223 -0
- package/dist/channel-gateway.d.ts +74 -0
- package/dist/channel-gateway.js +270 -0
- package/dist/channels.d.ts +120 -0
- package/dist/channels.js +432 -0
- package/dist/chat-store.d.ts +29 -0
- package/dist/chat-store.js +120 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +607 -0
- package/dist/command-screen.d.ts +12 -0
- package/dist/command-screen.js +44 -0
- package/dist/commit-gate.d.ts +98 -0
- package/dist/commit-gate.js +258 -0
- package/dist/companion-api.d.ts +37 -0
- package/dist/companion-api.js +101 -0
- package/dist/conversation-graph.d.ts +39 -0
- package/dist/conversation-graph.js +92 -0
- package/dist/cost-estimator.d.ts +27 -0
- package/dist/cost-estimator.js +42 -0
- package/dist/cron-runner.d.ts +31 -0
- package/dist/cron-runner.js +46 -0
- package/dist/dashboard/chat.html +326 -0
- package/dist/dashboard/dental.html +58 -0
- package/dist/dashboard/freebie.png +0 -0
- package/dist/dashboard/icon-192.png +0 -0
- package/dist/dashboard/index.html +892 -0
- package/dist/dashboard/manifest.json +15 -0
- package/dist/dashboard/service-worker.js +28 -0
- package/dist/dashboard-server.d.ts +37 -0
- package/dist/dashboard-server.js +457 -0
- package/dist/dental-intake-service.d.ts +37 -0
- package/dist/dental-intake-service.js +61 -0
- package/dist/dental-metrics.d.ts +25 -0
- package/dist/dental-metrics.js +37 -0
- package/dist/docking.d.ts +36 -0
- package/dist/docking.js +73 -0
- package/dist/finance-mcts-candidate.d.ts +37 -0
- package/dist/finance-mcts-candidate.js +106 -0
- package/dist/finance-regulation-kr.d.ts +33 -0
- package/dist/finance-regulation-kr.js +104 -0
- package/dist/finance-workflow.d.ts +135 -0
- package/dist/finance-workflow.js +242 -0
- package/dist/gateway.d.ts +18 -0
- package/dist/gateway.js +123 -0
- package/dist/governance.d.ts +39 -0
- package/dist/governance.js +48 -0
- package/dist/governed-executor.d.ts +31 -0
- package/dist/governed-executor.js +63 -0
- package/dist/governed-llm.d.ts +41 -0
- package/dist/governed-llm.js +83 -0
- package/dist/gpu-bridge.d.ts +16 -0
- package/dist/gpu-bridge.js +53 -0
- package/dist/health.d.ts +47 -0
- package/dist/health.js +66 -0
- package/dist/identity-link.d.ts +32 -0
- package/dist/identity-link.js +98 -0
- package/dist/index.d.ts +184 -0
- package/dist/index.js +417 -0
- package/dist/integrations/emr-adapter.d.ts +41 -0
- package/dist/integrations/emr-adapter.js +63 -0
- package/dist/kakao-oauth.d.ts +16 -0
- package/dist/kakao-oauth.js +87 -0
- package/dist/knowledge-graph.d.ts +53 -0
- package/dist/knowledge-graph.js +156 -0
- package/dist/llm.d.ts +65 -0
- package/dist/llm.js +357 -0
- package/dist/mcp-client-guard.d.ts +32 -0
- package/dist/mcp-client-guard.js +179 -0
- package/dist/mcp-macaroon.d.ts +75 -0
- package/dist/mcp-macaroon.js +161 -0
- package/dist/mcts-kernel-bridge.d.ts +36 -0
- package/dist/mcts-kernel-bridge.js +99 -0
- package/dist/mcts-prior.d.ts +79 -0
- package/dist/mcts-prior.js +170 -0
- package/dist/model-router.d.ts +51 -0
- package/dist/model-router.js +75 -0
- package/dist/multi-axis-lift.d.ts +43 -0
- package/dist/multi-axis-lift.js +141 -0
- package/dist/net-guard.d.ts +39 -0
- package/dist/net-guard.js +141 -0
- package/dist/onboarding.d.ts +38 -0
- package/dist/onboarding.js +94 -0
- package/dist/oracle-anchored-search.d.ts +25 -0
- package/dist/oracle-anchored-search.js +50 -0
- package/dist/oracle.d.ts +22 -0
- package/dist/oracle.js +116 -0
- package/dist/p6-governance.d.ts +150 -0
- package/dist/p6-governance.js +252 -0
- package/dist/pairing.d.ts +22 -0
- package/dist/pairing.js +81 -0
- package/dist/personalization.d.ts +35 -0
- package/dist/personalization.js +73 -0
- package/dist/pglite-hnsw-bridge.d.ts +118 -0
- package/dist/pglite-hnsw-bridge.js +311 -0
- package/dist/pglite-store.d.ts +59 -0
- package/dist/pglite-store.js +180 -0
- package/dist/playbook.d.ts +79 -0
- package/dist/playbook.js +83 -0
- package/dist/playbooks/dental-intake.d.ts +20 -0
- package/dist/playbooks/dental-intake.js +112 -0
- package/dist/predictive-agent.d.ts +157 -0
- package/dist/predictive-agent.js +535 -0
- package/dist/prompt-optimizer.d.ts +18 -0
- package/dist/prompt-optimizer.js +104 -0
- package/dist/rate-limiter.d.ts +25 -0
- package/dist/rate-limiter.js +75 -0
- package/dist/safety-anneal.d.ts +83 -0
- package/dist/safety-anneal.js +153 -0
- package/dist/sandbox-controller.d.ts +12 -0
- package/dist/sandbox-controller.js +95 -0
- package/dist/satisfaction-metrics.d.ts +26 -0
- package/dist/satisfaction-metrics.js +61 -0
- package/dist/sensor-bridge.d.ts +53 -0
- package/dist/sensor-bridge.js +133 -0
- package/dist/session-repair.d.ts +27 -0
- package/dist/session-repair.js +66 -0
- package/dist/slack-finance-intake.d.ts +42 -0
- package/dist/slack-finance-intake.js +122 -0
- package/dist/symbolic-dynamics.d.ts +113 -0
- package/dist/symbolic-dynamics.js +420 -0
- package/dist/telemetry.d.ts +19 -0
- package/dist/telemetry.js +68 -0
- package/dist/text-embedding.d.ts +6 -0
- package/dist/text-embedding.js +42 -0
- package/dist/tier-classifier.d.ts +20 -0
- package/dist/tier-classifier.js +58 -0
- package/dist/tier-guard.d.ts +36 -0
- package/dist/tier-guard.js +56 -0
- package/dist/tui.d.ts +9 -0
- package/dist/tui.js +214 -0
- package/dist/update-security.d.ts +31 -0
- package/dist/update-security.js +112 -0
- package/dist/v-calibration.d.ts +16 -0
- package/dist/v-calibration.js +42 -0
- package/dist/value-calibration.d.ts +41 -0
- package/dist/value-calibration.js +133 -0
- package/dist/value-head.d.ts +20 -0
- package/dist/value-head.js +91 -0
- package/dist/wal-buffer.d.ts +23 -0
- package/dist/wal-buffer.js +144 -0
- package/dist/wiki-synthesizer.d.ts +80 -0
- package/dist/wiki-synthesizer.js +0 -0
- package/dist/worker-agent.d.ts +10 -0
- package/dist/worker-agent.js +19 -0
- package/package.json +65 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// g_fast 신경 기호 역학 모델 — 표준 인터페이스 (G0: 정합·타입, 동작 불변)
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// 근거: doc/arxiv/gfast_neurosymbolic_dynamics_ko.md (이론),
|
|
6
|
+
// doc/N2World_gfast_설계구현계획서_v1.0.md (G0~G5).
|
|
7
|
+
// 하이브리드 상태 s_t=(z_t, m_t) 위에서 δ(결정성 게이트)·Φ(규칙 전이)·ρ(규칙 보상)를 정의한다.
|
|
8
|
+
//
|
|
9
|
+
// 정직 고지(제1계명): G0 은 *타입·어댑터·직렬화* 만 도입한다(동작 불변·회귀 0).
|
|
10
|
+
// - 실제 Φ(컴파일러 재평가)는 G1, δ/ρ 실장은 G2, MCTS 통합·ablation 은 G3.
|
|
11
|
+
// - 현재 잠재 z 는 미사용(순수 기호 경로). z 합류는 G4(브리지)에서만.
|
|
12
|
+
// ============================================================================
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.WarmTypeChecker = exports.ruleReward = exports.RHO_GAMMA = exports.RHO_BETA = exports.RHO_ALPHA = exports.isDecidable = void 0;
|
|
48
|
+
exports.candidateToEditAction = candidateToEditAction;
|
|
49
|
+
exports.fromSymbolicMetaTuple = fromSymbolicMetaTuple;
|
|
50
|
+
exports.toSymbolicMetaTuple = toSymbolicMetaTuple;
|
|
51
|
+
exports.serializeMeta = serializeMeta;
|
|
52
|
+
exports.deserializeMeta = deserializeMeta;
|
|
53
|
+
exports.errorCount = errorCount;
|
|
54
|
+
exports.isCompilerDecidableFile = isCompilerDecidableFile;
|
|
55
|
+
exports.recompileMeta = recompileMeta;
|
|
56
|
+
exports.makePhi = makePhi;
|
|
57
|
+
exports.recompileMetaWarm = recompileMetaWarm;
|
|
58
|
+
exports.gfastEvaluate = gfastEvaluate;
|
|
59
|
+
// ── 어댑터 ──────────────────────────────────────────────────────────────────
|
|
60
|
+
/** 후보(Candidate) → 편집 행동(EditAction). 핫패스 불변, 역학 인터페이스용 변환. */
|
|
61
|
+
function candidateToEditAction(c) {
|
|
62
|
+
return { id: c.id, kind: c.kind, file: c.file, content: c.content, command: c.command };
|
|
63
|
+
}
|
|
64
|
+
/** 코어 SymbolicMetaTuple(제로카피 슬롯 포맷) → MetaTuple. astErrorCode>0 ⇒ 오차 1건으로 표현. */
|
|
65
|
+
function fromSymbolicMetaTuple(s) {
|
|
66
|
+
const astErrors = s.astErrorCode && s.astErrorCode !== 0
|
|
67
|
+
? [{ line: 0, code: `AST${s.astErrorCode}`, msg: `ast error code ${s.astErrorCode}` }]
|
|
68
|
+
: [];
|
|
69
|
+
const depMap = {};
|
|
70
|
+
for (const [k, v] of Object.entries(s.projectDependencyMap ?? {}))
|
|
71
|
+
depMap[k] = [v];
|
|
72
|
+
return {
|
|
73
|
+
targetFile: s.targetFilePath,
|
|
74
|
+
astErrors,
|
|
75
|
+
depMap,
|
|
76
|
+
buildStatus: astErrors.length ? 'fail' : 'unknown',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** MetaTuple → 코어 SymbolicMetaTuple(다운캐스트, 손실 가능 — 첫 오차 코드만 보존). */
|
|
80
|
+
function toSymbolicMetaTuple(m) {
|
|
81
|
+
const first = m.astErrors[0];
|
|
82
|
+
const code = first ? parseInt(first.code.replace(/\D/g, ''), 10) || 1 : 0;
|
|
83
|
+
const projectDependencyMap = {};
|
|
84
|
+
for (const [k, v] of Object.entries(m.depMap ?? {}))
|
|
85
|
+
projectDependencyMap[k] = v[0] ?? '';
|
|
86
|
+
return { targetFilePath: m.targetFile, astErrorCode: code, projectDependencyMap };
|
|
87
|
+
}
|
|
88
|
+
/** MetaTuple 직렬화(JSON). 왕복 보존 검증용. */
|
|
89
|
+
function serializeMeta(m) {
|
|
90
|
+
return JSON.stringify(m);
|
|
91
|
+
}
|
|
92
|
+
function deserializeMeta(s) {
|
|
93
|
+
const o = JSON.parse(s);
|
|
94
|
+
return {
|
|
95
|
+
targetFile: String(o.targetFile ?? ''),
|
|
96
|
+
astErrors: Array.isArray(o.astErrors) ? o.astErrors : [],
|
|
97
|
+
depMap: o.depMap,
|
|
98
|
+
buildStatus: o.buildStatus === 'pass' || o.buildStatus === 'fail' ? o.buildStatus : 'unknown',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** 오차 개수 |E(m)| — ρ·δ 가 참조할 헬퍼. */
|
|
102
|
+
function errorCount(m) {
|
|
103
|
+
return m.astErrors.length;
|
|
104
|
+
}
|
|
105
|
+
// ============================================================================
|
|
106
|
+
// G1 — Φ 규칙 전이(실제 컴파일러 재평가). 편집을 VFS 복사본에 적용 → 컴파일러 진단으로
|
|
107
|
+
// m_{t+1} 을 결정론적으로 산정한다. 잠재 z 는 미변경(순수 기호 경로).
|
|
108
|
+
// 정직: 여기서 "비싼 2단계"는 *테스트 오라클* 이고, Φ 는 *컴파일러*(node --check/tsc)만
|
|
109
|
+
// 돌린다 — 컴파일은 테스트보다 값싸므로, ρ(G2)로 후보를 사전 정렬해 오라클 호출을 줄인다.
|
|
110
|
+
// 단, 의미적 정답성(테스트)은 컴파일로 결정 불가 → 그런 후보는 δ=0(오라클 위임, G2).
|
|
111
|
+
// ============================================================================
|
|
112
|
+
const child_process_1 = require("child_process");
|
|
113
|
+
const _fs = __importStar(require("fs/promises"));
|
|
114
|
+
const _os = __importStar(require("os"));
|
|
115
|
+
const _path = __importStar(require("path"));
|
|
116
|
+
function _sh(cmd, cwd, timeout = 15000) {
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
(0, child_process_1.exec)(cmd, { cwd, timeout }, (err, stdout, stderr) => resolve({ code: err ? (err.code ?? 1) : 0, stdout: (stdout || '').toString(), stderr: (stderr || '').toString() }));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/** 파일 확장자로 컴파일러 진단 지원 여부. JS=node --check(구문), TS=tsc(구문+타입). */
|
|
122
|
+
function isCompilerDecidableFile(file) {
|
|
123
|
+
return /\.(c?js|mjs)$/.test(file) || isTsFile(file);
|
|
124
|
+
}
|
|
125
|
+
function isTsFile(file) {
|
|
126
|
+
return /\.(m|c)?ts$/.test(file) && !/\.d\.ts$/.test(file);
|
|
127
|
+
}
|
|
128
|
+
/** tsc 실행 바이너리 경로(설치된 typescript). 미해결 시 null → TS 결정 불가(unknown). */
|
|
129
|
+
function _tscBin() {
|
|
130
|
+
try {
|
|
131
|
+
return require.resolve('typescript/bin/tsc');
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 컴파일러 재평가로 단일 파일의 MetaTuple 산정(결정론적).
|
|
139
|
+
* JS: `node --check`(구문). TS: `tsc --noEmit`(구문+**타입**) — node --check 가 못 잡는 타입 오류 포착.
|
|
140
|
+
*/
|
|
141
|
+
async function recompileMeta(dir, relFile) {
|
|
142
|
+
const abs = _path.join(dir, relFile);
|
|
143
|
+
const absU = abs.replace(/\\/g, '/');
|
|
144
|
+
if (isTsFile(relFile)) {
|
|
145
|
+
const tsc = _tscBin();
|
|
146
|
+
if (!tsc)
|
|
147
|
+
return { targetFile: relFile, astErrors: [], buildStatus: 'unknown' }; // tsc 없음 → 결정 불가
|
|
148
|
+
const r = await _sh(`node "${tsc.replace(/\\/g, '/')}" --noEmit --skipLibCheck --strict "${absU}"`, dir, 30000);
|
|
149
|
+
if (r.code === 0)
|
|
150
|
+
return { targetFile: relFile, astErrors: [], buildStatus: 'pass' };
|
|
151
|
+
// tsc 진단: "file.ts(line,col): error TSxxxx: msg"
|
|
152
|
+
const out = r.stdout + r.stderr;
|
|
153
|
+
const errs = [];
|
|
154
|
+
const re = /\((\d+),\d+\):\s*error\s+(TS\d+):\s*([^\n]*)/g;
|
|
155
|
+
let m;
|
|
156
|
+
while ((m = re.exec(out)) !== null) {
|
|
157
|
+
errs.push({ line: parseInt(m[1], 10), code: m[2], msg: m[3].trim().slice(0, 200) });
|
|
158
|
+
}
|
|
159
|
+
if (errs.length === 0)
|
|
160
|
+
errs.push({ line: 0, code: 'TS-ERR', msg: 'tsc error' });
|
|
161
|
+
return { targetFile: relFile, astErrors: errs, buildStatus: 'fail' };
|
|
162
|
+
}
|
|
163
|
+
if (!isCompilerDecidableFile(relFile)) {
|
|
164
|
+
// 컴파일러 결정 불가 → 진단 미상(δ=0 입력). buildStatus unknown.
|
|
165
|
+
return { targetFile: relFile, astErrors: [], buildStatus: 'unknown' };
|
|
166
|
+
}
|
|
167
|
+
const r = await _sh(`node --check "${absU}"`, dir);
|
|
168
|
+
if (r.code === 0)
|
|
169
|
+
return { targetFile: relFile, astErrors: [], buildStatus: 'pass' };
|
|
170
|
+
// node 구문 오류: stderr 에 "path:line" + "SyntaxError: msg" 형태.
|
|
171
|
+
const out = r.stderr || r.stdout;
|
|
172
|
+
const lineM = out.match(/:(\d+)\n/) || out.match(/:(\d+)$/m);
|
|
173
|
+
const msgM = out.match(/(SyntaxError:[^\n]*)/) || out.match(/(Error:[^\n]*)/);
|
|
174
|
+
const err = {
|
|
175
|
+
line: lineM ? parseInt(lineM[1], 10) : 0,
|
|
176
|
+
code: 'JS-SYNTAX',
|
|
177
|
+
msg: (msgM ? msgM[1] : 'syntax error').trim().slice(0, 200),
|
|
178
|
+
};
|
|
179
|
+
return { targetFile: relFile, astErrors: [err], buildStatus: 'fail' };
|
|
180
|
+
}
|
|
181
|
+
/** 편집을 임시 복사본에 적용(워크스페이스 보존). 복사본 경로 반환(호출자가 정리). */
|
|
182
|
+
async function _applyEditToCopy(workspaceDir, a) {
|
|
183
|
+
const scratch = await _fs.mkdtemp(_path.join(_os.tmpdir(), 'n2w-phi-'));
|
|
184
|
+
await _fs.cp(workspaceDir, scratch, { recursive: true });
|
|
185
|
+
if (a.kind === 'edit' && a.file) {
|
|
186
|
+
const fp = _path.join(scratch, a.file);
|
|
187
|
+
await _fs.mkdir(_path.dirname(fp), { recursive: true });
|
|
188
|
+
await _fs.writeFile(fp, a.content ?? '', 'utf8');
|
|
189
|
+
}
|
|
190
|
+
return scratch;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Φ 팩토리: 워크스페이스 기준 규칙 전이. 편집 a 를 복사본에 적용 → 컴파일러 재평가 →
|
|
194
|
+
* 새 MetaTuple(m_{t+1}). 결정론적·건전(컴파일러 의미론과 일치, 환각 없음 — [논문] 정리 1).
|
|
195
|
+
*/
|
|
196
|
+
function makePhi(workspaceDir) {
|
|
197
|
+
return async (m, a) => {
|
|
198
|
+
const file = (a.kind === 'edit' && a.file) ? a.file : m.targetFile;
|
|
199
|
+
const scratch = await _applyEditToCopy(workspaceDir, a);
|
|
200
|
+
try {
|
|
201
|
+
const next = await recompileMeta(scratch, file);
|
|
202
|
+
return { ...next, depMap: m.depMap };
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
await _fs.rm(scratch, { recursive: true, force: true });
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
// ============================================================================
|
|
210
|
+
// G2 — δ 결정성 게이트 + ρ 규칙 보상.
|
|
211
|
+
// δ: 편집의 효과가 컴파일러로 결정 가능한가(deny-by-default). 결정 가능 ⇒ Φ/ρ 기호 경로.
|
|
212
|
+
// ρ: AST 오차 델타 보상(얕은 신호). 오라클 앵커 아님(최종 v 는 테스트 오라클).
|
|
213
|
+
// 정직: 컴파일은 구문/타입을 결정하나 *의미적 테스트 통과* 는 결정 못 한다. 따라서 ρ 는
|
|
214
|
+
// 후보를 *사전 정렬* 해 비싼 테스트 오라클 호출을 줄이는 용도다(정답 판정 아님).
|
|
215
|
+
// ============================================================================
|
|
216
|
+
/** δ — 편집의 컴파일러 결정 가능성. JS 편집만 결정 가능(현재), 그 외(명령·비JS)=0. */
|
|
217
|
+
const isDecidable = (m, a) => {
|
|
218
|
+
if (a.kind !== 'edit')
|
|
219
|
+
return false; // 명령은 컴파일러로 결정 불가
|
|
220
|
+
const file = a.file ?? m.targetFile;
|
|
221
|
+
return isCompilerDecidableFile(file);
|
|
222
|
+
};
|
|
223
|
+
exports.isDecidable = isDecidable;
|
|
224
|
+
/** ρ 계수(보상 형상). 빌드 통과 보너스 γ 가 지배적이도록. */
|
|
225
|
+
exports.RHO_ALPHA = 0.1; // 오차 감소 1건당
|
|
226
|
+
exports.RHO_BETA = 0.2; // 새 오차 1건당(페널티)
|
|
227
|
+
exports.RHO_GAMMA = 1.0; // 빌드 통과 보너스
|
|
228
|
+
/** 두 메타의 "새 오차"(line+code 기준 mNext 에만 있는 것). */
|
|
229
|
+
function _newErrors(m, mNext) {
|
|
230
|
+
const key = (e) => `${e.line}:${e.code}`;
|
|
231
|
+
const prev = new Set(m.astErrors.map(key));
|
|
232
|
+
return mNext.astErrors.filter((e) => !prev.has(key(e))).length;
|
|
233
|
+
}
|
|
234
|
+
/** ρ = αΔ|E| − β|new| + γ·[build pass]. 얕은 사전 정렬 신호([논문] §3.5). */
|
|
235
|
+
const ruleReward = (m, mNext) => {
|
|
236
|
+
const dErr = m.astErrors.length - mNext.astErrors.length;
|
|
237
|
+
const neu = _newErrors(m, mNext);
|
|
238
|
+
const pass = mNext.buildStatus === 'pass' ? 1 : 0;
|
|
239
|
+
return exports.RHO_ALPHA * dErr - exports.RHO_BETA * neu + exports.RHO_GAMMA * pass;
|
|
240
|
+
};
|
|
241
|
+
exports.ruleReward = ruleReward;
|
|
242
|
+
// ============================================================================
|
|
243
|
+
// G3c — 웜(증분) 타입체크 서비스. 콜드 `tsc` 를 매 후보 spawn 하는 대신, in-process
|
|
244
|
+
// TypeScript Language Service 로 단일 파일 진단을 *증분* 으로 얻는다(프로세스 spawn·콜드
|
|
245
|
+
// 시작 제거). 첫 호출만 프로그램 빌드(웜업), 이후 호출은 값싸다.
|
|
246
|
+
// 정직: 단일 파일 격리 체크(라이브러리 + 디스크 폴백). 교차파일 타입 의존이 큰 경우엔
|
|
247
|
+
// 프로젝트 시드가 필요하다(현재 단일 파일 범위).
|
|
248
|
+
// ============================================================================
|
|
249
|
+
const _ts = __importStar(require("typescript"));
|
|
250
|
+
class WarmTypeChecker {
|
|
251
|
+
files = new Map();
|
|
252
|
+
service;
|
|
253
|
+
options;
|
|
254
|
+
constructor(opts) {
|
|
255
|
+
this.options = {
|
|
256
|
+
strict: true, noEmit: true, skipLibCheck: true,
|
|
257
|
+
target: _ts.ScriptTarget.ES2020, module: _ts.ModuleKind.CommonJS,
|
|
258
|
+
moduleResolution: _ts.ModuleResolutionKind.NodeJs, allowJs: true, ...opts,
|
|
259
|
+
};
|
|
260
|
+
const host = {
|
|
261
|
+
getScriptFileNames: () => [...this.files.keys()],
|
|
262
|
+
getScriptVersion: (f) => String(this.files.get(f)?.version ?? 0),
|
|
263
|
+
getScriptSnapshot: (f) => {
|
|
264
|
+
const e = this.files.get(f);
|
|
265
|
+
if (e)
|
|
266
|
+
return _ts.ScriptSnapshot.fromString(e.content);
|
|
267
|
+
if (!_ts.sys.fileExists(f))
|
|
268
|
+
return undefined;
|
|
269
|
+
const c = _ts.sys.readFile(f);
|
|
270
|
+
return c != null ? _ts.ScriptSnapshot.fromString(c) : undefined;
|
|
271
|
+
},
|
|
272
|
+
getCurrentDirectory: () => process.cwd(),
|
|
273
|
+
getCompilationSettings: () => this.options,
|
|
274
|
+
getDefaultLibFileName: (o) => _ts.getDefaultLibFilePath(o),
|
|
275
|
+
fileExists: (f) => this.files.has(f) || _ts.sys.fileExists(f),
|
|
276
|
+
readFile: (f) => this.files.get(f)?.content ?? _ts.sys.readFile(f),
|
|
277
|
+
readDirectory: _ts.sys.readDirectory,
|
|
278
|
+
directoryExists: _ts.sys.directoryExists,
|
|
279
|
+
getDirectories: _ts.sys.getDirectories,
|
|
280
|
+
};
|
|
281
|
+
this.service = _ts.createLanguageService(host, _ts.createDocumentRegistry());
|
|
282
|
+
}
|
|
283
|
+
/** TS 내부는 포워드슬래시 경로를 쓰므로 키를 정규화(Windows 백슬래시 불일치 방지). */
|
|
284
|
+
static norm(p) { return p.replace(/\\/g, '/'); }
|
|
285
|
+
/** 파일 내용 설정(버전 증가 → 증분 재검사). */
|
|
286
|
+
setFile(fileName, content) {
|
|
287
|
+
const key = WarmTypeChecker.norm(fileName);
|
|
288
|
+
const prev = this.files.get(key);
|
|
289
|
+
this.files.set(key, { content, version: (prev?.version ?? 0) + 1 });
|
|
290
|
+
}
|
|
291
|
+
/** 한 파일의 구문+의미 진단(증분). */
|
|
292
|
+
diagnosticsFor(key) {
|
|
293
|
+
const diags = [
|
|
294
|
+
...this.service.getSyntacticDiagnostics(key),
|
|
295
|
+
...this.service.getSemanticDiagnostics(key),
|
|
296
|
+
];
|
|
297
|
+
return diags.map((d) => {
|
|
298
|
+
const line = d.file && typeof d.start === 'number'
|
|
299
|
+
? d.file.getLineAndCharacterOfPosition(d.start).line + 1 : 0;
|
|
300
|
+
return { line, code: `TS${d.code}`, msg: _ts.flattenDiagnosticMessageText(d.messageText, '\n').slice(0, 200) };
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
/** 단일 파일의 구문+의미 진단(증분, in-process). spawn 없음. */
|
|
304
|
+
check(fileName, content) {
|
|
305
|
+
const key = WarmTypeChecker.norm(fileName);
|
|
306
|
+
this.setFile(key, content);
|
|
307
|
+
return this.diagnosticsFor(key);
|
|
308
|
+
}
|
|
309
|
+
// ── G3d: 프로젝트 시드(교차파일) ──────────────────────────────────────────
|
|
310
|
+
/** 워크스페이스의 .ts/.tsx 를 전부 시드(node_modules·.git 제외). 프로그램이 프로젝트 전체. */
|
|
311
|
+
async seedDir(dir) {
|
|
312
|
+
const walk = async (d) => {
|
|
313
|
+
let ents;
|
|
314
|
+
try {
|
|
315
|
+
ents = await _fs.readdir(d, { withFileTypes: true });
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
for (const ent of ents) {
|
|
321
|
+
if (ent.name === 'node_modules' || ent.name === '.git' || ent.name === 'dist')
|
|
322
|
+
continue;
|
|
323
|
+
const p = _path.join(d, ent.name);
|
|
324
|
+
if (ent.isDirectory())
|
|
325
|
+
await walk(p);
|
|
326
|
+
else if (/\.(m|c)?tsx?$/.test(ent.name) && !/\.d\.ts$/.test(ent.name)) {
|
|
327
|
+
this.setFile(p, await _fs.readFile(p, 'utf8'));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
await walk(dir);
|
|
332
|
+
return this.files.size;
|
|
333
|
+
}
|
|
334
|
+
/** 디렉토리를 *최초 1회만* 시드(재사용 checker의 워밍업 amortize). 이미 시드된 dir 은 skip. */
|
|
335
|
+
seededDirs = new Set();
|
|
336
|
+
async seedDirOnce(dir) {
|
|
337
|
+
const key = WarmTypeChecker.norm(dir);
|
|
338
|
+
if (this.seededDirs.has(key))
|
|
339
|
+
return this.files.size; // 이미 시드 → 재빌드 방지
|
|
340
|
+
this.seededDirs.add(key);
|
|
341
|
+
return this.seedDir(dir);
|
|
342
|
+
}
|
|
343
|
+
/** 주어진 파일 집합의 (file:line:code)→AstError 진단 맵. */
|
|
344
|
+
diagnosticsOver(keys) {
|
|
345
|
+
const m = new Map();
|
|
346
|
+
for (const fileName of keys) {
|
|
347
|
+
for (const e of this.diagnosticsFor(fileName))
|
|
348
|
+
m.set(`${fileName}:${e.line}:${e.code}`, e);
|
|
349
|
+
}
|
|
350
|
+
return m;
|
|
351
|
+
}
|
|
352
|
+
/** 시드된 전 파일 진단(프로젝트 전체) — 폴백·전수 검사용. */
|
|
353
|
+
allProjectErrors() {
|
|
354
|
+
return this.diagnosticsOver(this.files.keys());
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* G3e 최적화 — 편집 파일이 *영향을 줄 수 있는* 파일 집합: {편집파일} ∪ 그 파일을 (전이적으로)
|
|
358
|
+
* import 하는 의존자. 편집은 다른 파일의 내용을 바꾸지 않으므로 새 오차는 이 집합에만 생긴다(건전).
|
|
359
|
+
* `getFileReferences`(역참조)로 BFS. 미지원/실패 시 전 파일로 폴백(건전성 우선).
|
|
360
|
+
*/
|
|
361
|
+
affectedFiles(editedAbsFile) {
|
|
362
|
+
const start = WarmTypeChecker.norm(editedAbsFile);
|
|
363
|
+
const visited = new Set([start]);
|
|
364
|
+
const queue = [start];
|
|
365
|
+
try {
|
|
366
|
+
while (queue.length) {
|
|
367
|
+
const f = queue.shift();
|
|
368
|
+
const refs = this.service.getFileReferences(f); // 이 파일을 참조(import)하는 위치들
|
|
369
|
+
for (const r of refs) {
|
|
370
|
+
const fn = WarmTypeChecker.norm(r.fileName);
|
|
371
|
+
if (this.files.has(fn) && !visited.has(fn)) {
|
|
372
|
+
visited.add(fn);
|
|
373
|
+
queue.push(fn);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
return [...this.files.keys()]; // 폴백: 전 파일(건전성 유지)
|
|
380
|
+
}
|
|
381
|
+
return [...visited];
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* 편집이 *프로젝트에 새로 도입하는* 오차(교차파일 포함). **영향 파일 집합만** 전후 진단 비교
|
|
385
|
+
* (전수 대신 {편집파일}∪의존자) → 후보당 비용 절감. 체커 상태는 호출 후 원복.
|
|
386
|
+
* @param affected 사전 계산된 영향 집합(동일 파일을 여러 후보가 편집할 때 캐시 재사용). 미지정 시 자동.
|
|
387
|
+
*/
|
|
388
|
+
addedErrorsForEdit(editedAbsFile, content, affected) {
|
|
389
|
+
const key = WarmTypeChecker.norm(editedAbsFile);
|
|
390
|
+
const scope = affected ?? this.affectedFiles(key);
|
|
391
|
+
const before = this.diagnosticsOver(scope);
|
|
392
|
+
const old = this.files.get(key)?.content;
|
|
393
|
+
this.setFile(key, content);
|
|
394
|
+
const after = this.diagnosticsOver(scope);
|
|
395
|
+
if (old !== undefined)
|
|
396
|
+
this.setFile(key, old); // 원복(증분 캐시 유지)
|
|
397
|
+
const added = [];
|
|
398
|
+
for (const [k, e] of after)
|
|
399
|
+
if (!before.has(k))
|
|
400
|
+
added.push(e);
|
|
401
|
+
return added;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
exports.WarmTypeChecker = WarmTypeChecker;
|
|
405
|
+
/** 웜 체커로 MetaTuple 산정(콜드 tsc spawn 대신 in-process 증분). */
|
|
406
|
+
function recompileMetaWarm(checker, fileName, content) {
|
|
407
|
+
const errs = checker.check(fileName, content);
|
|
408
|
+
return { targetFile: fileName, astErrors: errs, buildStatus: errs.length === 0 ? 'pass' : 'fail' };
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* 한 후보에 대한 g_fast 기호 경로 평가: δ → (Φ → ρ). 결정 불가면 {decidable:false}(오라클 위임).
|
|
412
|
+
* 비싼 테스트 오라클 대신 *컴파일러* 만 돌린다.
|
|
413
|
+
*/
|
|
414
|
+
async function gfastEvaluate(workspaceDir, m0, a) {
|
|
415
|
+
if (!(0, exports.isDecidable)(m0, a))
|
|
416
|
+
return { decidable: false };
|
|
417
|
+
const phi = makePhi(workspaceDir);
|
|
418
|
+
const mNext = await phi(m0, a);
|
|
419
|
+
return { decidable: true, mNext, rho: (0, exports.ruleReward)(m0, mNext) };
|
|
420
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface Telemetry {
|
|
2
|
+
cpuPercent: number;
|
|
3
|
+
memUsedMB: number;
|
|
4
|
+
memPercent: number;
|
|
5
|
+
heapUsedMB: number;
|
|
6
|
+
uptimeSec: number;
|
|
7
|
+
loadAvg1: number;
|
|
8
|
+
activeTasks: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class TelemetrySource {
|
|
11
|
+
private lastCpu;
|
|
12
|
+
private lastTime;
|
|
13
|
+
private active;
|
|
14
|
+
taskStarted(): void;
|
|
15
|
+
taskEnded(): void;
|
|
16
|
+
setActive(n: number): void;
|
|
17
|
+
/** 직전 샘플 이후의 실제 CPU·메모리 사용을 측정해 반환한다. */
|
|
18
|
+
sample(): Telemetry;
|
|
19
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TelemetrySource = void 0;
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
class TelemetrySource {
|
|
39
|
+
lastCpu = process.cpuUsage();
|
|
40
|
+
lastTime = Date.now();
|
|
41
|
+
active = 0;
|
|
42
|
+
taskStarted() { this.active++; }
|
|
43
|
+
taskEnded() { this.active = Math.max(0, this.active - 1); }
|
|
44
|
+
setActive(n) { this.active = Math.max(0, n); }
|
|
45
|
+
/** 직전 샘플 이후의 실제 CPU·메모리 사용을 측정해 반환한다. */
|
|
46
|
+
sample() {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const cpu = process.cpuUsage(this.lastCpu); // delta(마이크로초)
|
|
49
|
+
const elapsedMs = Math.max(1, now - this.lastTime);
|
|
50
|
+
// CPU 마이크로초 → 밀리초, 벽시계 경과 대비 비율 = 사용률(%). 멀티코어면 100% 초과 가능 → 코어수로 클램프.
|
|
51
|
+
const cores = Math.max(1, os.cpus().length);
|
|
52
|
+
const cpuPercent = ((cpu.user + cpu.system) / 1000) / elapsedMs * 100 / cores;
|
|
53
|
+
this.lastCpu = process.cpuUsage();
|
|
54
|
+
this.lastTime = now;
|
|
55
|
+
const mem = process.memoryUsage();
|
|
56
|
+
const totalMem = os.totalmem();
|
|
57
|
+
return {
|
|
58
|
+
cpuPercent: +Math.min(100, Math.max(0, cpuPercent)).toFixed(1),
|
|
59
|
+
memUsedMB: +(mem.rss / 1048576).toFixed(1),
|
|
60
|
+
memPercent: +((mem.rss / totalMem) * 100).toFixed(2),
|
|
61
|
+
heapUsedMB: +(mem.heapUsed / 1048576).toFixed(1),
|
|
62
|
+
uptimeSec: Math.round(process.uptime()),
|
|
63
|
+
loadAvg1: +(os.loadavg()[0] || 0).toFixed(2),
|
|
64
|
+
activeTasks: this.active,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.TelemetrySource = TelemetrySource;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** 해시 bag-of-tokens 임베딩(차원 dim, L2 정규화). 한글/영문/숫자 토큰 지원. */
|
|
2
|
+
export declare function textEmbedding(text: string, dim?: number): number[];
|
|
3
|
+
/** 코사인 유사도∈[-1,1]. 정규화 벡터면 내적과 동일. */
|
|
4
|
+
export declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
5
|
+
/** 텍스트–컨텍스트 관련성∈[0,1]: 임베딩 코사인 유사도(음수는 0으로 클램프). */
|
|
6
|
+
export declare function embeddingRelevance(text: string, context: string): number;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// 텍스트 임베딩 기반 관련성(가치 랭킹 g_fast 신호 고도화)
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// 정직 고지: 이것은 **해시 기반 bag-of-tokens 임베딩**이다 — 실제 의미(LLM) 임베딩이 아니다.
|
|
6
|
+
// 단순 토큰-집합 겹침보다 빈도·정규화를 반영해 관련성을 graded(연속)로 준다는 점이 개선이다.
|
|
7
|
+
// (SkillManager 의 mock 임베딩과 동일 계열. 실 의미 임베딩은 후속 과제.)
|
|
8
|
+
// ============================================================================
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.textEmbedding = textEmbedding;
|
|
11
|
+
exports.cosineSimilarity = cosineSimilarity;
|
|
12
|
+
exports.embeddingRelevance = embeddingRelevance;
|
|
13
|
+
/** 해시 bag-of-tokens 임베딩(차원 dim, L2 정규화). 한글/영문/숫자 토큰 지원. */
|
|
14
|
+
function textEmbedding(text, dim = 128) {
|
|
15
|
+
const v = new Array(dim).fill(0);
|
|
16
|
+
const tokens = (text || '').toLowerCase().split(/[^a-z0-9가-힣]+/).filter((t) => t.length > 0);
|
|
17
|
+
for (const tok of tokens) {
|
|
18
|
+
let h = 0;
|
|
19
|
+
for (let i = 0; i < tok.length; i++)
|
|
20
|
+
h = (h * 31 + tok.charCodeAt(i)) % dim;
|
|
21
|
+
v[h] += 1;
|
|
22
|
+
}
|
|
23
|
+
const mag = Math.sqrt(v.reduce((a, x) => a + x * x, 0));
|
|
24
|
+
return mag === 0 ? v : v.map((x) => x / mag);
|
|
25
|
+
}
|
|
26
|
+
/** 코사인 유사도∈[-1,1]. 정규화 벡터면 내적과 동일. */
|
|
27
|
+
function cosineSimilarity(a, b) {
|
|
28
|
+
const n = Math.min(a.length, b.length);
|
|
29
|
+
let dot = 0, na = 0, nb = 0;
|
|
30
|
+
for (let i = 0; i < n; i++) {
|
|
31
|
+
dot += a[i] * b[i];
|
|
32
|
+
na += a[i] * a[i];
|
|
33
|
+
nb += b[i] * b[i];
|
|
34
|
+
}
|
|
35
|
+
if (na === 0 || nb === 0)
|
|
36
|
+
return 0;
|
|
37
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
38
|
+
}
|
|
39
|
+
/** 텍스트–컨텍스트 관련성∈[0,1]: 임베딩 코사인 유사도(음수는 0으로 클램프). */
|
|
40
|
+
function embeddingRelevance(text, context) {
|
|
41
|
+
return Math.max(0, cosineSimilarity(textEmbedding(text), textEmbedding(context)));
|
|
42
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type Tier = 0 | 1 | 2;
|
|
2
|
+
export interface ClassifyInput {
|
|
3
|
+
/** 분류 대상 텍스트(메시지 본문 등). */
|
|
4
|
+
text: string;
|
|
5
|
+
/** 데이터 종류 힌트(있으면 우선 적용). */
|
|
6
|
+
kind?: 'sensor' | 'voice' | 'credential' | 'task' | 'public' | 'unknown';
|
|
7
|
+
/** 명시적으로 공개 무해임이 보증된 경우만 true(예: 공개 문서 검색결과). */
|
|
8
|
+
publicAttested?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface TierDecision {
|
|
11
|
+
tier: Tier;
|
|
12
|
+
/** 발동한 규칙 id(감사용). */
|
|
13
|
+
ruleId: string;
|
|
14
|
+
/** 사람이 읽는 사유. */
|
|
15
|
+
reason: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 규칙기반 Tier 분류. 미분류·불확실은 Tier-0(default-deny).
|
|
19
|
+
*/
|
|
20
|
+
export declare function classifyTier(input: ClassifyInput): TierDecision;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// v2.5.2 Phase S — Tier 분류기 (규칙기반·기본거부)
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// 계획서 v2.5.2 §5: 데이터가 어떤 Tier를 *부여*받는지 결정. 분류가 틀리면 §4 경계는
|
|
6
|
+
// 종이다. 핵심 원칙:
|
|
7
|
+
// 1) 규칙기반(결정론적). LLM 자동분류는 보조일 뿐 신뢰 근거 아님(순환 금지).
|
|
8
|
+
// 2) 기본거부(default-deny): 미분류·불확실 = Tier-0(가장 보수적·로컬전용).
|
|
9
|
+
// Tier-0: 센서/음성 원본·PII·비밀·자격증명·미분류 → 로컬 모델만, 외부 송신 금지
|
|
10
|
+
// Tier-1: 일반 작업 컨텍스트(비민감) → 로컬 우선, 외부는 동의+로깅 시에만
|
|
11
|
+
// Tier-2: 공개·무해 → 폭넓은 제공사 허용
|
|
12
|
+
//
|
|
13
|
+
// 정직 고지(제1계명): 본 분류기는 *명시적 규칙*만 신뢰한다. 애매하면 Tier-0로 강등한다.
|
|
14
|
+
// ============================================================================
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.classifyTier = classifyTier;
|
|
17
|
+
// PII/비밀 탐지 규칙(보수적·명시적). 매칭되면 Tier-0.
|
|
18
|
+
const SECRET_PATTERNS = [
|
|
19
|
+
['PII-EMAIL', /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/],
|
|
20
|
+
['PII-PHONE', /\b01[016789][-\s]?\d{3,4}[-\s]?\d{4}\b/],
|
|
21
|
+
['PII-RRN', /\b\d{6}[-\s]?\d{7}\b/], // 주민등록번호 형태
|
|
22
|
+
['PII-CARD', /\b(?:\d[ -]?){13,16}\b/],
|
|
23
|
+
['SECRET-KEY', /\b(sk-[A-Za-z0-9]{8,}|xox[baprs]-[A-Za-z0-9-]{8,}|AKIA[0-9A-Z]{12,}|ghp_[A-Za-z0-9]{20,})\b/],
|
|
24
|
+
['SECRET-WORD', /\b(api[_\s-]?key|secret|password|passwd|token|credential|private[_\s-]?key)\b/i],
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* 규칙기반 Tier 분류. 미분류·불확실은 Tier-0(default-deny).
|
|
28
|
+
*/
|
|
29
|
+
function classifyTier(input) {
|
|
30
|
+
const text = input.text ?? '';
|
|
31
|
+
// 1) 종류 힌트 우선 — 민감 종류는 무조건 Tier-0
|
|
32
|
+
switch (input.kind) {
|
|
33
|
+
case 'sensor':
|
|
34
|
+
return { tier: 0, ruleId: 'KIND-SENSOR', reason: '물리 센서 원본(위치/카메라) → Tier-0' };
|
|
35
|
+
case 'voice':
|
|
36
|
+
return { tier: 0, ruleId: 'KIND-VOICE', reason: '음성 원본 → Tier-0' };
|
|
37
|
+
case 'credential':
|
|
38
|
+
return { tier: 0, ruleId: 'KIND-CREDENTIAL', reason: '자격증명 → Tier-0' };
|
|
39
|
+
default:
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
// 2) 본문에 PII/비밀 패턴이 있으면 Tier-0
|
|
43
|
+
for (const [id, re] of SECRET_PATTERNS) {
|
|
44
|
+
if (re.test(text)) {
|
|
45
|
+
return { tier: 0, ruleId: id, reason: `민감정보 패턴(${id}) 탐지 → Tier-0` };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// 3) 명시적으로 공개 무해임이 보증된 경우만 Tier-2
|
|
49
|
+
if (input.publicAttested === true && input.kind === 'public') {
|
|
50
|
+
return { tier: 2, ruleId: 'PUBLIC-ATTESTED', reason: '공개 무해 보증 → Tier-2' };
|
|
51
|
+
}
|
|
52
|
+
// 4) 일반 작업 텍스트로 명시된 경우만 Tier-1
|
|
53
|
+
if (input.kind === 'task') {
|
|
54
|
+
return { tier: 1, ruleId: 'KIND-TASK', reason: '일반 작업 컨텍스트(비민감) → Tier-1' };
|
|
55
|
+
}
|
|
56
|
+
// 5) 그 외(미분류·불확실) = 기본거부 → Tier-0
|
|
57
|
+
return { tier: 0, ruleId: 'DEFAULT-DENY', reason: '미분류·불확실 → 기본거부(Tier-0)' };
|
|
58
|
+
}
|