@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,36 @@
|
|
|
1
|
+
import { Tier } from './tier-classifier';
|
|
2
|
+
export type ProviderKind = 'local' | 'external';
|
|
3
|
+
/** 제공자 id → kind. 미등록 제공자는 보수적으로 external 취급. */
|
|
4
|
+
export declare function providerKind(provider: string): ProviderKind;
|
|
5
|
+
export interface RouteRequest {
|
|
6
|
+
tier: Tier;
|
|
7
|
+
provider: string;
|
|
8
|
+
/** Tier-1 외부 송신 시 사용자 동의가 있었는가(과제별 명시 동의). */
|
|
9
|
+
userConsent?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface RouteDecision {
|
|
12
|
+
allowed: boolean;
|
|
13
|
+
reason: string;
|
|
14
|
+
/** Tier-0인데 external 요청 시, 강제할 로컬 사용 권고. */
|
|
15
|
+
forceLocal?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface EgressAuditEntry {
|
|
18
|
+
t: number;
|
|
19
|
+
tier: Tier;
|
|
20
|
+
provider: string;
|
|
21
|
+
kind: ProviderKind;
|
|
22
|
+
allowed: boolean;
|
|
23
|
+
reason: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* TierGuard — LLM 호출 직전 라우팅 판정 + egress 감사.
|
|
27
|
+
* Tier-0 외부 송신은 *항상* 차단(allowed=false). 감사 로그로 `관측 0/N` 증명.
|
|
28
|
+
*/
|
|
29
|
+
export declare class TierGuard {
|
|
30
|
+
private audit;
|
|
31
|
+
evaluate(req: RouteRequest): RouteDecision;
|
|
32
|
+
/** 감사 로그 전체. */
|
|
33
|
+
auditLog(): EgressAuditEntry[];
|
|
34
|
+
/** Tier-0 데이터가 외부로 *허용된* 건수(반드시 0이어야 함). */
|
|
35
|
+
tier0ExternalAllowed(): number;
|
|
36
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// v2.5.2 Phase S — TierGuard (LLM 프롬프트 경로 egress 통제)
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// 계획서 v2.5.2 §5: TierGuard는 샌드박스 egress뿐 아니라 **LLM 프롬프트 경로**에도
|
|
6
|
+
// 적용된다. Tier-0 데이터의 외부 제공사 송신을 차단하고 로컬을 강제하며, 모든 시도를
|
|
7
|
+
// 감사한다. Tier-0 외부 송신이 1건이라도 관측되면(allowed) 헌법 제4조 위반이다.
|
|
8
|
+
//
|
|
9
|
+
// 제공자 분류: anthropic/google/openrouter/... = external, ollama/mlx/local = local.
|
|
10
|
+
// 정직 고지(제1계명): ZDR(무학습·무보존)은 계약일 뿐 기술 보장 아님. Tier-0는 *로컬만*.
|
|
11
|
+
// ============================================================================
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.TierGuard = void 0;
|
|
14
|
+
exports.providerKind = providerKind;
|
|
15
|
+
/** 제공자 id → kind. 미등록 제공자는 보수적으로 external 취급. */
|
|
16
|
+
function providerKind(provider) {
|
|
17
|
+
const p = provider.toLowerCase();
|
|
18
|
+
const local = ['local', 'ollama', 'mlx', 'llama.cpp', 'whisper-local'];
|
|
19
|
+
return local.some((l) => p.includes(l)) ? 'local' : 'external';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* TierGuard — LLM 호출 직전 라우팅 판정 + egress 감사.
|
|
23
|
+
* Tier-0 외부 송신은 *항상* 차단(allowed=false). 감사 로그로 `관측 0/N` 증명.
|
|
24
|
+
*/
|
|
25
|
+
class TierGuard {
|
|
26
|
+
audit = [];
|
|
27
|
+
evaluate(req) {
|
|
28
|
+
const kind = providerKind(req.provider);
|
|
29
|
+
let decision;
|
|
30
|
+
if (req.tier === 0) {
|
|
31
|
+
decision = kind === 'local'
|
|
32
|
+
? { allowed: true, reason: 'Tier-0 → 로컬 허용' }
|
|
33
|
+
: { allowed: false, reason: 'Tier-0 외부 송신 금지 → 로컬 강제', forceLocal: true };
|
|
34
|
+
}
|
|
35
|
+
else if (req.tier === 1) {
|
|
36
|
+
if (kind === 'local')
|
|
37
|
+
decision = { allowed: true, reason: 'Tier-1 → 로컬 우선 허용' };
|
|
38
|
+
else
|
|
39
|
+
decision = req.userConsent === true
|
|
40
|
+
? { allowed: true, reason: 'Tier-1 외부 송신 — 과제별 동의+로깅' }
|
|
41
|
+
: { allowed: false, reason: 'Tier-1 외부 송신 동의 없음 → 차단(로컬 우선)', forceLocal: true };
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
decision = { allowed: true, reason: 'Tier-2 → 허용' };
|
|
45
|
+
}
|
|
46
|
+
this.audit.push({ t: Date.now(), tier: req.tier, provider: req.provider, kind, allowed: decision.allowed, reason: decision.reason });
|
|
47
|
+
return decision;
|
|
48
|
+
}
|
|
49
|
+
/** 감사 로그 전체. */
|
|
50
|
+
auditLog() { return [...this.audit]; }
|
|
51
|
+
/** Tier-0 데이터가 외부로 *허용된* 건수(반드시 0이어야 함). */
|
|
52
|
+
tier0ExternalAllowed() {
|
|
53
|
+
return this.audit.filter((e) => e.tier === 0 && e.kind === 'external' && e.allowed).length;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.TierGuard = TierGuard;
|
package/dist/tui.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** 시각 폭 기준으로 오른쪽을 공백으로 채운다(CJK 더블폭 반영). 테스트용 export. */
|
|
2
|
+
export declare function padVisual(s: string, width: number): string;
|
|
3
|
+
/** 아주 가벼운 마크다운을 ANSI 로 변환한다(제목·불릿·인용·코드펜스·굵게·인라인코드). 테스트용 export. */
|
|
4
|
+
export declare function formatMarkdown(text: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Rich TUI 대화 세션. 영속/복원/도구 실행은 chat 과 동일(같은 chat-data/history.json).
|
|
7
|
+
* 플래그: --fresh(초기화), --export(마크다운 내보내고 종료).
|
|
8
|
+
*/
|
|
9
|
+
export declare function runRichTui(flags?: string[]): Promise<void>;
|
package/dist/tui.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Rich TUI — 터미널 대화 인터페이스 (둥근 패널·컬러·CJK 인식 줄바꿈)
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// CLI `chat`(plain REPL)의 풍부한 버전. blessed/ink 같은 무거운 프레임워크 없이
|
|
6
|
+
// chalk + wrap-ansi(+string-width)로 패널형 UI를 그린다. 대화 영속/복원/도구 실행은
|
|
7
|
+
// ChatStore + askAgentChat 를 그대로 재사용한다(웹/CLI 와 같은 chat-data/history.json).
|
|
8
|
+
// ============================================================================
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
43
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.padVisual = padVisual;
|
|
47
|
+
exports.formatMarkdown = formatMarkdown;
|
|
48
|
+
exports.runRichTui = runRichTui;
|
|
49
|
+
const readline = __importStar(require("readline"));
|
|
50
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
51
|
+
const wrap_ansi_1 = __importDefault(require("wrap-ansi"));
|
|
52
|
+
const string_width_1 = __importDefault(require("string-width"));
|
|
53
|
+
const llm_1 = require("./llm");
|
|
54
|
+
const chat_store_1 = require("./chat-store");
|
|
55
|
+
const index_1 = require("./index");
|
|
56
|
+
const ROUND = { tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│' };
|
|
57
|
+
function termWidth() {
|
|
58
|
+
return Math.max(40, Math.min(process.stdout.columns || 80, 110));
|
|
59
|
+
}
|
|
60
|
+
/** 시각 폭 기준으로 오른쪽을 공백으로 채운다(CJK 더블폭 반영). 테스트용 export. */
|
|
61
|
+
function padVisual(s, width) {
|
|
62
|
+
const pad = width - (0, string_width_1.default)(s);
|
|
63
|
+
return pad > 0 ? s + ' '.repeat(pad) : s;
|
|
64
|
+
}
|
|
65
|
+
/** 아주 가벼운 마크다운을 ANSI 로 변환한다(제목·불릿·인용·코드펜스·굵게·인라인코드). 테스트용 export. */
|
|
66
|
+
function formatMarkdown(text) {
|
|
67
|
+
const out = [];
|
|
68
|
+
let inCode = false;
|
|
69
|
+
for (const raw of (text || '').split('\n')) {
|
|
70
|
+
const line = raw.replace(/\r$/, '');
|
|
71
|
+
if (/^\s*```/.test(line)) {
|
|
72
|
+
inCode = !inCode;
|
|
73
|
+
out.push(chalk_1.default.dim('────'));
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (inCode) {
|
|
77
|
+
out.push(chalk_1.default.gray(line));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
let m;
|
|
81
|
+
if ((m = line.match(/^(#{1,6})\s+(.*)$/))) {
|
|
82
|
+
out.push(chalk_1.default.bold.cyan(m[2]));
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if ((m = line.match(/^\s*[-*]\s+(.*)$/))) {
|
|
86
|
+
out.push(chalk_1.default.green(' • ') + inline(m[1]));
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if ((m = line.match(/^\s*\d+\.\s+(.*)$/))) {
|
|
90
|
+
out.push(chalk_1.default.green(' ' + line.trim().split('.')[0] + '. ') + inline(line.replace(/^\s*\d+\.\s+/, '')));
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (/^\s*>/.test(line)) {
|
|
94
|
+
out.push(chalk_1.default.dim('▎ ' + line.replace(/^\s*>\s?/, '')));
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
out.push(inline(line));
|
|
98
|
+
}
|
|
99
|
+
return out.join('\n');
|
|
100
|
+
}
|
|
101
|
+
/** 인라인: **굵게**, `코드`. */
|
|
102
|
+
function inline(s) {
|
|
103
|
+
return s
|
|
104
|
+
.replace(/\*\*(.+?)\*\*/g, (_, t) => chalk_1.default.bold(t))
|
|
105
|
+
.replace(/`([^`]+)`/g, (_, t) => chalk_1.default.yellow(t));
|
|
106
|
+
}
|
|
107
|
+
/** 둥근 박스 패널을 그린다. CJK 폭을 반영해 테두리를 정렬. */
|
|
108
|
+
function panel(role, body) {
|
|
109
|
+
const width = termWidth();
|
|
110
|
+
const inner = width - 4; // 좌우 테두리(│ ) + 우측 공백
|
|
111
|
+
const color = role === 'user' ? chalk_1.default.blueBright : role === 'assistant' ? chalk_1.default.greenBright : chalk_1.default.magentaBright;
|
|
112
|
+
const title = role === 'user' ? '🧑 You' : role === 'assistant' ? '🤖 Ethan' : 'ⓘ 시스템';
|
|
113
|
+
const empty = role === 'assistant' && !body.trim();
|
|
114
|
+
const formatted = empty ? chalk_1.default.dim('(이 턴은 응답이 없었습니다)') : formatMarkdown(body);
|
|
115
|
+
const wrapped = (0, wrap_ansi_1.default)(formatted, inner, { hard: true, trim: false });
|
|
116
|
+
const top = color(ROUND.tl + ROUND.h + ' ') + color.bold(title) + ' ' + color(ROUND.h.repeat(Math.max(0, width - 4 - (0, string_width_1.default)(title))) + ROUND.tr);
|
|
117
|
+
const bottom = color(ROUND.bl + ROUND.h.repeat(width - 2) + ROUND.br);
|
|
118
|
+
const lines = wrapped.split('\n').map((ln) => color(ROUND.v) + ' ' + padVisual(ln, inner) + ' ' + color(ROUND.v));
|
|
119
|
+
return [top, ...lines, bottom].join('\n');
|
|
120
|
+
}
|
|
121
|
+
function banner(provider, model, storePath, resumed) {
|
|
122
|
+
const width = termWidth();
|
|
123
|
+
const line = chalk_1.default.cyan(ROUND.h.repeat(width));
|
|
124
|
+
const t = chalk_1.default.bold.cyan(' n2world · Ethan ') + chalk_1.default.dim('— Rich TUI 대화');
|
|
125
|
+
const meta = chalk_1.default.dim(` 제공자 ${provider}/${model} · 저장 ${storePath}` + (resumed ? ` · 이전 대화 ${resumed}턴 복원` : ''));
|
|
126
|
+
const help = chalk_1.default.dim(' 입력 후 Enter 전송 · 명령: ') + chalk_1.default.yellow('/exit /fresh /export /help');
|
|
127
|
+
return [line, t, meta, help, line].join('\n');
|
|
128
|
+
}
|
|
129
|
+
/** 입력 직전, 응답 대기 표시(완료 시 그 줄을 지운다). */
|
|
130
|
+
function thinkingLine() {
|
|
131
|
+
process.stdout.write(chalk_1.default.dim(' ⏳ Ethan 생각 중…\n'));
|
|
132
|
+
}
|
|
133
|
+
function eraseThinking() {
|
|
134
|
+
process.stdout.write('\x1b[1A\x1b[2K'); // 한 줄 위로 + 그 줄 삭제
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Rich TUI 대화 세션. 영속/복원/도구 실행은 chat 과 동일(같은 chat-data/history.json).
|
|
138
|
+
* 플래그: --fresh(초기화), --export(마크다운 내보내고 종료).
|
|
139
|
+
*/
|
|
140
|
+
async function runRichTui(flags = []) {
|
|
141
|
+
const store = new chat_store_1.ChatStore();
|
|
142
|
+
store.load();
|
|
143
|
+
if (flags.includes('--export')) {
|
|
144
|
+
if (store.count() === 0)
|
|
145
|
+
return void console.log(chalk_1.default.dim(`저장된 대화가 없습니다(${store.path_()}).`));
|
|
146
|
+
const fs = await import('fs/promises');
|
|
147
|
+
const path = await import('path');
|
|
148
|
+
const outPath = path.join('chat-data', `export-${Date.now()}.md`);
|
|
149
|
+
await fs.mkdir('chat-data', { recursive: true });
|
|
150
|
+
await fs.writeFile(outPath, store.exportMarkdown(), 'utf8');
|
|
151
|
+
return void console.log(chalk_1.default.green(`대화 ${store.count()}턴을 내보냈습니다: ${outPath}`));
|
|
152
|
+
}
|
|
153
|
+
const info = (0, llm_1.activeLlmInfo)();
|
|
154
|
+
if (!info.provider) {
|
|
155
|
+
console.error(chalk_1.default.red('[알림] LLM API 키가 없습니다. 루트 .env 에 GEMINI_API_KEY 또는 ANTHROPIC_API_KEY 를 설정하세요(.env.example).'));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
if (flags.includes('--fresh')) {
|
|
159
|
+
store.clear();
|
|
160
|
+
}
|
|
161
|
+
const sandbox = new index_1.LocalSandbox();
|
|
162
|
+
await sandbox.init();
|
|
163
|
+
const worker = new index_1.WorkerAgent(sandbox);
|
|
164
|
+
const runShell = async (cmd) => {
|
|
165
|
+
const res = await worker.executeTask(cmd);
|
|
166
|
+
return `[cmd: ${cmd}]\n${res.stdout}\n${res.stderr}`;
|
|
167
|
+
};
|
|
168
|
+
console.log(banner(info.provider, info.model || '', store.path_(), store.count()));
|
|
169
|
+
for (const t of store.history())
|
|
170
|
+
console.log(panel(t.role, t.content));
|
|
171
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
172
|
+
const ask = () => new Promise((resolve) => rl.question(chalk_1.default.blueBright.bold('\nyou ▸ '), resolve));
|
|
173
|
+
for (;;) {
|
|
174
|
+
const raw = (await ask()).trim();
|
|
175
|
+
if (raw === '')
|
|
176
|
+
continue;
|
|
177
|
+
if (/^\/(exit|quit|bye)$/i.test(raw))
|
|
178
|
+
break;
|
|
179
|
+
if (/^\/help$/i.test(raw)) {
|
|
180
|
+
console.log(panel('system', '명령: **/exit** 종료 · **/fresh** 새 대화 · **/export** 마크다운 저장 · **/help** 도움말\n그 외 입력은 모두 Ethan에게 전송됩니다.'));
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (/^\/fresh$/i.test(raw)) {
|
|
184
|
+
store.clear();
|
|
185
|
+
console.log(panel('system', '이전 대화를 초기화했습니다(새 대화 시작).'));
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (/^\/export$/i.test(raw)) {
|
|
189
|
+
const fs = await import('fs/promises');
|
|
190
|
+
const path = await import('path');
|
|
191
|
+
const outPath = path.join('chat-data', `export-${Date.now()}.md`);
|
|
192
|
+
await fs.mkdir('chat-data', { recursive: true });
|
|
193
|
+
await fs.writeFile(outPath, store.exportMarkdown(), 'utf8');
|
|
194
|
+
console.log(panel('system', `대화 ${store.count()}턴을 내보냈습니다: ${outPath}`));
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
console.log(panel('user', raw));
|
|
198
|
+
store.append({ role: 'user', content: raw });
|
|
199
|
+
thinkingLine();
|
|
200
|
+
let reply;
|
|
201
|
+
try {
|
|
202
|
+
reply = await (0, llm_1.askAgentChat)(store.history(), runShell);
|
|
203
|
+
}
|
|
204
|
+
catch (e) {
|
|
205
|
+
reply = `[오류] ${e?.message ?? e}`;
|
|
206
|
+
}
|
|
207
|
+
eraseThinking();
|
|
208
|
+
store.append({ role: 'assistant', content: reply });
|
|
209
|
+
console.log(panel('assistant', reply));
|
|
210
|
+
}
|
|
211
|
+
rl.close();
|
|
212
|
+
await sandbox.destroy();
|
|
213
|
+
console.log(chalk_1.default.dim(`\n대화를 종료합니다. (${store.count()}턴 저장됨: ${store.path_()})`));
|
|
214
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface ReleaseMeta {
|
|
2
|
+
version: string;
|
|
3
|
+
sha256: string;
|
|
4
|
+
signature: string;
|
|
5
|
+
}
|
|
6
|
+
export interface VerifyResult {
|
|
7
|
+
ok: boolean;
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
/** semver 단순 비교(major.minor.patch). a>b → 1. */
|
|
11
|
+
export declare function cmpVersion(a: string, b: string): number;
|
|
12
|
+
/** 서명 검증용 HMAC(대칭) — 데모. 실배포는 Ed25519 공개키 권장(아래 verifyEd25519). */
|
|
13
|
+
export declare function signMeta(version: string, sha256: string, secret: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* 다운로드 검증: 콘텐츠 해시 + 서명 + 다운그레이드 거부.
|
|
16
|
+
* @param content 다운로드한 바이너리(Buffer)
|
|
17
|
+
* @param meta 신뢰 채널에서 받은 릴리스 메타
|
|
18
|
+
* @param trust 검증 자료(HMAC secret 또는 Ed25519 공개키)
|
|
19
|
+
* @param currentVersion 설치된 현재 버전(없으면 '0.0.0')
|
|
20
|
+
*/
|
|
21
|
+
export declare function verifyDownload(content: Buffer, meta: ReleaseMeta, trust: {
|
|
22
|
+
hmacSecret?: string;
|
|
23
|
+
ed25519PublicKeyPem?: string;
|
|
24
|
+
requireAsymmetric?: boolean;
|
|
25
|
+
}, currentVersion?: string): VerifyResult;
|
|
26
|
+
/** 업데이트 실패 시 직전 양호 버전으로 롤백 결정. */
|
|
27
|
+
export declare function rollbackDecision(failed: VerifyResult, lastGood?: string): {
|
|
28
|
+
rollback: boolean;
|
|
29
|
+
to?: string;
|
|
30
|
+
reason: string;
|
|
31
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// P0 — 공급망 보안: Core 바이너리 서명검증 + TUF식 메타 + 롤백 (앱 v1.2 §4.1)
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// "curl|bash식 해시만" 금지. 다운로드 바이너리는 (a) 콘텐츠 해시 일치 (b) 신뢰키
|
|
6
|
+
// 서명 검증 (c) 다운그레이드(구버전) 거부 를 모두 통과해야 설치된다.
|
|
7
|
+
//
|
|
8
|
+
// 정직 고지(제1계명): 본 모듈은 *검증 로직*(테스트 가능)이다. 실제 macOS 공증·Windows
|
|
9
|
+
// Authenticode 서명은 인증서·OS 도구 필요(이 환경 미수행 — 패키징 단계 별도 검증).
|
|
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.cmpVersion = cmpVersion;
|
|
46
|
+
exports.signMeta = signMeta;
|
|
47
|
+
exports.verifyDownload = verifyDownload;
|
|
48
|
+
exports.rollbackDecision = rollbackDecision;
|
|
49
|
+
const crypto = __importStar(require("crypto"));
|
|
50
|
+
/** semver 단순 비교(major.minor.patch). a>b → 1. */
|
|
51
|
+
function cmpVersion(a, b) {
|
|
52
|
+
const pa = a.split('.').map((n) => parseInt(n, 10) || 0);
|
|
53
|
+
const pb = b.split('.').map((n) => parseInt(n, 10) || 0);
|
|
54
|
+
for (let i = 0; i < 3; i++) {
|
|
55
|
+
if ((pa[i] || 0) !== (pb[i] || 0))
|
|
56
|
+
return (pa[i] || 0) > (pb[i] || 0) ? 1 : -1;
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
/** 서명 검증용 HMAC(대칭) — 데모. 실배포는 Ed25519 공개키 권장(아래 verifyEd25519). */
|
|
61
|
+
function signMeta(version, sha256, secret) {
|
|
62
|
+
return crypto.createHmac('sha256', secret).update(`${version}:${sha256}`).digest('base64');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 다운로드 검증: 콘텐츠 해시 + 서명 + 다운그레이드 거부.
|
|
66
|
+
* @param content 다운로드한 바이너리(Buffer)
|
|
67
|
+
* @param meta 신뢰 채널에서 받은 릴리스 메타
|
|
68
|
+
* @param trust 검증 자료(HMAC secret 또는 Ed25519 공개키)
|
|
69
|
+
* @param currentVersion 설치된 현재 버전(없으면 '0.0.0')
|
|
70
|
+
*/
|
|
71
|
+
function verifyDownload(content, meta, trust, currentVersion = '0.0.0') {
|
|
72
|
+
// M1 — 운영 배포는 대칭키(HMAC) 금지. 클라이언트에 비밀을 임베드하면 추출→위조가 가능하므로
|
|
73
|
+
// Ed25519 공개키 검증을 강제한다. 명시 옵션 또는 NODE_ENV=production / 환경변수로 활성화.
|
|
74
|
+
const requireAsym = trust.requireAsymmetric
|
|
75
|
+
|| process.env.NODE_ENV === 'production'
|
|
76
|
+
|| /^(1|true|on)$/i.test(process.env.N2WORLD_UPDATE_REQUIRE_ED25519 || '');
|
|
77
|
+
if (requireAsym && !trust.ed25519PublicKeyPem) {
|
|
78
|
+
return { ok: false, reason: '운영 배포는 대칭키(HMAC) 금지 — Ed25519 공개키 검증 필요' };
|
|
79
|
+
}
|
|
80
|
+
// (a) 콘텐츠 해시
|
|
81
|
+
const actual = crypto.createHash('sha256').update(content).digest('hex');
|
|
82
|
+
if (actual !== meta.sha256)
|
|
83
|
+
return { ok: false, reason: `해시 불일치(${actual.slice(0, 8)}≠${meta.sha256.slice(0, 8)})` };
|
|
84
|
+
// (b) 서명 — Ed25519 우선(둘 다 주어지면 비대칭 우선).
|
|
85
|
+
let sigOk = false;
|
|
86
|
+
if (trust.ed25519PublicKeyPem) {
|
|
87
|
+
try {
|
|
88
|
+
sigOk = crypto.verify(null, Buffer.from(`${meta.version}:${meta.sha256}`), trust.ed25519PublicKeyPem, Buffer.from(meta.signature, 'base64'));
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
sigOk = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (trust.hmacSecret) {
|
|
95
|
+
const expected = signMeta(meta.version, meta.sha256, trust.hmacSecret);
|
|
96
|
+
sigOk = expected.length === meta.signature.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(meta.signature));
|
|
97
|
+
}
|
|
98
|
+
if (!sigOk)
|
|
99
|
+
return { ok: false, reason: '서명 검증 실패(신뢰키 불일치)' };
|
|
100
|
+
// (c) 다운그레이드 거부(롤백 공격 차단)
|
|
101
|
+
if (cmpVersion(meta.version, currentVersion) < 0)
|
|
102
|
+
return { ok: false, reason: `다운그레이드 거부(${meta.version} < ${currentVersion})` };
|
|
103
|
+
return { ok: true, reason: 'ok' };
|
|
104
|
+
}
|
|
105
|
+
/** 업데이트 실패 시 직전 양호 버전으로 롤백 결정. */
|
|
106
|
+
function rollbackDecision(failed, lastGood) {
|
|
107
|
+
if (failed.ok)
|
|
108
|
+
return { rollback: false, reason: '검증 통과 — 롤백 불요' };
|
|
109
|
+
if (!lastGood)
|
|
110
|
+
return { rollback: false, reason: '롤백 대상 없음 — 설치 중단' };
|
|
111
|
+
return { rollback: true, to: lastGood, reason: `검증 실패(${failed.reason}) → ${lastGood} 롤백` };
|
|
112
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface CalibSample {
|
|
2
|
+
/** 모델이 예측한 가치 v ∈ [0,1]. */
|
|
3
|
+
predicted: number;
|
|
4
|
+
/** 실제 성공 여부(독립 오라클 통과=1, 실패=0). */
|
|
5
|
+
outcome: 0 | 1;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Expected Calibration Error — 신뢰도 구간별 (평균 예측 v) vs (실제 성공률) 괴리의
|
|
9
|
+
* 표본가중 평균. 0에 가까울수록 잘 캘리브레이션됨.
|
|
10
|
+
*/
|
|
11
|
+
export declare function expectedCalibrationError(samples: CalibSample[], bins?: number): number;
|
|
12
|
+
/** ECE 합격 시에만 v 기반 위임 활성. 미달이면 균등 위임 회귀(영가설). */
|
|
13
|
+
export declare function delegationEnabled(samples: CalibSample[], eceThreshold?: number): {
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
ece: number;
|
|
16
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// v2.5.2 Phase 2 — 가치 v 캘리브레이션 (ECE) — 위임의 전제
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// 계획서 v2.5.2 §6: "v 낮은 서브태스크 로컬 위임"은 *v가 캘리브레이션됐을 때만* 유효.
|
|
6
|
+
// 쓰레기 v → 잘못된 위임. ECE(Expected Calibration Error)를 측정해 합격선 미달 시
|
|
7
|
+
// 위임을 비활성(균등 위임으로 회귀)한다.
|
|
8
|
+
//
|
|
9
|
+
// 정직 고지(제1계명): ECE는 (예측 v, 실제 성공 0/1) 쌍으로 측정한다(자가 채점 아님 —
|
|
10
|
+
// 실제 결과는 독립 오라클 통과 여부).
|
|
11
|
+
// ============================================================================
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.expectedCalibrationError = expectedCalibrationError;
|
|
14
|
+
exports.delegationEnabled = delegationEnabled;
|
|
15
|
+
/**
|
|
16
|
+
* Expected Calibration Error — 신뢰도 구간별 (평균 예측 v) vs (실제 성공률) 괴리의
|
|
17
|
+
* 표본가중 평균. 0에 가까울수록 잘 캘리브레이션됨.
|
|
18
|
+
*/
|
|
19
|
+
function expectedCalibrationError(samples, bins = 10) {
|
|
20
|
+
if (samples.length === 0)
|
|
21
|
+
return 1; // 표본 없음 = 미검증(보수적 최악)
|
|
22
|
+
const buckets = Array.from({ length: bins }, () => []);
|
|
23
|
+
for (const s of samples) {
|
|
24
|
+
const p = Math.max(0, Math.min(1, s.predicted));
|
|
25
|
+
const idx = Math.min(bins - 1, Math.floor(p * bins));
|
|
26
|
+
buckets[idx].push(s);
|
|
27
|
+
}
|
|
28
|
+
let ece = 0;
|
|
29
|
+
for (const b of buckets) {
|
|
30
|
+
if (b.length === 0)
|
|
31
|
+
continue;
|
|
32
|
+
const conf = b.reduce((a, s) => a + s.predicted, 0) / b.length;
|
|
33
|
+
const acc = b.reduce((a, s) => a + s.outcome, 0) / b.length;
|
|
34
|
+
ece += (b.length / samples.length) * Math.abs(conf - acc);
|
|
35
|
+
}
|
|
36
|
+
return ece;
|
|
37
|
+
}
|
|
38
|
+
/** ECE 합격 시에만 v 기반 위임 활성. 미달이면 균등 위임 회귀(영가설). */
|
|
39
|
+
function delegationEnabled(samples, eceThreshold = 0.1) {
|
|
40
|
+
const ece = expectedCalibrationError(samples);
|
|
41
|
+
return { enabled: ece <= eceThreshold, ece };
|
|
42
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** 배열을 [pLow, pHigh] 분위수로 윈저라이즈(이상치 클리핑). */
|
|
2
|
+
export declare function winsorize(values: number[], pLow?: number, pHigh?: number): number[];
|
|
3
|
+
/** 루트 자식 Q들을 min-max 정규화 → v_norm∈[0,1]. */
|
|
4
|
+
export declare function normalize(q: number, qMin: number, qMax: number): number;
|
|
5
|
+
export interface AuditEntry {
|
|
6
|
+
source: string;
|
|
7
|
+
raw: number;
|
|
8
|
+
calibrated: number;
|
|
9
|
+
penalty: number;
|
|
10
|
+
t: number;
|
|
11
|
+
}
|
|
12
|
+
export declare class ValueCalibrator {
|
|
13
|
+
private minObservations;
|
|
14
|
+
private history;
|
|
15
|
+
private auditLog;
|
|
16
|
+
private killed;
|
|
17
|
+
constructor(minObservations?: number);
|
|
18
|
+
/** 적대적 팽창 관측 기록: 소스가 주장한 v 와 오라클이 매긴 실제 결과를 비교용으로 저장. */
|
|
19
|
+
observe(source: string, claimedV: number, actualOutcome: number): void;
|
|
20
|
+
/** 소스의 과거 과대평가 정도 → 감쇠 계수∈[0.1,1]. 부풀릴수록 1에서 멀어진다. */
|
|
21
|
+
inflationPenalty(source: string): number;
|
|
22
|
+
/**
|
|
23
|
+
* 소비 직전 보정: 정규화된 v_norm 에 팽창 감쇠를 적용하고 감사 로그에 남긴다.
|
|
24
|
+
* 킬스위치가 내려가 있으면 0(자원 차단)을 반환한다.
|
|
25
|
+
*/
|
|
26
|
+
calibrate(source: string, vNorm: number): number;
|
|
27
|
+
/** v 와 오라클 실제 결과의 상관(영가설 판정용). |corr| 낮으면 v 스케줄링 근거 약함. */
|
|
28
|
+
correlationWithOracle(source: string): number;
|
|
29
|
+
getAuditLog(): AuditEntry[];
|
|
30
|
+
kill(): void;
|
|
31
|
+
reset(): void;
|
|
32
|
+
isKilled(): boolean;
|
|
33
|
+
}
|
|
34
|
+
/** 예산 상한 가드: 누적 토큰/비용이 상한을 넘으면 추가 소비를 차단. */
|
|
35
|
+
export declare class BudgetGuard {
|
|
36
|
+
private cap;
|
|
37
|
+
private spent;
|
|
38
|
+
constructor(cap: number);
|
|
39
|
+
trySpend(amount: number): boolean;
|
|
40
|
+
remaining(): number;
|
|
41
|
+
}
|