@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,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// BM 출원③ 보강 PoC — 금융 업무 자동화 워크플로우 (경비/송금 품의)
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// 특허 청구항 매핑(doc/N2World_특허전략_인프라알고리즘·BM_v1.0.md §4):
|
|
6
|
+
// [700] 옴니채널 수신 → process()의 입력(channel/requester 세션)
|
|
7
|
+
// [710] 산업 워크플로우 → FinanceWorkflowEngine (금융 템플릿)
|
|
8
|
+
// [720] 권한위임 토큰 caveat(규제경계) → Macaroon attenuate + FinanceCaveatVerifier
|
|
9
|
+
// [730] 독립 검증 오라클 → FinanceOracle (에이전트가 만들지 않은 규정/이중지급/직무분리 검사)
|
|
10
|
+
// [740] 거버넌스(비용·감사·킬스위치) → AuditLog + killSwitch
|
|
11
|
+
// [750] 두 갈래 신호(가치 v / 신뢰 t) → A2ATrustGate (t 미달 시 고가치라도 차단)
|
|
12
|
+
//
|
|
13
|
+
// 정직 고지(제1계명):
|
|
14
|
+
// - 보안 토큰·신뢰는 **실제 구현(agent-os-rd.ts)을 재사용**한다(가짜 게이트 금지).
|
|
15
|
+
// - 행동 후보 생성기는 **주입형(pluggable)**. PoC 기본값은 결정적 룰 생성기이며,
|
|
16
|
+
// 프로덕션 경로는 출원① 룰기반 MCTS(predictive-agent)다 — 이를 MCTS라고 표기하지 않는다.
|
|
17
|
+
// - 모든 차단/통과는 테스트로 재현·측정된다(동작 증거).
|
|
18
|
+
// ============================================================================
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.FinanceWorkflowEngine = exports.AuditLog = exports.FinanceOracle = exports.FinanceCaveatVerifier = exports.FinanceCaveat = void 0;
|
|
21
|
+
exports.issueFinanceToken = issueFinanceToken;
|
|
22
|
+
const agent_os_rd_1 = require("./agent-os-rd");
|
|
23
|
+
/** 720 — 토큰에 부가된 금융 규제 caveat(문자열 표현). */
|
|
24
|
+
exports.FinanceCaveat = {
|
|
25
|
+
amountMax: (limit) => `fin.amount <= ${limit}`,
|
|
26
|
+
approvalRequired: () => `fin.approval = required`,
|
|
27
|
+
segregationOfDuties: () => `fin.requester != approver`,
|
|
28
|
+
categoryAllow: (cats) => `fin.category in ${cats.join('|')}`,
|
|
29
|
+
};
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// 720 — FinanceCaveatVerifier: 토큰 서명 무결성(실제 Macaroon) + 금융 caveat 강제
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
class FinanceCaveatVerifier {
|
|
34
|
+
secret;
|
|
35
|
+
constructor(secret) {
|
|
36
|
+
this.secret = secret;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* ① 실제 MacaroonVerifier 로 서명체인·기본 caveat(target/expires/path) 검증
|
|
40
|
+
* ② 금융 caveat(fin.*)을 요청에 대해 강제
|
|
41
|
+
* 반환: { ok, violations }. 위반 사유는 감사 로그로 남는다.
|
|
42
|
+
*/
|
|
43
|
+
check(token, req, ctx) {
|
|
44
|
+
const violations = [];
|
|
45
|
+
// ① 서명/기본 caveat — 위조·범위밖·만료면 즉시 실패(HARD_GATE).
|
|
46
|
+
if (!agent_os_rd_1.MacaroonVerifier.verify(token, this.secret, ctx)) {
|
|
47
|
+
return { ok: false, violations: ['HARD_GATE 실패(서명/target/expires/path)'] };
|
|
48
|
+
}
|
|
49
|
+
// ② 금융 caveat 파싱·강제
|
|
50
|
+
let macaroon;
|
|
51
|
+
try {
|
|
52
|
+
macaroon = agent_os_rd_1.Macaroon.deserialize(token);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { ok: false, violations: ['토큰 파싱 실패'] };
|
|
56
|
+
}
|
|
57
|
+
for (const c of macaroon.caveats) {
|
|
58
|
+
const m1 = c.match(/^fin\.amount <= (\d+(?:\.\d+)?)$/);
|
|
59
|
+
if (m1) {
|
|
60
|
+
const limit = parseFloat(m1[1]);
|
|
61
|
+
if (!(req.amount <= limit))
|
|
62
|
+
violations.push(`금액 한도 초과(${req.amount} > ${limit})`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (c === 'fin.approval = required') {
|
|
66
|
+
if (!req.approver)
|
|
67
|
+
violations.push('승인자 미지정(승인 필요 caveat)');
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (c === 'fin.requester != approver') {
|
|
71
|
+
if (req.approver && req.approver === req.requester)
|
|
72
|
+
violations.push('직무분리 위반(품의자=승인자)');
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const m2 = c.match(/^fin\.category in (.+)$/);
|
|
76
|
+
if (m2) {
|
|
77
|
+
const allowed = m2[1].split('|');
|
|
78
|
+
if (!allowed.includes(req.category))
|
|
79
|
+
violations.push(`허용외 계정과목(${req.category})`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { ok: violations.length === 0, violations };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.FinanceCaveatVerifier = FinanceCaveatVerifier;
|
|
87
|
+
class FinanceOracle {
|
|
88
|
+
allowedCategories;
|
|
89
|
+
dedupeKey;
|
|
90
|
+
ruleset;
|
|
91
|
+
/** 이중지급 탐지용 원장: (payee|amount|date) → 최초 요청 id. */
|
|
92
|
+
paidLedger = new Map();
|
|
93
|
+
constructor(
|
|
94
|
+
/** 정책상 허용 계정과목(독립 정의 — 에이전트가 수정 못함). 규정셋 미지정 시 사용. */
|
|
95
|
+
allowedCategories = ['travel', 'meal', 'office', 'vendor', 'payroll'],
|
|
96
|
+
/** 이중지급 판정 키. */
|
|
97
|
+
dedupeKey = (r) => `${r.payee}|${r.amount}|${r.date}`,
|
|
98
|
+
/** 관할 규정셋(옵션). 지정 시 계정과목·SoD·승인·한도·고액보고를 규정셋이 결정. */
|
|
99
|
+
ruleset) {
|
|
100
|
+
this.allowedCategories = allowedCategories;
|
|
101
|
+
this.dedupeKey = dedupeKey;
|
|
102
|
+
this.ruleset = ruleset;
|
|
103
|
+
}
|
|
104
|
+
validate(req, generatedAction) {
|
|
105
|
+
const violations = [];
|
|
106
|
+
const flags = [];
|
|
107
|
+
// 0) 자기채점/조작 탐지: 생성된 행동이 검증을 우회하려는 하드코드 패턴인가.
|
|
108
|
+
const cheat = /skip[_ ]?(audit|oracle|check)|force[_ ]?approve|bypass/i.test(generatedAction);
|
|
109
|
+
if (cheat) {
|
|
110
|
+
return { passed: false, violations: ['오라클 우회 시도 탐지'], cheatDetected: true, flags };
|
|
111
|
+
}
|
|
112
|
+
// 1) 오라클 고유 검증(규정셋과 무관하게 항상): 금액·수취처
|
|
113
|
+
if (!(req.amount > 0))
|
|
114
|
+
violations.push('금액 비정상(≤0)');
|
|
115
|
+
if (!req.payee)
|
|
116
|
+
violations.push('수취처 누락');
|
|
117
|
+
// 2) 이중지급 탐지(오라클 원장 기반) — 동일 (수취처·금액·일자) 반복
|
|
118
|
+
const key = this.dedupeKey(req);
|
|
119
|
+
const dup = this.paidLedger.has(key);
|
|
120
|
+
if (dup)
|
|
121
|
+
violations.push(`이중지급 의심(기존 요청 ${this.paidLedger.get(key)} 와 동일)`);
|
|
122
|
+
// 3) 규정 준수 — 규정셋(있으면)이 계정과목·SoD·승인·한도·고액보고를 결정, 없으면 데모 규칙.
|
|
123
|
+
if (this.ruleset) {
|
|
124
|
+
const ev = this.ruleset.evaluate(req);
|
|
125
|
+
violations.push(...ev.blocks);
|
|
126
|
+
flags.push(...ev.flags);
|
|
127
|
+
if (dup)
|
|
128
|
+
flags.push('[KR-STR-001] 이중지급 의심 — 차단 및 보고/추가검토 대상');
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
if (!this.allowedCategories.includes(req.category))
|
|
132
|
+
violations.push(`규정외 계정과목(${req.category})`);
|
|
133
|
+
if (req.type === 'transfer' && !req.approver)
|
|
134
|
+
violations.push('송금은 승인자 필수');
|
|
135
|
+
if (req.approver && req.approver === req.requester)
|
|
136
|
+
violations.push('SoD 위반(품의자=승인자)');
|
|
137
|
+
}
|
|
138
|
+
const passed = violations.length === 0;
|
|
139
|
+
if (passed)
|
|
140
|
+
this.paidLedger.set(key, req.id); // 합격분만 원장 기록(집행됨으로 간주)
|
|
141
|
+
return { passed, violations, cheatDetected: false, flags };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.FinanceOracle = FinanceOracle;
|
|
145
|
+
class AuditLog {
|
|
146
|
+
entries = [];
|
|
147
|
+
seq = 0;
|
|
148
|
+
record(e) {
|
|
149
|
+
const auditId = `aud-${++this.seq}`;
|
|
150
|
+
this.entries.push({ ...e, auditId, ts: Date.now() });
|
|
151
|
+
return auditId;
|
|
152
|
+
}
|
|
153
|
+
all() { return [...this.entries]; }
|
|
154
|
+
}
|
|
155
|
+
exports.AuditLog = AuditLog;
|
|
156
|
+
const defaultCandidateGenerator = (req) => `${req.type === 'transfer' ? '송금' : '경비'} 품의 승인 및 지급 큐 등록: ${req.payee} ${req.amount}원 (${req.category})`;
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// 710 — FinanceWorkflowEngine: 채널 수신 → caveat → 오라클 → 실행 (두 갈래 신호 게이트)
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
class FinanceWorkflowEngine {
|
|
161
|
+
verifier;
|
|
162
|
+
trustGate;
|
|
163
|
+
oracle;
|
|
164
|
+
audit;
|
|
165
|
+
gen;
|
|
166
|
+
valueRef;
|
|
167
|
+
killSwitch;
|
|
168
|
+
governance;
|
|
169
|
+
constructor(opts) {
|
|
170
|
+
this.verifier = new FinanceCaveatVerifier(opts.secret);
|
|
171
|
+
this.trustGate = opts.trustGate;
|
|
172
|
+
this.oracle = opts.oracle ?? new FinanceOracle();
|
|
173
|
+
this.audit = opts.audit ?? new AuditLog();
|
|
174
|
+
this.gen = opts.candidateGenerator ?? defaultCandidateGenerator;
|
|
175
|
+
this.valueRef = opts.valueRefAmount ?? 1_000_000;
|
|
176
|
+
this.killSwitch = opts.killSwitch ?? (() => false);
|
|
177
|
+
this.governance = opts.governance;
|
|
178
|
+
}
|
|
179
|
+
auditLog() { return this.audit; }
|
|
180
|
+
/** 가치 v∈[0,1]: 사업 우선순위(금액 비례, 상한 1). 신뢰 t와 독립 산출. */
|
|
181
|
+
computeValue(req) {
|
|
182
|
+
return Math.max(0, Math.min(1, req.amount / this.valueRef));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 금융 품의 1건을 처리한다. 통제 순서: 킬스위치 → 신뢰(t) → caveat(720) → 오라클(730) → 실행.
|
|
186
|
+
* 각 단계 결과를 controls 에 누적하고 감사 로그에 기록한다.
|
|
187
|
+
*/
|
|
188
|
+
process(req, token, ctx) {
|
|
189
|
+
const controls = [];
|
|
190
|
+
const value = this.computeValue(req);
|
|
191
|
+
const finalize = (approved, decidedBy, trust, violations) => {
|
|
192
|
+
const auditId = this.audit.record({ requestId: req.id, decidedBy, approved, trust, value, violations });
|
|
193
|
+
// 740 거버넌스 영속 결선: 결정을 governance.record()로 흘려 sink(디스크)에 내구 기록.
|
|
194
|
+
this.governance?.record('finance-decision', JSON.stringify({
|
|
195
|
+
auditId, requestId: req.id, decidedBy, approved, trust: +trust.toFixed(3), value: +value.toFixed(3), violations,
|
|
196
|
+
}));
|
|
197
|
+
return { requestId: req.id, approved, decidedBy, controls, value, trust, violations, auditId };
|
|
198
|
+
};
|
|
199
|
+
// 740 킬스위치(로컬 토글 또는 거버넌스 킬스위치 어느 쪽이든 활성이면 중단)
|
|
200
|
+
if (this.killSwitch() || this.governance?.isKilled()) {
|
|
201
|
+
controls.push({ control: 'execute', passed: false, reason: '킬스위치 활성 — 자동화 중단' });
|
|
202
|
+
return finalize(false, 'execute', 0, ['killswitch']);
|
|
203
|
+
}
|
|
204
|
+
// 750 신뢰 t — 고가치(value↑)라도 t 미달이면 차단(두 갈래 신호 독립).
|
|
205
|
+
const t = this.trustGate.evaluate(req.requester, token, ctx);
|
|
206
|
+
controls.push({ control: 'trust', passed: t.allowed, reason: t.reason });
|
|
207
|
+
if (!t.allowed)
|
|
208
|
+
return finalize(false, 'trust', t.trust, [t.reason]);
|
|
209
|
+
// 720 caveat(규제 경계)
|
|
210
|
+
const cav = this.verifier.check(token, req, ctx);
|
|
211
|
+
controls.push({ control: 'caveat', passed: cav.ok, reason: cav.ok ? 'ok' : cav.violations.join('; ') });
|
|
212
|
+
if (!cav.ok) {
|
|
213
|
+
this.trustGate.ledgerRef().recordViolation(req.requester); // 위반은 평판 감점
|
|
214
|
+
return finalize(false, 'caveat', t.trust, cav.violations);
|
|
215
|
+
}
|
|
216
|
+
// 행동 후보 생성(주입형) → 730 독립 오라클 검증
|
|
217
|
+
const action = this.gen(req);
|
|
218
|
+
const ora = this.oracle.validate(req, action);
|
|
219
|
+
controls.push({ control: 'oracle', passed: ora.passed, reason: ora.passed ? 'ok' : ora.violations.join('; ') });
|
|
220
|
+
if (!ora.passed) {
|
|
221
|
+
if (ora.cheatDetected)
|
|
222
|
+
this.trustGate.ledgerRef().recordViolation(req.requester);
|
|
223
|
+
return finalize(false, 'oracle', t.trust, ora.violations);
|
|
224
|
+
}
|
|
225
|
+
// 규정 보고 플래그(CTR/STR 등 — 차단 아님)는 거버넌스 감사에 별도 기록(영속).
|
|
226
|
+
if (ora.flags && ora.flags.length > 0) {
|
|
227
|
+
this.governance?.record('compliance-flag', JSON.stringify({ requestId: req.id, flags: ora.flags }));
|
|
228
|
+
}
|
|
229
|
+
// 실행(시뮬 부킹) + 평판 가점
|
|
230
|
+
controls.push({ control: 'execute', passed: true, reason: action });
|
|
231
|
+
this.trustGate.ledgerRef().recordSuccess(req.requester);
|
|
232
|
+
return finalize(true, 'execute', t.trust, []);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
exports.FinanceWorkflowEngine = FinanceWorkflowEngine;
|
|
236
|
+
/** 헬퍼: 역할/세션에 금융 caveat 를 부가한 위임 토큰 발급(720). */
|
|
237
|
+
function issueFinanceToken(secret, identifier, target, caveats) {
|
|
238
|
+
let m = agent_os_rd_1.Macaroon.create(secret, identifier, target);
|
|
239
|
+
for (const c of caveats)
|
|
240
|
+
m = agent_os_rd_1.Macaroon.attenuate(m, c);
|
|
241
|
+
return agent_os_rd_1.Macaroon.serialize(m);
|
|
242
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export declare class GatewayServer extends EventEmitter {
|
|
3
|
+
private server;
|
|
4
|
+
private port;
|
|
5
|
+
constructor(port: number);
|
|
6
|
+
/** 실제 바인딩된 포트(port 0 으로 시작했으면 OS 할당 포트). */
|
|
7
|
+
getPort(): number;
|
|
8
|
+
start(): Promise<void>;
|
|
9
|
+
stop(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare class GatewayClient {
|
|
12
|
+
private socket;
|
|
13
|
+
private port;
|
|
14
|
+
constructor(port: number);
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
sendMessage(type: string, content?: string): void;
|
|
17
|
+
disconnect(): Promise<void>;
|
|
18
|
+
}
|
package/dist/gateway.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.GatewayClient = exports.GatewayServer = void 0;
|
|
37
|
+
const net = __importStar(require("net"));
|
|
38
|
+
const events_1 = require("events");
|
|
39
|
+
class GatewayServer extends events_1.EventEmitter {
|
|
40
|
+
server = null;
|
|
41
|
+
port;
|
|
42
|
+
constructor(port) {
|
|
43
|
+
super();
|
|
44
|
+
this.port = port;
|
|
45
|
+
}
|
|
46
|
+
/** 실제 바인딩된 포트(port 0 으로 시작했으면 OS 할당 포트). */
|
|
47
|
+
getPort() {
|
|
48
|
+
return this.port;
|
|
49
|
+
}
|
|
50
|
+
async start() {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
this.server = net.createServer((socket) => {
|
|
53
|
+
let buffer = '';
|
|
54
|
+
socket.on('data', (data) => {
|
|
55
|
+
buffer += data.toString();
|
|
56
|
+
let boundary = buffer.indexOf('\n');
|
|
57
|
+
while (boundary !== -1) {
|
|
58
|
+
const line = buffer.substring(0, boundary).trim();
|
|
59
|
+
buffer = buffer.substring(boundary + 1);
|
|
60
|
+
if (line.length > 0) {
|
|
61
|
+
try {
|
|
62
|
+
const message = JSON.parse(line);
|
|
63
|
+
this.emit('message', message);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
// ignore malformed JSON
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
boundary = buffer.indexOf('\n');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
const onError = (err) => { this.server.removeListener('error', onError); reject(err); };
|
|
74
|
+
this.server.once('error', onError); // EADDRINUSE 등 정직히 reject
|
|
75
|
+
this.server.listen(this.port, '127.0.0.1', () => {
|
|
76
|
+
this.server.removeListener('error', onError);
|
|
77
|
+
const addr = this.server.address();
|
|
78
|
+
if (addr && typeof addr === 'object' && addr.port)
|
|
79
|
+
this.port = addr.port; // port 0 → 실제 포트
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async stop() {
|
|
85
|
+
if (this.server) {
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
this.server.close(() => {
|
|
88
|
+
resolve();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.GatewayServer = GatewayServer;
|
|
95
|
+
class GatewayClient {
|
|
96
|
+
socket = null;
|
|
97
|
+
port;
|
|
98
|
+
constructor(port) {
|
|
99
|
+
this.port = port;
|
|
100
|
+
}
|
|
101
|
+
async connect() {
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
this.socket = new net.Socket();
|
|
104
|
+
this.socket.connect(this.port, '127.0.0.1', () => {
|
|
105
|
+
resolve();
|
|
106
|
+
});
|
|
107
|
+
this.socket.on('error', (err) => {
|
|
108
|
+
reject(err);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
sendMessage(type, content) {
|
|
113
|
+
if (this.socket) {
|
|
114
|
+
this.socket.write(JSON.stringify({ type, content }) + '\n');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async disconnect() {
|
|
118
|
+
if (this.socket) {
|
|
119
|
+
this.socket.destroy();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.GatewayClient = GatewayClient;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BudgetGuard } from './value-calibration';
|
|
2
|
+
export interface AuditEvent {
|
|
3
|
+
t: number;
|
|
4
|
+
type: string;
|
|
5
|
+
detail: string;
|
|
6
|
+
}
|
|
7
|
+
/** 감사 영속 sink(옵션). record() 시 동기 호출된다. 미지정이면 인메모리만(기존 동작). */
|
|
8
|
+
export type AuditSink = (e: AuditEvent) => void;
|
|
9
|
+
export declare class GovernanceController {
|
|
10
|
+
private budget;
|
|
11
|
+
/** 영속 sink(옵션) — 지정 시 모든 감사 이벤트를 디스크 등으로 내구 기록. */
|
|
12
|
+
private sink?;
|
|
13
|
+
private killed;
|
|
14
|
+
private audit;
|
|
15
|
+
constructor(budget?: BudgetGuard,
|
|
16
|
+
/** 영속 sink(옵션) — 지정 시 모든 감사 이벤트를 디스크 등으로 내구 기록. */
|
|
17
|
+
sink?: AuditSink | undefined);
|
|
18
|
+
/** S3 킬스위치: 즉시 모든 행동 차단. */
|
|
19
|
+
kill(reason?: string): void;
|
|
20
|
+
reset(): void;
|
|
21
|
+
isKilled(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* 행동 인가: 킬스위치 OFF ∧ 예산 내일 때만 허용. 모든 결정을 감사 로그에 남긴다.
|
|
24
|
+
* @param cost 이 행동의 비용(토큰/금액 등 동일 단위).
|
|
25
|
+
*/
|
|
26
|
+
authorize(action: string, cost?: number): {
|
|
27
|
+
allowed: boolean;
|
|
28
|
+
reason: string;
|
|
29
|
+
};
|
|
30
|
+
record(type: string, detail: string): void;
|
|
31
|
+
getAuditLog(): AuditEvent[];
|
|
32
|
+
budgetRemaining(): number;
|
|
33
|
+
/** 대시보드/CLI 노출용 상태 스냅샷. */
|
|
34
|
+
status(): {
|
|
35
|
+
killed: boolean;
|
|
36
|
+
budgetRemaining: number;
|
|
37
|
+
auditCount: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GovernanceController = void 0;
|
|
4
|
+
const value_calibration_1 = require("./value-calibration");
|
|
5
|
+
class GovernanceController {
|
|
6
|
+
budget;
|
|
7
|
+
sink;
|
|
8
|
+
killed = false;
|
|
9
|
+
audit = [];
|
|
10
|
+
constructor(budget = new value_calibration_1.BudgetGuard(Number.POSITIVE_INFINITY),
|
|
11
|
+
/** 영속 sink(옵션) — 지정 시 모든 감사 이벤트를 디스크 등으로 내구 기록. */
|
|
12
|
+
sink) {
|
|
13
|
+
this.budget = budget;
|
|
14
|
+
this.sink = sink;
|
|
15
|
+
}
|
|
16
|
+
/** S3 킬스위치: 즉시 모든 행동 차단. */
|
|
17
|
+
kill(reason = 'manual') { this.killed = true; this.record('killswitch', reason); }
|
|
18
|
+
reset() { this.killed = false; this.record('reset', ''); }
|
|
19
|
+
isKilled() { return this.killed; }
|
|
20
|
+
/**
|
|
21
|
+
* 행동 인가: 킬스위치 OFF ∧ 예산 내일 때만 허용. 모든 결정을 감사 로그에 남긴다.
|
|
22
|
+
* @param cost 이 행동의 비용(토큰/금액 등 동일 단위).
|
|
23
|
+
*/
|
|
24
|
+
authorize(action, cost = 0) {
|
|
25
|
+
if (this.killed) {
|
|
26
|
+
this.record('blocked', `${action} (killswitch)`);
|
|
27
|
+
return { allowed: false, reason: 'killswitch active' };
|
|
28
|
+
}
|
|
29
|
+
if (cost > 0 && !this.budget.trySpend(cost)) {
|
|
30
|
+
this.record('blocked', `${action} (budget, need=${cost}, left=${this.budget.remaining()})`);
|
|
31
|
+
return { allowed: false, reason: 'budget exceeded' };
|
|
32
|
+
}
|
|
33
|
+
this.record('authorized', `${action} cost=${cost}`);
|
|
34
|
+
return { allowed: true, reason: 'ok' };
|
|
35
|
+
}
|
|
36
|
+
record(type, detail) {
|
|
37
|
+
const e = { t: Date.now(), type, detail };
|
|
38
|
+
this.audit.push(e);
|
|
39
|
+
this.sink?.(e); // 영속 sink(있으면) — 디스크 등 내구 기록
|
|
40
|
+
}
|
|
41
|
+
getAuditLog() { return this.audit; }
|
|
42
|
+
budgetRemaining() { return this.budget.remaining(); }
|
|
43
|
+
/** 대시보드/CLI 노출용 상태 스냅샷. */
|
|
44
|
+
status() {
|
|
45
|
+
return { killed: this.killed, budgetRemaining: this.budget.remaining(), auditCount: this.audit.length };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.GovernanceController = GovernanceController;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { GovernanceController } from './governance';
|
|
2
|
+
import { CostApprover, ModelPricing } from './cost-estimator';
|
|
3
|
+
export interface GovernedExecutorOptions {
|
|
4
|
+
model?: string;
|
|
5
|
+
pricing?: Record<string, ModelPricing>;
|
|
6
|
+
costApprover?: CostApprover;
|
|
7
|
+
autoApproveBelowUsd?: number;
|
|
8
|
+
/** 테스트/대체용 LLM 호출 주입. 미지정 시 실제 llmComplete(키 필요). */
|
|
9
|
+
complete?: (prompt: string, system?: string) => Promise<string>;
|
|
10
|
+
}
|
|
11
|
+
export interface GovernedResult {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
reason: string;
|
|
14
|
+
text?: string;
|
|
15
|
+
costUsd?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class GovernedExecutor {
|
|
18
|
+
private gov;
|
|
19
|
+
private opts;
|
|
20
|
+
constructor(gov: GovernanceController, opts?: GovernedExecutorOptions);
|
|
21
|
+
/**
|
|
22
|
+
* 거버넌스로 게이팅된 LLM 호출. 예상 토큰으로 비용을 추정해 승인·예산을 거친 뒤에만 실행한다.
|
|
23
|
+
*/
|
|
24
|
+
llmCall(prompt: string, estTokensIn: number, estTokensOut: number, system?: string): Promise<GovernedResult>;
|
|
25
|
+
/** 임의의 비싼 행동을 거버넌스로 게이팅(킬스위치+예산). 비용 단위는 호출자 정의. */
|
|
26
|
+
guardedAction<T>(label: string, cost: number, run: () => Promise<T>): Promise<{
|
|
27
|
+
ok: boolean;
|
|
28
|
+
reason: string;
|
|
29
|
+
result?: T;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GovernedExecutor = void 0;
|
|
4
|
+
const cost_estimator_1 = require("./cost-estimator");
|
|
5
|
+
const llm_1 = require("./llm");
|
|
6
|
+
class GovernedExecutor {
|
|
7
|
+
gov;
|
|
8
|
+
opts;
|
|
9
|
+
constructor(gov, opts = {}) {
|
|
10
|
+
this.gov = gov;
|
|
11
|
+
this.opts = opts;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 거버넌스로 게이팅된 LLM 호출. 예상 토큰으로 비용을 추정해 승인·예산을 거친 뒤에만 실행한다.
|
|
15
|
+
*/
|
|
16
|
+
async llmCall(prompt, estTokensIn, estTokensOut, system) {
|
|
17
|
+
const model = this.opts.model ?? 'claude-opus-4-8';
|
|
18
|
+
// 1) 킬스위치
|
|
19
|
+
if (this.gov.isKilled()) {
|
|
20
|
+
this.gov.record('blocked', 'llm-call (killswitch)');
|
|
21
|
+
return { ok: false, reason: 'killswitch active' };
|
|
22
|
+
}
|
|
23
|
+
// 2) 비용 견적 + 3) 승인 게이트
|
|
24
|
+
const est = (0, cost_estimator_1.estimateCost)(model, estTokensIn, estTokensOut, this.opts.pricing);
|
|
25
|
+
const approval = await (0, cost_estimator_1.requireCostApproval)(est, {
|
|
26
|
+
approve: this.opts.costApprover,
|
|
27
|
+
autoApproveBelowUsd: this.opts.autoApproveBelowUsd,
|
|
28
|
+
});
|
|
29
|
+
if (!approval.approved) {
|
|
30
|
+
this.gov.record('blocked', `llm-call (cost ${approval.reason})`);
|
|
31
|
+
return { ok: false, reason: `cost not approved: ${approval.reason}`, costUsd: est.costUsd };
|
|
32
|
+
}
|
|
33
|
+
// 4) 예산 인가(킬스위치 재확인 포함)
|
|
34
|
+
const auth = this.gov.authorize('llm-call', est.costUsd);
|
|
35
|
+
if (!auth.allowed) {
|
|
36
|
+
return { ok: false, reason: auth.reason, costUsd: est.costUsd };
|
|
37
|
+
}
|
|
38
|
+
// 5) 실제 실행
|
|
39
|
+
const complete = this.opts.complete ?? llm_1.llmComplete;
|
|
40
|
+
try {
|
|
41
|
+
const text = await complete(prompt, system);
|
|
42
|
+
this.gov.record('executed', `llm-call cost=${est.costUsd}`);
|
|
43
|
+
return { ok: true, reason: 'ok', text, costUsd: est.costUsd };
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
this.gov.record('error', `llm-call ${err.message}`);
|
|
47
|
+
return { ok: false, reason: `execution error: ${err.message}`, costUsd: est.costUsd };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** 임의의 비싼 행동을 거버넌스로 게이팅(킬스위치+예산). 비용 단위는 호출자 정의. */
|
|
51
|
+
async guardedAction(label, cost, run) {
|
|
52
|
+
if (this.gov.isKilled()) {
|
|
53
|
+
this.gov.record('blocked', `${label} (killswitch)`);
|
|
54
|
+
return { ok: false, reason: 'killswitch active' };
|
|
55
|
+
}
|
|
56
|
+
const auth = this.gov.authorize(label, cost);
|
|
57
|
+
if (!auth.allowed)
|
|
58
|
+
return { ok: false, reason: auth.reason };
|
|
59
|
+
const result = await run();
|
|
60
|
+
return { ok: true, reason: 'ok', result };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.GovernedExecutor = GovernedExecutor;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ChatTurn } from './llm';
|
|
2
|
+
import { Tier } from './tier-classifier';
|
|
3
|
+
import { TierGuard } from './tier-guard';
|
|
4
|
+
export interface GovernedBackends {
|
|
5
|
+
external: (history: ChatTurn[], system?: string) => Promise<string>;
|
|
6
|
+
local: (history: ChatTurn[], system?: string) => Promise<string>;
|
|
7
|
+
localConfigured: () => boolean;
|
|
8
|
+
externalAvailable: () => boolean;
|
|
9
|
+
externalName: () => string;
|
|
10
|
+
}
|
|
11
|
+
export interface GovernedResult {
|
|
12
|
+
text: string;
|
|
13
|
+
tier: Tier;
|
|
14
|
+
provider: 'local' | 'external' | 'none';
|
|
15
|
+
blockedExternal: boolean;
|
|
16
|
+
reason: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const TIER0_NO_LOCAL_NOTICE = "[\uBCF4\uC548\u00B7Tier-0] \uBBFC\uAC10 \uB370\uC774\uD130(\uAC1C\uC778\uC815\uBCF4/\uC790\uACA9\uC99D\uBA85/\uC13C\uC11C\u00B7\uC74C\uC131)\uB294 \uC678\uBD80 \uBAA8\uB378\uB85C \uBCF4\uB0B4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB85C\uCEEC \uBAA8\uB378(OLLAMA_MODEL) \uBBF8\uC124\uC815\uC73C\uB85C \uCC98\uB9AC\uB97C \uBCF4\uB958\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uB85C\uCEEC \uBAA8\uB378\uC744 \uC124\uC815\uD558\uBA74 \uCC98\uB9AC\uB429\uB2C8\uB2E4.";
|
|
19
|
+
export declare const TIER1_NOTICE = "[Tier-1] \uC678\uBD80 \uBAA8\uB378 \uC0AC\uC6A9 \uB3D9\uC758\uAC00 \uC5C6\uACE0 \uB85C\uCEEC \uBAA8\uB378\uB3C4 \uC5C6\uC5B4 \uCC98\uB9AC\uB97C \uBCF4\uB958\uD588\uC2B5\uB2C8\uB2E4. \uB3D9\uC758(consent) \uB610\uB294 \uB85C\uCEEC \uBAA8\uB378\uC744 \uC124\uC815\uD558\uC138\uC694.";
|
|
20
|
+
export declare const NO_BACKEND_NOTICE = "[\uC54C\uB9BC] \uC0AC\uC6A9 \uAC00\uB2A5\uD55C LLM \uBC31\uC5D4\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(\uC678\uBD80 \uD0A4\u00B7\uB85C\uCEEC \uBAA8\uB378 \uBAA8\uB450 \uBBF8\uC124\uC815).";
|
|
21
|
+
/**
|
|
22
|
+
* Tier 라우팅 강제 에이전트. respond()가 실 실행 경로의 단일 강제 지점.
|
|
23
|
+
*/
|
|
24
|
+
export declare class GovernedAgent {
|
|
25
|
+
private backends;
|
|
26
|
+
private guard;
|
|
27
|
+
/** 채널 정책상 기준 Tier — *미분류(DEFAULT-DENY)*에만 적용. 민감 매칭은 항상 Tier-0 유지. */
|
|
28
|
+
private baselineTier?;
|
|
29
|
+
constructor(backends: GovernedBackends, opts?: {
|
|
30
|
+
guard?: TierGuard;
|
|
31
|
+
baselineTier?: Tier;
|
|
32
|
+
});
|
|
33
|
+
respond(history: ChatTurn[], opts?: {
|
|
34
|
+
consent?: boolean;
|
|
35
|
+
system?: string;
|
|
36
|
+
baselineTier?: Tier;
|
|
37
|
+
}): Promise<GovernedResult>;
|
|
38
|
+
guardRef(): TierGuard;
|
|
39
|
+
}
|
|
40
|
+
/** llm.ts 기반 기본 백엔드 구성(외부=askAgentChat, 로컬=localChat). */
|
|
41
|
+
export declare function defaultGovernedBackends(runShell: (cmd: string) => Promise<string>): GovernedBackends;
|