loadtoagent 1.0.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 (82) hide show
  1. package/README.ko.md +175 -0
  2. package/README.md +175 -0
  3. package/README.zh-CN.md +175 -0
  4. package/bin/loadtoagent.js +171 -0
  5. package/docs/ARCHITECTURE.md +30 -0
  6. package/docs/PROVIDER-CONTRACTS.md +58 -0
  7. package/docs/assets/loadtoagent-dashboard.png +0 -0
  8. package/docs/assets/loadtoagent-demo.gif +0 -0
  9. package/main.js +493 -0
  10. package/package.json +133 -0
  11. package/preload.js +75 -0
  12. package/renderer/app-agent-actions.js +285 -0
  13. package/renderer/app-bootstrap.js +95 -0
  14. package/renderer/app-dashboard.js +361 -0
  15. package/renderer/app-drawer-content.js +203 -0
  16. package/renderer/app-drawer-data.js +41 -0
  17. package/renderer/app-drawer.js +183 -0
  18. package/renderer/app-events-dialogs.js +169 -0
  19. package/renderer/app-events-filters.js +74 -0
  20. package/renderer/app-events-navigation.js +96 -0
  21. package/renderer/app-events-sessions.js +195 -0
  22. package/renderer/app-events.js +16 -0
  23. package/renderer/app-graph-layout.js +71 -0
  24. package/renderer/app-graph-model.js +107 -0
  25. package/renderer/app-graph-orchestration.js +76 -0
  26. package/renderer/app-graph-view.js +544 -0
  27. package/renderer/app-run-modal.js +221 -0
  28. package/renderer/app-session-render.js +243 -0
  29. package/renderer/app-tmux-render.js +247 -0
  30. package/renderer/app.js +608 -0
  31. package/renderer/fonts/geist-ext.woff2 +0 -0
  32. package/renderer/fonts/geist.woff2 +0 -0
  33. package/renderer/i18n-messages.js +355 -0
  34. package/renderer/i18n.js +122 -0
  35. package/renderer/index.html +386 -0
  36. package/renderer/shared.js +26 -0
  37. package/renderer/styles-agent-map.css +401 -0
  38. package/renderer/styles-cards.css +600 -0
  39. package/renderer/styles-collaboration.css +534 -0
  40. package/renderer/styles-components.css +1293 -0
  41. package/renderer/styles-onboarding.css +344 -0
  42. package/renderer/styles-overlays.css +284 -0
  43. package/renderer/styles-product.css +686 -0
  44. package/renderer/styles-responsive-product.css +689 -0
  45. package/renderer/styles-responsive-runtime.css +718 -0
  46. package/renderer/styles-responsive-shell.css +533 -0
  47. package/renderer/styles-responsive-workflows.css +778 -0
  48. package/renderer/styles-run-composer.css +695 -0
  49. package/renderer/styles-settings.css +606 -0
  50. package/renderer/styles-terminal.css +1241 -0
  51. package/renderer/styles-tmux.css +1162 -0
  52. package/renderer/styles-workflow-map.css +513 -0
  53. package/renderer/styles-workflows.css +1217 -0
  54. package/renderer/styles.css +486 -0
  55. package/renderer/terminal-agent.js +152 -0
  56. package/renderer/terminal-events.js +226 -0
  57. package/renderer/terminal-workbench.js +464 -0
  58. package/renderer/terminal.js +497 -0
  59. package/src/agentMonitor/claudeParser.js +197 -0
  60. package/src/agentMonitor/codexCollaboration.js +95 -0
  61. package/src/agentMonitor/codexParser.js +447 -0
  62. package/src/agentMonitor/genericParser.js +244 -0
  63. package/src/agentMonitor/hierarchy.js +248 -0
  64. package/src/agentMonitor/sessionFiles.js +86 -0
  65. package/src/agentMonitor.js +591 -0
  66. package/src/agentRunner.js +465 -0
  67. package/src/bridgeServer.js +246 -0
  68. package/src/contracts.js +71 -0
  69. package/src/diagnostics.js +23 -0
  70. package/src/ipc/registerAgentIpc.js +12 -0
  71. package/src/ipc/registerAppIpc.js +20 -0
  72. package/src/ipc/registerTerminalIpc.js +50 -0
  73. package/src/ipc/registerTmuxIpc.js +30 -0
  74. package/src/ipc/registerWorkspaceIpc.js +14 -0
  75. package/src/monitorWorker.js +273 -0
  76. package/src/processMonitor.js +394 -0
  77. package/src/providerRegistry.js +120 -0
  78. package/src/terminalManager.js +438 -0
  79. package/src/tmuxController.js +183 -0
  80. package/src/tmuxMonitor.js +432 -0
  81. package/src/updateManager.js +339 -0
  82. package/src/workspaceStore.js +77 -0
