local-coding-agent 1.1.0__py3-none-any.whl

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 (101) hide show
  1. local_coding_agent/__init__.py +314 -0
  2. local_coding_agent/__main__.py +5 -0
  3. local_coding_agent/acp_server/__init__.py +38 -0
  4. local_coding_agent/acp_server/_codec.py +95 -0
  5. local_coding_agent/acp_server/_protocol.py +13 -0
  6. local_coding_agent/acp_server/_server.py +663 -0
  7. local_coding_agent/acp_server/_session.py +24 -0
  8. local_coding_agent/acp_server/_tools.py +126 -0
  9. local_coding_agent/ast_compactor.py +130 -0
  10. local_coding_agent/atomizer.py +77 -0
  11. local_coding_agent/benchmark/__init__.py +57 -0
  12. local_coding_agent/benchmark/_cases.py +307 -0
  13. local_coding_agent/benchmark/_judge.py +175 -0
  14. local_coding_agent/benchmark/_oracles.py +157 -0
  15. local_coding_agent/benchmark/_runner.py +259 -0
  16. local_coding_agent/benchmark/_stats.py +22 -0
  17. local_coding_agent/benchmark/_summary.py +143 -0
  18. local_coding_agent/benchmark_oracle_worker.py +169 -0
  19. local_coding_agent/calibration.py +154 -0
  20. local_coding_agent/capability.py +450 -0
  21. local_coding_agent/chat_compaction.py +276 -0
  22. local_coding_agent/cli/__init__.py +12 -0
  23. local_coding_agent/cli/_dispatch.py +377 -0
  24. local_coding_agent/cli/_handlers.py +320 -0
  25. local_coding_agent/cli/_handlers2.py +725 -0
  26. local_coding_agent/cli/_input.py +61 -0
  27. local_coding_agent/cli/_main.py +159 -0
  28. local_coding_agent/cli/_parser.py +327 -0
  29. local_coding_agent/context_manager.py +155 -0
  30. local_coding_agent/controller/__init__.py +18 -0
  31. local_coding_agent/controller/_constants.py +108 -0
  32. local_coding_agent/controller/_controller.py +1008 -0
  33. local_coding_agent/controller/_post_apply.py +216 -0
  34. local_coding_agent/delegator.py +180 -0
  35. local_coding_agent/desktop/__init__.py +8 -0
  36. local_coding_agent/desktop/app.py +100 -0
  37. local_coding_agent/desktop/assets/lucide.min.js +12 -0
  38. local_coding_agent/desktop/assets/tailwind.css +1 -0
  39. local_coding_agent/desktop/client_js.py +1271 -0
  40. local_coding_agent/desktop/components.py +555 -0
  41. local_coding_agent/desktop/server/__init__.py +37 -0
  42. local_coding_agent/desktop/server/_handlers.py +2178 -0
  43. local_coding_agent/desktop/server/_models.py +204 -0
  44. local_coding_agent/desktop/server/_server.py +347 -0
  45. local_coding_agent/desktop/server/_telemetry.py +87 -0
  46. local_coding_agent/desktop/styles.py +76 -0
  47. local_coding_agent/desktop/ui.py +53 -0
  48. local_coding_agent/doctor.py +413 -0
  49. local_coding_agent/hooks/__init__.py +19 -0
  50. local_coding_agent/hooks/_adapters.py +458 -0
  51. local_coding_agent/hooks/_bridge.py +346 -0
  52. local_coding_agent/hooks/_decision.py +26 -0
  53. local_coding_agent/lsp/__init__.py +60 -0
  54. local_coding_agent/lsp/_client.py +322 -0
  55. local_coding_agent/lsp/_fallback.py +297 -0
  56. local_coding_agent/lsp/_manager.py +173 -0
  57. local_coding_agent/lsp/_mock.py +200 -0
  58. local_coding_agent/lsp/_types.py +398 -0
  59. local_coding_agent/mcp_config.py +398 -0
  60. local_coding_agent/mcp_server.py +377 -0
  61. local_coding_agent/memory.py +140 -0
  62. local_coding_agent/mode_router.py +200 -0
  63. local_coding_agent/model_scanner.py +525 -0
  64. local_coding_agent/monitor.py +635 -0
  65. local_coding_agent/observation_policy.py +184 -0
  66. local_coding_agent/ollama_adapter.py +736 -0
  67. local_coding_agent/plan_mode.py +653 -0
  68. local_coding_agent/prescriptions.py +278 -0
  69. local_coding_agent/profiles.py +239 -0
  70. local_coding_agent/repository_tools.py +782 -0
  71. local_coding_agent/ripgrep.py +326 -0
  72. local_coding_agent/semantic_linter.py +122 -0
  73. local_coding_agent/service.py +405 -0
  74. local_coding_agent/session_events.py +684 -0
  75. local_coding_agent/session_query.py +578 -0
  76. local_coding_agent/skill_config.py +426 -0
  77. local_coding_agent/smoke.py +268 -0
  78. local_coding_agent/speculative_racing.py +85 -0
  79. local_coding_agent/spill.py +258 -0
  80. local_coding_agent/stats.py +255 -0
  81. local_coding_agent/stdio.py +132 -0
  82. local_coding_agent/subagent.py +516 -0
  83. local_coding_agent/task.py +90 -0
  84. local_coding_agent/task_store.py +142 -0
  85. local_coding_agent/tasks.py +240 -0
  86. local_coding_agent/terminal/__init__.py +35 -0
  87. local_coding_agent/terminal/_manager.py +156 -0
  88. local_coding_agent/terminal/_process.py +148 -0
  89. local_coding_agent/terminal/_session.py +452 -0
  90. local_coding_agent/terminal/_tools.py +233 -0
  91. local_coding_agent/terminal/_types.py +60 -0
  92. local_coding_agent/tool_call_parser.py +178 -0
  93. local_coding_agent/validators.py +563 -0
  94. local_coding_agent/vram_fit.py +240 -0
  95. local_coding_agent/worker_pool.py +516 -0
  96. local_coding_agent-1.1.0.dist-info/METADATA +202 -0
  97. local_coding_agent-1.1.0.dist-info/RECORD +101 -0
  98. local_coding_agent-1.1.0.dist-info/WHEEL +5 -0
  99. local_coding_agent-1.1.0.dist-info/entry_points.txt +3 -0
  100. local_coding_agent-1.1.0.dist-info/licenses/LICENSE +21 -0
  101. local_coding_agent-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,555 @@
