@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.
Files changed (154) hide show
  1. package/dist/agent-os-rd.d.ts +100 -0
  2. package/dist/agent-os-rd.js +258 -0
  3. package/dist/audit-store.d.ts +14 -0
  4. package/dist/audit-store.js +107 -0
  5. package/dist/beta-runner.d.ts +95 -0
  6. package/dist/beta-runner.js +251 -0
  7. package/dist/beta.d.ts +102 -0
  8. package/dist/beta.js +180 -0
  9. package/dist/browser-agent.d.ts +90 -0
  10. package/dist/browser-agent.js +223 -0
  11. package/dist/channel-gateway.d.ts +74 -0
  12. package/dist/channel-gateway.js +270 -0
  13. package/dist/channels.d.ts +120 -0
  14. package/dist/channels.js +432 -0
  15. package/dist/chat-store.d.ts +29 -0
  16. package/dist/chat-store.js +120 -0
  17. package/dist/cli.d.ts +2 -0
  18. package/dist/cli.js +607 -0
  19. package/dist/command-screen.d.ts +12 -0
  20. package/dist/command-screen.js +44 -0
  21. package/dist/commit-gate.d.ts +98 -0
  22. package/dist/commit-gate.js +258 -0
  23. package/dist/companion-api.d.ts +37 -0
  24. package/dist/companion-api.js +101 -0
  25. package/dist/conversation-graph.d.ts +39 -0
  26. package/dist/conversation-graph.js +92 -0
  27. package/dist/cost-estimator.d.ts +27 -0
  28. package/dist/cost-estimator.js +42 -0
  29. package/dist/cron-runner.d.ts +31 -0
  30. package/dist/cron-runner.js +46 -0
  31. package/dist/dashboard/chat.html +326 -0
  32. package/dist/dashboard/dental.html +58 -0
  33. package/dist/dashboard/freebie.png +0 -0
  34. package/dist/dashboard/icon-192.png +0 -0
  35. package/dist/dashboard/index.html +892 -0
  36. package/dist/dashboard/manifest.json +15 -0
  37. package/dist/dashboard/service-worker.js +28 -0
  38. package/dist/dashboard-server.d.ts +37 -0
  39. package/dist/dashboard-server.js +457 -0
  40. package/dist/dental-intake-service.d.ts +37 -0
  41. package/dist/dental-intake-service.js +61 -0
  42. package/dist/dental-metrics.d.ts +25 -0
  43. package/dist/dental-metrics.js +37 -0
  44. package/dist/docking.d.ts +36 -0
  45. package/dist/docking.js +73 -0
  46. package/dist/finance-mcts-candidate.d.ts +37 -0
  47. package/dist/finance-mcts-candidate.js +106 -0
  48. package/dist/finance-regulation-kr.d.ts +33 -0
  49. package/dist/finance-regulation-kr.js +104 -0
  50. package/dist/finance-workflow.d.ts +135 -0
  51. package/dist/finance-workflow.js +242 -0
  52. package/dist/gateway.d.ts +18 -0
  53. package/dist/gateway.js +123 -0
  54. package/dist/governance.d.ts +39 -0
  55. package/dist/governance.js +48 -0
  56. package/dist/governed-executor.d.ts +31 -0
  57. package/dist/governed-executor.js +63 -0
  58. package/dist/governed-llm.d.ts +41 -0
  59. package/dist/governed-llm.js +83 -0
  60. package/dist/gpu-bridge.d.ts +16 -0
  61. package/dist/gpu-bridge.js +53 -0
  62. package/dist/health.d.ts +47 -0
  63. package/dist/health.js +66 -0
  64. package/dist/identity-link.d.ts +32 -0
  65. package/dist/identity-link.js +98 -0
  66. package/dist/index.d.ts +184 -0
  67. package/dist/index.js +417 -0
  68. package/dist/integrations/emr-adapter.d.ts +41 -0
  69. package/dist/integrations/emr-adapter.js +63 -0
  70. package/dist/kakao-oauth.d.ts +16 -0
  71. package/dist/kakao-oauth.js +87 -0
  72. package/dist/knowledge-graph.d.ts +53 -0
  73. package/dist/knowledge-graph.js +156 -0
  74. package/dist/llm.d.ts +65 -0
  75. package/dist/llm.js +357 -0
  76. package/dist/mcp-client-guard.d.ts +32 -0
  77. package/dist/mcp-client-guard.js +179 -0
  78. package/dist/mcp-macaroon.d.ts +75 -0
  79. package/dist/mcp-macaroon.js +161 -0
  80. package/dist/mcts-kernel-bridge.d.ts +36 -0
  81. package/dist/mcts-kernel-bridge.js +99 -0
  82. package/dist/mcts-prior.d.ts +79 -0
  83. package/dist/mcts-prior.js +170 -0
  84. package/dist/model-router.d.ts +51 -0
  85. package/dist/model-router.js +75 -0
  86. package/dist/multi-axis-lift.d.ts +43 -0
  87. package/dist/multi-axis-lift.js +141 -0
  88. package/dist/net-guard.d.ts +39 -0
  89. package/dist/net-guard.js +141 -0
  90. package/dist/onboarding.d.ts +38 -0
  91. package/dist/onboarding.js +94 -0
  92. package/dist/oracle-anchored-search.d.ts +25 -0
  93. package/dist/oracle-anchored-search.js +50 -0
  94. package/dist/oracle.d.ts +22 -0
  95. package/dist/oracle.js +116 -0
  96. package/dist/p6-governance.d.ts +150 -0
  97. package/dist/p6-governance.js +252 -0
  98. package/dist/pairing.d.ts +22 -0
  99. package/dist/pairing.js +81 -0
  100. package/dist/personalization.d.ts +35 -0
  101. package/dist/personalization.js +73 -0
  102. package/dist/pglite-hnsw-bridge.d.ts +118 -0
  103. package/dist/pglite-hnsw-bridge.js +311 -0
  104. package/dist/pglite-store.d.ts +59 -0
  105. package/dist/pglite-store.js +180 -0
  106. package/dist/playbook.d.ts +79 -0
  107. package/dist/playbook.js +83 -0
  108. package/dist/playbooks/dental-intake.d.ts +20 -0
  109. package/dist/playbooks/dental-intake.js +112 -0
  110. package/dist/predictive-agent.d.ts +157 -0
  111. package/dist/predictive-agent.js +535 -0
  112. package/dist/prompt-optimizer.d.ts +18 -0
  113. package/dist/prompt-optimizer.js +104 -0
  114. package/dist/rate-limiter.d.ts +25 -0
  115. package/dist/rate-limiter.js +75 -0
  116. package/dist/safety-anneal.d.ts +83 -0
  117. package/dist/safety-anneal.js +153 -0
  118. package/dist/sandbox-controller.d.ts +12 -0
  119. package/dist/sandbox-controller.js +95 -0
  120. package/dist/satisfaction-metrics.d.ts +26 -0
  121. package/dist/satisfaction-metrics.js +61 -0
  122. package/dist/sensor-bridge.d.ts +53 -0
  123. package/dist/sensor-bridge.js +133 -0
  124. package/dist/session-repair.d.ts +27 -0
  125. package/dist/session-repair.js +66 -0
  126. package/dist/slack-finance-intake.d.ts +42 -0
  127. package/dist/slack-finance-intake.js +122 -0
  128. package/dist/symbolic-dynamics.d.ts +113 -0
  129. package/dist/symbolic-dynamics.js +420 -0
  130. package/dist/telemetry.d.ts +19 -0
  131. package/dist/telemetry.js +68 -0
  132. package/dist/text-embedding.d.ts +6 -0
  133. package/dist/text-embedding.js +42 -0
  134. package/dist/tier-classifier.d.ts +20 -0
  135. package/dist/tier-classifier.js +58 -0
  136. package/dist/tier-guard.d.ts +36 -0
  137. package/dist/tier-guard.js +56 -0
  138. package/dist/tui.d.ts +9 -0
  139. package/dist/tui.js +214 -0
  140. package/dist/update-security.d.ts +31 -0
  141. package/dist/update-security.js +112 -0
  142. package/dist/v-calibration.d.ts +16 -0
  143. package/dist/v-calibration.js +42 -0
  144. package/dist/value-calibration.d.ts +41 -0
  145. package/dist/value-calibration.js +133 -0
  146. package/dist/value-head.d.ts +20 -0
  147. package/dist/value-head.js +91 -0
  148. package/dist/wal-buffer.d.ts +23 -0
  149. package/dist/wal-buffer.js +144 -0
  150. package/dist/wiki-synthesizer.d.ts +80 -0
  151. package/dist/wiki-synthesizer.js +0 -0
  152. package/dist/worker-agent.d.ts +10 -0
  153. package/dist/worker-agent.js +19 -0
  154. package/package.json +65 -0
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ // ============================================================================
3
+ // 클로즈드 베타 실행 하니스 (B0/B1 운영 — 접지 단계)
4
+ // ----------------------------------------------------------------------------
5
+ // 무한 회귀를 끊는 접지점은 **실사용자**다(master §2). 이 하니스는 실사용자 N명을 온보딩하고,
6
+ // 실 LLM 파이프라인으로 과제를 돌리고, 그들의 라벨(정오·만족)을 수집해 B1 MultiAxisLift 를
7
+ // **실데이터로** 산출한다. 재사용: B0 beta.ts(동의·온보딩·피드백·TierGuard) + personalization +
8
+ // multi-axis-lift + 실 LLM(llm.ts) + LocalSandbox.
9
+ //
10
+ // 제1계명(절대): **에이전트는 자기 채점지를 만들 수 없다.** 성공(정오)·만족 라벨은 **인간 참가자**가
11
+ // 준다. 합성/자체 생성 라벨은 베타 데이터가 아니다. 실 라벨이 모이기 전 리프트는 `(목표; 미측정)`.
12
+ // 헌법 제4조: 모든 베타 데이터는 **로컬에만** 머문다(외부 전송은 ConsentGate+TierGuard 통과 필요).
13
+ // ============================================================================
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.BetaSession = void 0;
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ const beta_1 = require("./beta");
52
+ const personalization_1 = require("./personalization");
53
+ const multi_axis_lift_1 = require("./multi-axis-lift");
54
+ const llm_1 = require("./llm");
55
+ function emptyRuntime() {
56
+ return { tasks: 0, oracleCalls: 0, totalTimeMs: 0, safetyChecked: 0, safetyBlocked: 0 };
57
+ }
58
+ /**
59
+ * 한 클로즈드 베타 운영 세션. 참가자별 온보딩→과제→피드백을 누적하고 B1 리프트를 산출한다.
60
+ * runShell 은 실 샌드박스 실행기(주입). 미주입 시 과제는 도구 없이 LLM 응답만 받는다.
61
+ */
62
+ class BetaSession {
63
+ consent = new beta_1.ConsentGate();
64
+ onboarding = new beta_1.OnboardingTracker();
65
+ feedback = new beta_1.FeedbackStore();
66
+ tierGuard = new beta_1.TierGuard();
67
+ profiles = new Map();
68
+ runtime = { on: emptyRuntime(), off: emptyRuntime() };
69
+ taskLog = [];
70
+ /** 참가자 온보딩 — 동의 범위 부여 + signup 기록(실 타임스탬프). */
71
+ onboard(userId, scopes = ['feedback-collection', 'telemetry-local'], at = Date.now()) {
72
+ for (const s of scopes)
73
+ this.consent.grant(userId, s, at);
74
+ this.onboarding.record(userId, 'signup', at);
75
+ if (!this.profiles.has(userId))
76
+ this.profiles.set(userId, new personalization_1.UserProfile(userId));
77
+ }
78
+ profile(userId) {
79
+ let p = this.profiles.get(userId);
80
+ if (!p) {
81
+ p = new personalization_1.UserProfile(userId);
82
+ this.profiles.set(userId, p);
83
+ }
84
+ return p;
85
+ }
86
+ /**
87
+ * 실 LLM 파이프라인으로 과제를 실행한다. arm='on' 이면 누적 사용자 컨텍스트를 주입(얕은 개인화),
88
+ * 'off' 면 주입하지 않는다(A/B 비교군). 효율(①호출·시간)을 실측 누적한다.
89
+ */
90
+ async runTask(userId, taskId, taskText, arm, runShell) {
91
+ this.onboarding.record(userId, 'first_run');
92
+ const prof = this.profile(userId);
93
+ const ctx = arm === 'on' ? prof.contextString() : '';
94
+ const prompt = ctx ? `사용자 맥락(개인화): ${ctx}\n\n과제: ${taskText}` : taskText;
95
+ let oracleCalls = 0;
96
+ const shell = runShell ?? (async () => '[샌드박스 미주입 — 도구 실행 없음]');
97
+ const t0 = Date.now();
98
+ let output;
99
+ try {
100
+ // 실 LLM 호출(askAgent). 도구 호출마다 oracleCalls 증가(효율 측정).
101
+ output = await (0, llm_1.askAgent)(prompt, async (cmd) => {
102
+ oracleCalls++;
103
+ return shell(cmd);
104
+ });
105
+ oracleCalls++; // 최소 1회 LLM 호출
106
+ }
107
+ catch (e) {
108
+ output = `[과제 실행 오류] ${e?.message ?? e}`;
109
+ }
110
+ const timeMs = Date.now() - t0;
111
+ // 사용자 맥락 누적(다음 과제의 개인화에 반영) — 로컬에만.
112
+ prof.observe(taskText);
113
+ const rt = this.runtime[arm];
114
+ rt.tasks++;
115
+ rt.oracleCalls += oracleCalls;
116
+ rt.totalTimeMs += timeMs;
117
+ this.taskLog.push({ userId, taskId, arm, taskText, output, t: Date.now() });
118
+ return { userId, taskId, arm, output, oracleCalls, timeMs, personalizationContext: ctx };
119
+ }
120
+ /**
121
+ * 참가자(인간)의 라벨을 기록한다. satisfaction1to5 는 1..5 별점(0..1로 정규화), correct 는 정오 판정.
122
+ * **이 라벨은 인간이 준다 — 자체 생성 금지(제1계명).**
123
+ */
124
+ recordFeedback(userId, taskId, satisfaction1to5, correct, arm, note) {
125
+ const sat = Math.max(0, Math.min(1, (satisfaction1to5 - 1) / 4)); // 1→0, 5→1
126
+ this.feedback.add({ userId, taskId, satisfaction: sat, correct, arm, note });
127
+ this.onboarding.record(userId, 'first_feedback');
128
+ }
129
+ /** 안전 점검 결과를 arm 에 누적(③ 안전 퇴행 — commit-gate/거버넌스 연동 지점). */
130
+ recordSafetyCheck(arm, blocked) {
131
+ const rt = this.runtime[arm];
132
+ rt.safetyChecked++;
133
+ if (blocked)
134
+ rt.safetyBlocked++;
135
+ }
136
+ onboardedCount() {
137
+ return this.onboarding.onboardedCount();
138
+ }
139
+ /** arm 별 ArmMetrics 구성 — 피드백(정오·만족) + 런타임(효율·안전)에서. */
140
+ armMetrics(arm) {
141
+ const labels = this.feedback.all().filter((l) => l.arm === arm);
142
+ const answered = labels.filter((l) => l.correct !== null);
143
+ const heldOutSuccesses = answered.filter((l) => l.correct === true).length;
144
+ const rt = this.runtime[arm];
145
+ return {
146
+ heldOutSuccesses,
147
+ heldOutTotal: answered.length,
148
+ oracleCalls: rt.oracleCalls,
149
+ totalTimeMs: rt.totalTimeMs,
150
+ redTeamBlocked: rt.safetyBlocked,
151
+ redTeamTotal: rt.safetyChecked,
152
+ badHabitReinforced: 0,
153
+ badHabitOpportunities: 0,
154
+ meanSatisfaction: this.feedback.meanSatisfaction(arm),
155
+ };
156
+ }
157
+ /**
158
+ * B1 MultiAxisLift 보고. **실 라벨이 없으면 정직하게 미측정**을 반환한다(자기 채점 금지).
159
+ * 두 arm 모두에 응답된 정오 라벨이 있어야 의미 있는 리프트가 산출된다.
160
+ */
161
+ liftReport() {
162
+ const on = this.armMetrics('on');
163
+ const off = this.armMetrics('off');
164
+ if (on.heldOutTotal === 0 || off.heldOutTotal === 0) {
165
+ return {
166
+ status: '(목표; 미측정)',
167
+ reason: '실사용자 정오 라벨이 두 arm(on/off) 모두에 필요하다. 베타 참가자가 과제를 수행·라벨링하기 전까지 리프트는 미측정이다(접지 영가설 — 바닥은 인간).',
168
+ onboarded: this.onboardedCount(),
169
+ };
170
+ }
171
+ return { status: 'measured', report: (0, multi_axis_lift_1.computeMultiAxisLift)(on, off), onboarded: this.onboardedCount() };
172
+ }
173
+ /** 이미 라벨된 (task,arm) 인가(중복 라벨 안내 회피용). */
174
+ isLabeled(taskId, arm) {
175
+ return this.feedback.all().some((l) => l.taskId === taskId && l.arm === arm);
176
+ }
177
+ /**
178
+ * 참가자 라벨링 워크시트(Markdown). 에이전트 출력과 **빈 라벨 칸** + 정확한 feedback 명령을 만든다.
179
+ * ★ 라벨(정오·만족)은 **실참가자가 직접 채운다** — 운영자/에이전트가 대신 채우지 않는다(제1계명).
180
+ */
181
+ worksheet(userId) {
182
+ const logs = this.taskLog.filter((l) => l.userId === userId).sort((a, b) => a.t - b.t);
183
+ const lines = [];
184
+ lines.push(`# 베타 라벨링 워크시트 — 참가자: ${userId}`);
185
+ lines.push('');
186
+ lines.push('> ⚠ 아래 각 과제의 에이전트 출력을 보고 **당신(참가자)이 직접** 정오(맞음/틀림)와');
187
+ lines.push('> 만족도(1~5)를 판단해 표시하세요. 운영자/AI 가 대신 채우면 안 됩니다(접지 원칙).');
188
+ lines.push('> 표시 후, 각 항목의 명령을 실행해 라벨을 기록하세요.');
189
+ lines.push('');
190
+ for (const l of logs) {
191
+ const labeled = this.isLabeled(l.taskId, l.arm) ? ' ✅(라벨됨)' : '';
192
+ lines.push(`## ${l.taskId} (arm=${l.arm})${labeled}`);
193
+ lines.push(`- **과제:** ${l.taskText}`);
194
+ lines.push(`- **에이전트 출력:**`);
195
+ lines.push(' > ' + l.output.replace(/\n/g, '\n > '));
196
+ lines.push(`- **정오 판단:** [ ] 맞음(y) [ ] 틀림(n)`);
197
+ lines.push(`- **만족도(1~5):** [ ]`);
198
+ lines.push('- **라벨 기록 명령(값 채워서 실행):**');
199
+ lines.push(` \`\`\``);
200
+ lines.push(` node packages/orchestrator/dist/cli.js beta feedback ${userId} ${l.taskId} 만족1-5 정오y_or_n --arm ${l.arm}`);
201
+ lines.push(` \`\`\``);
202
+ lines.push('');
203
+ }
204
+ if (logs.length === 0)
205
+ lines.push('(이 참가자의 실행된 과제가 없습니다. 먼저 `beta run` 을 수행하세요.)');
206
+ return lines.join('\n');
207
+ }
208
+ taskLogFor(userId) {
209
+ return this.taskLog.filter((l) => l.userId === userId);
210
+ }
211
+ /** 로컬 영속(헌법 제4조 — 외부 전송 아님). 디렉터리 자동 생성. */
212
+ save(filePath) {
213
+ const state = {
214
+ consent: this.consent.records_(),
215
+ onboarding: this.onboarding.all(),
216
+ feedback: this.feedback.all(),
217
+ egress: this.tierGuard.log(),
218
+ armRuntime: this.runtime,
219
+ taskLog: this.taskLog,
220
+ };
221
+ fs.mkdirSync(path.dirname(path.resolve(filePath)), { recursive: true });
222
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2), 'utf8');
223
+ }
224
+ /** 로컬 스냅샷에서 세션 복원(이어서 운영). */
225
+ static load(filePath) {
226
+ const s = new BetaSession();
227
+ let state = null;
228
+ try {
229
+ state = JSON.parse(fs.readFileSync(filePath, 'utf8'));
230
+ }
231
+ catch {
232
+ return s;
233
+ }
234
+ for (const c of state.consent) {
235
+ if (c.granted)
236
+ s.consent.grant(c.userId, c.scope, c.t);
237
+ else
238
+ s.consent.revoke(c.userId, c.scope, c.t);
239
+ }
240
+ for (const o of state.onboarding)
241
+ s.onboarding.record(o.userId, o.event, o.t);
242
+ for (const f of state.feedback)
243
+ s.feedback.add({ userId: f.userId, taskId: f.taskId, satisfaction: f.satisfaction, correct: f.correct, arm: f.arm, note: f.note, t: f.t });
244
+ if (state.armRuntime)
245
+ s.runtime = { on: { ...emptyRuntime(), ...state.armRuntime.on }, off: { ...emptyRuntime(), ...state.armRuntime.off } };
246
+ if (Array.isArray(state.taskLog))
247
+ s.taskLog = state.taskLog;
248
+ return s;
249
+ }
250
+ }
251
+ exports.BetaSession = BetaSession;
package/dist/beta.d.ts ADDED
@@ -0,0 +1,102 @@
1
+ export type DataTier = 0 | 1 | 2;
2
+ export type Destination = 'local' | 'external';
3
+ export type ConsentScope = 'llm-transmission' | 'stt' | 'telemetry-local' | 'feedback-collection';
4
+ export interface ConsentRecord {
5
+ userId: string;
6
+ scope: ConsentScope;
7
+ granted: boolean;
8
+ t: number;
9
+ }
10
+ export declare class ConsentGate {
11
+ private records;
12
+ private key;
13
+ /** 명시적 동의 기록(동의 시점 저장). */
14
+ grant(userId: string, scope: ConsentScope, at?: number): void;
15
+ /** 동의 철회 — 헌법 제4조: 사용자는 언제든 철회할 수 있다. */
16
+ revoke(userId: string, scope: ConsentScope, at?: number): void;
17
+ hasConsent(userId: string, scope: ConsentScope): boolean;
18
+ /**
19
+ * 동의 필요 행동의 게이트. 동의 없으면 차단(allowed=false).
20
+ * 되돌릴 수 없는/외부 영향 행동 전에 호출한다(영향 프리뷰의 진입점).
21
+ */
22
+ requireConsent(userId: string, scope: ConsentScope): {
23
+ allowed: boolean;
24
+ reason: string;
25
+ };
26
+ records_(): ConsentRecord[];
27
+ }
28
+ export interface EgressAttempt {
29
+ tier: DataTier;
30
+ dest: Destination;
31
+ blocked: boolean;
32
+ reason: string;
33
+ t: number;
34
+ }
35
+ export declare class TierGuard {
36
+ private attempts;
37
+ /**
38
+ * 데이터 전송 게이트. Tier-0 의 external 전송은 무조건 차단(`관측 0/N`).
39
+ * 로컬 전송은 모든 Tier 허용. Tier 1/2 external 은 동의 전제(호출측에서 ConsentGate 확인).
40
+ */
41
+ guardEgress(tier: DataTier, dest: Destination): {
42
+ allowed: boolean;
43
+ reason: string;
44
+ };
45
+ /** `관측 0/N` 보고용 — tier0ExfilObserved 는 항상 0 이어야 안전 충족. */
46
+ stats(): {
47
+ externalAttempts: number;
48
+ tier0ExternalAttempts: number;
49
+ tier0ExfilObserved: number;
50
+ };
51
+ log(): EgressAttempt[];
52
+ }
53
+ export type OnboardingEvent = 'signup' | 'first_run' | 'first_feedback';
54
+ export interface OnboardingRecord {
55
+ userId: string;
56
+ event: OnboardingEvent;
57
+ t: number;
58
+ }
59
+ export declare class OnboardingTracker {
60
+ private records;
61
+ /** 실제 이벤트 발생 시각을 기록. at 미지정 시 현재 시각(실측). */
62
+ record(userId: string, event: OnboardingEvent, at?: number): void;
63
+ private firstOf;
64
+ /** signup → first_run 까지 실제 소요(초). 둘 다 있어야 측정값, 아니면 null(미측정). */
65
+ timeToFirstTaskSec(userId: string): number | null;
66
+ /** signup 이벤트가 있는 고유 사용자 수(= 온보딩된 N). */
67
+ onboardedCount(): number;
68
+ eventsFor(userId: string): OnboardingRecord[];
69
+ all(): OnboardingRecord[];
70
+ }
71
+ export interface FeedbackLabel {
72
+ userId: string;
73
+ taskId: string;
74
+ /** 만족도∈[0,1] (예: 별점 1..5 → 0..1로 정규화해 저장). */
75
+ satisfaction: number;
76
+ /** 사용자가 결과를 정답으로 판단했는가. 미응답이면 null. */
77
+ correct: boolean | null;
78
+ /** 개인화 ON/OFF (A/B 귀속 — B1 에서 분리 집계). */
79
+ arm?: 'on' | 'off';
80
+ note?: string;
81
+ t: number;
82
+ }
83
+ export declare class FeedbackStore {
84
+ private labels;
85
+ add(label: Omit<FeedbackLabel, 't'> & {
86
+ t?: number;
87
+ }): void;
88
+ all(): FeedbackLabel[];
89
+ forTask(taskId: string): FeedbackLabel[];
90
+ /** 평균 만족도(arm 필터 가능). 라벨 없으면 null(미측정 — 정직). */
91
+ meanSatisfaction(arm?: 'on' | 'off'): number | null;
92
+ count(): number;
93
+ }
94
+ export interface BetaSnapshot {
95
+ consent: ConsentRecord[];
96
+ onboarding: OnboardingRecord[];
97
+ feedback: FeedbackLabel[];
98
+ egress: EgressAttempt[];
99
+ }
100
+ /** 베타 상태를 로컬 파일에 저장(외부 반출 아님). 디렉터리는 자동 생성. */
101
+ export declare function saveBetaSnapshot(filePath: string, snap: BetaSnapshot): void;
102
+ export declare function loadBetaSnapshot(filePath: string): BetaSnapshot | null;
package/dist/beta.js ADDED
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ // ============================================================================
3
+ // B0 클로즈드 베타 인프라 — 동의 게이트 · 온보딩 추적 · 피드백 파이프라인 · Tier 가드
4
+ // ----------------------------------------------------------------------------
5
+ // v2.4 §2 GroundingFirst: 무한 회귀를 끊는 접지점은 실사용자다. 이 모듈은 실사용자 N명을
6
+ // 온보딩하고, 그들의 라벨을 접지 스택 3층(모델-비평 캘리브레이션)·B1 4축(④ 만족)의 입력으로
7
+ // 저장한다. 모든 텔레메트리는 실데이터(타임스탬프)이며 Math.random 가짜를 쓰지 않는다(제1계명).
8
+ //
9
+ // 데이터는 로컬에만 머문다(헌법 제4조 — 로컬 기억 보호). 외부 전송은 ConsentGate + TierGuard 통과 필요.
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.FeedbackStore = exports.OnboardingTracker = exports.TierGuard = exports.ConsentGate = void 0;
46
+ exports.saveBetaSnapshot = saveBetaSnapshot;
47
+ exports.loadBetaSnapshot = loadBetaSnapshot;
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ // ---------------------------------------------------------------------------
51
+ // ConsentGate — 헌법 제4조(영향 프리뷰·로컬 기억 보호) + PRIVACY 동의 게이트
52
+ // ---------------------------------------------------------------------------
53
+ class ConsentGate {
54
+ records = new Map();
55
+ key(userId, scope) {
56
+ return `${userId}::${scope}`;
57
+ }
58
+ /** 명시적 동의 기록(동의 시점 저장). */
59
+ grant(userId, scope, at = Date.now()) {
60
+ this.records.set(this.key(userId, scope), { userId, scope, granted: true, t: at });
61
+ }
62
+ /** 동의 철회 — 헌법 제4조: 사용자는 언제든 철회할 수 있다. */
63
+ revoke(userId, scope, at = Date.now()) {
64
+ this.records.set(this.key(userId, scope), { userId, scope, granted: false, t: at });
65
+ }
66
+ hasConsent(userId, scope) {
67
+ return this.records.get(this.key(userId, scope))?.granted === true;
68
+ }
69
+ /**
70
+ * 동의 필요 행동의 게이트. 동의 없으면 차단(allowed=false).
71
+ * 되돌릴 수 없는/외부 영향 행동 전에 호출한다(영향 프리뷰의 진입점).
72
+ */
73
+ requireConsent(userId, scope) {
74
+ if (this.hasConsent(userId, scope))
75
+ return { allowed: true, reason: 'consent granted' };
76
+ return { allowed: false, reason: `동의 필요: ${scope} (사용자 ${userId} 미동의 — 차단)` };
77
+ }
78
+ records_() {
79
+ return [...this.records.values()];
80
+ }
81
+ }
82
+ exports.ConsentGate = ConsentGate;
83
+ class TierGuard {
84
+ attempts = [];
85
+ /**
86
+ * 데이터 전송 게이트. Tier-0 의 external 전송은 무조건 차단(`관측 0/N`).
87
+ * 로컬 전송은 모든 Tier 허용. Tier 1/2 external 은 동의 전제(호출측에서 ConsentGate 확인).
88
+ */
89
+ guardEgress(tier, dest) {
90
+ const blocked = tier === 0 && dest === 'external';
91
+ const reason = blocked
92
+ ? 'Tier-0 외부 반출 차단 (관측 0/N 보장)'
93
+ : 'allowed';
94
+ this.attempts.push({ tier, dest, blocked, reason, t: Date.now() });
95
+ return { allowed: !blocked, reason };
96
+ }
97
+ /** `관측 0/N` 보고용 — tier0ExfilObserved 는 항상 0 이어야 안전 충족. */
98
+ stats() {
99
+ const ext = this.attempts.filter((a) => a.dest === 'external');
100
+ const t0 = ext.filter((a) => a.tier === 0);
101
+ return {
102
+ externalAttempts: ext.length,
103
+ tier0ExternalAttempts: t0.length,
104
+ tier0ExfilObserved: t0.filter((a) => !a.blocked).length, // 실제로 나간 것(0 이어야 함)
105
+ };
106
+ }
107
+ log() {
108
+ return this.attempts;
109
+ }
110
+ }
111
+ exports.TierGuard = TierGuard;
112
+ class OnboardingTracker {
113
+ records = [];
114
+ /** 실제 이벤트 발생 시각을 기록. at 미지정 시 현재 시각(실측). */
115
+ record(userId, event, at = Date.now()) {
116
+ this.records.push({ userId, event, t: at });
117
+ }
118
+ firstOf(userId, event) {
119
+ const xs = this.records
120
+ .filter((r) => r.userId === userId && r.event === event)
121
+ .sort((a, b) => a.t - b.t);
122
+ return xs.length ? xs[0].t : null;
123
+ }
124
+ /** signup → first_run 까지 실제 소요(초). 둘 다 있어야 측정값, 아니면 null(미측정). */
125
+ timeToFirstTaskSec(userId) {
126
+ const su = this.firstOf(userId, 'signup');
127
+ const fr = this.firstOf(userId, 'first_run');
128
+ if (su === null || fr === null)
129
+ return null;
130
+ return +((fr - su) / 1000).toFixed(1);
131
+ }
132
+ /** signup 이벤트가 있는 고유 사용자 수(= 온보딩된 N). */
133
+ onboardedCount() {
134
+ return new Set(this.records.filter((r) => r.event === 'signup').map((r) => r.userId)).size;
135
+ }
136
+ eventsFor(userId) {
137
+ return this.records.filter((r) => r.userId === userId).sort((a, b) => a.t - b.t);
138
+ }
139
+ all() {
140
+ return this.records;
141
+ }
142
+ }
143
+ exports.OnboardingTracker = OnboardingTracker;
144
+ class FeedbackStore {
145
+ labels = [];
146
+ add(label) {
147
+ const sat = Math.max(0, Math.min(1, label.satisfaction));
148
+ this.labels.push({ ...label, satisfaction: sat, t: label.t ?? Date.now() });
149
+ }
150
+ all() {
151
+ return this.labels;
152
+ }
153
+ forTask(taskId) {
154
+ return this.labels.filter((l) => l.taskId === taskId);
155
+ }
156
+ /** 평균 만족도(arm 필터 가능). 라벨 없으면 null(미측정 — 정직). */
157
+ meanSatisfaction(arm) {
158
+ const xs = (arm ? this.labels.filter((l) => l.arm === arm) : this.labels).map((l) => l.satisfaction);
159
+ if (xs.length === 0)
160
+ return null;
161
+ return +(xs.reduce((a, b) => a + b, 0) / xs.length).toFixed(3);
162
+ }
163
+ count() {
164
+ return this.labels.length;
165
+ }
166
+ }
167
+ exports.FeedbackStore = FeedbackStore;
168
+ /** 베타 상태를 로컬 파일에 저장(외부 반출 아님). 디렉터리는 자동 생성. */
169
+ function saveBetaSnapshot(filePath, snap) {
170
+ fs.mkdirSync(path.dirname(path.resolve(filePath)), { recursive: true });
171
+ fs.writeFileSync(filePath, JSON.stringify(snap, null, 2), 'utf8');
172
+ }
173
+ function loadBetaSnapshot(filePath) {
174
+ try {
175
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
176
+ }
177
+ catch {
178
+ return null;
179
+ }
180
+ }
@@ -0,0 +1,90 @@
1
+ export type BrowserVerb = 'goto' | 'read' | 'click' | 'type' | 'extract' | 'back' | 'vclick';
2
+ export interface BrowserAction {
3
+ verb: BrowserVerb;
4
+ url?: string;
5
+ ref?: string;
6
+ text?: string;
7
+ query?: string;
8
+ }
9
+ /** 지각 단위 — 상호작용 가능한 요소(의미 DOM). */
10
+ export interface InteractiveElement {
11
+ ref: string;
12
+ role: string;
13
+ name: string;
14
+ }
15
+ /** 요소 목록을 LLM 친화 라인으로 압축(토큰 효율). 테스트용 export. */
16
+ export declare function formatElements(els: InteractiveElement[], max?: number): string;
17
+ export interface ScreenResult {
18
+ allowed: boolean;
19
+ reason: string;
20
+ }
21
+ /**
22
+ * 브라우저 액션 보안 스크린(deny-by-default for 위험).
23
+ * read/back/extract 는 허용, goto 는 http(s)만, click 은 금융/인증/되돌릴수없는 라벨이면 차단,
24
+ * type 은 비밀/결제 필드면 차단(자격증명 입력 금지).
25
+ */
26
+ export declare function screenBrowserAction(action: BrowserAction, targetName?: string): ScreenResult;
27
+ /** 스크린샷 결과(비전 폴백용). */
28
+ export interface Screenshot {
29
+ base64: string;
30
+ width: number;
31
+ height: number;
32
+ }
33
+ /**
34
+ * 비전 그라운딩 로케이터(#2 폴백). 스크린샷+설명 → 정규화 좌표(0~1) 또는 null.
35
+ * llm.ts 의 멀티모달 모델로 구현(주입). 비용이 크므로 DOM(#3)로 안 될 때만 쓴다.
36
+ */
37
+ export type VisionLocator = (screenshotBase64: string, description: string) => Promise<{
38
+ x: number;
39
+ y: number;
40
+ } | null>;
41
+ /** 구동 계층 추상화(테스트에서 모의 가능). */
42
+ export interface IBrowserController {
43
+ goto(url: string): Promise<void>;
44
+ /** 현재 페이지의 상호작용 요소를 추출하고 ref 를 부여(의미 DOM #3). */
45
+ readElements(): Promise<InteractiveElement[]>;
46
+ click(ref: string): Promise<void>;
47
+ type(ref: string, text: string): Promise<void>;
48
+ extractText(): Promise<string>;
49
+ back(): Promise<void>;
50
+ close(): Promise<void>;
51
+ /** 비전 폴백용 스크린샷. */
52
+ screenshot(): Promise<Screenshot>;
53
+ /** 비전 폴백용 좌표 클릭(픽셀). */
54
+ clickAt(x: number, y: number): Promise<void>;
55
+ }
56
+ /**
57
+ * Playwright 기반 로컬 컨트롤러(#1 구동). Playwright 는 동적 import(선택 설치).
58
+ * 미설치 시 init() 이 명확히 throw → 상위에서 정직한 안내로 변환.
59
+ */
60
+ export declare class PlaywrightBrowserController implements IBrowserController {
61
+ private headless;
62
+ private browser;
63
+ private page;
64
+ constructor(headless?: boolean);
65
+ init(): Promise<void>;
66
+ goto(url: string): Promise<void>;
67
+ readElements(): Promise<InteractiveElement[]>;
68
+ click(ref: string): Promise<void>;
69
+ type(ref: string, text: string): Promise<void>;
70
+ extractText(): Promise<string>;
71
+ back(): Promise<void>;
72
+ screenshot(): Promise<Screenshot>;
73
+ clickAt(x: number, y: number): Promise<void>;
74
+ close(): Promise<void>;
75
+ }
76
+ /**
77
+ * BrowserAgent — 지각(read)+구동(click/type/goto)을 보안 스크린으로 감싼 실행기.
78
+ * act() 가 LLM 도구(controlBrowser)의 백엔드가 된다. 모든 액션은 screenBrowserAction 통과.
79
+ */
80
+ export declare class BrowserAgent {
81
+ private ctrl;
82
+ private vision?;
83
+ private lastElements;
84
+ /** 비전 폴백 로케이터(선택). 없으면 vclick 은 안내만. */
85
+ constructor(ctrl: IBrowserController, vision?: VisionLocator | undefined);
86
+ /** 한 액션 실행 → 사람이 읽는/LLM이 읽는 결과 문자열. */
87
+ act(action: BrowserAction): Promise<string>;
88
+ private refreshAndFormat;
89
+ close(): Promise<void>;
90
+ }