codehere 0.6.0-alpha.1 → 0.6.1

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.
@@ -0,0 +1,505 @@
1
+ const SHARED_STYLES = `
2
+ :root {
3
+ --bg: hsl(0 0% 3.9%); --bg-1: hsl(0 0% 6%); --bg-2: hsl(0 0% 9%);
4
+ --fg: hsl(0 0% 98%); --fg-70: rgba(255,255,255,0.70);
5
+ --fg-50: rgba(255,255,255,0.50); --fg-40: rgba(255,255,255,0.40);
6
+ --fg-30: rgba(255,255,255,0.30); --fg-20: rgba(255,255,255,0.20);
7
+ --border: rgba(255,255,255,0.10); --border-soft: rgba(255,255,255,0.06);
8
+ --accent: #ff9a76; --accent-wash: rgba(255,154,118,0.10);
9
+ --allow: #28c840; --block: #ff5f57; --warn: #febc2e;
10
+ --radius: 8px;
11
+ --sans: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
12
+ --mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
13
+ }
14
+ * { box-sizing: border-box; margin: 0; }
15
+ body {
16
+ margin: 0; background: var(--bg); color: var(--fg);
17
+ font-family: var(--sans); font-size: 13px; line-height: 1.55;
18
+ -webkit-font-smoothing: antialiased; height: 100vh; overflow: hidden;
19
+ }
20
+ .app { height: 100vh; display: flex; flex-direction: column; }
21
+
22
+ .header {
23
+ display: flex; align-items: center; gap: 16px;
24
+ padding: 10px 20px; border-bottom: 1px solid var(--border-soft);
25
+ background: rgba(0,0,0,0.3); flex-shrink: 0;
26
+ }
27
+ .logo { font-family: var(--mono); font-size: 14px; font-weight: 600; color: var(--fg); }
28
+ .budget { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--fg-50); }
29
+ .budget.safe { color: var(--fg-50); }
30
+ .budget.warn { color: var(--warn); }
31
+ .budget.block { color: var(--block); font-weight: 600; }
32
+ .v1-link {
33
+ font-family: var(--mono); font-size: 11px; color: var(--fg-40);
34
+ text-decoration: none; padding: 4px 10px; border: 1px solid var(--border);
35
+ border-radius: 6px;
36
+ }
37
+ .v1-link:hover { color: var(--fg); border-color: var(--fg-30); }
38
+
39
+ .squad-row {
40
+ display: flex; align-items: center; gap: 12px;
41
+ padding: 6px 20px; border-bottom: 1px solid var(--border-soft);
42
+ font-size: 11px; color: var(--fg-50); flex-shrink: 0;
43
+ }
44
+ .squad-agent { display: flex; align-items: center; gap: 4px; }
45
+ .squad-dot { width: 6px; height: 6px; border-radius: 50%; }
46
+ .squad-dot.ready { background: var(--allow); }
47
+ .squad-dot.busy { background: var(--warn); }
48
+ .squad-dot.offline { background: var(--fg-30); }
49
+
50
+ .bar {
51
+ display: flex; align-items: center; gap: 8px;
52
+ padding: 10px 20px; border-bottom: 1px solid var(--border-soft);
53
+ flex-shrink: 0;
54
+ }
55
+ .new-btn {
56
+ background: var(--accent); color: #000; font-weight: 600;
57
+ border: none; border-radius: 6px; padding: 6px 14px;
58
+ font-family: var(--sans); font-size: 12px; cursor: pointer; white-space: nowrap;
59
+ }
60
+ .new-btn:hover { filter: brightness(1.05); }
61
+
62
+ .filter-tab {
63
+ font-family: var(--mono); font-size: 11px; color: var(--fg-50);
64
+ cursor: pointer; padding: 4px 10px; border-radius: 6px;
65
+ border: 1px solid transparent; white-space: nowrap;
66
+ }
67
+ .filter-tab:hover { color: var(--fg); background: rgba(255,255,255,0.04); }
68
+ .filter-tab.active { color: var(--fg); background: rgba(255,154,118,0.08); border-color: rgba(255,154,118,0.25); }
69
+ .filter-tab .cnt { color: var(--fg-40); margin-left: 2px; }
70
+
71
+ .content { flex: 1; overflow-y: auto; }
72
+ .content-inner { padding: 16px 20px; max-width: 900px; margin: 0 auto; }
73
+ .section-label {
74
+ font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em;
75
+ color: var(--fg-40); text-transform: uppercase; margin: 16px 0 8px;
76
+ }
77
+ .section-label:first-child { margin-top: 0; }
78
+
79
+ .task-card {
80
+ background: rgba(255,255,255,0.02); border: 1px solid var(--border);
81
+ border-radius: 8px; padding: 12px 14px; margin-bottom: 6px;
82
+ cursor: pointer; transition: border-color 0.15s, background 0.15s;
83
+ }
84
+ .task-card:hover { border-color: var(--fg-30); background: rgba(255,255,255,0.04); }
85
+ .card-row1 { display: flex; align-items: center; gap: 8px; }
86
+ .status-icon { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
87
+ .status-icon.running { background: var(--accent); animation: pulse 1.5s ease infinite; }
88
+ .status-icon.paused { background: var(--warn); }
89
+ .status-icon.done { background: var(--allow); }
90
+ .status-icon.failed { background: var(--block); }
91
+ .status-icon.crashed { background: var(--block); }
92
+ .status-icon.killed { background: var(--fg-30); }
93
+ .status-icon.queued { background: var(--fg-30); }
94
+ .status-icon.blocked { background: var(--warn); }
95
+ @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
96
+
97
+ .card-title { font-weight: 600; color: var(--fg); font-size: 13px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
98
+ .card-agent { font-family: var(--mono); font-size: 10px; color: var(--fg-50); background: rgba(255,255,255,0.04); padding: 2px 6px; border-radius: 4px; }
99
+ .card-row2 { display: flex; align-items: center; gap: 10px; margin-top: 6px; font-family: var(--mono); font-size: 10px; color: var(--fg-40); }
100
+ .card-row3 { margin-top: 6px; display: flex; gap: 6px; }
101
+ .card-btn {
102
+ font-family: var(--mono); font-size: 10px; padding: 3px 8px;
103
+ border: 1px solid var(--border); border-radius: 4px; cursor: pointer;
104
+ background: transparent; color: var(--fg-50);
105
+ }
106
+ .card-btn:hover { color: var(--fg); border-color: var(--fg-30); }
107
+ .card-btn.kill:hover { color: var(--block); border-color: var(--block); }
108
+ .card-btn.pause:hover { color: var(--warn); border-color: var(--warn); }
109
+ .card-btn.verify:hover { color: var(--allow); border-color: var(--allow); }
110
+ .card-btn.restart:hover { color: var(--accent); border-color: var(--accent); }
111
+ .verify-badge { font-family: var(--mono); font-size: 9px; padding: 1px 5px; border-radius: 4px; }
112
+ .verify-badge.verified { color: var(--allow); background: rgba(40,200,64,0.10); }
113
+ .verify-badge.unverified { color: var(--fg-30); background: rgba(255,255,255,0.04); }
114
+ .verify-badge.failed { color: var(--block); background: rgba(255,95,87,0.10); }
115
+
116
+ .empty-state { text-align: center; padding: 60px 20px; color: var(--fg-40); }
117
+ .empty-state h2 { color: var(--fg-70); margin-bottom: 8px; font-weight: 500; font-size: 15px; }
118
+
119
+ .skeleton { padding: 16px 20px; max-width: 900px; margin: 0 auto; }
120
+ .skeleton-card {
121
+ height: 60px; border-radius: 8px; margin-bottom: 8px;
122
+ background: linear-gradient(90deg, rgba(255,255,255,0.02) 25%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0.02) 75%);
123
+ background-size: 200% 100%; animation: shimmer 1.5s ease infinite;
124
+ }
125
+ @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
126
+
127
+ .detail-header {
128
+ display: flex; align-items: center; gap: 10px; margin-bottom: 12px;
129
+ }
130
+ .back-btn {
131
+ font-family: var(--mono); font-size: 12px; color: var(--accent);
132
+ cursor: pointer; padding: 4px 8px; border-radius: 4px;
133
+ border: 1px solid rgba(255,154,118,0.2);
134
+ }
135
+ .back-btn:hover { background: var(--accent-wash); }
136
+ .detail-title { font-weight: 600; font-size: 15px; flex: 1; }
137
+ .detail-meta { font-family: var(--mono); font-size: 11px; color: var(--fg-50); margin-bottom: 16px; }
138
+ .detail-actions { display: flex; gap: 6px; }
139
+
140
+ .panel {
141
+ border: 1px solid var(--border); border-radius: 8px;
142
+ padding: 12px 14px; margin-bottom: 12px;
143
+ }
144
+ .panel-title { font-family: var(--mono); font-size: 10px; color: var(--fg-40); text-transform: uppercase; margin-bottom: 8px; }
145
+ .outcome-item { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--fg-70); margin-bottom: 4px; }
146
+ .outcome-item .check { color: var(--allow); }
147
+ .outcome-item .cross { color: var(--block); }
148
+ .outcome-item .pending { color: var(--fg-40); }
149
+ .progress-dots { display: flex; gap: 3px; margin: 8px 0; }
150
+ .pdot { width: 10px; height: 10px; border-radius: 50%; border: 1px solid var(--border); }
151
+ .pdot.done { background: var(--allow); border-color: var(--allow); }
152
+ .pdot.current { background: var(--accent); border-color: var(--accent); animation: pulse 1.5s ease infinite; }
153
+ .pdot.pending { background: transparent; }
154
+
155
+ .verdict { font-family: var(--mono); font-size: 11px; margin-top: 8px; }
156
+ .verdict.unverified { color: var(--fg-40); }
157
+ .verdict.verified { color: var(--allow); }
158
+ .verdict.failed { color: var(--block); }
159
+
160
+ details.collapsible { margin-bottom: 12px; }
161
+ details.collapsible summary {
162
+ font-family: var(--mono); font-size: 11px; color: var(--fg-50);
163
+ cursor: pointer; padding: 8px 0; user-select: none;
164
+ }
165
+ details.collapsible summary:hover { color: var(--fg); }
166
+ .turn { padding: 6px 0; border-bottom: 1px solid var(--border-soft); font-size: 12px; }
167
+ .turn.user { color: var(--accent); }
168
+ .turn.agent { color: var(--fg-70); }
169
+ .tc { font-size: 11px; padding: 4px 0; color: var(--fg-50); font-family: var(--mono); }
170
+ .tc code { color: var(--accent); }
171
+
172
+ .input-area {
173
+ border-top: 1px solid var(--border-soft); padding: 12px 20px;
174
+ background: var(--bg-1); flex-shrink: 0;
175
+ }
176
+ .input-bar {
177
+ display: flex; align-items: center; gap: 10px;
178
+ background: var(--bg-2); border: 1px solid var(--border);
179
+ border-radius: 10px; padding: 10px 14px; max-width: 900px; margin: 0 auto;
180
+ }
181
+ .input-bar:focus-within { border-color: rgba(255,154,118,0.4); }
182
+ .input-bar.warn { border-color: var(--warn); }
183
+ .input-bar.block { border-color: var(--block); }
184
+ .input-ta {
185
+ flex: 1; background: transparent; border: none;
186
+ color: var(--fg); font-family: var(--sans); font-size: 13px;
187
+ resize: none; outline: none; height: 22px; line-height: 22px;
188
+ }
189
+ .input-ta::placeholder { color: var(--fg-40); }
190
+ .input-ta:disabled { color: var(--fg-30); }
191
+ .input-meta { font-family: var(--mono); font-size: 10px; color: var(--fg-40); white-space: nowrap; }
192
+ .send-btn {
193
+ background: var(--accent); color: #000; border: none; border-radius: 6px;
194
+ width: 28px; height: 28px; display: flex; align-items: center;
195
+ justify-content: center; cursor: pointer; flex-shrink: 0;
196
+ transition: transform 0.2s;
197
+ }
198
+ .send-btn:hover { filter: brightness(1.05); }
199
+ .send-btn:disabled { opacity: 0.35; cursor: default; }
200
+ .offline-banner {
201
+ text-align: center; padding: 4px; font-size: 10px; font-family: var(--mono);
202
+ color: var(--warn); background: rgba(254,188,46,0.06);
203
+ border-bottom: 1px solid rgba(254,188,46,0.15);
204
+ }
205
+ ::-webkit-scrollbar { width: 6px; }
206
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
207
+ `;
208
+ const APP_SCRIPT = `
209
+ const _cached = {};
210
+ let _filter = 'all', _detail = null, _offline = false;
211
+ const ICONS = { queued:'\\u23F3', running:'\\u25CF', paused:'\\u23F8', blocked:'\\uD83D\\uDD12', done:'\\u2713', failed:'\\u2715', crashed:'!', killed:'\\u2298' };
212
+ const STAT_ORDER = ['running','queued','paused','blocked','done','failed','crashed','killed'];
213
+ const FILTERS = { all: s=>true, running: s=>['running','queued','paused','blocked'].includes(s.status), done: s=>['done','completed'].includes(s.status), failed: s=>['failed','crashed','killed','error'].includes(s.status) };
214
+
215
+ function ms(t) { return t?'\\u2713':'\\u2715'; }
216
+ function dur(ms) { if(!ms&&ms!==0)return''; const s=Math.floor(ms/1000); if(s<60)return s+'s'; return Math.floor(s/60)+'m '+(s%60)+'s'; }
217
+ function title(p) { return (p||'Untitled').split('\\n')[0].slice(0,80); }
218
+ function allS(d) { const sp=d.spawned||d.sessions||[], pe=d.persisted||[], seen=new Set(sp.map(s=>s.sessionId)); return [...sp,...pe.filter(p=>!seen.has(p.sessionId))]; }
219
+ function cardActions(s) {
220
+ const m={
221
+ running:'<button class="card-btn pause" onclick="event.stopPropagation();a(\\'pause\\',\\''+s+'\\')">Pause</button><button class="card-btn kill" onclick="event.stopPropagation();a(\\'kill\\',\\''+s+'\\')">Kill</button>',
222
+ paused:'<button class="card-btn" onclick="event.stopPropagation();a(\\'resume\\',\\''+s+'\\')">Resume</button><button class="card-btn kill" onclick="event.stopPropagation();a(\\'kill\\',\\''+s+'\\')">Kill</button>',
223
+ queued:'<button class="card-btn kill" onclick="event.stopPropagation();a(\\'kill\\',\\''+s+'\\')">Cancel</button>',
224
+ crashed:'<button class="card-btn restart" onclick="event.stopPropagation();a(\\'restart\\',\\''+s+'\\')">Restart</button>',
225
+ failed:'<button class="card-btn restart" onclick="event.stopPropagation();a(\\'restart\\',\\''+s+'\\')">Restart</button>',
226
+ blocked:'<button class="card-btn kill" onclick="event.stopPropagation();a(\\'kill\\',\\''+s+'\\')">Cancel</button>',
227
+ };
228
+ return m[mapped(s)]||'';
229
+ }
230
+ function statIcon(s) { return '<span class="status-icon '+s+'"></span>'; }
231
+ function verB(v) { return v&&v!=='unverified'?'<span class="verify-badge '+v+'">'+v+'</span>':''; }
232
+ function tagHtml(t) { return t.map(x=>'<span style="font-size:10px;color:var(--fg-40);font-family:var(--mono)">'+x+'</span>').join(''); }
233
+
234
+ function st(s) {
235
+ if(s==='completed')return'done'; if(s==='error'||s==='killed')return'failed'; if(s==='paused')return'paused'; return s;
236
+ }
237
+ function aStat(s) { return s==='blocked'?'paused':s==='killed'?'failed':s==='crashed'?'failed':st(s); }
238
+ function mapped(s) { return s==='queued'?'running':s==='blocked'?'paused':aStat(s); }
239
+
240
+ function findS(id) { const s=allS(_cached.tasks||{}); return s.find(x=>x.sessionId===id)||null; }
241
+
242
+ function render() {
243
+ if(_detail){ const t=findS(_detail); if(t)renderD(t); else{ _detail=null; renderL(); } }
244
+ else renderL();
245
+ upCnt(); upBud(); upSq();
246
+ }
247
+
248
+ function renderL() {
249
+ const c=document.getElementById('task-list'), all=allS(_cached.tasks||{});
250
+ const fl=all.filter(FILTERS[_filter]||FILTERS.all);
251
+ let html='';
252
+ for(const g of STAT_ORDER){
253
+ const items=fl.filter(x=>aStat(x.status)===g||(g==='running'&&['running','queued'].includes(x.status))||(g==='failed'&&['crashed','killed','error'].includes(x.status)));
254
+ if(!items.length)continue;
255
+ const label={running:'Running',queued:'Running',paused:'Paused',blocked:'Blocked',done:'Done',failed:'Failed',crashed:'Failed',killed:'Failed'}[g]||g;
256
+ html+='<div class="section-label">'+label+'</div>';
257
+ for(const t of items)html+=cardH(t);
258
+ }
259
+ if(!html){
260
+ const h={all:'No tasks yet. Type a task below to get started.',running:'No running tasks.',done:'No completed tasks.',failed:'No failed tasks.'};
261
+ html='<div class="empty-state"><h2>'+(h[_filter]||'No tasks found.')+'</h2></div>';
262
+ }
263
+ c.innerHTML=html;
264
+ }
265
+
266
+ function cardH(t) {
267
+ const s=st(t.status), steps=(t.steps||[]).length, files=t.artifacts?.files?.length??0, d=dur(t.durationMs||(t.completedAt?t.completedAt-t.startedAt:Date.now()-t.startedAt));
268
+ const co=t.cost?t.cost.spent:null;
269
+ return '<div class="task-card" onclick="showD(\\''+t.sessionId+'\\')">'
270
+ +'<div class="card-row1">'+statIcon(s)+'<span class="card-title">'+title(t.prompt)+'</span>'+tagHtml(t.tags||[])+'<span class="card-agent">'+(t.agent||'?')+'</span></div>'
271
+ +'<div class="card-row2"><span>'+d+'</span>'+(co!==null?'<span>· $'+(co/100).toFixed(2)+'</span>':'')+'<span>· '+steps+' tools</span><span>· '+files+' files</span>'+verB(t.verification)+'</div>'
272
+ +(cardActions(t.sessionId)?'<div class="card-row3">'+cardActions(t.sessionId)+'</div>':'')+'</div>';
273
+ }
274
+
275
+ function upCnt() {
276
+ const s=allS(_cached.tasks||{}), f={all:s.length,running:s.filter(x=>['running','queued','paused','blocked'].includes(x.status)).length,done:s.filter(x=>x.status==='completed'||x.status==='done').length,failed:s.filter(x=>['error','killed','crashed','failed'].includes(x.status)).length};
277
+ const set=(id,n)=>{const e=document.getElementById(id);if(e)e.textContent=n;};
278
+ set('c-all',f.all);set('c-running',f.running);set('c-done',f.done);set('c-failed',f.failed);
279
+ }
280
+
281
+ function upBud() {
282
+ const p=_cached.pricing||_cached.tasks?.pricing;
283
+ const e=document.getElementById('budget');
284
+ if(!p||!p.dailyBudgetCents){e.textContent='';e.className='budget';return;}
285
+ const d=p.dailyBudgetCents,s=p.spentTodayCents||0;
286
+ e.textContent='$'+(s/100).toFixed(2)+'/$'+(d/100).toFixed(2);
287
+ e.className='budget'+(s>=d?' block':s>=d*0.8?' warn':' safe');
288
+ const ib=document.querySelector('.input-bar');
289
+ if(ib){ib.className='input-bar'+(s>=d?' block':s>=d*0.8?' warn':'');}
290
+ }
291
+
292
+ function upSq() {
293
+ const d=_cached.readiness||{}, a=d.agents||[], auto=d.auto?[{id:'auto',label:'Auto (Conductor)',state:d.auto.state}]:[], all=[...auto,...a];
294
+ const e=document.getElementById('squad');
295
+ e.innerHTML=all.map(x=>{const r=agentS(x.state),n=x.label||x.name||x.id||'?';return'<span class="squad-agent"><span class="squad-dot '+r+'"></span>'+n+'</span>';}).join('');
296
+ if(!all.length)e.innerHTML='<span style="color:var(--fg-40)">No agents configured</span>';
297
+ }
298
+ function agentS(state){if(!state||state==='error')return'offline';if(state==='installed'||state==='ready')return'ready';return'busy';}
299
+
300
+ function setFilter(f){
301
+ _filter=f;
302
+ document.querySelectorAll('.filter-tab').forEach(el=>el.classList.toggle('active',el.dataset.filter===f));
303
+ if(_cached.tasks)render();
304
+ }
305
+
306
+ function showD(id){
307
+ _detail=id;
308
+ const t=findS(id);
309
+ if(!t){_detail=null;return;}
310
+ document.getElementById('task-list-view').style.display='none';
311
+ document.getElementById('task-detail-view').style.display='block';
312
+ renderD(t);
313
+ }
314
+ function hideD(){
315
+ _detail=null;
316
+ document.getElementById('task-list-view').style.display='';
317
+ document.getElementById('task-detail-view').style.display='none';
318
+ render();
319
+ }
320
+
321
+ function renderD(t) {
322
+ const s=st(t.status), steps=(t.steps||[]), files=t.artifacts?.files||[], d=dur(t.durationMs||(t.completedAt?t.completedAt-t.startedAt:Date.now()-t.startedAt));
323
+ const co=t.cost?t.cost.spent:null, v=t.verification||'unverified';
324
+ const nStep=steps.length, cStep=steps.filter(x=>x.status==='completed').length;
325
+ const progDots=Array.from({length:Math.max(nStep,3)},(_,i)=>i<cStep?'<span class="pdot done"></span>':i===cStep?'<span class="pdot current"></span>':'<span class="pdot pending"></span>').join('');
326
+
327
+ let oHtml='<div class="panel"><div class="panel-title">Outcome</div>';
328
+ if(t.status==='running'||t.status==='queued'){
329
+ oHtml+='<div class="progress-dots">'+progDots+'</div><div class="outcome-item pending">'+cStep+'/'+Math.max(nStep,3)+' steps</div>';
330
+ } else {
331
+ oHtml+=(files.length?'<div class="outcome-item"><span class="check">'+ms(true)+'</span> '+files.length+' files changed</div>':'');
332
+ oHtml+='<div class="outcome-item"><span class="'+(nStep>0?'check':'pending')+'">'+(nStep>0?ms(true):'\\u2022')+'</span> '+nStep+' tool calls</div>';
333
+ oHtml+=t.finalReport?'<div class="outcome-item"><span class="check">'+ms(true)+'</span> Final Report</div>':'';
334
+ oHtml+='<div class="verdict '+v+'">Status: '+v+(v==='unverified'?' \\u2014 review the output and verify manually':'')+'</div>';
335
+ if(v==='unverified')oHtml+='<button class="card-btn verify" style="margin-top:8px" onclick="vfy(\\''+t.sessionId+'\\')">Verify</button>';
336
+ }
337
+ oHtml+='</div>';
338
+
339
+ const turns=t.turns||t.steps||[], tc=t.toolTrace||[];
340
+ const convHtml='<details class="collapsible"><summary>'+(turns.length>0?turns.length+' messages':'No messages')+'</summary>'
341
+ +turns.map(u=>'<div class="turn '+(u.role||'agent')+'"><b>'+(u.role||'agent')+':</b> '+(u.content||u.output||JSON.stringify(u))+'</div>').join('')+'</details>';
342
+ const traceHtml='<details class="collapsible"><summary>'+(tc.length>0?tc.length+' tool calls':'No tool trace')+'</summary>'
343
+ +tc.map(z=>'<div class="tc"><code>'+z.tool+'</code> '+(z.args||'')+' <span style="color:var(--fg-30)">'+(z.durationMs?z.durationMs+'ms':'')+'</span></div>').join('')+'</details>';
344
+
345
+ const inputDisabled=t.status!=='running'&&t.status!=='done'&&t.status!=='failed'&&t.status!=='crashed';
346
+ const ta=document.getElementById('detail-ta'), btn=document.getElementById('detail-btn');
347
+ if(ta){ta.disabled=inputDisabled;ta.placeholder=inputDisabled?'Follow-up unavailable for this state':'Request changes or ask a question...';}
348
+ if(btn)btn.disabled=inputDisabled;
349
+
350
+ document.getElementById('detail-view').innerHTML='<div class="detail-header">'
351
+ +'<span class="back-btn" onclick="hideD()">\\u2190 Back</span>'
352
+ +'<span class="detail-title">'+title(t.prompt)+'</span>'
353
+ +'<span class="card-agent" style="margin-left:auto">'+(t.agent||'?')+'</span>'
354
+ +'</div>'
355
+ +'<div class="detail-meta"><span class="status-icon '+s+'"></span> '+s+' \\u00B7 '+d+(co!==null?' \\u00B7 $'+(co/100).toFixed(2):'')+'</div>'
356
+ +oHtml+convHtml+traceHtml;
357
+ }
358
+
359
+ async function a(action, id) {
360
+ if(action==='kill'){ try{await fetch('/api/task/'+id+'/kill',{method:'POST'});fetchAll();}catch(e){al('Failed: '+e.message);} return; }
361
+ if(action==='pause'){ al('Pause not yet wired to agent API'); return; }
362
+ if(action==='resume'){ al('Resume not yet wired to agent API'); return; }
363
+ if(action==='restart'){ al('Restart not yet wired'); return; }
364
+ }
365
+
366
+ function vfy(id) {
367
+ const s=allS(_cached.tasks||{}), t=s.find(x=>x.sessionId===id);
368
+ if(!t)return;
369
+ t.verification='verified';
370
+ render();
371
+ }
372
+
373
+ function al(m){const e=document.getElementById('toast');if(e){e.textContent=m;e.style.display='block';setTimeout(()=>{e.style.display='none';},3000);}}
374
+
375
+ async function fetchAll() {
376
+ const sk=document.getElementById('skeleton');if(sk){const p=sk.parentNode;if(p)p.removeChild(sk);}
377
+ try {
378
+ const [t,r]=await Promise.all([fetch('/api/tasks').then(x=>x.json()),fetch('/api/readiness/agents').then(x=>x.json())]);
379
+ _cached.tasks=t;_cached.readiness=r;
380
+ if(t.pricing)_cached.pricing=t.pricing;
381
+ render();
382
+ }catch(e){console.error('fetch error',e);}
383
+ try {
384
+ if(!_cached.pricing){
385
+ const p=await fetch('/api/spend/today').then(x=>x.json());
386
+ if(p&&p.budget){_cached.pricing={dailyBudgetCents:Math.round((p.budget.dailyUsd||0)*100),spentTodayCents:Math.round((p.totalUsd||0)*100)}; upBud();}
387
+ }
388
+ }catch{}
389
+ }
390
+
391
+ async function submit(ta,btn){
392
+ const p=ta.value.trim();
393
+ if(!p)return;
394
+ btn.disabled=true;ta.disabled=true;
395
+ try {
396
+ const r=await fetch('/api/task',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({prompt:p})});
397
+ if(!r.ok){const e=await r.json();al('Failed: '+(e.error||'unknown'));}else{
398
+ ta.value='';btn.textContent='\\u2713';setTimeout(()=>{btn.textContent='\\u2191';btn.disabled=false;ta.disabled=false;ta.focus();},1200);
399
+ fetchAll();
400
+ }
401
+ }catch(e){al('Network error: '+e.message);btn.disabled=false;ta.disabled=false;}
402
+ }
403
+
404
+ function offlineMode(v){
405
+ _offline=v;
406
+ const b=document.getElementById('offline-banner');
407
+ if(b)b.style.display=v?'block':'none';
408
+ }
409
+
410
+ document.addEventListener('DOMContentLoaded',()=>{
411
+ fetchAll();
412
+
413
+ const es=new EventSource('/api/events/sessions');
414
+ let pollTimer=setInterval(fetchAll,30000), sseTimer=null;
415
+ es.addEventListener('message',e=>{try{const m=JSON.parse(e.data);if(m.type==='change')fetchAll();}catch{}});
416
+ es.addEventListener('error',()=>{
417
+ clearInterval(pollTimer);pollTimer=setInterval(fetchAll,8000);
418
+ if(sseTimer)clearTimeout(sseTimer);
419
+ sseTimer=setTimeout(()=>{clearInterval(pollTimer);pollTimer=setInterval(fetchAll,30000);sseTimer=null;},60000);
420
+ });
421
+ es.addEventListener('open',()=>{if(sseTimer){clearInterval(pollTimer);pollTimer=setInterval(fetchAll,8000);setTimeout(()=>{clearInterval(pollTimer);pollTimer=setInterval(fetchAll,30000);},10000);}});
422
+
423
+ window.addEventListener('offline',()=>offlineMode(true));
424
+ window.addEventListener('online',()=>{offlineMode(false);fetchAll();});
425
+
426
+ document.addEventListener('keydown',e=>{
427
+ if(e.key==='Escape'&&_detail)hideD();
428
+ if((e.metaKey||e.ctrlKey)&&e.key==='k'){e.preventDefault();const ta=document.getElementById('main-ta')||document.getElementById('detail-ta');if(ta)ta.focus();}
429
+ });
430
+
431
+ const ta=document.getElementById('main-ta'), btn=document.getElementById('main-btn');
432
+ if(btn)btn.addEventListener('click',()=>submit(ta,btn));
433
+ if(ta)ta.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey&&!ta.disabled&&btn&&!btn.disabled){e.preventDefault();submit(ta,btn);}});
434
+
435
+ const dta=document.getElementById('detail-ta'), dbtn=document.getElementById('detail-btn');
436
+ if(dbtn)dbtn.addEventListener('click',()=>submit(dta,dbtn));
437
+ if(dta)dta.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey&&!dta.disabled&&dbtn&&!dbtn.disabled){e.preventDefault();submit(dta,dbtn);}});
438
+ });
439
+ `;
440
+ export const TASK_HTML_V2 = `<!doctype html>
441
+ <html lang="en">
442
+ <head>
443
+ <meta charset="utf-8" />
444
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
445
+ <title>codehere</title>
446
+ <style>${SHARED_STYLES}</style>
447
+ </head>
448
+ <body>
449
+ <div class="app">
450
+ <div class="header">
451
+ <span class="logo">codehere</span>
452
+ <span class="budget safe" id="budget"></span>
453
+ <a href="/" class="v1-link">v1 Dashboard</a>
454
+ </div>
455
+
456
+ <div class="squad-row" id="squad">
457
+ <span style="color:var(--fg-30)">Squad:</span>
458
+ <span class="squad-agent"><span class="squad-dot offline"></span>loading...</span>
459
+ </div>
460
+
461
+ <div class="bar">
462
+ <button class="new-btn" onclick="document.getElementById('main-ta')?.focus()">+ New Task</button>
463
+ <span class="filter-tab active" data-filter="all" onclick="setFilter('all')">All <span class="cnt" id="c-all">0</span></span>
464
+ <span class="filter-tab" data-filter="running" onclick="setFilter('running')">&bull; Running <span class="cnt" id="c-running">0</span></span>
465
+ <span class="filter-tab" data-filter="done" onclick="setFilter('done')">&check; Done <span class="cnt" id="c-done">0</span></span>
466
+ <span class="filter-tab" data-filter="failed" onclick="setFilter('failed')">&times; Failed <span class="cnt" id="c-failed">0</span></span>
467
+ </div>
468
+
469
+ <div class="content">
470
+ <div id="task-list-view" class="content-inner">
471
+ <div id="task-list">
472
+ <div class="skeleton" id="skeleton">
473
+ <div class="skeleton-card"></div>
474
+ <div class="skeleton-card"></div>
475
+ <div class="skeleton-card"></div>
476
+ </div>
477
+ </div>
478
+ </div>
479
+ <div id="task-detail-view" style="display:none" class="content-inner">
480
+ <div id="detail-view"></div>
481
+ <div class="input-area" style="border-top:none;padding:12px 0">
482
+ <div class="input-bar" style="max-width:100%">
483
+ <textarea class="input-ta" id="detail-ta" placeholder="Request changes or ask a question..." rows="1" disabled></textarea>
484
+ <div class="input-meta"><span>Auto</span></div>
485
+ <button class="send-btn" id="detail-btn" disabled>&#8593;</button>
486
+ </div>
487
+ </div>
488
+ </div>
489
+ </div>
490
+
491
+ <div class="input-area">
492
+ <div class="input-bar">
493
+ <textarea class="input-ta" id="main-ta" placeholder="Describe a task or ask a question..." rows="1"></textarea>
494
+ <div class="input-meta"><span>Auto</span></div>
495
+ <button class="send-btn" id="main-btn">&#8593;</button>
496
+ </div>
497
+ </div>
498
+
499
+ <div id="offline-banner" class="offline-banner" style="display:none">You're offline. Actions will resume when connected.</div>
500
+ <div id="toast" style="display:none;position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:var(--bg-2);border:1px solid var(--border);border-radius:8px;padding:8px 16px;font-size:12px;color:var(--fg-70);z-index:100"></div>
501
+ </div>
502
+ <script>${APP_SCRIPT}</script>
503
+ </body>
504
+ </html>`;
505
+ //# sourceMappingURL=ui-page-v2.js.map