@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,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// P6 — 거버넌스 이벤트 버스 · 헌법 롤백 · 정온 시간 게이트 · v2.x 완료 종합 점검
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// (계획서 v2.4 §7 P6, §6 완료 정의, 헌법 §0 반-mock 정직성)
|
|
6
|
+
//
|
|
7
|
+
// 정직 고지(제1계명): 모든 집계는 **실 이벤트**에서 온다. `Math.random` 가짜 텔레메트리 금지.
|
|
8
|
+
// v2.x 완료 점검은 **베타 미가동 항목을 정직하게 unmet 으로 표기**한다(억지 통과 금지).
|
|
9
|
+
//
|
|
10
|
+
// 재사용: `governance.ts`의 GovernanceController(킬스위치)를 롤백의 중단 경로로 사용한다.
|
|
11
|
+
// ============================================================================
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.QuietTimeGate = exports.DEFAULT_QUIET_CONFIG = exports.ConstitutionalRollback = exports.DEFAULT_ROLLBACK_CONFIG = exports.GovernanceEventBus = exports.DEFAULT_MAX_RETAINED_EVENTS = void 0;
|
|
14
|
+
exports.v2DoDReport = v2DoDReport;
|
|
15
|
+
/** 기본 이벤트 보관 상한(링버퍼). 장수명 대시보드에서 메모리 무한 증가 방지. */
|
|
16
|
+
exports.DEFAULT_MAX_RETAINED_EVENTS = 1000;
|
|
17
|
+
/**
|
|
18
|
+
* 위반/차단 이벤트 버스 — 실 이벤트만 집계·구독 배포(가짜 텔레메트리 0).
|
|
19
|
+
*
|
|
20
|
+
* 메모리: 최근 `maxRetained` 건만 링버퍼로 보관(표시용 `recent`/`all`). **누적 집계(`counts`)는
|
|
21
|
+
* 보관과 독립적으로 증분 카운터에 유지**되므로, 보관을 상한해도 all-time 통계(특히 안전 핵심인
|
|
22
|
+
* `tier0ExfilEscaped`)는 정확하다. 장수명 서버에서 `events` 가 무한 증가하던 문제 해소.
|
|
23
|
+
*/
|
|
24
|
+
class GovernanceEventBus {
|
|
25
|
+
listeners = [];
|
|
26
|
+
events = []; // 최근 maxRetained 건만(링버퍼)
|
|
27
|
+
maxRetained;
|
|
28
|
+
// 누적 집계(보관 상한과 독립 — all-time 정확).
|
|
29
|
+
agg = {
|
|
30
|
+
total: 0,
|
|
31
|
+
blocked: 0,
|
|
32
|
+
t0attempt: 0,
|
|
33
|
+
t0escaped: 0,
|
|
34
|
+
denyDefault: 0,
|
|
35
|
+
hardFail: 0,
|
|
36
|
+
criticCalls: 0,
|
|
37
|
+
tokens: 0,
|
|
38
|
+
};
|
|
39
|
+
constructor(maxRetained = exports.DEFAULT_MAX_RETAINED_EVENTS) {
|
|
40
|
+
this.maxRetained = Math.max(1, maxRetained);
|
|
41
|
+
}
|
|
42
|
+
subscribe(fn) {
|
|
43
|
+
this.listeners.push(fn);
|
|
44
|
+
return () => {
|
|
45
|
+
const i = this.listeners.indexOf(fn);
|
|
46
|
+
if (i >= 0)
|
|
47
|
+
this.listeners.splice(i, 1);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
publish(e) {
|
|
51
|
+
// 누적 집계 증분(보관 상한과 독립 — 이전엔 counts() 가 events 전체를 순회했다).
|
|
52
|
+
this.agg.total++;
|
|
53
|
+
if (!e.allowed)
|
|
54
|
+
this.agg.blocked++;
|
|
55
|
+
if (e.dataTier === 0 && e.destination === 'external')
|
|
56
|
+
this.agg.t0attempt++;
|
|
57
|
+
if (e.tier0ExfilEscaped)
|
|
58
|
+
this.agg.t0escaped++;
|
|
59
|
+
if (e.layer === 'deny-by-default')
|
|
60
|
+
this.agg.denyDefault++;
|
|
61
|
+
if (e.hardGate === false)
|
|
62
|
+
this.agg.hardFail++;
|
|
63
|
+
if (e.cost?.criticCalls)
|
|
64
|
+
this.agg.criticCalls += e.cost.criticCalls;
|
|
65
|
+
if (e.cost?.tokens)
|
|
66
|
+
this.agg.tokens += e.cost.tokens;
|
|
67
|
+
// 최근 이벤트만 보관(메모리 상한) — ConstitutionalRollback.window 와 동일 링버퍼 관용구.
|
|
68
|
+
this.events.push(e);
|
|
69
|
+
if (this.events.length > this.maxRetained)
|
|
70
|
+
this.events.shift();
|
|
71
|
+
for (const fn of this.listeners)
|
|
72
|
+
fn(e);
|
|
73
|
+
}
|
|
74
|
+
recent(n = 50) {
|
|
75
|
+
return this.events.slice(-n);
|
|
76
|
+
}
|
|
77
|
+
/** 보관된(최근 maxRetained 건) 이벤트. all-time 누적이 필요하면 `counts()` 를 쓴다. */
|
|
78
|
+
all() {
|
|
79
|
+
return this.events;
|
|
80
|
+
}
|
|
81
|
+
/** 실측 누적 집계 — 대시보드가 표시할 안전·비용 지표(보관 상한과 무관하게 all-time). */
|
|
82
|
+
counts() {
|
|
83
|
+
const a = this.agg;
|
|
84
|
+
return {
|
|
85
|
+
total: a.total,
|
|
86
|
+
blocked: a.blocked,
|
|
87
|
+
blockRate: a.total ? +(a.blocked / a.total).toFixed(4) : 0,
|
|
88
|
+
tier0ExternalAttempts: a.t0attempt,
|
|
89
|
+
tier0ExfilEscaped: a.t0escaped,
|
|
90
|
+
denyByDefault: a.denyDefault,
|
|
91
|
+
hardGateFailures: a.hardFail,
|
|
92
|
+
criticCalls: a.criticCalls,
|
|
93
|
+
totalTokens: a.tokens,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.GovernanceEventBus = GovernanceEventBus;
|
|
98
|
+
exports.DEFAULT_ROLLBACK_CONFIG = {
|
|
99
|
+
denyRateWindow: 20,
|
|
100
|
+
denyRateThreshold: 0.6,
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* 헌법 위반(① Tier-0 실유출 ② 차단율 급증)을 이벤트 버스로 구독해 **즉시 중단·롤백**한다.
|
|
104
|
+
* 중단은 GovernanceController.kill 로(재사용), 회귀는 권장 폴백 신호로 노출(P3.5 단순화 철회·개인화 OFF 등).
|
|
105
|
+
*/
|
|
106
|
+
class ConstitutionalRollback {
|
|
107
|
+
gov;
|
|
108
|
+
cfg;
|
|
109
|
+
state = { rolledBack: false, reason: '', at: null, recommendedFallback: null };
|
|
110
|
+
window = []; // 최근 이벤트의 blocked 여부
|
|
111
|
+
constructor(gov, cfg = exports.DEFAULT_ROLLBACK_CONFIG) {
|
|
112
|
+
this.gov = gov;
|
|
113
|
+
this.cfg = cfg;
|
|
114
|
+
}
|
|
115
|
+
/** 버스에 구독한다(반환: 구독 해제 함수). */
|
|
116
|
+
attach(bus) {
|
|
117
|
+
return bus.subscribe((e) => this.onEvent(e));
|
|
118
|
+
}
|
|
119
|
+
trigger(reason, fallback) {
|
|
120
|
+
if (this.state.rolledBack)
|
|
121
|
+
return; // 이미 롤백됨
|
|
122
|
+
this.state = { rolledBack: true, reason, at: Date.now(), recommendedFallback: fallback };
|
|
123
|
+
this.gov.kill(`constitutional-rollback: ${reason}`); // 즉시 중단(재사용)
|
|
124
|
+
}
|
|
125
|
+
onEvent(e) {
|
|
126
|
+
// ① Tier-0 데이터 실유출 — 단 1건이라도 즉시 롤백(헌법 최우선).
|
|
127
|
+
if (e.tier0ExfilEscaped) {
|
|
128
|
+
this.trigger('Tier-0 데이터 외부 유출 관측(헌법 위반)', 'Tier-0 외부 전송 전면 차단 + 운영자 확인');
|
|
129
|
+
}
|
|
130
|
+
// ② 차단율 급증(레드팀 거부·위반 폭주) — 슬라이딩 윈도우.
|
|
131
|
+
this.window.push(!e.allowed);
|
|
132
|
+
if (this.window.length > this.cfg.denyRateWindow)
|
|
133
|
+
this.window.shift();
|
|
134
|
+
if (this.window.length >= this.cfg.denyRateWindow) {
|
|
135
|
+
const rate = this.window.filter(Boolean).length / this.window.length;
|
|
136
|
+
if (rate >= this.cfg.denyRateThreshold) {
|
|
137
|
+
this.trigger(`차단율 급증(${(rate * 100).toFixed(0)}% ≥ ${(this.cfg.denyRateThreshold * 100).toFixed(0)}%)`, '능동성 강도 하향 + 개인화 OFF(부칙3) + P3.5 hard-gate 폴백');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
status() {
|
|
142
|
+
return { ...this.state };
|
|
143
|
+
}
|
|
144
|
+
/** 운영자 확인 후 롤백 해제(킬스위치 리셋 포함). */
|
|
145
|
+
reset() {
|
|
146
|
+
this.state = { rolledBack: false, reason: '', at: null, recommendedFallback: null };
|
|
147
|
+
this.window = [];
|
|
148
|
+
this.gov.reset();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
exports.ConstitutionalRollback = ConstitutionalRollback;
|
|
152
|
+
exports.DEFAULT_QUIET_CONFIG = { startHour: 22, endHour: 7, intensity: 0.3 };
|
|
153
|
+
const URGENCY_SCORE = { low: 0.2, normal: 0.5, high: 0.9 };
|
|
154
|
+
class QuietTimeGate {
|
|
155
|
+
cfg;
|
|
156
|
+
constructor(cfg = {}) {
|
|
157
|
+
this.cfg = { ...exports.DEFAULT_QUIET_CONFIG, ...cfg };
|
|
158
|
+
}
|
|
159
|
+
setIntensity(x) {
|
|
160
|
+
this.cfg.intensity = Math.max(0, Math.min(1, x));
|
|
161
|
+
}
|
|
162
|
+
config() {
|
|
163
|
+
return { ...this.cfg };
|
|
164
|
+
}
|
|
165
|
+
/** 주어진 시각이 정온 시간대인가(자정 넘김 처리). */
|
|
166
|
+
isQuiet(date = new Date()) {
|
|
167
|
+
const h = date.getHours();
|
|
168
|
+
const { startHour, endHour } = this.cfg;
|
|
169
|
+
if (startHour === endHour)
|
|
170
|
+
return false;
|
|
171
|
+
if (startHour < endHour)
|
|
172
|
+
return h >= startHour && h < endHour; // 같은 날
|
|
173
|
+
return h >= startHour || h < endHour; // 자정 넘김(22→7)
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 백그라운드 능동 작업을 허용할지. 정온 시간엔 억제하되, 긴급도와 능동성 강도로 조절한다.
|
|
177
|
+
* 규칙: 정온 아니면 허용. 정온이면 (urgencyScore + intensity ≥ 1.0) 일 때만 허용.
|
|
178
|
+
* - intensity=0: 정온 중 전부 억제(high 도). intensity=1: 전부 허용.
|
|
179
|
+
* - high(0.9)는 intensity≥0.1, normal(0.5)은 intensity≥0.5, low(0.2)는 intensity≥0.8 필요.
|
|
180
|
+
*/
|
|
181
|
+
allowBackgroundAction(date = new Date(), urgency = 'normal') {
|
|
182
|
+
if (!this.isQuiet(date))
|
|
183
|
+
return { allowed: true, reason: '정온 시간 아님 — 허용' };
|
|
184
|
+
const allowed = URGENCY_SCORE[urgency] + this.cfg.intensity >= 1.0 - 1e-9;
|
|
185
|
+
return {
|
|
186
|
+
allowed,
|
|
187
|
+
reason: allowed
|
|
188
|
+
? `정온이나 긴급도(${urgency})·강도(${this.cfg.intensity}) 충족 — 허용`
|
|
189
|
+
: `정온 시간 — 백그라운드 억제(긴급도 ${urgency}, 강도 ${this.cfg.intensity})`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
exports.QuietTimeGate = QuietTimeGate;
|
|
194
|
+
/**
|
|
195
|
+
* v2.x 완료 정의 5개를 점검한다. **베타 미가동이면 ①②를 정직하게 unmet 으로** 표기한다
|
|
196
|
+
* (접지 영가설 — master §2: 바닥은 인간이다). 억지 통과 금지.
|
|
197
|
+
*/
|
|
198
|
+
function v2DoDReport(inp) {
|
|
199
|
+
const criteria = [
|
|
200
|
+
{
|
|
201
|
+
name: '① 출시(클로즈드 베타 N명 인도)',
|
|
202
|
+
status: inp.betaUsersDelivered > 0 ? 'met' : 'unmet',
|
|
203
|
+
evidence: inp.betaUsersDelivered > 0
|
|
204
|
+
? `베타 ${inp.betaUsersDelivered}명 인도(코드·로그 증거).`
|
|
205
|
+
: 'B0 베타 인프라(동의·온보딩·피드백·Tier 가드)는 구현·검증됐으나 **실사용자 인도 0명** — 미가동(정직).',
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: '② 핵심 가설(실사용자 리프트 측정)',
|
|
209
|
+
status: inp.realUserLiftMeasured ? 'met' : 'unmet',
|
|
210
|
+
evidence: inp.realUserLiftMeasured
|
|
211
|
+
? 'B1 MultiAxisLift 가 실사용자 데이터로 측정됨.'
|
|
212
|
+
: 'B1 MultiAxisLift 4축 측정 코드·통계 검정은 구현됐으나 **실사용자 라벨 부재 → (목표; 미측정)**. 만족 축 미반영.',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: '③ 안전(거부율 분리 보고 + Tier-0 0/N)',
|
|
216
|
+
status: inp.safetySeparatedReported && inp.tier0ExfilObserved === 0 ? 'met' : 'partial',
|
|
217
|
+
evidence: `P4.7 위반 포착 규칙/비평 분리 보고 + P5 Tier-0 외부 반출 관측 ${inp.tier0ExfilObserved}/N. ` +
|
|
218
|
+
(inp.safetySeparatedReported && inp.tier0ExfilObserved === 0
|
|
219
|
+
? '합성/실제 일부 분리 — 인간-라벨 레드팀/베타 골드는 (목표; 미측정).'
|
|
220
|
+
: '분리 보고 또는 Tier-0 보장 미충족.'),
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: '④ 정직성((목표;미측정) 준수·mock 0·명칭 인플레이션 0)',
|
|
224
|
+
status: inp.honestyHeld ? 'met' : 'unmet',
|
|
225
|
+
evidence: inp.honestyHeld
|
|
226
|
+
? '전 단계 측정 전 주장은 (목표;미측정) 표기, 가짜 텔레메트리·시뮬 0, 실 LLM/오라클/HMAC 호출 증거 첨부.'
|
|
227
|
+
: '정직성 위반 발견.',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: '⑤ 복잡도(영가설 준수 — 정당화 못 한 부품 0)',
|
|
231
|
+
status: inp.complexityWithinBudget ? 'met' : 'partial',
|
|
232
|
+
evidence: inp.complexityWithinBudget
|
|
233
|
+
? '신규는 사실상 모델-비평 축소·SmoothSafetyAnneal·DurabilitySpec; ParetoFrontierKeeper 제거, 자가치유 연기. 영가설로 관리.'
|
|
234
|
+
: '복잡도 예산 초과 또는 정당화 미흡 부품 존재.',
|
|
235
|
+
},
|
|
236
|
+
];
|
|
237
|
+
const metCount = criteria.filter((c) => c.status === 'met').length;
|
|
238
|
+
const partialCount = criteria.filter((c) => c.status === 'partial').length;
|
|
239
|
+
const unmetCount = criteria.filter((c) => c.status === 'unmet').length;
|
|
240
|
+
const complete = metCount === criteria.length;
|
|
241
|
+
return {
|
|
242
|
+
criteria,
|
|
243
|
+
metCount,
|
|
244
|
+
partialCount,
|
|
245
|
+
unmetCount,
|
|
246
|
+
complete,
|
|
247
|
+
verdict: complete
|
|
248
|
+
? 'v2.x 완료 — 5개 정의 충족.'
|
|
249
|
+
: `v2.x **미완(정직)** — ${metCount}/5 met, ${partialCount} partial, ${unmetCount} unmet. ` +
|
|
250
|
+
'핵심 미충족은 ①출시·②실사용자 리프트(베타 미가동). 다음 산출물은 새 계획서가 아니라 **베타 실행**이다(접지 영가설).',
|
|
251
|
+
};
|
|
252
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface PairingChallenge {
|
|
2
|
+
qrToken: string;
|
|
3
|
+
pin: string;
|
|
4
|
+
expiresAt: number;
|
|
5
|
+
}
|
|
6
|
+
export interface PairingResult {
|
|
7
|
+
ok: boolean;
|
|
8
|
+
reason: string;
|
|
9
|
+
companionToken?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class PairingManager {
|
|
12
|
+
private ttlMs;
|
|
13
|
+
private pending;
|
|
14
|
+
constructor(ttlMs?: number);
|
|
15
|
+
/** PC가 QR+PIN 발급. */
|
|
16
|
+
issue(now?: number): PairingChallenge;
|
|
17
|
+
/**
|
|
18
|
+
* 모바일이 QR 스캔 + PIN 입력으로 페어링 시도. 일회성·만료·PIN·루프백 검증.
|
|
19
|
+
* @param loopbackOrLan 동일 LAN/루프백 경로 여부(외부 릴레이는 옵트인 — 기본 false 거부)
|
|
20
|
+
*/
|
|
21
|
+
redeem(qrToken: string, pin: string, loopbackOrLan: boolean, now?: number): PairingResult;
|
|
22
|
+
}
|
package/dist/pairing.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// P4 — 모바일↔PC 페어링 계약 (앱 v1.2 §4.3) — TS측(검증 가능)
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// QR 토큰(일회성·만료·PIN 바인딩)으로 폰을 PC 상주 에이전트의 리모컨으로 페어링.
|
|
6
|
+
// 성공 시 companion 토큰을 교환한다. 위조/만료/재사용/PIN 불일치는 거부.
|
|
7
|
+
//
|
|
8
|
+
// 정직 고지(제1계명): 본 모듈은 *페어링 핸드셰이크 계약*(테스트 가능). 모바일 앱·
|
|
9
|
+
// 카메라 QR·mDNS는 네이티브(이 환경 미빌드 — companions/mobile 스캐폴드).
|
|
10
|
+
// ============================================================================
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.PairingManager = void 0;
|
|
46
|
+
const crypto = __importStar(require("crypto"));
|
|
47
|
+
class PairingManager {
|
|
48
|
+
ttlMs;
|
|
49
|
+
pending = new Map();
|
|
50
|
+
constructor(ttlMs = 2 * 60 * 1000) {
|
|
51
|
+
this.ttlMs = ttlMs;
|
|
52
|
+
}
|
|
53
|
+
/** PC가 QR+PIN 발급. */
|
|
54
|
+
issue(now = Date.now()) {
|
|
55
|
+
const qrToken = crypto.randomBytes(16).toString('base64url');
|
|
56
|
+
const pin = String(crypto.randomInt(100000, 999999)); // 6자리
|
|
57
|
+
this.pending.set(qrToken, { pin, expiresAt: now + this.ttlMs });
|
|
58
|
+
return { qrToken, pin, expiresAt: now + this.ttlMs };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 모바일이 QR 스캔 + PIN 입력으로 페어링 시도. 일회성·만료·PIN·루프백 검증.
|
|
62
|
+
* @param loopbackOrLan 동일 LAN/루프백 경로 여부(외부 릴레이는 옵트인 — 기본 false 거부)
|
|
63
|
+
*/
|
|
64
|
+
redeem(qrToken, pin, loopbackOrLan, now = Date.now()) {
|
|
65
|
+
const p = this.pending.get(qrToken);
|
|
66
|
+
if (!p)
|
|
67
|
+
return { ok: false, reason: '토큰 없음/이미 사용됨' };
|
|
68
|
+
if (now > p.expiresAt) {
|
|
69
|
+
this.pending.delete(qrToken);
|
|
70
|
+
return { ok: false, reason: '만료' };
|
|
71
|
+
}
|
|
72
|
+
if (!loopbackOrLan)
|
|
73
|
+
return { ok: false, reason: '로컬(LAN/루프백) 경로 아님 — 외부 릴레이는 옵트인' };
|
|
74
|
+
if (pin !== p.pin)
|
|
75
|
+
return { ok: false, reason: 'PIN 불일치' };
|
|
76
|
+
this.pending.delete(qrToken); // 일회성 소모
|
|
77
|
+
const companionToken = crypto.randomBytes(24).toString('base64url');
|
|
78
|
+
return { ok: true, reason: 'ok', companionToken };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.PairingManager = PairingManager;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface SkillCandidate {
|
|
2
|
+
id: string;
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
export interface PriorWeight {
|
|
6
|
+
id: string;
|
|
7
|
+
weight: number;
|
|
8
|
+
}
|
|
9
|
+
export interface PersonalizationOptions {
|
|
10
|
+
/** A/B 토글. false면 균등 사전확률(개인화 OFF = baseline). */
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
/** softmax 온도(>0). 낮을수록 상위 후보에 집중. */
|
|
13
|
+
temperature?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 사용자 컨텍스트로 후보 스킬에 가중한 사전확률(합=1)을 만든다.
|
|
17
|
+
* enabled=false면 균등 분포(개인화 OFF). enabled=true면 임베딩 코사인 유사도 softmax.
|
|
18
|
+
* 후보가 비면 빈 배열을 반환한다.
|
|
19
|
+
*/
|
|
20
|
+
export declare function personalizedPrior(candidates: SkillCandidate[], userContext: string, opts: PersonalizationOptions): PriorWeight[];
|
|
21
|
+
/**
|
|
22
|
+
* 사용자 프로필: 베타 사용자가 자주 쓴 컨텍스트/스킬을 누적. (로컬에만 머문다 — 헌법 제4조.)
|
|
23
|
+
* 개인화 컨텍스트 문자열을 만든다(최근 사용 가중).
|
|
24
|
+
*/
|
|
25
|
+
export declare class UserProfile {
|
|
26
|
+
readonly userId: string;
|
|
27
|
+
private maxTerms;
|
|
28
|
+
private terms;
|
|
29
|
+
constructor(userId: string, maxTerms?: number);
|
|
30
|
+
/** 사용자가 실행/선택한 컨텍스트를 누적(최근 것 우선 유지). */
|
|
31
|
+
observe(contextText: string): void;
|
|
32
|
+
/** 개인화 컨텍스트 주입 문자열(누적 토큰). 비면 ''. */
|
|
33
|
+
contextString(): string;
|
|
34
|
+
termCount(): number;
|
|
35
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// B0 / Week-1 최소 개인화 MVP — 임베딩 가중 사전확률 + 컨텍스트 주입
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// 정직 고지(구현강도 상한): 이것은 **얕은 개인화**다 — 사용자 컨텍스트와 후보 스킬의
|
|
6
|
+
// 해시 bag-of-tokens 임베딩 코사인 유사도로 사전확률을 가중할 뿐이다(neural latent 아님).
|
|
7
|
+
// B1 의 MultiAxisLift 가 죽지 않을 "최소 두께"만 구현한다(과한 두께는 단순화 원칙 위반).
|
|
8
|
+
// A/B 토글(enabled): OFF면 균등 사전확률(비교군 baseline), ON이면 임베딩 가중.
|
|
9
|
+
// ============================================================================
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.UserProfile = void 0;
|
|
12
|
+
exports.personalizedPrior = personalizedPrior;
|
|
13
|
+
const text_embedding_1 = require("./text-embedding");
|
|
14
|
+
/**
|
|
15
|
+
* 사용자 컨텍스트로 후보 스킬에 가중한 사전확률(합=1)을 만든다.
|
|
16
|
+
* enabled=false면 균등 분포(개인화 OFF). enabled=true면 임베딩 코사인 유사도 softmax.
|
|
17
|
+
* 후보가 비면 빈 배열을 반환한다.
|
|
18
|
+
*/
|
|
19
|
+
function personalizedPrior(candidates, userContext, opts) {
|
|
20
|
+
const n = candidates.length;
|
|
21
|
+
if (n === 0)
|
|
22
|
+
return [];
|
|
23
|
+
// 개인화 OFF: 균등 사전확률(비교군).
|
|
24
|
+
if (!opts.enabled) {
|
|
25
|
+
const w = 1 / n;
|
|
26
|
+
return candidates.map((c) => ({ id: c.id, weight: w }));
|
|
27
|
+
}
|
|
28
|
+
const temp = opts.temperature && opts.temperature > 0 ? opts.temperature : 0.25;
|
|
29
|
+
const ctxVec = (0, text_embedding_1.textEmbedding)(userContext);
|
|
30
|
+
// 코사인 유사도∈[-1,1] → softmax. 컨텍스트가 비면 유사도 0(균등으로 수렴).
|
|
31
|
+
const sims = candidates.map((c) => (0, text_embedding_1.cosineSimilarity)((0, text_embedding_1.textEmbedding)(c.content), ctxVec));
|
|
32
|
+
const maxSim = Math.max(...sims);
|
|
33
|
+
const exps = sims.map((s) => Math.exp((s - maxSim) / temp)); // 수치 안정화
|
|
34
|
+
const z = exps.reduce((a, b) => a + b, 0);
|
|
35
|
+
if (z <= 0 || !isFinite(z)) {
|
|
36
|
+
const w = 1 / n;
|
|
37
|
+
return candidates.map((c) => ({ id: c.id, weight: w }));
|
|
38
|
+
}
|
|
39
|
+
return candidates.map((c, i) => ({ id: c.id, weight: exps[i] / z }));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 사용자 프로필: 베타 사용자가 자주 쓴 컨텍스트/스킬을 누적. (로컬에만 머문다 — 헌법 제4조.)
|
|
43
|
+
* 개인화 컨텍스트 문자열을 만든다(최근 사용 가중).
|
|
44
|
+
*/
|
|
45
|
+
class UserProfile {
|
|
46
|
+
userId;
|
|
47
|
+
maxTerms;
|
|
48
|
+
terms = [];
|
|
49
|
+
constructor(userId, maxTerms = 64) {
|
|
50
|
+
this.userId = userId;
|
|
51
|
+
this.maxTerms = maxTerms;
|
|
52
|
+
}
|
|
53
|
+
/** 사용자가 실행/선택한 컨텍스트를 누적(최근 것 우선 유지). */
|
|
54
|
+
observe(contextText) {
|
|
55
|
+
const toks = (contextText || '')
|
|
56
|
+
.toLowerCase()
|
|
57
|
+
.split(/[^a-z0-9가-힣]+/)
|
|
58
|
+
.filter((t) => t.length > 1);
|
|
59
|
+
for (const t of toks)
|
|
60
|
+
this.terms.push(t);
|
|
61
|
+
if (this.terms.length > this.maxTerms) {
|
|
62
|
+
this.terms = this.terms.slice(this.terms.length - this.maxTerms);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** 개인화 컨텍스트 주입 문자열(누적 토큰). 비면 ''. */
|
|
66
|
+
contextString() {
|
|
67
|
+
return this.terms.join(' ');
|
|
68
|
+
}
|
|
69
|
+
termCount() {
|
|
70
|
+
return this.terms.length;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.UserProfile = UserProfile;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { HnswIndex } from '@n2world/core';
|
|
2
|
+
import { StoreMode } from './pglite-store';
|
|
3
|
+
export interface VecWrite {
|
|
4
|
+
id: string;
|
|
5
|
+
content: string;
|
|
6
|
+
embedding: number[];
|
|
7
|
+
}
|
|
8
|
+
export interface InsertOptions {
|
|
9
|
+
/** 즉시 publish(검색 가시화). false 면 stale 상태로 두고 이후 publish() 로 일괄 가시화. */
|
|
10
|
+
publish?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* 테스트 전용: WAL durable append(=ack) **후, PGLite 적용 전** 크래시를 모사한다.
|
|
13
|
+
* ack 는 이미 반환됐고 WAL 에는 남아, recover() 의 재생 대상이 된다.
|
|
14
|
+
*/
|
|
15
|
+
simulateCrashBeforeApply?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** epoch 읽기 가드 — 리더가 캡처한 버퍼와 그 세대(epoch). */
|
|
18
|
+
export interface ReadGuard {
|
|
19
|
+
index: HnswIndex;
|
|
20
|
+
epoch: number;
|
|
21
|
+
}
|
|
22
|
+
export interface DurabilityReport {
|
|
23
|
+
storeMode: StoreMode;
|
|
24
|
+
/** ack 반환된 쓰기 수(= WAL durable append 성공 수). */
|
|
25
|
+
ackedWrites: number;
|
|
26
|
+
/** 관측된 torn-read(부분/혼합 세대 읽기). 0 이어야 함. */
|
|
27
|
+
tornReadObserved: number;
|
|
28
|
+
/** epoch 회수된 retired 버퍼 수(RCU 동작 증거). */
|
|
29
|
+
reclaimedBuffers: number;
|
|
30
|
+
/** RCU 위반 시도(리더가 남은 버퍼를 회수하려 한 횟수). 0 이어야 함. */
|
|
31
|
+
unsafeReclaimAttempts: number;
|
|
32
|
+
/** 동시 보유 버퍼 최대치(메모리 ~2배 근거). 정상이면 ≤ 2. */
|
|
33
|
+
peakLiveBuffers: number;
|
|
34
|
+
/** insert→publish 가시화 윈도우 최대치(ms, 측정값). `임베딩 stale ≤ W`. */
|
|
35
|
+
staleWindowMs: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* PGLite(영속) ↔ HNSW(검색) 동기화 브리지.
|
|
39
|
+
* - 쓰기: WAL durable append(ack) → PGLite 적용 → WAL ack(삭제) → (옵션) publish.
|
|
40
|
+
* - 읽기: active 버퍼 참조를 캡처(epoch guard)해 검색 — 삽입에 막히지 않음(분리).
|
|
41
|
+
* - 교체: 새 인덱스를 빌드 후 원자 swap, 구 버퍼는 리더가 빠질 때 epoch 회수(RCU).
|
|
42
|
+
*/
|
|
43
|
+
export declare class PgliteHnswSyncBridge {
|
|
44
|
+
private readonly storeDir;
|
|
45
|
+
private readonly walPath;
|
|
46
|
+
private store;
|
|
47
|
+
private wal;
|
|
48
|
+
private items;
|
|
49
|
+
private activeIndex;
|
|
50
|
+
private activeEpoch;
|
|
51
|
+
/** epoch → 활성 리더 수(active/retired 공통). */
|
|
52
|
+
private readers;
|
|
53
|
+
/** 회수 대기 중인 구 버퍼(리더가 남아 아직 못 버림). epoch → index. */
|
|
54
|
+
private retired;
|
|
55
|
+
/** 세대별 멤버십(torn-read 검증용): epoch → 그 세대에 보였던 id 집합. */
|
|
56
|
+
private genIds;
|
|
57
|
+
private ackedWrites;
|
|
58
|
+
private tornReadObserved;
|
|
59
|
+
private reclaimedBuffers;
|
|
60
|
+
private unsafeReclaimAttempts;
|
|
61
|
+
private peakLiveBuffers;
|
|
62
|
+
private staleSamplesMs;
|
|
63
|
+
/** publish 안 된(가시화 대기) 쓰기의 삽입 시각. id→t. */
|
|
64
|
+
private pendingSince;
|
|
65
|
+
private initialized;
|
|
66
|
+
constructor(storeDir: string | undefined, walPath: string);
|
|
67
|
+
init(): Promise<void>;
|
|
68
|
+
storeMode(): StoreMode;
|
|
69
|
+
/**
|
|
70
|
+
* 벡터 쓰기. **ack 는 WAL durable append 직후 반환된다**(인메모리 직후 아님).
|
|
71
|
+
* 반환 시점 = 내구 보장 시점. 이후 PGLite 적용·publish.
|
|
72
|
+
*/
|
|
73
|
+
insert(w: VecWrite, opts?: InsertOptions): Promise<{
|
|
74
|
+
acked: boolean;
|
|
75
|
+
}>;
|
|
76
|
+
/** WAL 의 in-flight 항목을 PGLite 에 재생(크래시 복구). 복구된 건수 반환. */
|
|
77
|
+
recover(): Promise<number>;
|
|
78
|
+
/**
|
|
79
|
+
* 검색. active 버퍼 참조를 epoch 가드로 캡처해 **동기** 검색한다.
|
|
80
|
+
* 캡처한 참조는 검색 동안 불변 → 부분/혼합 세대(torn-read) 불가.
|
|
81
|
+
*/
|
|
82
|
+
search(query: number[], k?: number): Promise<string[]>;
|
|
83
|
+
/**
|
|
84
|
+
* P2 인계 — 안정적 읽기 스냅샷 인터페이스. `openSnapshot()` → `snapshotSearch()`* → `closeSnapshot()`.
|
|
85
|
+
* 스냅샷이 열려 있는 동안 그 세대(epoch) 버퍼는 RCU 로 보호되어 회수되지 않는다(관계 추출 등
|
|
86
|
+
* 다단계 읽기가 일관된 단일 세대 위에서 동작하도록 보장). 닫으면 회수 가능해진다.
|
|
87
|
+
*/
|
|
88
|
+
openSnapshot(): ReadGuard;
|
|
89
|
+
/** 스냅샷 안의 검색(동기). 결과는 캡처한 세대의 멤버여야 한다 — 아니면 torn-read 로 카운트. */
|
|
90
|
+
snapshotSearch(g: ReadGuard, query: number[], k?: number): string[];
|
|
91
|
+
closeSnapshot(g: ReadGuard): void;
|
|
92
|
+
/** 가시화: 인메모리 사본으로 새 인덱스를 빌드해 원자 swap(검색 경로를 막지 않음). */
|
|
93
|
+
publish(): void;
|
|
94
|
+
durability(): DurabilityReport;
|
|
95
|
+
count(): Promise<number>;
|
|
96
|
+
/**
|
|
97
|
+
* 내구성 실측: ack 반환된 id 들이 **durable PGLite 원본**에 실제로 존재하는지 확인한다.
|
|
98
|
+
* `ack 유실 0/N` 은 이 검증의 missing.length === 0 으로 증명한다(스텁 아님 — 원본 조회).
|
|
99
|
+
*/
|
|
100
|
+
verifyDurable(expectedIds: string[]): Promise<{
|
|
101
|
+
present: number;
|
|
102
|
+
missing: string[];
|
|
103
|
+
}>;
|
|
104
|
+
close(): Promise<void>;
|
|
105
|
+
private applyWrite;
|
|
106
|
+
/** 적용 완료된 id 의 WAL 항목을 ack(삭제). */
|
|
107
|
+
private ackApplied;
|
|
108
|
+
/** 인메모리 사본으로 새 인덱스를 빌드하고 원자 swap + 구 버퍼 retire(RCU). */
|
|
109
|
+
private rebuildAndSwap;
|
|
110
|
+
/**
|
|
111
|
+
* RCU 회수 — **리더가 0 인 epoch 만** 버퍼/세대를 회수한다.
|
|
112
|
+
* 리더가 남았는데 호출되면 회수하지 않고 위반으로 카운트한다(불변식 가드).
|
|
113
|
+
*/
|
|
114
|
+
private safeReclaim;
|
|
115
|
+
private acquireRead;
|
|
116
|
+
private releaseRead;
|
|
117
|
+
private trackPeakBuffers;
|
|
118
|
+
}
|