1
+ """Modular HTML components for the Desktop AI Coding Harness."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .. import __version__
6
+
7
+
8
+ def _with_version(html: str) -> str:
9
+ return html.replace("%VERSION%", __version__)
10
+
11
+
12
+ def render_header() -> str:
13
+ return """
14
+ <!-- TOP BAR -->
15
+ <header class="h-11 border-b border-[var(--border-main)] bg-[var(--bg-header)] px-3.5 flex items-center justify-between shrink-0">
16
+
17
+ <!-- Left: Brand + 3 Distinct Dedicated Popover Buttons -->
18
+ <div class="flex items-center gap-2.5">
19
+ <button onclick="toggleSidebar()" class="p-1 rounded hover:bg-zinc-800/40 text-zinc-400 hover:text-zinc-200 transition" title="Toggle Sessions (Ctrl+B)">
20
+ <i data-lucide="panel-left" class="w-3.5 h-3.5"></i>
21
+ </button>
22
+
23
+ <div class="flex items-center gap-1.5 pr-2.5 border-r border-[var(--border-main)] font-medium text-xs">
24
+ <span class="w-2 h-2 rounded-sm bg-cyan-500"></span>
25
+ <span class="font-semibold text-[var(--text-main)]">Local Harness</span>
26
+ </div>
27
+
28
+ <!-- 1. VRAM Button -> Opens GPU Modal -->
29
+ <button onclick="openModal('gpuModal')" class="inline-flex items-center gap-1.5 px-2 py-0.5 rounded bg-[var(--bg-card-subtle)] hover:border-zinc-500 border border-[var(--border-main)] text-[var(--text-muted)] transition cursor-pointer font-mono text-[11px] num-tabular" title="Open GPU & VRAM Cockpit">
30
+ <span class="w-1.5 h-1.5 rounded-full bg-emerald-500" id="topGpuDot"></span>
31
+ <span class="text-zinc-500 font-sans text-[10px]">VRAM</span> <span id="telemetryVram">0.0/8.0G</span>
32
+ </button>
33
+
34
+ <!-- 2. Model Button -> Opens Model Selector Modal -->
35
+ <button onclick="openModal('modelModal')" class="inline-flex items-center gap-1.5 px-2 py-0.5 rounded bg-[var(--bg-card-subtle)] hover:border-zinc-500 border border-[var(--border-main)] text-[var(--text-muted)] transition cursor-pointer font-mono text-[11px]" title="Switch Model Profile">
36
+ <span class="text-cyan-500 font-sans text-[10px]" id="backendLabel">OLLAMA</span>
37
+ <span class="text-[var(--text-main)]" id="telemetryModel">qwen2.5-coder</span>
38
+ </button>
39
+
40
+ <!-- 3. Server Status Button -> Opens Server Engine Modal -->
41
+ <button onclick="openModal('serverModal')" class="inline-flex items-center gap-1 px-2 py-0.5 rounded bg-[var(--bg-card-subtle)] hover:border-zinc-500 border border-[var(--border-main)] text-zinc-400 hover:text-zinc-200 transition font-mono text-[11px]" title="Manage Local Inference Servers">
42
+ <span class="w-1.5 h-1.5 rounded-full bg-amber-500" id="serverLiveDot"></span>
43
+ <span id="serverLiveText" class="text-[10px] font-sans">Checking...</span>
44
+ </button>
45
+ </div>
46
+
47
+ <!-- Center: Segmented Navigation -->
48
+ <div class="inline-flex items-center p-0.5 bg-[var(--bg-card-subtle)] border border-[var(--border-main)] rounded-md font-sans">
49
+ <button onclick="switchTab('chat')" id="tab-btn-chat" class="tab-btn px-2.5 py-1 text-[11px] font-medium rounded transition flex items-center gap-1.5 bg-[var(--bg-card)] text-[var(--text-main)] shadow-sm border border-[var(--border-main)]">
50
+ <i data-lucide="message-square" class="w-3 h-3 text-cyan-500"></i>
51
+ <span>Interactive Chat</span>
52
+ </button>
53
+ <button onclick="switchTab('delegated')" id="tab-btn-delegated" class="tab-btn px-2.5 py-1 text-[11px] font-medium rounded transition flex items-center gap-1.5 text-[var(--text-muted)] hover:text-[var(--text-main)]">
54
+ <i data-lucide="inbox" class="w-3 h-3"></i>
55
+ <span>Delegated Tasks</span>
56
+ <span class="px-1 py-0.1 rounded bg-cyan-500/10 border border-cyan-500/30 text-cyan-500 text-[9px] font-mono font-semibold" id="delegatedBadgeCount">0</span>
57
+ </button>
58
+ </div>
59
+
60
+ <!-- Right: Workspace + Theme Toggle + Settings Button -->
61
+ <div class="flex items-center gap-2 text-xs">
62
+ <div class="flex items-center gap-1 px-2 py-0.5 rounded bg-[var(--bg-card-subtle)] border border-[var(--border-main)] font-mono text-[11px] text-[var(--text-muted)]">
63
+ <i data-lucide="git-branch" class="w-3 h-3 text-zinc-500"></i>
64
+ <span class="text-[var(--text-main)]" id="workspaceName">workspace</span>
65
+ <span class="text-emerald-500 text-[10px] font-sans font-medium" id="workspaceBranch">• main*</span>
66
+ </div>
67
+
68
+ <!-- Theme Switcher -->
69
+ <button onclick="toggleTheme()" class="p-1.5 rounded hover:bg-zinc-800/40 text-[var(--text-muted)] hover:text-[var(--text-main)] transition" title="Toggle Dark/Light Theme">
70
+ <i data-lucide="sun-medium" id="themeIconSun" class="w-3.5 h-3.5 hidden dark:block"></i>
71
+ <i data-lucide="moon" id="themeIconMoon" class="w-3.5 h-3.5 block dark:hidden"></i>
72
+ </button>
73
+
74
+ <!-- Settings Button -> Opens Settings Modal -->
75
+ <button onclick="openModal('settingsModal')" class="p-1.5 rounded hover:bg-zinc-800/40 text-[var(--text-muted)] hover:text-[var(--text-main)] transition" title="Preferences, Model Scanner &amp; Diagnostics">
76
+ <i data-lucide="settings" class="w-3.5 h-3.5"></i>
77
+ </button>
78
+ </div>
79
+ </header>
80
+ """
81
+
82
+
83
+ def render_sidebar() -> str:
84
+ _HTML = """
85
+ <!-- LEFT SIDEBAR: SESSION HISTORY -->
86
+ <aside id="sessionSidebar" class="w-60 bg-[var(--bg-sidebar)] border-r border-[var(--border-main)] flex flex-col h-full shrink-0 transition-all duration-150">
87
+
88
+ <div class="p-2.5 border-b border-[var(--border-main)]">
89
+ <button onclick="startNewSession()" class="w-full py-1 px-2.5 rounded bg-[var(--bg-card)] hover:border-zinc-500 border border-[var(--border-main)] text-[11px] font-medium text-[var(--text-main)] transition flex items-center justify-between shadow-sm">
90
+ <span class="flex items-center gap-1.5">
91
+ <i data-lucide="plus" class="w-3 h-3 text-cyan-500"></i>
92
+ <span>New Task Session</span>
93
+ </span>
94
+ <kbd class="text-[9px] font-mono text-zinc-500 bg-[var(--bg-card-subtle)] px-1 rounded border border-[var(--border-main)]">Ctrl+N</kbd>
95
+ </button>
96
+ </div>
97
+
98
+ <div class="px-2.5 pt-2 pb-1 flex items-center gap-1 text-[10px] font-medium text-[var(--text-muted)]">
99
+ <button data-session-filter="all" onclick="filterSessions('all', event)" class="filter-chip px-1.5 py-0.5 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-[var(--text-main)] font-semibold">All</button>
100
+ <button data-session-filter="user" onclick="filterSessions('user', event)" class="filter-chip px-1.5 py-0.5 rounded text-zinc-500 hover:text-[var(--text-main)]">User</button>
101
+ <button data-session-filter="agent" onclick="filterSessions('agent', event)" class="filter-chip px-1.5 py-0.5 rounded text-zinc-500 hover:text-[var(--text-main)]">Agent</button>
102
+ </div>
103
+
104
+ <div class="flex-1 overflow-y-auto p-1.5 space-y-1 text-xs font-sans" id="sessionList">
105
+ <!-- Sessions rendered dynamically -->
106
+ </div>
107
+
108
+ <div class="p-2 border-t border-[var(--border-main)] text-[10px] font-mono text-zinc-500 flex items-center justify-between">
109
+ <span id="sessionCounter">0 Sessions</span>
110
+ <span>Harness v%VERSION%</span>
111
+ </div>
112
+ </aside>
113
+ """
114
+ return _with_version(_HTML)
115
+
116
+
117
+ def render_chat_panel() -> str:
118
+ return """
119
+ <!-- TAB 1: INTERACTIVE CHAT MODE -->
120
+ <div id="view-chat" class="tab-view flex-1 flex h-full">
121
+
122
+ <!-- Center: Chat Stream -->
123
+ <div class="flex-1 flex flex-col bg-[var(--bg-app)] border-r border-[var(--border-main)] h-full overflow-hidden">
124
+
125
+ <div id="chatMessages" class="flex-1 overflow-y-auto p-5 space-y-3.5 select-text">
126
+ <!-- Welcome Guidance -->
127
+ <div class="flex items-start gap-2.5 max-w-2xl">
128
+ <div class="w-6 h-6 rounded bg-cyan-500/10 border border-cyan-500/30 flex items-center justify-center shrink-0 text-[10px] font-mono font-semibold text-cyan-500">
129
+ AI
130
+ </div>
131
+ <div class="flex-1 space-y-2">
132
+ <div class="text-[11px] font-medium text-zinc-500 flex items-center gap-1.5">
133
+ <span>Local Coding Harness</span>
134
+ <span class="text-[9px] font-mono text-zinc-500">• Backend status shown above</span>
135
+ </div>
136
+ <div class="p-3.5 rounded-lg bg-[var(--bg-card)] border border-[var(--border-main)] text-xs text-[var(--text-main)] leading-relaxed space-y-2">
137
+ <p>Welcome! Enter your coding instructions below or select a preset. The controller will compact the AST, formulate atomic SEARCH/REPLACE diffs, and verify with local test runners.</p>
138
+ <div class="flex items-center gap-2 pt-1">
139
+ <span class="text-[10px] font-mono text-zinc-500">Active Engine:</span>
140
+ <span class="px-1.5 py-0.2 rounded bg-[var(--bg-card-subtle)] border border-[var(--border-main)] font-mono text-cyan-400 text-[10px]" id="welcomeModelLabel">qwen2.5-coder</span>
141
+ <span class="px-1.5 py-0.2 rounded bg-[var(--bg-card-subtle)] border border-[var(--border-main)] font-mono text-cyan-400 text-[10px]" id="welcomeModeLabel">Mode: auto</span>
142
+ <button onclick="openModal('modelModal')" class="text-[10px] text-cyan-500 hover:underline">Change Model</button>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+
149
+ <!-- Presets -->
150
+ <div class="px-5 py-1.5 border-t border-[var(--border-main)] bg-[var(--bg-header)] flex items-center gap-1.5 overflow-x-auto text-xs">
151
+ <span class="text-zinc-500 text-[10px] font-mono shrink-0">Presets:</span>
152
+ <button onclick="setPromptAndRun('Fix off-by-one error in sliding window index')" class="px-2 py-0.5 rounded bg-[var(--bg-card-subtle)] hover:bg-[var(--bg-card)] border border-[var(--border-main)] text-[var(--text-muted)] hover:text-[var(--text-main)] text-[11px] transition shrink-0 flex items-center gap-1">
153
+ <i data-lucide="zap" class="w-2.5 h-2.5 text-amber-500"></i> Fix sliding window
154
+ </button>
155
+ <button onclick="setPromptAndRun('Write pytest unit tests for tax calculation')" class="px-2 py-0.5 rounded bg-[var(--bg-card-subtle)] hover:bg-[var(--bg-card)] border border-[var(--border-main)] text-[var(--text-muted)] hover:text-[var(--text-main)] text-[11px] transition shrink-0 flex items-center gap-1">
156
+ <i data-lucide="test-tube" class="w-2.5 h-2.5 text-cyan-500"></i> Unit test tax logic
157
+ </button>
158
+ <button onclick="setPromptAndRun('Refactor calculate_total to use integer cents')" class="px-2 py-0.5 rounded bg-[var(--bg-card-subtle)] hover:bg-[var(--bg-card)] border border-[var(--border-main)] text-[var(--text-muted)] hover:text-[var(--text-main)] text-[11px] transition shrink-0 flex items-center gap-1">
159
+ <i data-lucide="wrench" class="w-2.5 h-2.5 text-emerald-500"></i> Refactor tax cents
160
+ </button>
161
+ </div>
162
+
163
+ <!-- Input Bar -->
164
+ <div class="p-3 bg-[var(--bg-app)] border-t border-[var(--border-main)]">
165
+ <!-- Mode Selector -->
166
+ <div class="flex items-center gap-2 mb-2">
167
+ <span class="text-zinc-500 text-[10px] font-mono shrink-0">Mode:</span>
168
+ <div class="inline-flex items-center p-0.5 bg-[var(--bg-card-subtle)] border border-[var(--border-main)] rounded-md font-sans">
169
+ <button onclick="setMode('chat', this)" id="mode-btn-chat" class="mode-btn px-2 py-0.5 text-[10px] font-medium rounded transition text-[var(--text-muted)] hover:text-[var(--text-main)]">Chat</button>
170
+ <button onclick="setMode('build', this)" id="mode-btn-build" class="mode-btn px-2 py-0.5 text-[10px] font-medium rounded transition text-[var(--text-muted)] hover:text-[var(--text-main)]">Build</button>
171
+ <button onclick="setMode('plan', this)" id="mode-btn-plan" class="mode-btn px-2 py-0.5 text-[10px] font-medium rounded transition text-[var(--text-muted)] hover:text-[var(--text-main)]">Plan</button>
172
+ <button onclick="setMode('hybrid', this)" id="mode-btn-hybrid" class="mode-btn px-2 py-0.5 text-[10px] font-medium rounded transition text-[var(--text-muted)] hover:text-[var(--text-main)]">Auto</button>
173
+ </div>
174
+ <span class="px-1.5 py-0.2 rounded bg-[var(--bg-card-subtle)] border border-[var(--border-main)] font-mono text-[10px] text-cyan-400" id="modeBadge">Mode: auto</span>
175
+ </div>
176
+ <div class="relative flex items-center bg-[var(--bg-input)] border border-[var(--border-main)] rounded-lg focus-within:border-cyan-500 focus-within:ring-1 focus-within:ring-cyan-500/20 transition shadow-inner">
177
+ <input id="chatInput" type="text" placeholder="Instruct local model to fix, refactor, or test (Enter to send)..." class="w-full bg-transparent px-3 py-2 text-xs text-[var(--text-main)] placeholder-zinc-500 outline-none" onkeydown="if(event.key==='Enter') handleUserSubmit()">
178
+ <button onclick="handleUserSubmit()" id="btnSendChat" class="mr-1.5 px-2.5 py-1 rounded bg-cyan-600 hover:bg-cyan-500 text-zinc-950 font-semibold text-xs transition flex items-center justify-center">
179
+ <span>Run</span>
180
+ </button>
181
+ </div>
182
+ </div>
183
+
184
+ </div>
185
+
186
+ <!-- Right: Split Code Diff Studio -->
187
+ <div class="w-[460px] lg:w-[540px] bg-[var(--bg-header)] flex flex-col h-full overflow-hidden">
188
+
189
+ <div class="h-9 px-3 border-b border-[var(--border-main)] flex items-center justify-between bg-[var(--bg-card-subtle)]">
190
+ <div class="flex items-center gap-2 text-xs font-mono text-[var(--text-main)]">
191
+ <i data-lucide="file-code" class="w-3 h-3 text-cyan-500"></i>
192
+ <span id="diffFileName">No active diff</span>
193
+ <span class="text-[9px] text-zinc-500 font-mono font-semibold" id="diffStatsTag">No proposal</span>
194
+ </div>
195
+
196
+ <div class="flex items-center gap-1 font-mono text-[10px]">
197
+ <button onclick="copyActiveDiff()" class="px-2 py-0.5 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-[var(--text-muted)] hover:text-[var(--text-main)] transition">
198
+ Copy
199
+ </button>
200
+ <button onclick="applyProposalAction()" id="btnApply" disabled class="px-2.5 py-0.5 rounded bg-emerald-600 hover:bg-emerald-500 text-zinc-950 font-semibold font-sans transition disabled:opacity-40 disabled:cursor-not-allowed">
201
+ Apply (Ctrl+A)
202
+ </button>
203
+ <button onclick="rollbackAction()" id="btnRollback" disabled class="px-2 py-0.5 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-[var(--text-muted)] hover:text-[var(--text-main)] transition disabled:opacity-40 disabled:cursor-not-allowed">
204
+ Rollback
205
+ </button>
206
+ </div>
207
+ </div>
208
+
209
+ <div id="diffContentArea" class="flex-1 overflow-y-auto font-mono text-[11px] leading-relaxed p-2 select-text">
210
+ <div class="p-8 text-center text-zinc-500 text-xs">No active diff proposal. Run a prompt or select a task session.</div>
211
+ </div>
212
+
213
+ <div class="p-2 bg-[var(--bg-app)] border-t border-[var(--border-main)] flex items-center justify-between text-xs font-mono text-[10px]">
214
+ <div class="flex items-center gap-1 text-emerald-500" id="diffEvidenceStatus">
215
+ <i data-lucide="check-circle-2" class="w-3 h-3"></i>
216
+ <span>Oracles: Not run</span>
217
+ </div>
218
+ <span class="text-zinc-500">Mediated Rollback Active</span>
219
+ </div>
220
+ </div>
221
+
222
+ </div>
223
+ """
224
+
225
+
226
+ def render_delegated_panel() -> str:
227
+ return """
228
+ <!-- TAB 2: DELEGATED TASKS MODE -->
229
+ <div id="view-delegated" class="tab-view flex-1 hidden h-full flex flex-col overflow-hidden">
230
+
231
+ <!-- Background Task Queue: submit + live list -->
232
+ <div class="px-5 py-3 border-b border-[var(--border-main)] bg-[var(--bg-sidebar)] space-y-2 shrink-0">
233
+ <div class="flex items-center justify-between">
234
+ <h2 class="text-xs font-semibold text-[var(--text-main)] flex items-center gap-1.5">
235
+ <i data-lucide="layers" class="w-3.5 h-3.5 text-cyan-500"></i>
236
+ <span>Background Task Queue</span>
237
+ </h2>
238
+ <span class="text-[10px] font-mono text-zinc-500">Runs sequentially in background</span>
239
+ </div>
240
+ <form id="taskQueueForm" onsubmit="submitQueuedTask(event)" class="space-y-1.5">
241
+ <textarea id="taskQueueGoal" rows="2" placeholder="Describe the atomic coding task to delegate..."
242
+ class="w-full bg-[var(--bg-input)] border border-[var(--border-main)] rounded px-2.5 py-1.5 text-xs text-[var(--text-main)] placeholder-zinc-500 outline-none focus:border-cyan-500 resize-y"></textarea>
243
+ <div class="flex flex-wrap items-center gap-1.5">
244
+ <input id="taskQueueFiles" type="text" placeholder="Files (comma-separated)"
245
+ class="flex-1 min-w-[140px] bg-[var(--bg-input)] border border-[var(--border-main)] rounded px-2 py-1 text-[11px] font-mono text-[var(--text-main)] placeholder-zinc-500 outline-none focus:border-cyan-500">
246
+ <input id="taskQueueChecks" type="text" placeholder="Checks (comma-separated commands)"
247
+ class="flex-1 min-w-[140px] bg-[var(--bg-input)] border border-[var(--border-main)] rounded px-2 py-1 text-[11px] font-mono text-[var(--text-main)] placeholder-zinc-500 outline-none focus:border-cyan-500">
248
+ <input id="taskQueueProfile" type="text" placeholder="Profile (default)"
249
+ class="w-36 bg-[var(--bg-input)] border border-[var(--border-main)] rounded px-2 py-1 text-[11px] font-mono text-[var(--text-main)] placeholder-zinc-500 outline-none focus:border-cyan-500">
250
+ <button type="submit" id="btnSubmitTask"
251
+ class="px-2.5 py-1 rounded bg-cyan-600 hover:bg-cyan-500 text-zinc-950 font-semibold text-[11px] transition">
252
+ Submit
253
+ </button>
254
+ </div>
255
+ </form>
256
+ <div id="taskQueueList" class="space-y-1 max-h-44 overflow-y-auto"></div>
257
+ </div>
258
+
259
+ <div class="flex-1 grid grid-cols-1 md:grid-cols-3 min-h-0 overflow-hidden">
260
+
261
+ <!-- Left: TaskEnvelope Card -->
262
+ <div class="p-5 bg-[var(--bg-sidebar)] border-r border-[var(--border-main)] space-y-3.5 overflow-y-auto text-xs">
263
+ <div class="flex items-center justify-between pb-2 border-b border-[var(--border-main)]">
264
+ <h2 class="text-xs font-semibold text-[var(--text-main)] flex items-center gap-1.5">
265
+ <i data-lucide="inbox" class="w-3.5 h-3.5 text-cyan-500"></i>
266
+ <span>Task Envelope</span>
267
+ </h2>
268
+ <span class="px-1.5 py-0.2 rounded bg-zinc-500/10 border border-zinc-500/30 text-zinc-500 font-mono text-[9px] font-semibold" id="delegatedStatusTag">
269
+ NO PROPOSAL
270
+ </span>
271
+ </div>
272
+
273
+ <div class="space-y-1">
274
+ <label class="text-[10px] font-mono uppercase text-zinc-500">Delegating Host Agent</label>
275
+ <div class="p-2 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-xs text-[var(--text-main)] font-mono flex items-center gap-1.5" id="delegatedAgent">
276
+ <i data-lucide="bot" class="w-3 h-3 text-purple-500"></i>
277
+ <span>Codex / Claude Code (MCP)</span>
278
+ </div>
279
+ </div>
280
+
281
+ <div class="space-y-1">
282
+ <label class="text-[10px] font-mono uppercase text-zinc-500">Task ID</label>
283
+ <div class="p-2 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-xs text-[var(--text-main)] font-mono" id="delegatedTaskId">
284
+ None
285
+ </div>
286
+ </div>
287
+
288
+ <div class="space-y-1">
289
+ <label class="text-[10px] font-mono uppercase text-zinc-500">Goal</label>
290
+ <div class="p-2 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-xs text-[var(--text-main)] leading-relaxed" id="delegatedGoal">
291
+ No delegated task selected.
292
+ </div>
293
+ </div>
294
+
295
+ <div class="space-y-1">
296
+ <label class="text-[10px] font-mono uppercase text-zinc-500">Allowlisted Files</label>
297
+ <div class="p-2 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-xs text-cyan-500 font-mono" id="delegatedFiles">
298
+ -
299
+ </div>
300
+ </div>
301
+
302
+ <div class="space-y-1">
303
+ <label class="text-[10px] font-mono uppercase text-zinc-500">Targeted Checks</label>
304
+ <div class="p-2 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-xs text-emerald-500 font-mono" id="delegatedChecks">
305
+ Not selected
306
+ </div>
307
+ </div>
308
+
309
+ <div class="pt-2 space-y-1.5">
310
+ <button onclick="applyProposalAction()" id="btnDelegatedApply" disabled class="w-full py-1.5 rounded bg-emerald-600 hover:bg-emerald-500 text-zinc-950 font-semibold text-xs transition flex items-center justify-center gap-1.5 disabled:opacity-40 disabled:cursor-not-allowed">
311
+ <i data-lucide="check" class="w-3.5 h-3.5"></i>
312
+ <span>Apply Proposal (Ctrl+A)</span>
313
+ </button>
314
+ <button onclick="rollbackAction()" id="btnDelegatedRollback" disabled class="w-full py-1.5 rounded bg-[var(--bg-card)] hover:border-zinc-500 border border-[var(--border-main)] text-[var(--text-muted)] font-medium text-xs transition flex items-center justify-center gap-1.5 disabled:opacity-40 disabled:cursor-not-allowed">
315
+ <i data-lucide="rotate-ccw" class="w-3.5 h-3.5"></i>
316
+ <span>Auto-Rollback (git restore)</span>
317
+ </button>
318
+ </div>
319
+ </div>
320
+
321
+ <!-- Right: Monaco Split Diff -->
322
+ <div class="col-span-2 bg-[var(--bg-app)] flex flex-col h-full overflow-hidden">
323
+ <div class="h-9 px-3 border-b border-[var(--border-main)] flex items-center justify-between bg-[var(--bg-card-subtle)]">
324
+ <div class="text-xs font-mono text-[var(--text-main)] flex items-center gap-1.5">
325
+ <i data-lucide="file-code" class="w-3 h-3 text-cyan-500"></i>
326
+ <span id="delegatedFileName">No proposal selected</span>
327
+ <span class="text-zinc-500 text-[10px]" id="delegatedProposalLabel">(Not applied)</span>
328
+ </div>
329
+ </div>
330
+
331
+ <div id="delegatedDiffContent" class="flex-1 overflow-y-auto p-3 font-mono text-[11px] leading-relaxed select-text space-y-0.5">
332
+ <div class="p-8 text-center text-zinc-500">Select an agent delegated session on the left to inspect diff.</div>
333
+ </div>
334
+
335
+ <div class="p-2.5 bg-[var(--bg-header)] border-t border-[var(--border-main)] flex items-center justify-between text-xs">
336
+ <div class="flex items-center gap-1.5 text-emerald-500 font-mono text-[11px]">
337
+ <i data-lucide="shield-check" class="w-3.5 h-3.5"></i>
338
+ <span id="delegatedEvidenceTag">Evidence: Not run</span>
339
+ </div>
340
+ </div>
341
+ </div>
342
+
343
+ </div>
344
+ </div>
345
+ """
346
+
347
+
348
+ def render_modals() -> str:
349
+ _HTML = """
350
+ <!-- MODAL 1: GPU & NVIDIA-SMI TELEMETRY DIALOG -->
351
+ <div id="gpuModal" class="modal-dialog fixed inset-0 bg-black/60 backdrop-blur-xs flex items-center justify-center p-4 z-50 hidden">
352
+ <div class="bg-[var(--bg-card)] border border-[var(--border-main)] rounded-xl w-full max-w-md overflow-hidden shadow-xl">
353
+ <div class="px-4 py-3 border-b border-[var(--border-main)] flex items-center justify-between bg-[var(--bg-card-subtle)]">
354
+ <div class="flex items-center gap-2">
355
+ <i data-lucide="hard-drive" class="w-3.5 h-3.5 text-emerald-500"></i>
356
+ <h3 class="text-xs font-semibold text-[var(--text-main)]">GPU &amp; VRAM Hardware Cockpit</h3>
357
+ </div>
358
+ <button onclick="closeModals()" class="p-1 rounded hover:bg-zinc-800/40 text-zinc-400 hover:text-zinc-200"><i data-lucide="x" class="w-3.5 h-3.5"></i></button>
359
+ </div>
360
+
361
+ <div class="p-4 space-y-3.5 text-xs">
362
+ <div class="p-3 rounded-lg bg-[var(--bg-app)] border border-[var(--border-main)] space-y-2">
363
+ <div class="flex items-center justify-between">
364
+ <span class="text-xs font-bold text-[var(--text-main)]" id="gpuDeviceName">NVIDIA GPU</span>
365
+ <span class="px-1.5 py-0.2 rounded bg-emerald-500/10 border border-emerald-500/30 text-emerald-500 font-mono text-[9px]">nvidia-smi</span>
366
+ </div>
367
+
368
+ <div class="flex items-center justify-between text-[11px]">
369
+ <span class="text-zinc-400">VRAM Usage:</span>
370
+ <span class="font-mono font-bold text-[var(--text-main)]" id="gpuVramText">VRAM unavailable</span>
371
+ </div>
372
+
373
+ <div class="w-full h-2 rounded bg-[var(--bg-card-subtle)] overflow-hidden">
374
+ <div class="h-full bg-emerald-500 rounded transition-all duration-300" id="gpuVramBar" style="width: 0%"></div>
375
+ </div>
376
+
377
+ <div class="grid grid-cols-2 gap-2 pt-1 font-mono text-[10px] text-zinc-400 border-t border-[var(--border-main)]">
378
+ <div>GPU Load: <span class="text-[var(--text-main)] font-semibold" id="gpuLoadPct">—</span></div>
379
+ <div>Temp: <span class="text-[var(--text-main)] font-semibold" id="gpuTemp">—</span></div>
380
+ </div>
381
+ </div>
382
+
383
+ <div class="flex items-center justify-between pt-1">
384
+ <button onclick="warmupActiveModel()" id="btnPreloadModel" class="px-2.5 py-1 rounded bg-[var(--bg-card-subtle)] hover:bg-[var(--bg-card)] border border-[var(--border-main)] text-[10px] font-medium text-[var(--text-main)] transition">
385
+ ⚡ Preload Model
386
+ </button>
387
+ <button onclick="unloadAllVram()" id="btnUnloadVram" class="px-2.5 py-1 rounded bg-red-500/10 hover:bg-red-500/20 border border-red-500/30 text-red-400 text-[10px] font-medium transition">
388
+ Eject ALL from VRAM
389
+ </button>
390
+ </div>
391
+ </div>
392
+
393
+ <div class="px-4 py-2 border-t border-[var(--border-main)] bg-[var(--bg-card-subtle)] flex justify-end">
394
+ <button onclick="closeModals()" class="px-3 py-1 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-xs font-medium text-[var(--text-main)]">Done</button>
395
+ </div>
396
+ </div>
397
+ </div>
398
+
399
+ <!-- MODAL 2: MODEL & PROFILE SELECTOR DIALOG -->
400
+ <div id="modelModal" class="modal-dialog fixed inset-0 bg-black/60 backdrop-blur-xs flex items-center justify-center p-4 z-50 hidden">
401
+ <div class="bg-[var(--bg-card)] border border-[var(--border-main)] rounded-xl w-full max-w-md overflow-hidden shadow-xl">
402
+ <div class="px-4 py-3 border-b border-[var(--border-main)] flex items-center justify-between bg-[var(--bg-card-subtle)]">
403
+ <div class="flex items-center gap-2">
404
+ <i data-lucide="cpu" class="w-3.5 h-3.5 text-cyan-500"></i>
405
+ <h3 class="text-xs font-semibold text-[var(--text-main)]">Model &amp; Profile Selector</h3>
406
+ </div>
407
+ <button onclick="closeModals()" class="p-1 rounded hover:bg-zinc-800/40 text-zinc-400 hover:text-zinc-200"><i data-lucide="x" class="w-3.5 h-3.5"></i></button>
408
+ </div>
409
+
410
+ <div class="p-4 space-y-3.5 text-xs">
411
+ <div class="space-y-1.5">
412
+ <div class="flex items-center justify-between">
413
+ <label class="text-[11px] font-medium text-[var(--text-muted)]">Active Model Profile</label>
414
+ <button onclick="fetchAndPopulateModels()" id="btnRefreshModels" class="text-[10px] text-cyan-500 hover:underline flex items-center gap-1">
415
+ <i data-lucide="refresh-cw" class="w-2.5 h-2.5"></i> <span>Refresh List</span>
416
+ </button>
417
+ </div>
418
+ <select id="modalProfileSelect" onchange="changeProfile(this.value)" class="w-full bg-[var(--bg-app)] border border-[var(--border-main)] rounded px-2.5 py-2 text-xs text-[var(--text-main)] outline-none focus:border-cyan-500 font-mono">
419
+ <option value="qwen2.5-coder">Loading discovered models...</option>
420
+ </select>
421
+ </div>
422
+
423
+ <div class="space-y-1.5">
424
+ <label class="text-[11px] font-medium text-[var(--text-muted)]">Context Window Override <span class="text-zinc-500">(tokens, optional)</span></label>
425
+ <input id="modalCtxInput" type="number" min="512" step="512" placeholder="profile default" onchange="onCtxOverrideChange(this.value)" class="w-full bg-[var(--bg-app)] border border-[var(--border-main)] rounded px-2.5 py-2 text-xs text-[var(--text-main)] outline-none focus:border-cyan-500 font-mono">
426
+ <div class="text-[10px] text-zinc-500">For llama-server models the backend restarts with the new -c on your next prompt. Use this when you hit "model context too small".</div>
427
+ </div>
428
+
429
+ <div class="p-3 rounded bg-[var(--bg-app)] border border-[var(--border-main)] space-y-1 font-mono text-[10px] text-zinc-400">
430
+ <div class="flex justify-between"><span>Provider:</span><span class="text-[var(--text-main)] font-semibold" id="profProvider">ollama</span></div>
431
+ <div class="flex justify-between"><span>Context Limit:</span><span class="text-[var(--text-main)] font-semibold" id="profCtx">8192 tokens</span></div>
432
+ <div class="flex justify-between"><span>Endpoint:</span><span class="text-cyan-400 font-semibold" id="profEndpoint">http://127.0.0.1:11434</span></div>
433
+ </div>
434
+
435
+ <div class="pt-1 flex items-center justify-between">
436
+ <button onclick="warmupActiveModel()" id="btnModalPreload" class="px-2.5 py-1 rounded bg-[var(--bg-card-subtle)] hover:bg-[var(--bg-card)] border border-[var(--border-main)] text-[10px] font-medium text-[var(--text-main)] transition flex items-center gap-1.5">
437
+ <span>⚡ Load into VRAM</span>
438
+ </button>
439
+ <span class="text-[10px] text-zinc-500 font-mono" id="labelModelLoadStatus"></span>
440
+ </div>
441
+ </div>
442
+
443
+ <div class="px-4 py-2 border-t border-[var(--border-main)] bg-[var(--bg-card-subtle)] flex justify-end">
444
+ <button onclick="closeModals()" class="px-3 py-1 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-xs font-medium text-[var(--text-main)]">Apply</button>
445
+ </div>
446
+ </div>
447
+ </div>
448
+
449
+ <!-- MODAL 3: INFERENCE SERVER PROCESS MANAGER DIALOG -->
450
+ <div id="serverModal" class="modal-dialog fixed inset-0 bg-black/60 backdrop-blur-xs flex items-center justify-center p-4 z-50 hidden">
451
+ <div class="bg-[var(--bg-card)] border border-[var(--border-main)] rounded-xl w-full max-w-md overflow-hidden shadow-xl">
452
+ <div class="px-4 py-3 border-b border-[var(--border-main)] flex items-center justify-between bg-[var(--bg-card-subtle)]">
453
+ <div class="flex items-center gap-2">
454
+ <i data-lucide="server" class="w-3.5 h-3.5 text-amber-500"></i>
455
+ <h3 class="text-xs font-semibold text-[var(--text-main)]">Local Inference Servers</h3>
456
+ </div>
457
+ <button onclick="closeModals()" class="p-1 rounded hover:bg-zinc-800/40 text-zinc-400 hover:text-zinc-200"><i data-lucide="x" class="w-3.5 h-3.5"></i></button>
458
+ </div>
459
+
460
+ <div class="p-4 space-y-3 text-xs">
461
+ <div class="p-2.5 rounded bg-[var(--bg-app)] border border-[var(--border-main)]">
462
+ <div class="font-medium text-[11px] flex items-center gap-1.5">
463
+ <span class="w-2 h-2 rounded-full bg-zinc-500" id="dotOllama"></span>
464
+ <span>Ollama Engine (:11434)</span>
465
+ </div>
466
+ <div class="text-[10px] text-zinc-500 font-mono mt-0.5" id="labelOllamaStatus">Checking...</div>
467
+ </div>
468
+
469
+ <div class="p-2.5 rounded bg-[var(--bg-app)] border border-[var(--border-main)]">
470
+ <div class="font-medium text-[11px] flex items-center gap-1.5">
471
+ <span class="w-2 h-2 rounded-full bg-zinc-500" id="dotLlama"></span>
472
+ <span>llama-server Engine (:8080)</span>
473
+ </div>
474
+ <div class="text-[10px] text-zinc-500 font-mono mt-0.5" id="labelLlamaStatus">Checking...</div>
475
+ </div>
476
+
477
+ <p class="text-[10px] text-zinc-500 leading-relaxed">
478
+ Servers launch together with a model. Pick a model in the Model &amp; Profile Selector and press Load into VRAM — the matching engine starts automatically.
479
+ </p>
480
+ </div>
481
+
482
+ <div class="px-4 py-2 border-t border-[var(--border-main)] bg-[var(--bg-card-subtle)] flex justify-end">
483
+ <button onclick="closeModals()" class="px-3 py-1 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-xs font-medium text-[var(--text-main)]">Close</button>
484
+ </div>
485
+ </div>
486
+ </div>
487
+
488
+ <!-- MODAL 4: SYSTEM SETTINGS & MODEL SCANNER DIALOG -->
489
+ <div id="settingsModal" class="modal-dialog fixed inset-0 bg-black/60 backdrop-blur-xs flex items-center justify-center p-4 z-50 hidden">
490
+ <div class="bg-[var(--bg-card)] border border-[var(--border-main)] rounded-xl w-full max-w-md overflow-hidden shadow-xl">
491
+ <div class="px-4 py-3 border-b border-[var(--border-main)] flex items-center justify-between bg-[var(--bg-card-subtle)]">
492
+ <div class="flex items-center gap-2">
493
+ <i data-lucide="settings" class="w-3.5 h-3.5 text-cyan-500"></i>
494
+ <h3 class="text-xs font-semibold text-[var(--text-main)]">Preferences &amp; Model Registry</h3>
495
+ </div>
496
+ <button onclick="closeModals()" class="p-1 rounded hover:bg-zinc-800/40 text-zinc-400 hover:text-zinc-200"><i data-lucide="x" class="w-3.5 h-3.5"></i></button>
497
+ </div>
498
+
499
+ <div class="p-4 space-y-3 text-xs">
500
+ <!-- Model Scanner Action Card -->
501
+ <div class="p-3 rounded-lg bg-[var(--bg-app)] border border-[var(--border-main)] space-y-2">
502
+ <div class="flex items-center justify-between">
503
+ <span class="text-xs font-medium text-[var(--text-main)] flex items-center gap-1.5">
504
+ <i data-lucide="search" class="w-3 h-3 text-cyan-500"></i> Universal Model Scanner
505
+ </span>
506
+ <button onclick="triggerModelScan(true)" id="btnDeepScan" class="px-2 py-0.5 rounded bg-cyan-600 hover:bg-cyan-500 text-zinc-950 font-semibold text-[10px] transition">
507
+ Deep Drive Scan
508
+ </button>
509
+ </div>
510
+ <p class="text-[10px] text-zinc-500 leading-relaxed">
511
+ Scans all system drives for local GGUF models. Discovered models are saved to <code class="font-mono text-zinc-400">~/.local_coding_agent/models.json</code> without leaking off-machine.
512
+ </p>
513
+ <div class="flex items-center gap-1 pt-1">
514
+ <input id="inputCustomDir" type="text" placeholder="Add custom folder (e.g. D:/my_models)..." class="flex-1 bg-[var(--bg-card)] border border-[var(--border-main)] rounded px-2 py-1 text-[10px] text-[var(--text-main)] outline-none focus:border-cyan-500 font-mono">
515
+ <button onclick="addCustomModelFolder()" class="px-2.5 py-1 rounded bg-[var(--bg-card-subtle)] hover:bg-[var(--bg-card)] border border-[var(--border-main)] text-[10px] font-semibold text-zinc-300 transition">
516
+ + Add
517
+ </button>
518
+ </div>
519
+ </div>
520
+
521
+ <!-- Doctor Action Card -->
522
+ <div class="p-3 rounded-lg bg-[var(--bg-app)] border border-[var(--border-main)] flex items-center justify-between">
523
+ <div>
524
+ <div class="text-xs font-medium text-[var(--text-main)]">Self-Healing Doctor (doctor --fix)</div>
525
+ <div class="text-[10px] text-zinc-500 font-mono">Sync MCP configs &amp; IDE skills.</div>
526
+ </div>
527
+ <button onclick="runDoctorCheck()" id="btnDoctor" class="px-2.5 py-1 rounded bg-[var(--bg-card)] hover:border-zinc-500 border border-[var(--border-main)] text-[11px] font-medium text-[var(--text-main)] transition">
528
+ Run Doctor
529
+ </button>
530
+ </div>
531
+
532
+ <div class="p-3 rounded-lg bg-[var(--bg-app)] border border-[var(--border-main)] space-y-1 font-mono text-[10px] text-zinc-400">
533
+ <div class="text-[11px] font-semibold text-[var(--text-main)] font-sans mb-1">Workspace Environment</div>
534
+ <div>Path: <span class="text-zinc-300" id="setWorkspacePath">.</span></div>
535
+ <div>Harness Core: <span class="text-emerald-400">v%VERSION% (R23 Desktop)</span></div>
536
+ </div>
537
+ </div>
538
+
539
+ <div class="px-4 py-2 border-t border-[var(--border-main)] bg-[var(--bg-card-subtle)] flex justify-end">
540
+ <button onclick="closeModals()" class="px-3 py-1 rounded bg-[var(--bg-card)] border border-[var(--border-main)] text-xs font-medium text-[var(--text-main)]">Done</button>
541
+ </div>
542
+ </div>
543
+ </div>
544
+ """
545
+ return _with_version(_HTML)
546
+
547
+
548
+ def render_toast() -> str:
549
+ return """
550
+ <!-- Notification Toast -->
551
+ <div id="toast" class="fixed bottom-4 right-4 px-3.5 py-2 rounded-md bg-[var(--bg-card)] border border-[var(--border-main)] text-[var(--text-main)] text-xs shadow-lg flex items-center gap-1.5 transform translate-y-2 opacity-0 transition-all pointer-events-none z-50 font-mono text-[11px]">
552
+ <i data-lucide="check" class="w-3 h-3 text-emerald-500"></i>
553
+ <span id="toastText">Action completed</span>
554
+ </div>
555
+ """
@@ -0,0 +1,37 @@
1
+ """Desktop API and Webview HTTP Server for Local AI Coding Harness.
2
+
3
+ Provides real hardware GPU telemetry directly from nvidia-smi, multi-backend probing
4
+ (Ollama & llama-server), live model discovery, server process controls, model load/unload
5
+ VRAM management, real workspace file introspection, and mediated execution with auto-rollback.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ # Re-exported module references so tests that patch
11
+ # `local_coding_agent.desktop.server.os` / `.subprocess` keep working.
12
+ import os
13
+ import subprocess
14
+
15
+ from ._handlers import DesktopRequestHandler
16
+ from ._models import (
17
+ _classify_backend_error,
18
+ discover_local_ollama_models,
19
+ find_discovered_gguf,
20
+ profile_model_is_available,
21
+ resolve_model_profile,
22
+ select_available_profile,
23
+ )
24
+ from ._server import DesktopServer
25
+ from ._telemetry import get_nvidia_gpu_telemetry
26
+
27
+ __all__ = [
28
+ "DesktopServer",
29
+ "DesktopRequestHandler",
30
+ "get_nvidia_gpu_telemetry",
31
+ "discover_local_ollama_models",
32
+ "find_discovered_gguf",
33
+ "resolve_model_profile",
34
+ "_classify_backend_error",
35
+ "profile_model_is_available",
36
+ "select_available_profile",
37
+ ]