ltcai 4.0.0 → 4.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 (195) hide show
  1. package/README.md +42 -33
  2. package/desktop/electron/main.cjs +44 -0
  3. package/docs/CHANGELOG.md +106 -0
  4. package/docs/REALTIME_COLLABORATION.md +3 -3
  5. package/docs/V3_FRONTEND.md +9 -8
  6. package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +65 -0
  7. package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +70 -0
  8. package/docs/V4_1_VALIDATION_REPORT.md +47 -0
  9. package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +95 -45
  10. package/docs/kg-schema.md +6 -2
  11. package/docs/spec-vs-impl.md +10 -10
  12. package/frontend/index.html +24 -0
  13. package/frontend/openapi.json +14190 -0
  14. package/frontend/src/App.tsx +184 -0
  15. package/frontend/src/api/client.ts +317 -0
  16. package/frontend/src/api/openapi.ts +16637 -0
  17. package/frontend/src/components/primitives.tsx +204 -0
  18. package/frontend/src/components/ui/badge.tsx +27 -0
  19. package/frontend/src/components/ui/button.tsx +37 -0
  20. package/frontend/src/components/ui/card.tsx +22 -0
  21. package/frontend/src/components/ui/input.tsx +16 -0
  22. package/frontend/src/components/ui/textarea.tsx +16 -0
  23. package/frontend/src/lib/utils.ts +33 -0
  24. package/frontend/src/main.tsx +23 -0
  25. package/frontend/src/pages/Act.tsx +245 -0
  26. package/frontend/src/pages/Ask.tsx +200 -0
  27. package/frontend/src/pages/Brain.tsx +267 -0
  28. package/frontend/src/pages/Capture.tsx +158 -0
  29. package/frontend/src/pages/Library.tsx +187 -0
  30. package/frontend/src/pages/System.tsx +344 -0
  31. package/frontend/src/routes.ts +85 -0
  32. package/frontend/src/store/appStore.ts +54 -0
  33. package/frontend/src/styles.css +107 -0
  34. package/kg_schema.py +2 -603
  35. package/knowledge_graph.py +37 -4958
  36. package/latticeai/__init__.py +1 -1
  37. package/latticeai/api/admin.py +15 -16
  38. package/latticeai/api/agents.py +13 -6
  39. package/latticeai/api/auth.py +19 -11
  40. package/latticeai/api/invitations.py +100 -0
  41. package/latticeai/api/knowledge_graph.py +4 -11
  42. package/latticeai/api/plugins.py +3 -6
  43. package/latticeai/api/realtime.py +4 -7
  44. package/latticeai/api/setup.py +5 -4
  45. package/latticeai/api/static_routes.py +13 -16
  46. package/latticeai/api/ui_redirects.py +26 -0
  47. package/latticeai/api/workflow_designer.py +39 -6
  48. package/latticeai/api/workspace.py +24 -10
  49. package/latticeai/app_factory.py +88 -17
  50. package/latticeai/brain/_kg_common.py +1123 -0
  51. package/latticeai/brain/discovery.py +1455 -0
  52. package/latticeai/brain/documents.py +218 -0
  53. package/latticeai/brain/ingest.py +644 -0
  54. package/latticeai/brain/projection.py +561 -0
  55. package/latticeai/brain/provenance.py +401 -0
  56. package/latticeai/brain/retrieval.py +1316 -0
  57. package/latticeai/brain/schema.py +640 -0
  58. package/latticeai/brain/store.py +216 -0
  59. package/latticeai/brain/write_master.py +225 -0
  60. package/latticeai/core/invitations.py +131 -0
  61. package/latticeai/core/marketplace.py +1 -1
  62. package/latticeai/core/multi_agent.py +1 -1
  63. package/latticeai/core/policy.py +54 -0
  64. package/latticeai/core/realtime.py +65 -44
  65. package/latticeai/core/sessions.py +31 -5
  66. package/latticeai/core/users.py +147 -0
  67. package/latticeai/core/workspace_os.py +420 -20
  68. package/latticeai/services/agent_runtime.py +242 -4
  69. package/latticeai/services/run_executor.py +328 -0
  70. package/latticeai/services/workspace_service.py +27 -19
  71. package/package.json +54 -27
  72. package/scripts/build_frontend_assets.mjs +38 -0
  73. package/scripts/bump_version.py +1 -1
  74. package/scripts/export_openapi.py +31 -0
  75. package/scripts/lint_frontend.mjs +86 -0
  76. package/scripts/run_python.mjs +47 -0
  77. package/src-tauri/Cargo.lock +4833 -0
  78. package/src-tauri/Cargo.toml +19 -0
  79. package/src-tauri/build.rs +3 -0
  80. package/src-tauri/capabilities/default.json +7 -0
  81. package/src-tauri/src/main.rs +78 -0
  82. package/src-tauri/tauri.conf.json +36 -0
  83. package/static/app/asset-manifest.json +32 -0
  84. package/static/app/assets/core-CwxXejkd.js +2 -0
  85. package/static/app/assets/core-CwxXejkd.js.map +1 -0
  86. package/static/app/assets/index-CJRAzNnf.js +333 -0
  87. package/static/app/assets/index-CJRAzNnf.js.map +1 -0
  88. package/static/app/assets/index-CSwBBgf4.css +2 -0
  89. package/static/app/index.html +25 -0
  90. package/static/manifest.json +2 -2
  91. package/static/sw.js +4 -4
  92. package/scripts/build_v3_assets.mjs +0 -170
  93. package/scripts/lint_v3.mjs +0 -97
  94. package/static/account.html +0 -113
  95. package/static/activity.html +0 -73
  96. package/static/admin.html +0 -486
  97. package/static/agents.html +0 -139
  98. package/static/chat.html +0 -841
  99. package/static/css/reference/account.css +0 -439
  100. package/static/css/reference/admin.css +0 -610
  101. package/static/css/reference/base.css +0 -1661
  102. package/static/css/reference/chat.css +0 -4623
  103. package/static/css/reference/graph.css +0 -1016
  104. package/static/css/responsive.css +0 -861
  105. package/static/graph.html +0 -122
  106. package/static/platform.css +0 -104
  107. package/static/plugins.html +0 -136
  108. package/static/scripts/account.js +0 -238
  109. package/static/scripts/admin.js +0 -1614
  110. package/static/scripts/chat.js +0 -5081
  111. package/static/scripts/graph.js +0 -1804
  112. package/static/scripts/platform.js +0 -64
  113. package/static/scripts/ux.js +0 -167
  114. package/static/scripts/workspace.js +0 -948
  115. package/static/v3/asset-manifest.json +0 -56
  116. package/static/v3/css/lattice.base.49deefb5.css +0 -128
  117. package/static/v3/css/lattice.base.css +0 -128
  118. package/static/v3/css/lattice.components.cde18231.css +0 -472
  119. package/static/v3/css/lattice.components.css +0 -472
  120. package/static/v3/css/lattice.shell.29d36d85.css +0 -452
  121. package/static/v3/css/lattice.shell.css +0 -452
  122. package/static/v3/css/lattice.tokens.304cbc40.css +0 -135
  123. package/static/v3/css/lattice.tokens.css +0 -135
  124. package/static/v3/css/lattice.views.0a18b6c5.css +0 -360
  125. package/static/v3/css/lattice.views.css +0 -360
  126. package/static/v3/index.html +0 -68
  127. package/static/v3/js/app.356e6452.js +0 -26
  128. package/static/v3/js/app.js +0 -26
  129. package/static/v3/js/core/api.7a308b89.js +0 -568
  130. package/static/v3/js/core/api.js +0 -568
  131. package/static/v3/js/core/components.f25b3b93.js +0 -230
  132. package/static/v3/js/core/components.js +0 -230
  133. package/static/v3/js/core/dom.a2773eb0.js +0 -148
  134. package/static/v3/js/core/dom.js +0 -148
  135. package/static/v3/js/core/router.584570f2.js +0 -37
  136. package/static/v3/js/core/router.js +0 -37
  137. package/static/v3/js/core/routes.7222343d.js +0 -93
  138. package/static/v3/js/core/routes.js +0 -93
  139. package/static/v3/js/core/shell.a1657f20.js +0 -391
  140. package/static/v3/js/core/shell.js +0 -391
  141. package/static/v3/js/core/store.204a08b2.js +0 -113
  142. package/static/v3/js/core/store.js +0 -113
  143. package/static/v3/js/views/admin-audit.660a1fb1.js +0 -185
  144. package/static/v3/js/views/admin-audit.js +0 -185
  145. package/static/v3/js/views/admin-permissions.a7ae5f09.js +0 -177
  146. package/static/v3/js/views/admin-permissions.js +0 -177
  147. package/static/v3/js/views/admin-policies.3658fd86.js +0 -102
  148. package/static/v3/js/views/admin-policies.js +0 -102
  149. package/static/v3/js/views/admin-private-vpc.7d342d36.js +0 -135
  150. package/static/v3/js/views/admin-private-vpc.js +0 -135
  151. package/static/v3/js/views/admin-security.07c66b72.js +0 -180
  152. package/static/v3/js/views/admin-security.js +0 -180
  153. package/static/v3/js/views/admin-users.03bac88c.js +0 -168
  154. package/static/v3/js/views/admin-users.js +0 -168
  155. package/static/v3/js/views/agents.014d0b74.js +0 -541
  156. package/static/v3/js/views/agents.js +0 -541
  157. package/static/v3/js/views/chat.e6dd7dd0.js +0 -601
  158. package/static/v3/js/views/chat.js +0 -601
  159. package/static/v3/js/views/files.adad14c1.js +0 -365
  160. package/static/v3/js/views/files.js +0 -365
  161. package/static/v3/js/views/graph-canvas.17c15d65.js +0 -509
  162. package/static/v3/js/views/graph-canvas.js +0 -509
  163. package/static/v3/js/views/home.24f8b8ae.js +0 -200
  164. package/static/v3/js/views/home.js +0 -200
  165. package/static/v3/js/views/hooks.37895880.js +0 -220
  166. package/static/v3/js/views/hooks.js +0 -220
  167. package/static/v3/js/views/hybrid-search.2fb63ed9.js +0 -194
  168. package/static/v3/js/views/hybrid-search.js +0 -194
  169. package/static/v3/js/views/knowledge-graph.5e40cbeb.js +0 -509
  170. package/static/v3/js/views/knowledge-graph.js +0 -509
  171. package/static/v3/js/views/marketplace.ab0583d4.js +0 -141
  172. package/static/v3/js/views/marketplace.js +0 -141
  173. package/static/v3/js/views/mcp.99b5c6a7.js +0 -114
  174. package/static/v3/js/views/mcp.js +0 -114
  175. package/static/v3/js/views/memory.4ebdf474.js +0 -147
  176. package/static/v3/js/views/memory.js +0 -147
  177. package/static/v3/js/views/models.a1ffa147.js +0 -256
  178. package/static/v3/js/views/models.js +0 -256
  179. package/static/v3/js/views/my-computer.d9d9ae1c.js +0 -463
  180. package/static/v3/js/views/my-computer.js +0 -463
  181. package/static/v3/js/views/pipeline.c522f1ce.js +0 -157
  182. package/static/v3/js/views/pipeline.js +0 -157
  183. package/static/v3/js/views/planning.9ac3e313.js +0 -153
  184. package/static/v3/js/views/planning.js +0 -153
  185. package/static/v3/js/views/settings.8631fa5e.js +0 -318
  186. package/static/v3/js/views/settings.js +0 -318
  187. package/static/v3/js/views/skills.c6c2f965.js +0 -109
  188. package/static/v3/js/views/skills.js +0 -109
  189. package/static/v3/js/views/tools.e4f11276.js +0 -108
  190. package/static/v3/js/views/tools.js +0 -108
  191. package/static/v3/js/views/workflows.26c57290.js +0 -128
  192. package/static/v3/js/views/workflows.js +0 -128
  193. package/static/workflows.html +0 -146
  194. package/static/workspace.css +0 -1121
  195. package/static/workspace.html +0 -357