@@ -0,0 +1,386 @@
1
+ <!doctype html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'" />
7
+ <title data-i18n="app.document.title">LoadToAgent · AI 작업 도우미</title>
8
+ <link rel="stylesheet" href="styles.css" />
9
+ <link rel="stylesheet" href="styles-components.css" />
10
+ <link rel="stylesheet" href="styles-cards.css" />
11
+ <link rel="stylesheet" href="styles-overlays.css" />
12
+ <link rel="stylesheet" href="styles-agent-map.css" />
13
+ <link rel="stylesheet" href="styles-workflows.css" />
14
+ <link rel="stylesheet" href="styles-workflow-map.css" />
15
+ <link rel="stylesheet" href="styles-collaboration.css" />
16
+ <link rel="stylesheet" href="styles-tmux.css" />
17
+ <link rel="stylesheet" href="styles-terminal.css" />
18
+ <link rel="stylesheet" href="styles-run-composer.css" />
19
+ <link rel="stylesheet" href="styles-product.css" />
20
+ <link rel="stylesheet" href="styles-onboarding.css" />
21
+ <link rel="stylesheet" href="styles-settings.css" />
22
+ <link rel="stylesheet" href="styles-responsive-shell.css" />
23
+ <link rel="stylesheet" href="styles-responsive-workflows.css" />
24
+ <link rel="stylesheet" href="styles-responsive-runtime.css" />
25
+ <link rel="stylesheet" href="styles-responsive-product.css" />
26
+ <link rel="stylesheet" href="../node_modules/@xterm/xterm/css/xterm.css" />
27
+ </head>
28
+ <body>
29
+ <a class="skip-link" href="#mainContent" data-i18n="app.accessibility.skip_to_content">본문으로 바로가기</a>
30
+ <div class="app-shell">
31
+ <aside class="sidebar">
32
+ <div class="brand">
33
+ <div class="brand-orbit" aria-hidden="true"><span></span></div>
34
+ <div><strong>LoadToAgent</strong><small data-i18n="app.brand.subtitle">AI 작업 도우미</small></div>
35
+ </div>
36
+
37
+ <nav class="view-nav" aria-label="화면 선택" data-i18n-aria-label="app.nav.label">
38
+ <button class="nav-item active" type="button" data-view="all"><span class="nav-icon">⌂</span><span data-i18n="app.nav.home">홈</span><b id="navAllCount">0</b></button>
39
+ <button class="nav-item" type="button" data-view="active"><span class="nav-icon live-dot"></span><span data-i18n="app.nav.active">진행 중</span><b id="navActiveCount">0</b></button>
40
+ <button class="nav-item" type="button" data-view="waiting"><span class="nav-icon">!</span><span data-i18n="app.nav.needs_review">내 확인 필요</span><b id="navWaitingCount">0</b></button>
41
+ <div class="nav-group-label" data-i18n="app.nav.continue_session">기존 세션에 이어서 입력</div>
42
+ <button class="nav-item" type="button" data-view="terminal"><span class="nav-icon">›_</span><span data-i18n="app.nav.session_terminal">세션 터미널</span><b id="navTerminalCount">0</b></button>
43
+ <div class="nav-group-label tmux-only" data-i18n="ui.tmux_only">tmux 전용</div>
44
+ <button class="nav-item tmux-nav-item" type="button" data-view="tmux"><span class="nav-icon">▦</span><span data-i18n="app.nav.tmux">tmux 작업</span><b id="navTmuxCount">0</b></button>
45
+ <div class="nav-group-label" data-i18n="app.nav.application">프로그램</div>
46
+ <button class="nav-item settings-nav-item" type="button" data-view="settings"><span class="nav-icon">⚙</span><span data-i18n="app.nav.settings">설정</span><b id="navUpdateBadge" class="hidden">NEW</b></button>
47
+ <button id="mobileMoreBtn" class="nav-item mobile-more-nav" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="mobileToolsMenu"><span class="nav-icon">•••</span><span data-i18n="app.nav.more">더보기</span></button>
48
+ </nav>
49
+
50
+ <section class="sidebar-section">
51
+ <div class="section-title"><span data-i18n="ui.available_ai">사용 가능한 AI</span><button id="probeBtn" class="icon-button" type="button" title="AI 연결 상태 다시 확인" data-i18n-title="ui.check_ai_connections_again" aria-label="AI 연결 상태 다시 확인" data-i18n-aria-label="ui.check_ai_connections_again">↻</button></div>
52
+ <div id="providerRail" class="provider-rail"></div>
53
+ </section>
54
+
55
+ <section class="sidebar-section workspace-section">
56
+ <div class="section-title"><span data-i18n="ui.workspaces">작업 폴더</span><button id="addWorkspaceBtn" class="icon-button" type="button" title="작업 폴더 추가" data-i18n-title="ui.add_workspace" aria-label="작업 폴더 추가" data-i18n-aria-label="ui.add_workspace">+</button></div>
57
+ <div id="workspaceList" class="workspace-list"></div>
58
+ </section>
59
+
60
+ <div class="sidebar-footer">
61
+ <span class="privacy-light"></span>
62
+ <div><strong data-i18n="ui.secure_on_this_computer">내 컴퓨터에서 안전하게</strong><small data-i18n="ui.conversation_history_is_read_only_on_this_pc">대화 기록은 이 PC에서만 읽습니다</small></div>
63
+ <span id="sidebarAppVersion" class="sidebar-app-version">v—</span>
64
+ </div>
65
+ </aside>
66
+
67
+ <main id="mainContent" class="main-stage" tabindex="-1">
68
+ <header class="topbar">
69
+ <div>
70
+ <p id="pageEyebrow" class="eyebrow" data-i18n="ui.ai_work_overview">AI 작업 현황</p>
71
+ <h1 id="pageTitle" data-i18n="ui.see_all_ai_work_at_a_glance">AI 작업을 한눈에 확인하세요.</h1>
72
+ <p id="pageSubtitle" class="subtitle" data-i18n="ui.active_work_and_items_needing_your_review_appear_first_find">진행 중인 일과 내 확인이 필요한 일을 먼저 보여주고, 나머지 기록은 아래에서 찾을 수 있습니다.</p>
73
+ </div>
74
+ <div class="top-actions">
75
+ <button id="guideBtn" class="guide-toggle" type="button" aria-controls="beginnerGuide" aria-expanded="true"><span aria-hidden="true">?</span><span><b data-i18n="ui.10_minute_quick_start">10분 시작 가이드</b><small id="guideButtonProgress" data-i18n="common.progress" data-i18n-params='{"current":0,"total":4}'>0 / 4 완료</small></span></button>
76
+ <div class="sync-state"><span></span><div><b>LIVE</b><small id="lastSync" data-i18n="ui.connecting">연결 중</small></div></div>
77
+ <button id="newRunBtn" class="primary-button new-run-cta" type="button" aria-label="AI에게 새 일 맡기기" data-i18n-aria-label="ui.assign_a_new_task_to_ai" aria-keyshortcuts="Meta+N Control+N"><span class="new-run-cta-icon" aria-hidden="true">+</span><span class="new-run-cta-copy"><b data-i18n="ui.new_ai_task">새 AI 작업</b><small><kbd>⌘N</kbd></small></span></button>
78
+ </div>
79
+ </header>
80
+
81
+ <section id="updateNotice" class="update-notice hidden" aria-live="polite">
82
+ <span class="update-notice-signal" aria-hidden="true"><i></i></span>
83
+ <div><b id="updateNoticeTitle" data-i18n="ui.a_new_version_is_available">새 버전을 사용할 수 있습니다.</b><small id="updateNoticeText" data-i18n="ui.download_the_update_from_settings">설정에서 업데이트 파일을 받을 수 있습니다.</small></div>
84
+ <button id="updateNoticeBtn" type="button">업데이트 보기 <span>→</span></button>
85
+ </section>
86
+
87
+ <section id="settingsSection" class="settings-section hidden" aria-labelledby="settingsTitle">
88
+ <header class="settings-head">
89
+ <div class="settings-emblem" aria-hidden="true"><span>↟</span><i></i></div>
90
+ <div><p class="eyebrow" data-i18n="ui.preferences">환경 설정</p><h2 id="settingsTitle" data-i18n="settings.title">프로그램 설정</h2><p data-i18n="ui.choose_the_display_language_and_compare_the_installed_version_with">표시 언어를 선택하고, 현재 설치된 버전과 최신 정식 버전을 확인할 수 있습니다.</p></div>
91
+ </header>
92
+
93
+ <section class="language-settings-card" aria-labelledby="languageSettingsTitle">
94
+ <div class="language-settings-mark" aria-hidden="true">文</div>
95
+ <div class="language-settings-copy">
96
+ <span data-i18n="ui.language">언어</span>
97
+ <h3 id="languageSettingsTitle" data-i18n="settings.language.title">표시 언어</h3>
98
+ <p data-i18n="ui.choose_the_language_used_for_menus_and_guidance_changes_apply">메뉴와 안내 문구에 사용할 언어를 선택하세요. 변경 사항은 바로 적용되고 다음 실행에도 유지됩니다.</p>
99
+ </div>
100
+ <label class="language-select" for="languageSelect">
101
+ <span data-i18n="settings.language.label">앱 언어</span>
102
+ <select id="languageSelect" aria-describedby="languageSettingsTitle">
103
+ <option value="ko" data-i18n="settings.language.ko_name">한국어</option>
104
+ <option value="en">English</option>
105
+ <option value="zh-CN">简体中文</option>
106
+ </select>
107
+ </label>
108
+ </section>
109
+
110
+ <div id="updatePanel" class="update-panel" data-update-status="idle">
111
+ <div class="version-route" aria-label="설치 버전과 최신 버전 비교" data-i18n-aria-label="ui.compare_installed_and_latest_versions">
112
+ <article class="version-node local-version-node">
113
+ <span data-i18n="ui.installed_version">현재 설치 버전</span>
114
+ <strong id="currentVersion">v—</strong>
115
+ <small id="installationType" data-i18n="ui.checking_installation_type">설치 방식 확인 중</small>
116
+ </article>
117
+ <div class="version-connector" aria-hidden="true"><i></i><span data-i18n="ui.version_comparison">버전 비교</span><i></i></div>
118
+ <article class="version-node remote-version-node">
119
+ <span data-i18n="ui.latest_github_tag">GitHub 최신 태그</span>
120
+ <strong id="latestVersion" data-i18n="ui.not_checked">확인 전</strong>
121
+ <small id="releasePublishedAt" data-i18n="ui.stable_releases_only">정식 릴리스 기준</small>
122
+ </article>
123
+ </div>
124
+
125
+ <div class="update-console">
126
+ <div class="update-state-mark" aria-hidden="true"><span id="updateStateGlyph">·</span></div>
127
+ <div class="update-state-copy"><span id="updateStateLabel" data-i18n="ui.version_status">버전 상태</span><h3 id="updateStateTitle" data-i18n="ui.checking_for_updates">업데이트를 확인하고 있습니다.</h3><p id="updateStateText" data-i18n="ui.reading_the_latest_stable_github_release_tag">GitHub의 최신 정식 릴리스 태그를 읽는 중입니다.</p></div>
128
+ <div class="update-actions">
129
+ <button id="checkUpdateBtn" class="ghost-button" type="button" data-i18n="settings.update.check">업데이트 확인</button>
130
+ <button id="installUpdateBtn" class="primary-button hidden" type="button" data-i18n="settings.update.download">업데이트 파일 받기</button>
131
+ </div>
132
+ </div>
133
+
134
+ <div id="updateProgress" class="update-progress hidden" aria-live="polite">
135
+ <div><span data-i18n="ui.update_download">업데이트 파일 다운로드</span><b id="updateProgressLabel">0%</b></div>
136
+ <div class="update-progress-track" role="progressbar" aria-label="업데이트 파일 다운로드" data-i18n-aria-label="ui.update_download" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><i id="updateProgressBar"></i></div>
137
+ <small id="updateProgressBytes" data-i18n="ui.preparing">준비 중</small>
138
+ </div>
139
+
140
+ <div id="updateError" class="update-error hidden" role="status"></div>
141
+ <div id="releaseNotes" class="release-notes hidden"><div><span data-i18n="ui.release_notes">변경 사항</span><button id="openReleaseBtn" type="button" data-i18n="ui.view_all_on_github">GitHub에서 전체 보기 ↗</button></div><pre id="releaseNotesText"></pre></div>
142
+ </div>
143
+
144
+ <div class="settings-meta-grid">
145
+ <article><span data-i18n="ui.update_source">업데이트 기준</span><b data-i18n="ui.stable_github_releases">GitHub 정식 릴리스</b><p data-i18n="ui.drafts_and_prereleases_are_excluded_only_tags_newer_than_the">초안과 프리릴리스는 제외하고, 패키지 버전보다 높은 태그만 안내합니다.</p></article>
146
+ <article><span data-i18n="ui.file_verification">파일 검증</span><b data-i18n="ui.sha256_size_check">SHA-256 · 크기 확인</b><p data-i18n="ui.the_installer_will_not_open_if_its_hash_or_size">GitHub가 제공하는 해시와 파일 크기가 다르면 설치 파일을 열지 않습니다.</p></article>
147
+ <article><span data-i18n="ui.runtime">런타임</span><b id="runtimeVersions">Electron — · Node —</b><p data-i18n="ui.selects_the_windows_installer_or_macos_dmg_that_matches_the">운영체제와 CPU에 맞는 Windows 설치 파일 또는 macOS DMG를 선택합니다.</p></article>
148
+ </div>
149
+ </section>
150
+
151
+ <section id="beginnerGuide" class="beginner-guide" aria-labelledby="beginnerGuideTitle">
152
+ <div class="beginner-guide-intro">
153
+ <span class="guide-time" data-i18n="ui.first_10_minutes">첫 10분 코스</span>
154
+ <b id="beginnerGuideTitle" data-i18n="ui.these_four_steps_are_all_you_need">이 네 가지만 익히면 충분해요</b>
155
+ <span id="guideProgressText" data-i18n="ui.try_each_step_to_explore_the_app">하나씩 눌러 직접 둘러보세요.</span>
156
+ <div class="guide-progress" role="progressbar" aria-label="시작 가이드 진행률" data-i18n-aria-label="ui.quick_start_progress" aria-valuemin="0" aria-valuemax="4" aria-valuenow="0"><i id="guideProgressBar"></i></div>
157
+ </div>
158
+ <ol>
159
+ <li data-guide-step="create"><button type="button" data-guide-action="create"><i>1</i><span><b data-i18n="ui.assign_work_to_ai">AI에게 일 맡기기</b><small data-i18n="ui.choose_a_task_and_workspace_to_get_started">할 일과 작업 폴더를 고르면 바로 시작돼요.</small><em data-i18n="ui.try_it">직접 해보기 →</em></span></button></li>
160
+ <li data-guide-step="active"><button type="button" data-guide-action="active"><i>2</i><span><b data-i18n="ui.check_progress">진행 상황 확인</b><small data-i18n="ui.ai_with_a_green_indicator_is_working_now">초록 표시가 있는 AI는 지금 일하고 있어요.</small><em data-i18n="ui.view_active_work">진행 중 보기 →</em></span></button></li>
161
+ <li data-guide-step="waiting"><button type="button" data-guide-action="waiting"><i>3</i><span><b data-i18n="ui.find_items_to_review">확인할 일 찾기</b><small data-i18n="ui.items_marked_needs_review_need_a_response_or_choice">‘내 확인 필요’ 상태에는 답변이나 선택이 필요해요.</small><em data-i18n="ui.view_review_items">확인할 일 보기 →</em></span></button></li>
162
+ <li data-guide-step="detail"><button type="button" data-guide-action="detail"><i>4</i><span><b data-i18n="drawer.title">작업 자세히 보기</b><small data-i18n="ui.see_conversation_progress_and_usage_in_one_place">대화, 진행 과정, 사용량을 한곳에서 확인해요.</small><em data-i18n="ui.open_a_task">작업 열어보기 →</em></span></button></li>
163
+ </ol>
164
+ <button id="dismissGuideBtn" class="guide-dismiss" type="button" data-i18n="ui.collapse_guide">가이드 접기</button>
165
+ </section>
166
+
167
+ <section id="terminalSection" class="terminal-section hidden" aria-label="명령창 직접 다루기">
168
+ <div class="terminal-section-head">
169
+ <div><span class="terminal-logo">›_</span><div><p class="eyebrow" data-i18n="ui.conversation_and_execution_together">대화와 실행 화면을 한곳에서</p><h2 data-i18n="app.nav.session_terminal">세션 터미널</h2><p id="terminalPlatformExplain" class="section-explain" data-i18n="ui.choose_a_session_on_the_left_to_continue_with_its">왼쪽에서 세션을 고르면 이전 AI 대화와 실시간 터미널을 나란히 보며 그대로 이어서 작업할 수 있습니다.</p></div></div>
170
+ <div class="terminal-create-actions">
171
+ <button id="newPowerShellBtn" class="accent" type="button" data-i18n="ui.windows_session">+ Windows 세션</button>
172
+ <button id="newWslBtn" type="button" data-i18n="ui.linux_session">+ Linux 세션</button>
173
+ </div>
174
+ </div>
175
+ <div class="terminal-layout">
176
+ <aside class="terminal-resource-panel">
177
+ <div class="terminal-resource-head"><div><b data-i18n="ui.my_terminal_sessions">내 터미널 세션</b><span id="terminalSessionSummary" data-i18n="common.running" data-i18n-params='{"count":0}'>0개 실행 중</span></div><span class="terminal-resource-live"><i></i>LIVE</span></div>
178
+ <p id="terminalReorderHelp" class="sr-only" data-i18n="terminal.reorder_help">세션 옆의 위아래 버튼을 누르거나, 세션에 초점을 둔 뒤 Alt와 위아래 화살표 키를 눌러 순서를 바꿀 수 있습니다.</p>
179
+ <div id="terminalSessionList" class="terminal-session-list"></div>
180
+ <div class="terminal-resource-tip"><span>⌘</span><p><b data-i18n="ui.sessions_stay_open_when_you_change_views">세션은 화면을 옮겨도 유지됩니다.</b><small data-i18n="ui.output_and_draft_commands_remain_when_you_return_from_another">다른 작업을 보고 돌아와도 출력과 작성 중인 명령이 그대로 남아 있어요.</small></p></div>
181
+ </aside>
182
+
183
+ <div id="terminalWorkbenchMount" class="terminal-workbench-mount"><div id="terminalWorkbench" class="terminal-workbench">
184
+ <div class="terminal-controlbar">
185
+ <div class="terminal-target-cluster"><span id="terminalTargetIcon" class="terminal-target-icon">›_</span><div id="terminalTargetMeta" class="terminal-target-meta"><b data-i18n="ui.select_a_session">세션을 선택해 주세요</b><span data-i18n="ui.choose_a_session_on_the_left_or_create_a_new">왼쪽 목록에서 이어갈 세션을 고르거나 새 세션을 만드세요.</span></div></div>
186
+ <div class="terminal-workspace-actions">
187
+ <div id="terminalConnectionState" class="terminal-connection-state"><i></i><span data-i18n="ui.waiting_for_selection">선택 대기</span></div>
188
+ <div class="terminal-key-actions">
189
+ <button type="button" data-terminal-signal="interrupt" title="현재 실행 중인 명령 중단" data-i18n-title="ui.interrupt_the_running_command"><span data-i18n="ui.interrupt">실행 중단</span><kbd>Ctrl C</kbd></button>
190
+ <button type="button" data-terminal-signal="clear" title="터미널 화면 정리" data-i18n-title="ui.clear_terminal_screen"><span data-i18n="ui.clear">화면 정리</span><kbd>Ctrl L</kbd></button>
191
+ <button id="terminalRestartBtn" type="button" data-i18n="ui.restart">다시 시작</button>
192
+ <button id="terminalAttachBtn" class="hidden" type="button" data-i18n="ui.control_directly">직접 조작하기</button>
193
+ <button id="terminalCloseBtn" class="terminal-danger-button" type="button" data-i18n="ui.end_session">세션 종료</button>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ <div id="terminalTmuxTools" class="terminal-tmux-tools hidden">
198
+ <button type="button" data-tmux-manage="rename-session" data-i18n="ui.rename_workspace">작업 묶음 이름 바꾸기</button>
199
+ <button type="button" data-tmux-manage="new-window" data-i18n="ui.add_window">창 추가</button>
200
+ <button type="button" data-tmux-manage="split-horizontal" data-i18n="ui.split_left_right">좌우로 나누기</button>
201
+ <button type="button" data-tmux-manage="split-vertical" data-i18n="ui.split_top_bottom">상하로 나누기</button>
202
+ <label>창 배치 <select id="terminalTmuxLayout"><option value="tiled" data-i18n="ui.tiled">바둑판</option><option value="even-horizontal" data-i18n="ui.equal_width">같은 너비</option><option value="even-vertical" data-i18n="ui.equal_height">같은 높이</option><option value="main-horizontal" data-i18n="ui.main_window_on_top">중요 창 위쪽</option><option value="main-vertical" data-i18n="ui.main_window_on_left">중요 창 왼쪽</option></select></label>
203
+ <button type="button" class="danger" data-tmux-manage="kill-pane" data-i18n="ui.close_selected_pane">선택한 칸 닫기</button>
204
+ <button type="button" class="danger" data-tmux-manage="kill-window" data-i18n="ui.close_entire_window">창 전체 닫기</button>
205
+ <button type="button" class="danger" data-tmux-manage="kill-session" data-i18n="ui.end_workspace">작업 묶음 끝내기</button>
206
+ </div>
207
+ <div id="terminalStage" class="terminal-stage">
208
+ <aside id="terminalHistoryPanel" class="terminal-history-panel hidden" aria-label="선택한 AI의 이전 대화" data-i18n-aria-label="ui.previous_conversation_for_selected_ai">
209
+ <header class="terminal-history-head">
210
+ <div><span data-i18n="ui.ai_conversation_history">AI 대화 기록</span><b id="terminalHistoryTitle" data-i18n="ui.connected_ai_session">연결된 AI 세션</b><small id="terminalHistoryMeta" data-i18n="ui.loading_conversation_history">대화 기록을 불러오는 중</small></div>
211
+ <button id="terminalHistoryToggle" type="button" aria-expanded="true" title="대화 영역 접기" data-i18n-title="ui.collapse_conversation_panel">‹</button>
212
+ </header>
213
+ <div id="terminalHistoryList" class="terminal-history-list"></div>
214
+ <footer><span class="session-lock-dot"></span><b data-i18n="ui.this_conversation_is_connected_to_the_terminal_on_the_right">이 대화가 오른쪽 터미널과 연결되어 있습니다</b></footer>
215
+ </aside>
216
+ <section class="terminal-console-pane" aria-label="실시간 터미널과 명령 입력" data-i18n-aria-label="ui.live_terminal_and_command_input">
217
+ <header class="terminal-console-head"><div><span class="terminal-console-pulse"></span><div><b data-i18n="ui.live_terminal">실시간 터미널</b><small id="terminalConsoleCaption" data-i18n="ui.select_a_session_to_show_its_output_here">세션을 선택하면 출력이 여기에 표시됩니다</small></div></div><span id="terminalConsoleState" class="terminal-console-state" data-i18n="ui.waiting_for_selection">선택 대기</span></header>
218
+ <div id="terminalViewport" class="terminal-viewport"><div id="terminalEmpty" class="terminal-empty"><span>›_</span><h3 data-i18n="ui.choose_a_session_to_continue">이어갈 세션을 선택하세요</h3><p data-i18n="ui.choose_a_session_on_the_left_or_create_one_to">왼쪽 세션을 고르거나 새 세션을 만들면 실시간 출력과 명령 입력창이 열립니다.</p></div></div>
219
+ <form id="terminalCommandForm" class="terminal-command-form">
220
+ <div class="terminal-command-label"><label id="terminalCommandLabel" for="terminalCommandInput" data-i18n="ui.send_command_to_terminal">터미널에 명령 보내기</label><span data-i18n="ui.enter_to_send_shift_enter_for_newline">Enter 전송 · Shift+Enter 줄바꿈</span></div>
221
+ <div class="terminal-command-composer"><textarea id="terminalCommandInput" rows="2" placeholder="먼저 왼쪽에서 세션을 선택하세요" data-i18n-placeholder="ui.select_a_session_on_the_left_first" spellcheck="false"></textarea><button type="submit"><span data-i18n="common.send">보내기</span><b>↵</b></button></div>
222
+ </form>
223
+ <div id="terminalNotice" class="terminal-notice" role="status" aria-live="polite" aria-atomic="true"><span class="terminal-notice-dot"></span><span data-i18n="ui.select_a_session_to_start_typing">세션을 선택하면 바로 입력할 수 있습니다.</span></div>
224
+ </section>
225
+ </div>
226
+ </div></div>
227
+ </div>
228
+ </section>
229
+
230
+ <section id="tmuxSection" class="tmux-section hidden" aria-label="여러 명령창 작업 보기" data-i18n-aria-label="ui.view_multi_terminal_work">
231
+ <div class="tmux-section-head">
232
+ <div><span class="tmux-logo">tmux</span><div><p class="eyebrow" data-i18n="ui.tmux_workspace">tmux 전용 공간</p><h2 data-i18n="ui.work_grouped_with_tmux">tmux로 묶은 작업</h2><p class="section-explain" data-i18n="ui.only_work_using_tmux_appears_here_regular_terminals_are_kept">tmux를 사용하는 작업만 모아 봅니다. 일반 명령창은 이 화면에 섞이지 않습니다.</p></div></div>
233
+ <div class="tmux-section-actions"><button id="refreshTmuxTerminalBtn" type="button" data-i18n="ui.refresh">↻ 새로고침</button><button id="newTmuxSessionBtn" class="accent" type="button" data-i18n="ui.create_tmux_workspace">+ tmux 작업 만들기</button></div>
234
+ </div>
235
+ <div id="tmuxStats" class="tmux-stats"></div>
236
+ <div class="tmux-map-toolbar">
237
+ <div id="tmuxBreadcrumbs" class="graph-breadcrumbs" aria-label="tmux 탐색 경로" data-i18n-aria-label="ui.tmux_navigation_path"></div>
238
+ <button id="tmuxResetBtn" class="graph-reset hidden" type="button" data-i18n="ui.back_to_all">전체 목록으로</button>
239
+ </div>
240
+ <div id="tmuxMap" class="tmux-map"></div>
241
+ <section id="tmuxControlSection" class="tmux-control-section" aria-label="tmux 명령창 조작" data-i18n-aria-label="ui.control_tmux_terminals">
242
+ <div class="tmux-control-head"><div><p class="eyebrow" data-i18n="ui.tmux_controls">tmux 전용 조작</p><h3 data-i18n="ui.selected_tmux_terminal">선택한 tmux 명령창</h3></div><p data-i18n="ui.choose_a_pane_in_the_map_above_to_view_output">위 지도에서 칸을 고르면 출력 확인, 명령 전송, 창 나누기와 종료를 여기서 처리합니다.</p></div>
243
+ <div class="terminal-layout tmux-control-layout">
244
+ <aside class="terminal-resource-panel tmux-resource-panel">
245
+ <div class="terminal-resource-head tmux-resources"><div><b data-i18n="ui.tmux_terminals_only">tmux 안의 명령창만</b><span data-i18n="ui.regular_terminals_are_hidden">일반 명령창은 표시하지 않음</span></div></div>
246
+ <div id="terminalTmuxList" class="terminal-tmux-list"></div>
247
+ </aside>
248
+ <div id="tmuxWorkbenchMount" class="terminal-workbench-mount"></div>
249
+ </div>
250
+ </section>
251
+ </section>
252
+
253
+ <section id="liveSection" class="live-section hidden" aria-label="현재 진행 중인 AI 작업" data-i18n-aria-label="ui.ai_work_currently_in_progress">
254
+ <div class="live-section-head">
255
+ <div><span class="live-beacon"></span><div><p class="eyebrow" data-i18n="ui.active_now">지금 진행 중</p><h2 data-i18n="ui.work_assigned_to_ai">AI들이 맡은 일</h2></div></div>
256
+ <p data-i18n="ui.open_a_task_to_see_who_started_it_and_what">일을 누르면 누가 시작했고, 다른 AI에게 어떤 도움을 맡겼는지 쉽게 볼 수 있습니다.</p>
257
+ </div>
258
+ <div class="agent-map-toolbar">
259
+ <div id="graphBreadcrumbs" class="graph-breadcrumbs" aria-label="에이전트 탐색 경로" data-i18n-aria-label="ui.agent_navigation_path"></div>
260
+ <button id="graphResetBtn" class="graph-reset hidden" type="button" data-i18n="ui.back_to_task_list">작업 목록으로</button>
261
+ </div>
262
+ <div id="liveSessionGrid" class="live-session-grid"></div>
263
+ </section>
264
+
265
+ <section id="globalStats" class="global-stats" aria-label="전체 통계" data-i18n-aria-label="ui.overall_statistics"></section>
266
+ <section id="providerOverview" class="provider-overview" aria-label="AI별 현황" data-i18n-aria-label="ui.status_by_ai"></section>
267
+
268
+ <section id="sessionSection" class="sessions-section">
269
+ <div class="sessions-heading">
270
+ <div><p class="eyebrow" data-i18n="ui.history">지난 기록</p><h2 id="viewTitle" data-i18n="ui.recent_conversations_and_tasks">최근 대화와 작업</h2></div>
271
+ <div class="session-tools">
272
+ <label class="search-box"><span aria-hidden="true">⌕</span><input id="searchInput" type="search" aria-label="작업 검색" data-i18n-aria-label="ui.search_tasks" placeholder="작업 이름, AI, 폴더 찾기" data-i18n-placeholder="ui.find_task_ai_or_folder" autocomplete="off" /></label>
273
+ <div id="providerFilter" class="provider-filter" role="group" aria-label="AI 제공사 필터" data-i18n-aria-label="ui.ai_provider_filter"></div>
274
+ <p id="providerFilterStatus" class="sr-only" role="status" aria-live="polite"></p>
275
+ <select id="sortSelect" aria-label="정렬" data-i18n-aria-label="ui.sort"><option value="recent" data-i18n="ui.most_recently_active">최근에 움직인 순서</option><option value="tokens" data-i18n="ui.highest_usage">많이 사용한 순서</option><option value="context" data-i18n="ui.highest_context_usage">기억 공간을 많이 쓴 순서</option></select>
276
+ </div>
277
+ </div>
278
+ <div id="sessionGrid" class="session-grid"></div>
279
+ <button id="loadMoreBtn" class="load-more hidden" type="button" data-i18n="ui.show_more_tasks">작업 더 보기</button>
280
+ <div id="emptyState" class="empty-state hidden">
281
+ <div class="empty-orbit"><span></span></div>
282
+ <h3 data-i18n="ui.no_tasks_to_show_yet">아직 보여줄 작업이 없습니다</h3>
283
+ <p data-i18n="ui.change_the_search_filters_or_assign_a_new_task_to">검색 조건을 바꾸거나 AI에게 새 일을 맡겨보세요.</p>
284
+ <button class="primary-button" data-open-run data-i18n="ui.assign_a_new_task_to_ai">AI에게 새 일 맡기기</button>
285
+ </div>
286
+ </section>
287
+ </main>
288
+ </div>
289
+
290
+ <nav id="mobileToolsMenu" class="mobile-tools-menu hidden" aria-label="고급 기능과 설정" data-i18n-aria-label="ui.advanced_tools_and_settings">
291
+ <div><b data-i18n="app.nav.more">더보기</b><span data-i18n="ui.advanced_tools_to_open_only_when_needed">필요할 때만 여는 고급 기능이에요.</span></div>
292
+ <button type="button" data-mobile-view="terminal"><i aria-hidden="true">›_</i><span><b data-i18n="app.nav.session_terminal">세션 터미널</b><small data-i18n="ui.continue_an_existing_ai_conversation_directly">기존 AI 대화를 직접 이어서 입력</small></span></button>
293
+ <button type="button" data-mobile-view="tmux"><i aria-hidden="true">▦</i><span><b data-i18n="app.nav.tmux">tmux 작업</b><small data-i18n="ui.manage_multiple_terminals_together">여러 명령창을 묶어 관리</small></span></button>
294
+ <button type="button" data-mobile-view="settings"><i aria-hidden="true">⚙</i><span><b data-i18n="settings.title">프로그램 설정</b><small data-i18n="ui.check_version_and_updates">버전과 업데이트 확인</small></span></button>
295
+ </nav>
296
+
297
+ <div id="drawerBackdrop" class="drawer-backdrop hidden"></div>
298
+ <aside id="detailDrawer" class="detail-drawer" role="dialog" aria-modal="true" aria-labelledby="drawerTitle" aria-hidden="true" tabindex="-1" inert>
299
+ <div class="drawer-head">
300
+ <div id="drawerProviderMark" class="provider-mark">AI</div>
301
+ <div class="drawer-title"><span id="drawerProvider" data-i18n="ui.ai_task">AI 작업</span><h2 id="drawerTitle" data-i18n="drawer.title">작업 자세히 보기</h2></div>
302
+ <button id="closeDrawerBtn" class="close-button" aria-label="닫기" data-i18n-aria-label="common.close">×</button>
303
+ </div>
304
+ <div id="drawerMeta" class="drawer-meta"></div>
305
+ <div class="drawer-tabs" role="tablist">
306
+ <button id="drawerTabChat" class="drawer-tab active" role="tab" aria-selected="true" aria-controls="drawerContent" tabindex="0" data-tab="chat" data-i18n="ui.conversation">대화 내용</button>
307
+ <button id="drawerTabLifecycle" class="drawer-tab" role="tab" aria-selected="false" aria-controls="drawerContent" tabindex="-1" data-tab="lifecycle" data-i18n="ui.progress">진행 과정</button>
308
+ <button id="drawerTabTokens" class="drawer-tab" role="tab" aria-selected="false" aria-controls="drawerContent" tabindex="-1" data-tab="tokens" data-i18n="ui.usage">사용량</button>
309
+ </div>
310
+ <div id="drawerContent" class="drawer-content" role="tabpanel" aria-labelledby="drawerTabChat" tabindex="0"></div>
311
+ </aside>
312
+
313
+ <div id="runModal" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="runModalTitle" aria-describedby="runModalDescription">
314
+ <form id="runForm" class="run-modal run-composer">
315
+ <div class="modal-head">
316
+ <div><p class="eyebrow" data-i18n="ui.start_ai_task">AI 작업 시작</p><h2 id="runModalTitle" data-i18n="ui.what_should_the_ai_work_on">무슨 일을 맡길까요?</h2><p id="runModalDescription" class="run-modal-intro" data-i18n="ui.describe_the_task_then_choose_which_ai_will_work_in">할 일을 먼저 적고, 어떤 AI가 어디서 작업할지 선택하세요.</p></div>
317
+ <button type="button" id="closeRunModalBtn" class="close-button" aria-label="새 작업 창 닫기" data-i18n-aria-label="ui.close_new_task_dialog">×</button>
318
+ </div>
319
+ <section class="run-step run-request-step" aria-labelledby="runRequestLabel">
320
+ <div class="run-step-head"><i>1</i><span><b id="runRequestLabel" data-i18n="ui.describe_the_task">할 일 적기</b><small data-i18n="ui.include_the_expected_result_and_constraints_for_better_accuracy">결과와 제약을 함께 적으면 더 정확해요.</small></span></div>
321
+ <label class="run-prompt-field"><span class="sr-only" data-i18n="ui.task_for_ai">AI에게 맡길 일</span><textarea id="runPrompt" required rows="5" maxlength="8000" placeholder="예: 로그인 화면의 오류를 찾아 고치고, 관련 테스트까지 실행해줘" data-i18n-placeholder="ui.example_find_and_fix_the_login_screen_error_then_run"></textarea></label>
322
+ <div class="run-prompt-tools"><div class="run-prompt-examples" aria-label="빠른 요청 예시" data-i18n-aria-label="ui.quick_prompt_examples"><button type="button" data-run-prompt-example="문제의 원인을 찾아 고치고 관련 테스트까지 실행해줘" data-i18n="ui.fix_a_bug">오류 수정</button><button type="button" data-run-prompt-example="현재 코드를 검토하고 중요도 순으로 개선해줘" data-i18n="ui.review_code">코드 검토</button><button type="button" data-run-prompt-example="현재 기능을 검증하는 테스트를 추가하고 실행해줘" data-i18n="ui.add_tests">테스트 추가</button></div><span id="runPromptCount" class="run-prompt-count">0 / 8,000</span></div>
323
+ </section>
324
+ <section class="run-step run-setup-step" aria-labelledby="runSetupLabel">
325
+ <div class="run-step-head"><i>2</i><span><b id="runSetupLabel" data-i18n="ui.choose_how_to_run">실행 방법 선택</b><small data-i18n="ui.choose_an_ai_and_workspace_to_finish_setup">AI와 작업 폴더만 고르면 준비가 끝납니다.</small></span></div>
326
+ <div id="runProviderLabel" class="field-label" data-i18n="ui.which_ai_should_handle_it">어떤 AI에게 맡길까요?</div>
327
+ <div id="runProviderPicker" class="run-provider-picker" role="group" aria-labelledby="runProviderLabel"></div>
328
+ <div id="runProviderHelp" class="run-provider-help hidden" role="status"></div>
329
+ <div class="run-folder-field"><label class="field-label" for="runCwd" data-i18n="ui.workspaces">작업 폴더</label><div class="path-field"><input id="runCwd" required placeholder="프로젝트 폴더를 선택하세요" data-i18n-placeholder="ui.choose_a_project_folder" /><button type="button" id="pickRunCwdBtn" data-i18n="ui.browse">폴더 찾기</button></div></div>
330
+ <div id="runWorkspaceSuggestions" class="run-workspace-suggestions" aria-label="최근 작업 폴더" data-i18n-aria-label="ui.recent_workspaces"></div>
331
+ <label class="permission-toggle"><input id="allowWrites" type="checkbox" /><span class="toggle-track"></span><div><strong data-i18n="ui.allow_ai_to_edit_files">AI가 파일을 고칠 수 있게 허용</strong><small data-i18n="ui.when_enabled_the_ai_can_directly_edit_files_in_the">켜면 선택한 폴더의 파일을 직접 수정합니다.</small></div></label>
332
+ <div id="runPermissionHint" class="run-permission-hint hidden" role="status" data-i18n="ui.this_request_appears_to_need_file_changes_without_edit_permission">이 요청은 파일 수정이 필요해 보여요. 위의 수정 허용을 켜지 않으면 AI가 코드 변경 없이 검토만 합니다.</div>
333
+ </section>
334
+ <details class="run-advanced">
335
+ <summary><span><b data-i18n="ui.advanced_settings">고급 설정</b><small data-i18n="ui.use_only_when_selecting_a_specific_model">특정 모델을 직접 지정할 때만 사용</small></span><i aria-hidden="true">↓</i></summary>
336
+ <label><span>사용 모델 <small data-i18n="ui.leave_blank_if_unsure">몰라도 비워두세요</small></span><input id="runModel" placeholder="비워두면 AI가 알아서 선택합니다" data-i18n-placeholder="ui.leave_blank_to_let_the_ai_choose" /></label>
337
+ </details>
338
+ <div id="runError" class="form-error hidden" role="alert" aria-live="assertive"></div>
339
+ <div class="modal-actions run-modal-actions"><span class="run-shortcut-hint"><kbd>⌘</kbd><kbd>Enter</kbd> 시작</span><button type="button" id="cancelRunBtn" class="ghost-button" data-i18n="common.cancel">취소</button><button type="submit" class="primary-button run-submit-button"><span class="run-submit-icon" aria-hidden="true">↗</span><span id="runSubmitLabel" data-i18n="ui.assign_to_ai">AI에게 맡기기</span></button></div>
340
+ </form>
341
+ </div>
342
+
343
+ <div id="toast" class="toast hidden" role="status" aria-live="polite" aria-atomic="true"></div>
344
+ <div id="tmuxCreateModal" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="tmuxCreateTitle">
345
+ <form id="tmuxCreateForm" class="run-modal terminal-modal">
346
+ <div class="modal-head"><div><p class="eyebrow" data-i18n="ui.advanced_work">고급 작업</p><h2 id="tmuxCreateTitle" data-i18n="ui.create_multi_window_workspace">여러 창 작업 만들기</h2></div><button type="button" id="closeTmuxCreateBtn" class="close-button" aria-label="여러 창 작업 만들기 닫기" data-i18n-aria-label="ui.close_multi_window_workspace_dialog">×</button></div>
347
+ <div class="form-grid">
348
+ <label><span id="tmuxEnvironmentLabel" data-i18n="ui.tmux_environment">tmux 환경</span><select id="tmuxCreateDistro" required></select></label>
349
+ <label><span data-i18n="ui.workspace_name">작업 묶음 이름</span><input id="tmuxCreateName" required pattern="[A-Za-z0-9_.\-]+" placeholder="my-work" /></label>
350
+ </div>
351
+ <label><span>시작 폴더 <small data-i18n="ui.optional">선택 사항</small></span><input id="tmuxCreateCwd" placeholder="프로젝트의 절대 경로" data-i18n-placeholder="ui.absolute_project_path" /></label>
352
+ <label><span>처음 실행할 명령 <small data-i18n="ui.optional">선택 사항</small></span><textarea id="tmuxCreateCommand" rows="4" placeholder="예: claude 또는 codex" data-i18n-placeholder="ui.example_claude_or_codex"></textarea></label>
353
+ <div id="tmuxCreateError" class="form-error hidden" role="alert" aria-live="assertive"></div>
354
+ <div class="modal-actions"><button type="button" id="cancelTmuxCreateBtn" class="ghost-button" data-i18n="common.cancel">취소</button><button type="submit" class="primary-button" data-i18n="ui.create_workspace">작업 묶음 만들기</button></div>
355
+ </form>
356
+ </div>
357
+ <script src="../node_modules/@xterm/xterm/lib/xterm.js"></script>
358
+ <script src="../node_modules/@xterm/addon-fit/lib/addon-fit.js"></script>
359
+ <script src="i18n-messages.js"></script>
360
+ <script src="i18n.js"></script>
361
+ <script src="shared.js"></script>
362
+ <script src="app.js"></script>
363
+ <script src="app-dashboard.js"></script>
364
+ <script src="app-graph-model.js"></script>
365
+ <script src="app-graph-view.js"></script>
366
+ <script src="app-graph-layout.js"></script>
367
+ <script src="app-graph-orchestration.js"></script>
368
+ <script src="app-tmux-render.js"></script>
369
+ <script src="app-session-render.js"></script>
370
+ <script src="app-agent-actions.js"></script>
371
+ <script src="app-drawer-data.js"></script>
372
+ <script src="app-drawer-content.js"></script>
373
+ <script src="app-drawer.js"></script>
374
+ <script src="app-run-modal.js"></script>
375
+ <script src="app-events-navigation.js"></script>
376
+ <script src="app-events-sessions.js"></script>
377
+ <script src="app-events-filters.js"></script>
378
+ <script src="app-events-dialogs.js"></script>
379
+ <script src="app-events.js"></script>
380
+ <script src="app-bootstrap.js"></script>
381
+ <script src="terminal-workbench.js"></script>
382
+ <script src="terminal-agent.js"></script>
383
+ <script src="terminal-events.js"></script>
384
+ <script src="terminal.js"></script>
385
+ </body>
386
+ </html>
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Small, dependency-free helpers shared by classic renderer scripts.
5
+ * Keeping these on one frozen namespace avoids duplicate implementations
6
+ * without introducing a bundler or changing Electron's preload boundary.
7
+ */
8
+ window.LoadToAgentRendererUtils = Object.freeze({
9
+ $: selector => document.querySelector(selector),
10
+ $$: selector => [...document.querySelectorAll(selector)],
11
+ esc(value) {
12
+ return String(value == null ? '' : value)
13
+ .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
14
+ .replace(/"/g, '&quot;').replace(/'/g, '&#039;');
15
+ },
16
+ uiLocale() {
17
+ return window.LoadToAgentI18n?.getLocaleTag() || 'ko-KR';
18
+ },
19
+ providerLabel(provider) {
20
+ return ({ claude: 'Claude', codex: 'GPT · Codex', gemini: 'Gemini', grok: 'Grok' })[provider] || 'AI';
21
+ },
22
+ reportRecoverableError(operation, error) {
23
+ const message = error && error.message ? error.message : String(error || 'unknown error');
24
+ console.warn(`[LoadToAgent:${operation}] ${message}`);
25
+ },
26
+ });