package/static/admin.html DELETED
@@ -1,486 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="ko">
3
-
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content">
7
- <title>Lattice AI Admin</title>
8
- <script src="/static/scripts/ux.js"></script>
9
- <link rel="manifest" href="/manifest.json">
10
- <meta name="theme-color" content="#f3ecff">
11
- <meta name="apple-mobile-web-app-capable" content="yes">
12
- <link rel="apple-touch-icon" href="/icons/apple-touch-icon.png">
13
- <link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32.png">
14
- <link rel="stylesheet" href="/static/vendor/fonts/inter.css">
15
- <link rel="stylesheet" href="/static/vendor/icons/tabler-icons.min.css">
16
- <link rel="stylesheet" href="/static/css/tokens.css">
17
- <link rel="stylesheet" href="/static/css/reference/base.css">
18
- <link rel="stylesheet" href="/static/css/reference/account.css">
19
- <link rel="stylesheet" href="/static/css/reference/admin.css">
20
- <link rel="stylesheet" href="/static/css/reference/graph.css">
21
- <link rel="stylesheet" href="/static/css/reference/chat.css">
22
- <link rel="stylesheet" href="/static/css/responsive.css">
23
- <script src="/static/vendor/chart.umd.min.js"></script>
24
- </head>
25
-
26
- <body class="lattice-ref-admin">
27
- <div class="sidebar-overlay" onclick="closeAdminRail&&closeAdminRail()"></div>
28
- <button class="admin-rail-toggle graph-nav-toggle" onclick="toggleAdminRail&&toggleAdminRail()" title="메뉴" aria-label="관리자 메뉴 열기"><i class="ti ti-menu-2"></i></button>
29
- <aside class="reference-rail admin-rail">
30
- <div class="rail-brand"><i class="ti ti-shield-lock"></i><strong>LATTICE AI</strong><span>Administrator</span></div>
31
- <nav>
32
- <a class="active" href="#dashboard" data-admin-nav="dashboard"><i class="ti ti-layout-dashboard"></i> <span data-i18n="nav_dashboard">대시보드</span></a>
33
- <a href="#users" data-admin-nav="users"><i class="ti ti-users"></i> <span data-i18n="nav_users">사용자 관리</span></a>
34
- <a href="#permissions" data-admin-nav="permissions"><i class="ti ti-key"></i> <span data-i18n="nav_permissions">권한 관리</span></a>
35
- <a href="#sso" data-admin-nav="sso"><i class="ti ti-lock-access"></i> <span data-i18n="nav_sso">SSO 관리</span></a>
36
- <a href="#enterprise" data-admin-nav="enterprise"><i class="ti ti-building-skyscraper"></i> <span data-i18n="nav_enterprise">Enterprise</span></a>
37
- <a href="#security" data-admin-nav="security"><i class="ti ti-shield-check"></i> <span data-i18n="nav_security">보안 모니터링</span></a>
38
- <a href="#audit" data-admin-nav="audit"><i class="ti ti-report-search"></i> <span data-i18n="nav_audit">감사 로그</span></a>
39
- <a href="/workspace"><i class="ti ti-layout-dashboard"></i> <span>Workspace OS</span></a>
40
- <a href="/chat"><i class="ti ti-message-circle"></i> <span data-i18n="nav_chat">채팅으로</span></a>
41
- </nav>
42
- <div class="rail-project">
43
- <strong>admin@lattice.ai</strong>
44
- <span data-i18n="system_admin">시스템 관리자</span>
45
- </div>
46
- </aside>
47
- <div class="page">
48
- <header class="topbar">
49
- <div class="brand">
50
- <div class="brand-mark"><i class="ti ti-shield-lock"></i></div>
51
- <div>
52
- <h1>Lattice AI Admin</h1>
53
- <p data-i18n="admin_sub">관리자 대시보드</p>
54
- </div>
55
- </div>
56
- <div class="top-actions">
57
- <a class="btn" href="/"><i class="ti ti-arrow-left"></i> <span data-i18n="btn_back">채팅으로</span></a>
58
- <div class="lang-picker" id="admin-lang-picker">
59
- <button class="btn" id="admin-lang-btn" onclick="toggleLangMenu('admin-lang-picker')" title="Language">🌐</button>
60
- <div class="lang-picker-menu" id="admin-lang-picker-menu">
61
- <div class="lang-option" id="admin-lang-ko" onclick="setLang('ko')">🇰🇷 한국어</div>
62
- <div class="lang-option" id="admin-lang-en" onclick="setLang('en')">🇺🇸 English</div>
63
- </div>
64
- </div>
65
- <button class="btn primary" id="refresh-btn" type="button"><i class="ti ti-refresh"></i> <span data-i18n="btn_refresh">새로고침</span></button>
66
- <button class="btn danger" id="logout-btn" type="button"><i class="ti ti-logout"></i> <span data-i18n="btn_logout">로그아웃</span></button>
67
- </div>
68
- </header>
69
-
70
- <main class="content">
71
- <div id="access-notice" class="notice" style="display:none"></div>
72
-
73
- <section class="admin-view active" id="admin-view-dashboard" data-admin-view="dashboard">
74
- <section class="hero">
75
- <div>
76
- <h2 data-i18n="hero_title">관리자 대시보드</h2>
77
- <p data-i18n="hero_desc">운영 현황, 세션, 모델, VPC 상태를 요약해서 보여줍니다.</p>
78
- </div>
79
- <div class="session-card">
80
- <div class="label" data-i18n="current_session">현재 세션</div>
81
- <div class="value" id="session-value" data-i18n="checking_session">세션 확인 중...</div>
82
- </div>
83
- </section>
84
-
85
- <section class="summary-grid" id="summary-grid">
86
- <div class="summary-card">
87
- <div class="label" data-i18n="card_total_users">전체 사용자</div>
88
- <div class="value" id="total-users">-</div>
89
- <div class="meta" id="total-users-meta">사용자 계정 수</div>
90
- </div>
91
- <div class="summary-card">
92
- <div class="label" data-i18n="card_messages">활성 메시지</div>
93
- <div class="value" id="total-messages">-</div>
94
- <div class="meta" id="total-messages-meta">최근 대화 활동</div>
95
- </div>
96
- <div class="summary-card">
97
- <div class="label" data-i18n="card_model">현재 모델</div>
98
- <div class="value" id="current-model">-</div>
99
- <div class="meta" id="current-model-meta">로드된 모델 수: -</div>
100
- </div>
101
- <div class="summary-card">
102
- <div class="label" data-i18n="card_vpc">VPC 상태</div>
103
- <div class="value" id="vpc-status">-</div>
104
- <div class="meta" id="vpc-status-meta">Private network state</div>
105
- </div>
106
- </section>
107
-
108
- <section class="panel">
109
- <div class="panel-header">
110
- <div>
111
- <h3 data-i18n="chart_title">메시지 활동 (최근 14일)</h3>
112
- <p data-i18n="chart_desc">사용자 메시지와 AI 응답 수를 날짜별로 표시합니다.</p>
113
- </div>
114
- </div>
115
- <div class="panel-body chart-body">
116
- <canvas id="activity-chart" height="80"></canvas>
117
- </div>
118
- </section>
119
-
120
- <section class="panel-grid">
121
- <article class="panel">
122
- <div class="panel-header">
123
- <div>
124
- <h3>Private VPC</h3>
125
- <p data-i18n="vpc_desc">네트워크 프로필과 운영 상태를 수정합니다.</p>
126
- </div>
127
- <span class="tag" id="admin-pill"><i class="ti ti-user-cog"></i> Admin</span>
128
- </div>
129
- <div class="panel-body">
130
- <div class="form-grid">
131
- <div class="field"><label for="vpc-provider">Provider</label><input id="vpc-provider" placeholder="AWS"></div>
132
- <div class="field"><label for="vpc-region">Region</label><input id="vpc-region" placeholder="ap-northeast-2"></div>
133
- <div class="field"><label for="vpc-cidr">CIDR Block</label><input id="vpc-cidr" placeholder="10.42.0.0/16"></div>
134
- <div class="field"><label for="vpc-endpoint">Private Endpoint</label><input id="vpc-endpoint" placeholder="ltcai-private.local"></div>
135
- <div class="field"><label for="vpc-vpn">VPN Status</label><input id="vpc-vpn" placeholder="standby"></div>
136
- <div class="field"><label for="vpc-peering">Peering Status</label><input id="vpc-peering" placeholder="not_configured"></div>
137
- <div class="field full"><label for="vpc-subnets">Private Subnets</label><input id="vpc-subnets" placeholder="10.42.10.0/24, 10.42.20.0/24"></div>
138
- <div class="field full"><label for="vpc-notes">Notes</label><textarea id="vpc-notes" data-i18n-ph="vpc_notes_ph" placeholder="운영 메모"></textarea></div>
139
- </div>
140
- <div class="toolbar">
141
- <div class="status-copy" id="vpc-save-status" data-i18n="vpc_loading">불러오는 중...</div>
142
- <button class="btn primary" id="save-vpc-btn" type="button"><i class="ti ti-device-floppy"></i> <span data-i18n="vpc_save">저장</span></button>
143
- </div>
144
- </div>
145
- </article>
146
-
147
- <article class="panel">
148
- <div class="panel-header">
149
- <div>
150
- <h3 data-i18n="current_session">현재 세션</h3>
151
- <p data-i18n="session_desc">현재 로그인한 계정과 관리자 API 상태를 확인합니다.</p>
152
- </div>
153
- </div>
154
- <div class="panel-body">
155
- <div class="tag-row" id="session-tags"></div>
156
- <div class="footer-space"></div>
157
- <div class="notice" id="session-help" data-i18n="session_help_fail">로그인 정보가 없으면 이 화면의 관리자 API를 사용할 수 없습니다.</div>
158
- </div>
159
- </article>
160
- </section>
161
- </section>
162
-
163
- <section class="admin-view" id="admin-view-users" data-admin-view="users">
164
- <section class="panel">
165
- <div class="panel-header">
166
- <div>
167
- <h3 data-i18n="users_title">사용자 관리</h3>
168
- <p data-i18n="users_desc">등록된 사용자와 활성/비활성 상태를 관리합니다.</p>
169
- </div>
170
- </div>
171
- <div class="panel-body">
172
- <div class="table-wrap" id="user-table-wrap">
173
- <div class="preview" style="padding:14px" data-i18n="loading">불러오는 중...</div>
174
- </div>
175
- </div>
176
- </section>
177
-
178
- <section class="panel">
179
- <div class="panel-header">
180
- <div>
181
- <h3 data-i18n="invite_title">초대 링크</h3>
182
- <p data-i18n="invite_desc">새 사용자를 초대할 링크를 확인하고 복사합니다.</p>
183
- </div>
184
- </div>
185
- <div class="panel-body">
186
- <div class="inline-control">
187
- <input id="invite-link-input" type="text" readonly>
188
- <button class="btn primary" id="copy-invite-btn" type="button"><i class="ti ti-copy"></i> <span data-i18n="btn_copy">복사</span></button>
189
- </div>
190
- <div id="invite-gate-info" class="status-copy"></div>
191
- </div>
192
- </section>
193
- </section>
194
-
195
- <section class="admin-view" id="admin-view-permissions" data-admin-view="permissions">
196
- <section class="panel">
197
- <div class="panel-header">
198
- <div>
199
- <h3 data-i18n="permissions_title">권한 관리</h3>
200
- <p data-i18n="permissions_desc">사용자별 기본 모드, 고급 모드, 관리자 모드 권한을 확인합니다.</p>
201
- </div>
202
- </div>
203
- <div class="panel-body">
204
- <div class="table-wrap" id="permission-table-wrap">
205
- <div class="preview" style="padding:14px" data-i18n="loading">불러오는 중...</div>
206
- </div>
207
- </div>
208
- </section>
209
- </section>
210
-
211
- <section class="admin-view" id="admin-view-sso" data-admin-view="sso">
212
- <section class="panel">
213
- <div class="panel-header">
214
- <div>
215
- <h3 data-i18n="sso_title">SSO 관리</h3>
216
- <p data-i18n="sso_desc">Okta 또는 Microsoft Entra ID OIDC 설정을 저장하고 로그인 플로우에 연결합니다.</p>
217
- </div>
218
- <div class="tag-row" id="sso-status-tags"></div>
219
- </div>
220
- <div class="panel-body">
221
- <div class="form-grid">
222
- <div class="field">
223
- <label for="sso-provider-template" data-i18n="sso_provider_template">제공자 템플릿</label>
224
- <select id="sso-provider-template">
225
- <option value="okta">Okta</option>
226
- <option value="entra">Microsoft Entra ID</option>
227
- <option value="custom">Custom OIDC</option>
228
- </select>
229
- </div>
230
- <div class="field">
231
- <label for="sso-provider-name" data-i18n="sso_provider_name">제공자 이름</label>
232
- <input id="sso-provider-name" placeholder="Okta">
233
- </div>
234
- <div class="field full">
235
- <label for="sso-discovery-url" data-i18n="sso_discovery_url">OIDC Discovery URL</label>
236
- <input id="sso-discovery-url" placeholder="https://your-domain.okta.com/oauth2/default/.well-known/openid-configuration">
237
- </div>
238
- <div class="field">
239
- <label for="sso-client-id" data-i18n="sso_client_id">Client ID</label>
240
- <input id="sso-client-id" autocomplete="off">
241
- </div>
242
- <div class="field">
243
- <label for="sso-client-secret" data-i18n="sso_client_secret">Client Secret</label>
244
- <input id="sso-client-secret" type="password" autocomplete="new-password" data-i18n-ph="sso_secret_ph" placeholder="비워두면 기존 값을 유지합니다">
245
- </div>
246
- <div class="field full">
247
- <label for="sso-redirect-uri" data-i18n="sso_redirect_uri">Redirect URI</label>
248
- <input id="sso-redirect-uri" placeholder="http://localhost:4825/auth/sso/callback">
249
- </div>
250
- <div class="field full">
251
- <label for="sso-scopes" data-i18n="sso_scopes">Scopes</label>
252
- <input id="sso-scopes" placeholder="openid email profile">
253
- </div>
254
- </div>
255
- <div class="sso-template-help" id="sso-template-help"></div>
256
- <div class="toolbar">
257
- <div class="status-copy" id="sso-save-status" data-i18n="sso_loading">SSO 설정을 불러오는 중...</div>
258
- <div class="button-row">
259
- <button class="btn" id="test-sso-btn" type="button"><i class="ti ti-external-link"></i> <span data-i18n="sso_test">SSO 로그인 테스트</span></button>
260
- <button class="btn primary" id="save-sso-btn" type="button"><i class="ti ti-device-floppy"></i> <span data-i18n="sso_save">SSO 설정 저장</span></button>
261
- </div>
262
- </div>
263
- </div>
264
- </section>
265
- </section>
266
-
267
- <section class="admin-view" id="admin-view-enterprise" data-admin-view="enterprise">
268
- <section class="panel">
269
- <div class="panel-header">
270
- <div>
271
- <h3 data-i18n="enterprise_title">Enterprise Admin</h3>
272
- <p data-i18n="enterprise_desc">Admin policies, audit export, SIEM export, organization settings, and capability status.</p>
273
- </div>
274
- <div class="tag-row" id="enterprise-status-tags"></div>
275
- </div>
276
- <div class="panel-body">
277
- <div class="enterprise-grid" id="enterprise-capability-status"></div>
278
- </div>
279
- </section>
280
-
281
- <section class="panel-grid">
282
- <article class="panel">
283
- <div class="panel-header">
284
- <div>
285
- <h3 data-i18n="enterprise_policies">Admin Policies</h3>
286
- <p data-i18n="enterprise_policies_desc">Effective Community policy and Enterprise policy-pack status.</p>
287
- </div>
288
- </div>
289
- <div class="panel-body" id="enterprise-admin-policies"></div>
290
- </article>
291
- <article class="panel">
292
- <div class="panel-header">
293
- <div>
294
- <h3 data-i18n="enterprise_org">Organization Settings</h3>
295
- <p data-i18n="enterprise_org_desc">Workspace governance and organization capability status.</p>
296
- </div>
297
- </div>
298
- <div class="panel-body" id="enterprise-org-settings"></div>
299
- </article>
300
- </section>
301
-
302
- <section class="panel-grid">
303
- <article class="panel">
304
- <div class="panel-header">
305
- <div>
306
- <h3 data-i18n="enterprise_audit_export">Audit Export</h3>
307
- <p data-i18n="enterprise_audit_export_desc">Local export remains available in Community; retention is an Enterprise extension point.</p>
308
- </div>
309
- </div>
310
- <div class="panel-body" id="enterprise-audit-export"></div>
311
- </article>
312
- <article class="panel">
313
- <div class="panel-header">
314
- <div>
315
- <h3 data-i18n="enterprise_siem">SIEM Export</h3>
316
- <p data-i18n="enterprise_siem_desc">Preview the SIEM envelope without streaming external events in Community.</p>
317
- </div>
318
- <button class="btn" id="refresh-siem-btn" type="button"><i class="ti ti-refresh"></i> <span>SIEM</span></button>
319
- </div>
320
- <div class="panel-body">
321
- <div id="enterprise-siem-export"></div>
322
- <pre class="enterprise-json" id="enterprise-siem-preview"></pre>
323
- </div>
324
- </article>
325
- </section>
326
- </section>
327
-
328
- <section class="admin-view" id="admin-view-security" data-admin-view="security">
329
- <!-- Security & Audit Command Center (피드백 #5) -->
330
- <section class="panel" id="security-overview-panel">
331
- <div class="panel-header">
332
- <div>
333
- <h3>AI 보안 감사 콘솔</h3>
334
- <p>사용자별 위험/준수 채팅 및 파일, 민감정보 유형 분포, 원문 조회 현황을 한눈에 확인합니다.</p>
335
- </div>
336
- <div class="panel-tools">
337
- <div class="export-control">
338
- <button class="btn" id="security-cc-export-toggle" type="button">
339
- <i class="ti ti-download"></i>
340
- <span>보안 리포트 추출</span>
341
- </button>
342
- <div class="export-options" id="security-cc-export-options">
343
- <button class="table-btn" type="button" data-cc-scope="overview" data-cc-format="json">Overview JSON</button>
344
- <button class="table-btn" type="button" data-cc-scope="users" data-cc-format="csv">User Risk CSV</button>
345
- <button class="table-btn" type="button" data-cc-scope="users" data-cc-format="xlsx">User Risk Excel</button>
346
- <button class="table-btn" type="button" data-cc-scope="events" data-cc-format="csv">Events CSV</button>
347
- <button class="table-btn" type="button" data-cc-scope="events" data-cc-format="json">Events JSON</button>
348
- <button class="table-btn" type="button" data-cc-scope="overview" data-cc-format="pdf">Security PDF</button>
349
- </div>
350
- </div>
351
- </div>
352
- </div>
353
- <div class="panel-body">
354
- <div class="audit-grid" id="security-cc-cards"></div>
355
- <div class="two-col" style="margin-top:18px">
356
- <div class="subpanel">
357
- <h4><i class="ti ti-users"></i> 사용자별 위험/준수 (채팅 + 파일)</h4>
358
- <div class="table-wrap" id="security-cc-users">
359
- <div class="preview" style="padding:14px">불러오는 중...</div>
360
- </div>
361
- <canvas id="security-cc-user-chart" height="180" style="margin-top:12px"></canvas>
362
- </div>
363
- <div class="subpanel">
364
- <h4><i class="ti ti-chart-donut"></i> 민감정보 유형 분포</h4>
365
- <canvas id="security-cc-field-chart" height="200"></canvas>
366
- <div id="security-cc-field-legend" style="margin-top:10px;font-size:12px;color:var(--muted-text)"></div>
367
- </div>
368
- </div>
369
- <div class="two-col" style="margin-top:18px">
370
- <div class="subpanel">
371
- <h4><i class="ti ti-message-2"></i> 민감 채팅 모니터</h4>
372
- <div class="table-wrap" id="security-cc-chats">
373
- <div class="preview" style="padding:14px">불러오는 중...</div>
374
- </div>
375
- </div>
376
- <div class="subpanel">
377
- <h4><i class="ti ti-file-shield"></i> 위험 파일 모니터</h4>
378
- <div class="table-wrap" id="security-cc-files">
379
- <div class="preview" style="padding:14px">불러오는 중...</div>
380
- </div>
381
- </div>
382
- </div>
383
- <div class="two-col" style="margin-top:18px">
384
- <div class="subpanel">
385
- <h4><i class="ti ti-timeline"></i> 감사 타임라인</h4>
386
- <div class="table-wrap" id="security-cc-timeline">
387
- <div class="preview" style="padding:14px">불러오는 중...</div>
388
- </div>
389
- </div>
390
- <div class="subpanel">
391
- <h4><i class="ti ti-code"></i> Raw Data Explorer</h4>
392
- <div style="display:flex;gap:8px;margin-bottom:8px;flex-wrap:wrap">
393
- <button class="table-btn" type="button" data-cc-raw="audit">감사 로그</button>
394
- <button class="table-btn" type="button" data-cc-raw="history">대화 원문</button>
395
- <button class="table-btn" type="button" data-cc-raw="files">파일 인덱스</button>
396
- </div>
397
- <pre id="security-cc-raw" style="max-height:280px;overflow:auto;background:rgba(0,0,0,.04);padding:12px;border-radius:8px;font-size:12px;white-space:pre-wrap;">선택한 scope의 raw JSON이 여기에 표시됩니다. (관리자 원문 조회는 별도로 감사로그에 기록됩니다.)</pre>
398
- </div>
399
- </div>
400
- </div>
401
- </section>
402
-
403
- <section class="panel">
404
- <div class="panel-header">
405
- <div>
406
- <h3 data-i18n="sensitivity_title">보안 모니터링</h3>
407
- <p data-i18n="sensitivity_desc">민감정보, 위험 필드, 준수 필드를 집중적으로 확인합니다.</p>
408
- </div>
409
- <div class="panel-tools">
410
- <div class="export-control">
411
- <button class="btn" id="security-export-toggle" type="button">
412
- <i class="ti ti-download"></i>
413
- <span data-i18n="security_export_toggle">보안 모니터링 로그 추출</span>
414
- </button>
415
- <div class="export-options" id="security-export-options">
416
- <button class="table-btn" type="button" data-export-scope="security" data-export-format="txt" data-i18n="export_txt">TXT 추출</button>
417
- <button class="table-btn" type="button" data-export-scope="security" data-export-format="excel" data-i18n="export_excel">Excel 추출</button>
418
- <button class="table-btn" type="button" data-export-scope="security" data-export-format="csv" data-i18n="export_csv">CSV 추출</button>
419
- </div>
420
- </div>
421
- <div class="tag-row" id="sensitivity-summary"></div>
422
- </div>
423
- </div>
424
- <div class="panel-body">
425
- <div class="two-col">
426
- <div class="subpanel">
427
- <h4><i class="ti ti-alert-triangle"></i> <span data-i18n="risk_fields">위험 필드</span></h4>
428
- <div class="list" id="risk-fields"></div>
429
- </div>
430
- <div class="subpanel">
431
- <h4><i class="ti ti-shield-check"></i> <span data-i18n="compliance_fields">준수 필드</span></h4>
432
- <div class="list" id="compliance-fields"></div>
433
- </div>
434
- </div>
435
- </div>
436
- </section>
437
- </section>
438
-
439
- <section class="admin-view" id="admin-view-audit" data-admin-view="audit">
440
- <section class="panel">
441
- <div class="panel-header">
442
- <div>
443
- <h3 data-i18n="audit_title">감사 로그</h3>
444
- <p data-i18n="audit_desc">AI 사용량, 업로드, 민감정보 감지, 삭제/정리 이벤트를 보존합니다.</p>
445
- </div>
446
- <div class="panel-tools">
447
- <div class="export-control">
448
- <button class="btn" id="audit-export-toggle" type="button">
449
- <i class="ti ti-download"></i>
450
- <span data-i18n="audit_export_toggle">감사 로그 추출</span>
451
- </button>
452
- <div class="export-options" id="audit-export-options">
453
- <button class="table-btn" type="button" data-export-scope="audit" data-export-format="txt" data-i18n="export_txt">TXT 추출</button>
454
- <button class="table-btn" type="button" data-export-scope="audit" data-export-format="excel" data-i18n="export_excel">Excel 추출</button>
455
- <button class="table-btn" type="button" data-export-scope="audit" data-export-format="csv" data-i18n="export_csv">CSV 추출</button>
456
- </div>
457
- </div>
458
- <div class="tag-row" id="audit-summary-tags"></div>
459
- </div>
460
- </div>
461
- <div class="panel-body">
462
- <div class="audit-grid" id="audit-metrics"></div>
463
- <div class="two-col">
464
- <div class="subpanel">
465
- <h4><i class="ti ti-users"></i> <span data-i18n="audit_user_risk">사용자 사용량 및 위험도</span></h4>
466
- <div class="table-wrap" id="audit-user-table">
467
- <div class="preview" style="padding:14px" data-i18n="loading">불러오는 중...</div>
468
- </div>
469
- </div>
470
- <div class="subpanel">
471
- <h4><i class="ti ti-history"></i> <span data-i18n="audit_trail">감사 이벤트</span></h4>
472
- <div class="table-wrap" id="audit-event-table">
473
- <div class="preview" style="padding:14px" data-i18n="loading">불러오는 중...</div>
474
- </div>
475
- </div>
476
- </div>
477
- </div>
478
- </section>
479
- </section>
480
- </main>
481
- </div>
482
-
483
- <script src="/static/scripts/admin.js"></script>
484
- </body>
485
-
486
- </html>
@@ -1,139 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content" />
6
- <title>Multi-Agent Runtime — Lattice AI</title>
7
- <script src="/static/scripts/ux.js"></script>
8
- <link rel="stylesheet" href="/static/css/tokens.css" />
9
- <link rel="stylesheet" href="/static/platform.css" />
10
- <link rel="stylesheet" href="/static/css/responsive.css" />
11
- </head>
12
- <body>
13
- <main>
14
- <h1>Multi-Agent Runtime</h1>
15
- <p class="sub">Planner · Executor · Reviewer · Researcher · Release — with handoff, context packets, memory, retry, and replay.</p>
16
-
17
- <div class="section">
18
- <label>Goal</label>
19
- <textarea id="goal" placeholder="e.g. Draft a release checklist for v2.0.0">Summarize and verify the latest workspace activity</textarea>
20
- <label>Roles (pipeline)</label>
21
- <div id="roleChips" class="row"></div>
22
- <div class="row" style="margin-top:14px"><button id="runBtn">Run agents</button></div>
23
- </div>
24
-
25
- <div class="section">
26
- <h3>Run result</h3>
27
- <div id="result"><div class="empty">No run yet.</div></div>
28
- </div>
29
-
30
- <div class="section">
31
- <h3>Recent agent runs</h3>
32
- <div id="runs"><div class="empty">Loading…</div></div>
33
- </div>
34
-
35
- <div class="section">
36
- <h3>Replay viewer</h3>
37
- <div id="replay"><div class="empty">Select a recent run.</div></div>
38
- </div>
39
- </main>
40
-
41
- <script type="module">
42
- import { mountHeader, api, escapeHtml, badge, toast } from "/static/scripts/platform.js";
43
- mountHeader("/agents");
44
-
45
- const selected = new Set(["planner", "executor", "reviewer"]);
46
- async function loadRoles() {
47
- const data = await api("/agents/api/roles");
48
- document.getElementById("roleChips").innerHTML = data.roles.map((r) =>
49
- `<label class="badge" style="cursor:pointer"><input type="checkbox" value="${r.role}" ${selected.has(r.role)?"checked":""} style="width:auto;margin-right:6px">${escapeHtml(r.role)}</label>`
50
- ).join(" ");
51
- document.getElementById("roleChips").addEventListener("change", (e) => {
52
- if (e.target.checked) selected.add(e.target.value); else selected.delete(e.target.value);
53
- });
54
- }
55
-
56
- function renderTimeline(timeline) {
57
- return (timeline || []).map((t) => {
58
- const label = (t.event || "").startsWith("handoff_") ? `↪ ${escapeHtml(t.event)} ${escapeHtml(t.from||"")} → ${escapeHtml(t.to||"")}`
59
- : t.event === "handoff" ? `↪ handoff ${escapeHtml(t.from)} → ${escapeHtml(t.to)}`
60
- : t.event === "role" ? `● ${escapeHtml(t.role)} ${badge(t.status)}`
61
- : t.event === "retry_requested" ? `↻ retry ${escapeHtml(t.reason||"")}`
62
- : t.event === "review_approved" ? `✓ review approved`
63
- : `· ${escapeHtml(t.event)}`;
64
- return `<div class="timeline-item">${label}<div class="t-meta">${escapeHtml(t.note||t.timestamp||"")}</div></div>`;
65
- }).join("");
66
- }
67
-
68
- function renderHandoffs(handoffs) {
69
- if (!handoffs?.length) return `<div class="empty">No handoffs recorded.</div>`;
70
- return handoffs.map((h) => `<div class="timeline-item">
71
- <strong>${escapeHtml(h.handoff_id)}</strong> ${badge(h.status)}
72
- <div class="t-meta">${escapeHtml(h.source_agent)} → ${escapeHtml(h.target_agent)} · ${escapeHtml(h.reason||"")}</div>
73
- </div>`).join("");
74
- }
75
-
76
- function renderReview(result) {
77
- const reviews = result.review_history || [];
78
- const retries = result.retry_history || [];
79
- return `<div class="grid two">
80
- <div>${reviews.length ? reviews.map((r) => `<div class="timeline-item">${badge(r.outcome)} ${escapeHtml(r.reason||"")}<div class="t-meta">retry ${r.retry_count}</div></div>`).join("") : `<div class="empty">No review history.</div>`}</div>
81
- <div>${retries.length ? retries.map((r) => `<div class="timeline-item">${badge("retry " + r.retry)} ${escapeHtml(r.reason||"")}<div class="t-meta">limit ${r.limit}</div></div>`).join("") : `<div class="empty">No retries.</div>`}</div>
82
- </div>`;
83
- }
84
-
85
- document.getElementById("runBtn").addEventListener("click", async () => {
86
- const btn = document.getElementById("runBtn");
87
- btn.disabled = true;
88
- try {
89
- const res = await api("/agents/api/run", { method: "POST", body: JSON.stringify({
90
- goal: document.getElementById("goal").value, roles: [...selected], inputs: {}
91
- }) });
92
- const r = res.result;
93
- document.getElementById("result").innerHTML = `
94
- <div class="card">
95
- <div class="row"><h3>${escapeHtml(r.output)}</h3><div class="spacer"></div>${badge(r.status)}</div>
96
- <div class="meta">retries: ${r.retries} · roles: ${(r.roles_run||[]).join(" → ")}</div>
97
- <div class="section"><h3>Handoff chain</h3>${renderHandoffs(r.handoffs)}</div>
98
- <div class="section"><h3>Review panel</h3>${renderReview(r)}</div>
99
- <div class="section"><h3>Timeline</h3>${renderTimeline(r.timeline)}</div>
100
- </div>`;
101
- toast(`Agent run: ${r.status}`);
102
- await loadRuns();
103
- } catch (err) { toast(err.message); } finally { btn.disabled = false; }
104
- });
105
-
106
- async function loadRuns() {
107
- const data = await api("/agents/api/runs");
108
- const runs = data.runs || [];
109
- const box = document.getElementById("runs");
110
- if (!runs.length) { box.innerHTML = `<div class="empty">No agent runs yet.</div>`; return; }
111
- box.innerHTML = runs.slice(0, 20).map((r) => `
112
- <div class="card" style="margin-bottom:10px">
113
- <div class="row"><h3>${escapeHtml((r.input||"").slice(0,80))}</h3><div class="spacer"></div>${badge(r.status)}</div>
114
- <div class="meta">${escapeHtml(r.agent_id)} · ${escapeHtml(r.created_at)} · ${(r.timeline||[]).length} timeline events</div>
115
- <div class="row" style="margin-top:10px"><button class="ghost" data-replay="${r.id}">Replay</button></div>
116
- </div>`).join("");
117
- }
118
-
119
- document.getElementById("runs").addEventListener("click", async (e) => {
120
- const btn = e.target.closest("button[data-replay]");
121
- if (!btn) return;
122
- const out = document.getElementById("replay");
123
- out.innerHTML = `<div class="empty">Loading replay…</div>`;
124
- try {
125
- const data = await api(`/agents/api/runs/${btn.dataset.replay}/replay`);
126
- const frames = data.replay.frames || [];
127
- out.innerHTML = frames.map((f) => `<div class="timeline-item">
128
- <div class="row"><strong>${escapeHtml(f.event)}</strong><div class="spacer"></div>${badge(f.decision || "event")}</div>
129
- <div class="t-meta">${escapeHtml(String(f.actor||""))} · ${escapeHtml(f.when||"")}</div>
130
- <pre>${escapeHtml(JSON.stringify({ why: f.why, input: f.input, output: f.output }, null, 2))}</pre>
131
- </div>`).join("") || `<div class="empty">No replay frames.</div>`;
132
- } catch (err) { out.innerHTML = `<div class="empty">${escapeHtml(err.message)}</div>`; }
133
- });
134
-
135
- loadRoles().catch((e) => toast(e.message));
136
- loadRuns().catch(() => {});
137
- </script>
138
- </body>
139
- </html>