aicodeman 1.1.13 → 1.1.15

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 (65) hide show
  1. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  2. package/dist/web/public/{app.a24e7b65.js → app.61e2966f.js} +13 -12
  3. package/dist/web/public/app.61e2966f.js.br +0 -0
  4. package/dist/web/public/app.61e2966f.js.gz +0 -0
  5. package/dist/web/public/constants.1c779517.js.gz +0 -0
  6. package/dist/web/public/image-input.0ea86695.js.gz +0 -0
  7. package/dist/web/public/index.html +7 -7
  8. package/dist/web/public/index.html.br +0 -0
  9. package/dist/web/public/index.html.gz +0 -0
  10. package/dist/web/public/input-cjk.4e950e0a.js.gz +0 -0
  11. package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
  12. package/dist/web/public/mobile-handlers.b5a667ef.js.gz +0 -0
  13. package/dist/web/public/mobile.38346326.css.gz +0 -0
  14. package/dist/web/public/{notification-manager.9c984ac2.js → notification-manager.ca9dc40d.js} +1 -1
  15. package/dist/web/public/notification-manager.ca9dc40d.js.br +0 -0
  16. package/dist/web/public/{notification-manager.9c984ac2.js.gz → notification-manager.ca9dc40d.js.gz} +0 -0
  17. package/dist/web/public/orchestrator-panel.js +2 -2
  18. package/dist/web/public/orchestrator-panel.js.br +0 -0
  19. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  20. package/dist/web/public/{panels-ui.f3f08e26.js → panels-ui.aedcf302.js} +17 -17
  21. package/dist/web/public/panels-ui.aedcf302.js.br +0 -0
  22. package/dist/web/public/panels-ui.aedcf302.js.gz +0 -0
  23. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  24. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  25. package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
  26. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  27. package/dist/web/public/{session-ui.1463b824.js → session-ui.0ee6c334.js} +5 -5
  28. package/dist/web/public/{session-ui.1463b824.js.br → session-ui.0ee6c334.js.br} +0 -0
  29. package/dist/web/public/{session-ui.1463b824.js.gz → session-ui.0ee6c334.js.gz} +0 -0
  30. package/dist/web/public/settings-ui.38feb956.js.gz +0 -0
  31. package/dist/web/public/{styles.e0388c26.css → styles.4d2984bd.css} +1 -1
  32. package/dist/web/public/styles.4d2984bd.css.br +0 -0
  33. package/dist/web/public/styles.4d2984bd.css.gz +0 -0
  34. package/dist/web/public/{subagent-windows.07e139f2.js → subagent-windows.69b60c35.js} +8 -4
  35. package/dist/web/public/subagent-windows.69b60c35.js.br +0 -0
  36. package/dist/web/public/subagent-windows.69b60c35.js.gz +0 -0
  37. package/dist/web/public/sw.js.gz +0 -0
  38. package/dist/web/public/terminal-ui.70596bbd.js.gz +0 -0
  39. package/dist/web/public/ultracode-panel.js +13 -23
  40. package/dist/web/public/ultracode-panel.js.br +0 -0
  41. package/dist/web/public/ultracode-panel.js.gz +0 -0
  42. package/dist/web/public/ultracode-windows.js +449 -12
  43. package/dist/web/public/ultracode-windows.js.br +0 -0
  44. package/dist/web/public/ultracode-windows.js.gz +0 -0
  45. package/dist/web/public/upload.html.gz +0 -0
  46. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  47. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  48. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  49. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  50. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  51. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  52. package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
  53. package/dist/web/public/vendor/xterm.css.gz +0 -0
  54. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  55. package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
  56. package/package.json +1 -1
  57. package/dist/web/public/app.a24e7b65.js.br +0 -0
  58. package/dist/web/public/app.a24e7b65.js.gz +0 -0
  59. package/dist/web/public/notification-manager.9c984ac2.js.br +0 -0
  60. package/dist/web/public/panels-ui.f3f08e26.js.br +0 -0
  61. package/dist/web/public/panels-ui.f3f08e26.js.gz +0 -0
  62. package/dist/web/public/styles.e0388c26.css.br +0 -0
  63. package/dist/web/public/styles.e0388c26.css.gz +0 -0
  64. package/dist/web/public/subagent-windows.07e139f2.js.br +0 -0
  65. package/dist/web/public/subagent-windows.07e139f2.js.gz +0 -0
@@ -33,9 +33,12 @@
33
33
  Object.assign(CodemanApp.prototype, {
34
34
  /** Lazily seed the floating-window state maps (constructor also seeds them). */
35
35
  _ensureUltracodeWindowState() {
36
- if (!this.ultracodeWindows) this.ultracodeWindows = new Map(); // runId -> { element, parentSessionId, dragListeners, collapsed }
36
+ if (!this.ultracodeWindows) this.ultracodeWindows = new Map(); // runId -> { element, parentSessionId, dragListeners }
37
37
  if (!this.ultracodeWindowsClosed) this.ultracodeWindowsClosed = new Set(); // runIds the user dismissed
38
38
  if (!this.ultracodeWindowCloseTimers) this.ultracodeWindowCloseTimers = new Map(); // runId -> setTimeout id
39
+ if (!this.ultracodeAgentWindows) this.ultracodeAgentWindows = new Map(); // agentId -> { element, runId, dragListeners }
40
+ if (!this.minimizedUltracodeRuns) this.minimizedUltracodeRuns = new Map(); // sessionId -> Set<runId> minimized to a tab
41
+ if (!this.minimizedUltracodeAgents) this.minimizedUltracodeAgents = new Map(); // sessionId -> Map<agentId,{runId,label}>
39
42
  if (this.ultracodeWindowZIndex === undefined) this.ultracodeWindowZIndex = 1000;
40
43
  },
41
44
 
@@ -86,6 +89,21 @@ Object.assign(CodemanApp.prototype, {
86
89
 
87
90
  const active = this._isWorkflowRunActive(run);
88
91
 
92
+ // Minimized to a tab — keep it there (don't re-pop a window). Clear the tab badge a
93
+ // short while after the run finishes, mirroring the floating window's finish grace.
94
+ if (this._isUltracodeRunMinimized(runId)) {
95
+ if (!active && !this.ultracodeWindowCloseTimers.has(runId)) {
96
+ const timer = setTimeout(() => {
97
+ this.ultracodeWindowCloseTimers.delete(runId);
98
+ this._removeMinimizedUltracodeRun(runId);
99
+ this.renderSessionTabs();
100
+ this.updateConnectionLines();
101
+ }, 8000);
102
+ this.ultracodeWindowCloseTimers.set(runId, timer);
103
+ }
104
+ return;
105
+ }
106
+
89
107
  if (active) {
90
108
  // Run is alive — cancel any pending auto-close.
91
109
  const pending = this.ultracodeWindowCloseTimers.get(runId);
@@ -134,11 +152,11 @@ Object.assign(CodemanApp.prototype, {
134
152
  const run = this.workflowRuns && this.workflowRuns.get(runId);
135
153
  if (!run) return;
136
154
  this.ultracodeWindowsClosed.delete(runId); // an explicit open overrides a past dismissal
155
+ this._removeMinimizedUltracodeRun(runId); // …and a past minimize-to-tab
137
156
  const existing = this.ultracodeWindows.get(runId);
138
157
  if (existing) {
139
- // Already open — bring to front, expand if collapsed, refresh.
158
+ // Already open — bring to front and refresh.
140
159
  existing.element.style.zIndex = ++this.ultracodeWindowZIndex;
141
- if (existing.collapsed) this.toggleUltracodeWindowCollapse(runId);
142
160
  this.renderUltracodeWindowContent(runId);
143
161
  this._fetchWorkflowRunDetail(runId);
144
162
  this.updateConnectionLines();
@@ -167,7 +185,7 @@ Object.assign(CodemanApp.prototype, {
167
185
  <span class="uw-status"></span>
168
186
  </div>
169
187
  <div class="ultracode-window-actions">
170
- <button class="uw-min" type="button" title="Collapse">─</button>
188
+ <button class="uw-min" type="button" title="Minimize to tab">─</button>
171
189
  <button class="uw-close" type="button" title="Close">&times;</button>
172
190
  </div>
173
191
  </div>
@@ -198,7 +216,7 @@ Object.assign(CodemanApp.prototype, {
198
216
 
199
217
  win.querySelector('.uw-min').addEventListener('click', (e) => {
200
218
  e.stopPropagation();
201
- this.toggleUltracodeWindowCollapse(runId);
219
+ this.minimizeUltracodeWindowToTab(runId);
202
220
  });
203
221
  win.querySelector('.uw-close').addEventListener('click', (e) => {
204
222
  e.stopPropagation();
@@ -211,19 +229,261 @@ Object.assign(CodemanApp.prototype, {
211
229
  nameEl.addEventListener('click', () => this.selectSession(parentSessionId));
212
230
  }
213
231
 
214
- this.ultracodeWindows.set(runId, { element: win, parentSessionId, dragListeners, collapsed: false });
232
+ this.ultracodeWindows.set(runId, { element: win, parentSessionId, dragListeners });
215
233
 
216
234
  this.renderUltracodeWindowContent(runId);
217
235
  this._fetchWorkflowRunDetail(runId); // pull agents[] for the body
218
236
  this.updateConnectionLines();
219
237
  },
220
238
 
221
- /** Collapse/expand the window to header-only (line stays connected). */
222
- toggleUltracodeWindowCollapse(runId) {
239
+ // ── Minimize a run window into its originating tab (same idiom as subagent windows) ──
240
+
241
+ /** Is this run currently minimized to a tab (so auto-pop should leave it alone)? */
242
+ _isUltracodeRunMinimized(runId) {
243
+ if (!this.minimizedUltracodeRuns) return false;
244
+ for (const set of this.minimizedUltracodeRuns.values()) {
245
+ if (set.has(runId)) return true;
246
+ }
247
+ return false;
248
+ },
249
+
250
+ /** Drop a run from the minimized-to-tab tracking (all sessions, or a specific one). */
251
+ _removeMinimizedUltracodeRun(runId, sessionId) {
252
+ if (!this.minimizedUltracodeRuns) return;
253
+ if (sessionId) {
254
+ const set = this.minimizedUltracodeRuns.get(sessionId);
255
+ if (set) {
256
+ set.delete(runId);
257
+ if (!set.size) this.minimizedUltracodeRuns.delete(sessionId);
258
+ }
259
+ return;
260
+ }
261
+ for (const [sid, set] of this.minimizedUltracodeRuns) {
262
+ if (set.delete(runId) && !set.size) this.minimizedUltracodeRuns.delete(sid);
263
+ }
264
+ },
265
+
266
+ /**
267
+ * Minimize the floating run window into its originating session tab: record it as
268
+ * minimized (so a badge renders on the tab), genie-animate the window toward that
269
+ * tab, then remove the floating element. Restorable from the tab badge dropdown.
270
+ */
271
+ minimizeUltracodeWindowToTab(runId) {
272
+ this._ensureUltracodeWindowState();
223
273
  const data = this.ultracodeWindows.get(runId);
224
274
  if (!data) return;
225
- data.collapsed = !data.collapsed;
226
- data.element.classList.toggle('collapsed', data.collapsed);
275
+
276
+ let parentSessionId = data.parentSessionId;
277
+ if (!parentSessionId) {
278
+ const summary = this.workflowRuns && this.workflowRuns.get(runId);
279
+ parentSessionId = summary ? this._resolveUltracodeParentSession(summary) : null;
280
+ }
281
+ // No tab to fly into → fall back to a plain close so the window isn't orphaned.
282
+ if (!parentSessionId) {
283
+ this.closeUltracodeWindow(runId, true);
284
+ return;
285
+ }
286
+
287
+ // Cancel any pending finish auto-close — the badge owns the run's lifecycle now.
288
+ const pending = this.ultracodeWindowCloseTimers.get(runId);
289
+ if (pending) {
290
+ clearTimeout(pending);
291
+ this.ultracodeWindowCloseTimers.delete(runId);
292
+ }
293
+
294
+ if (!this.minimizedUltracodeRuns.has(parentSessionId)) this.minimizedUltracodeRuns.set(parentSessionId, new Set());
295
+ this.minimizedUltracodeRuns.get(parentSessionId).add(runId);
296
+
297
+ const element = data.element;
298
+ const dragListeners = data.dragListeners;
299
+ this._animateUltracodeWindowToTab(element, parentSessionId, () => {
300
+ this._teardownUltracodeDrag(dragListeners);
301
+ if (element) element.remove();
302
+ this.ultracodeWindows.delete(runId);
303
+ // Full rebuild so the tab badge renders (the incremental path only knows subagent badges).
304
+ this._fullRenderSessionTabs();
305
+ this.updateConnectionLines();
306
+ });
307
+ },
308
+
309
+ /** Genie the window toward the center of its tab, then invoke `done` to tear it down. */
310
+ _animateUltracodeWindowToTab(element, sessionId, done) {
311
+ const tab = sessionId ? document.querySelector(`.session-tab[data-id="${sessionId}"]`) : null;
312
+ if (!tab || !element) {
313
+ done();
314
+ return;
315
+ }
316
+ const w = element.getBoundingClientRect();
317
+ const t = tab.getBoundingClientRect();
318
+ const dx = t.left + t.width / 2 - (w.left + w.width / 2);
319
+ const dy = t.top + t.height / 2 - (w.top + w.height / 2);
320
+ element.style.transformOrigin = 'center center';
321
+ element.style.transition = 'transform 0.26s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.26s ease';
322
+ element.style.pointerEvents = 'none';
323
+ requestAnimationFrame(() => {
324
+ element.style.transform = `translate(${dx}px, ${dy}px) scale(0.06)`;
325
+ element.style.opacity = '0';
326
+ });
327
+ let finished = false;
328
+ const finish = () => {
329
+ if (finished) return;
330
+ finished = true;
331
+ done();
332
+ };
333
+ element.addEventListener('transitionend', finish, { once: true });
334
+ setTimeout(finish, 320); // fallback in case transitionend doesn't fire
335
+ },
336
+
337
+ /** Tab badge (with restore/dismiss dropdown) for runs minimized to this session's tab. */
338
+ renderUltracodeTabBadge(sessionId) {
339
+ this._ensureUltracodeWindowState();
340
+ const runSet = this.minimizedUltracodeRuns.get(sessionId);
341
+ const agentMap = this.minimizedUltracodeAgents.get(sessionId);
342
+ const total = (runSet ? runSet.size : 0) + (agentMap ? agentMap.size : 0);
343
+ if (total === 0) return '';
344
+
345
+ const trunc = (s) => (s.length > 25 ? s.slice(0, 25) + '…' : s);
346
+ const items = [];
347
+ // Minimized run windows (🧬) first…
348
+ if (runSet) {
349
+ for (const runId of runSet) {
350
+ const run = this.workflowRuns && this.workflowRuns.get(runId);
351
+ const name = run ? run.workflowName || run.summary || runId : runId;
352
+ const statusCls = this._workflowStatusClass(run ? String(run.status || '') : '');
353
+ items.push(
354
+ `<div class="subagent-dropdown-item" onclick="event.stopPropagation(); app.restoreUltracodeRunFromTab(${escapeHtml(JSON.stringify(runId))},${escapeHtml(JSON.stringify(sessionId))})" title="Click to restore run">` +
355
+ `<span class="subagent-dropdown-status ${statusCls}"></span>` +
356
+ `<span class="ultracode-dd-icon">🧬</span>` +
357
+ `<span class="subagent-dropdown-name">${escapeHtml(trunc(name))}</span>` +
358
+ `<span class="subagent-dropdown-close" onclick="event.stopPropagation(); app.dismissMinimizedUltracodeRun(${escapeHtml(JSON.stringify(runId))},${escapeHtml(JSON.stringify(sessionId))})" title="Dismiss">&times;</span>` +
359
+ `</div>`
360
+ );
361
+ }
362
+ }
363
+ // …then minimized agent transcripts (📄).
364
+ if (agentMap) {
365
+ for (const [agentId, entry] of agentMap) {
366
+ const name = (entry && entry.label) || agentId;
367
+ items.push(
368
+ `<div class="subagent-dropdown-item" onclick="event.stopPropagation(); app.restoreUltracodeAgentFromTab(${escapeHtml(JSON.stringify(agentId))},${escapeHtml(JSON.stringify(sessionId))})" title="Click to restore transcript">` +
369
+ `<span class="subagent-dropdown-status"></span>` +
370
+ `<span class="ultracode-dd-icon">📄</span>` +
371
+ `<span class="subagent-dropdown-name">${escapeHtml(trunc(name))}</span>` +
372
+ `<span class="subagent-dropdown-close" onclick="event.stopPropagation(); app.dismissMinimizedUltracodeAgent(${escapeHtml(JSON.stringify(agentId))},${escapeHtml(JSON.stringify(sessionId))})" title="Dismiss">&times;</span>` +
373
+ `</div>`
374
+ );
375
+ }
376
+ }
377
+ const label = total === 1 ? 'ULTRA' : `ULTRA (${total})`;
378
+ return (
379
+ `<span class="tab-ultracode-badge" onmouseenter="app.showSubagentDropdown(this)" onmouseleave="app.scheduleHideSubagentDropdown(this)" onclick="event.stopPropagation(); app.pinSubagentDropdown(this);">` +
380
+ `<span class="subagent-label">${label}</span>` +
381
+ `<div class="subagent-dropdown" onmouseenter="app.cancelHideSubagentDropdown()" onmouseleave="app.scheduleHideSubagentDropdown(this.parentElement)">${items.join('')}</div>` +
382
+ `</span>`
383
+ );
384
+ },
385
+
386
+ /** Restore a minimized run from its tab badge: re-open the floating window. */
387
+ restoreUltracodeRunFromTab(runId, sessionId) {
388
+ this._ensureUltracodeWindowState();
389
+ this._removeMinimizedUltracodeRun(runId, sessionId);
390
+ this._fullRenderSessionTabs();
391
+ this.openUltracodeWindowForRun(runId);
392
+ },
393
+
394
+ /** Dismiss a minimized run from its tab badge (don't re-pop it). */
395
+ dismissMinimizedUltracodeRun(runId, sessionId) {
396
+ this._ensureUltracodeWindowState();
397
+ this._removeMinimizedUltracodeRun(runId, sessionId);
398
+ this.ultracodeWindowsClosed.add(runId);
399
+ this._fullRenderSessionTabs();
400
+ this.updateConnectionLines();
401
+ },
402
+
403
+ // ── Minimize an agent transcript window into its tab (same idiom as run windows) ──
404
+
405
+ /** Is this agent transcript currently minimized to a tab? */
406
+ _isUltracodeAgentMinimized(agentId) {
407
+ if (!this.minimizedUltracodeAgents) return false;
408
+ for (const map of this.minimizedUltracodeAgents.values()) {
409
+ if (map.has(agentId)) return true;
410
+ }
411
+ return false;
412
+ },
413
+
414
+ /** Look up a minimized agent's {runId,label} entry (across sessions). */
415
+ _getMinimizedUltracodeAgent(agentId) {
416
+ if (!this.minimizedUltracodeAgents) return null;
417
+ for (const map of this.minimizedUltracodeAgents.values()) {
418
+ if (map.has(agentId)) return map.get(agentId);
419
+ }
420
+ return null;
421
+ },
422
+
423
+ /** Drop an agent from minimized tracking (all sessions, or a specific one). */
424
+ _removeMinimizedUltracodeAgent(agentId, sessionId) {
425
+ if (!this.minimizedUltracodeAgents) return;
426
+ if (sessionId) {
427
+ const map = this.minimizedUltracodeAgents.get(sessionId);
428
+ if (map) {
429
+ map.delete(agentId);
430
+ if (!map.size) this.minimizedUltracodeAgents.delete(sessionId);
431
+ }
432
+ return;
433
+ }
434
+ for (const [sid, map] of this.minimizedUltracodeAgents) {
435
+ if (map.delete(agentId) && !map.size) this.minimizedUltracodeAgents.delete(sid);
436
+ }
437
+ },
438
+
439
+ /** Minimize an agent transcript window into the run's originating session tab. */
440
+ minimizeUltracodeAgentWindowToTab(agentId) {
441
+ this._ensureUltracodeWindowState();
442
+ const info = this.ultracodeAgentWindows.get(agentId);
443
+ if (!info) return;
444
+ const runId = info.runId;
445
+ const summary = runId && this.workflowRuns ? this.workflowRuns.get(runId) : null;
446
+ let parentSessionId = summary ? this._resolveUltracodeParentSession(summary) : null;
447
+ if (!parentSessionId && this.activeSessionId && this.sessions && this.sessions.has(this.activeSessionId)) {
448
+ parentSessionId = this.activeSessionId;
449
+ }
450
+ // No tab to fly into → plain close rather than orphan it.
451
+ if (!parentSessionId) {
452
+ this.closeUltracodeAgentWindow(agentId);
453
+ return;
454
+ }
455
+ const labelEl = info.element.querySelector('.uw-name');
456
+ const label = labelEl ? labelEl.textContent : agentId;
457
+ if (!this.minimizedUltracodeAgents.has(parentSessionId))
458
+ this.minimizedUltracodeAgents.set(parentSessionId, new Map());
459
+ this.minimizedUltracodeAgents.get(parentSessionId).set(agentId, { runId, label });
460
+
461
+ const element = info.element;
462
+ const dragListeners = info.dragListeners;
463
+ this._animateUltracodeWindowToTab(element, parentSessionId, () => {
464
+ this._teardownUltracodeDrag(dragListeners);
465
+ if (element) element.remove();
466
+ this.ultracodeAgentWindows.delete(agentId);
467
+ this._fullRenderSessionTabs();
468
+ this.updateConnectionLines();
469
+ });
470
+ },
471
+
472
+ /** Restore a minimized agent transcript from its tab badge: re-open its window. */
473
+ restoreUltracodeAgentFromTab(agentId, sessionId) {
474
+ this._ensureUltracodeWindowState();
475
+ const entry = this._getMinimizedUltracodeAgent(agentId);
476
+ const runId = entry ? entry.runId : null;
477
+ this._removeMinimizedUltracodeAgent(agentId, sessionId);
478
+ this._fullRenderSessionTabs();
479
+ this.openUltracodeAgentWindow(agentId, runId);
480
+ },
481
+
482
+ /** Dismiss a minimized agent transcript from its tab badge. */
483
+ dismissMinimizedUltracodeAgent(agentId, sessionId) {
484
+ this._ensureUltracodeWindowState();
485
+ this._removeMinimizedUltracodeAgent(agentId, sessionId);
486
+ this._fullRenderSessionTabs();
227
487
  this.updateConnectionLines();
228
488
  },
229
489
 
@@ -260,19 +520,149 @@ Object.assign(CodemanApp.prototype, {
260
520
  }
261
521
  },
262
522
 
523
+ // ── Agent-transcript windows ────────────────────────────────────────────────
524
+ // Clicking an agent card (in a run window OR the dock panel) opens the agent's
525
+ // live transcript as its OWN in-page floating window, line-tied to its parent run
526
+ // window (or the run's session tab when that window is closed). Replaces the old
527
+ // detached `window.open` browser popup so the transcript stays inside the same
528
+ // draggable, connector-line floating-window system as the run windows.
529
+
530
+ /** Open (or focus) the floating transcript window for a workflow agent. */
531
+ async openUltracodeAgentWindow(agentId, runId) {
532
+ this._ensureUltracodeWindowState();
533
+ if (!agentId) return;
534
+ this._removeMinimizedUltracodeAgent(agentId); // an explicit open overrides a past minimize
535
+ const existing = this.ultracodeAgentWindows.get(agentId);
536
+ if (existing && existing.element) {
537
+ // Already open — bring to front and refresh transcript.
538
+ existing.element.style.zIndex = ++this.ultracodeWindowZIndex;
539
+ this.updateConnectionLines();
540
+ } else if (!this.createUltracodeAgentWindow(agentId, runId)) {
541
+ return;
542
+ }
543
+ // Body shows a loading state until the fetch lands (re-fetch on focus too, so a
544
+ // still-running agent's transcript grows as you re-click).
545
+ const data = this._fetchWorkflowAgentTranscript ? await this._fetchWorkflowAgentTranscript(agentId) : null;
546
+ this.renderUltracodeAgentWindowContent(agentId, data);
547
+ },
548
+
549
+ /** Build and mount the floating agent-transcript window shell near its parent. */
550
+ createUltracodeAgentWindow(agentId, runId) {
551
+ this._ensureUltracodeWindowState();
552
+ if (this.ultracodeAgentWindows.has(agentId)) return this.ultracodeAgentWindows.get(agentId).element;
553
+
554
+ const label = this._ultracodeAgentLabel(agentId, runId) || agentId;
555
+ const win = document.createElement('div');
556
+ win.className = 'ultracode-window ultracode-agent-window spawning';
557
+ win.id = `ultracode-agent-window-${agentId}`;
558
+ win.style.zIndex = ++this.ultracodeWindowZIndex;
559
+ win.innerHTML = `
560
+ <div class="ultracode-window-header">
561
+ <div class="ultracode-window-title" title="${escapeHtml(label)} — transcript">
562
+ <span class="icon">📄</span>
563
+ <span class="uw-name">${escapeHtml(label)}</span>
564
+ </div>
565
+ <div class="ultracode-window-actions">
566
+ <button class="uw-min" type="button" title="Minimize to tab">─</button>
567
+ <button class="uw-close" type="button" title="Close">&times;</button>
568
+ </div>
569
+ </div>
570
+ <div class="ultracode-window-body">
571
+ <div class="subagent-empty">Loading transcript…</div>
572
+ </div>
573
+ `;
574
+
575
+ // Position: offset from the parent run window if it's open, else cascade.
576
+ const parentWin = runId ? this.ultracodeWindows.get(runId) : null;
577
+ if (parentWin && parentWin.element) {
578
+ const r = parentWin.element.getBoundingClientRect();
579
+ win.style.left = `${Math.max(8, Math.min(r.left + 40, window.innerWidth - 472))}px`;
580
+ win.style.top = `${Math.max(8, Math.min(r.top + 40, window.innerHeight - 160))}px`;
581
+ } else {
582
+ const n = this.ultracodeAgentWindows.size;
583
+ win.style.left = `${Math.min(140 + n * 28, Math.max(8, window.innerWidth - 472))}px`;
584
+ win.style.top = `${110 + n * 28}px`;
585
+ }
586
+
587
+ document.body.appendChild(win);
588
+ requestAnimationFrame(() => win.classList.remove('spawning'));
589
+
590
+ const header = win.querySelector('.ultracode-window-header');
591
+ const dragListeners = this.makeWindowDraggable(win, header);
592
+ win.querySelector('.uw-min').addEventListener('click', (e) => {
593
+ e.stopPropagation();
594
+ this.minimizeUltracodeAgentWindowToTab(agentId);
595
+ });
596
+ win.querySelector('.uw-close').addEventListener('click', (e) => {
597
+ e.stopPropagation();
598
+ this.closeUltracodeAgentWindow(agentId);
599
+ });
600
+
601
+ this.ultracodeAgentWindows.set(agentId, { element: win, runId, dragListeners });
602
+ this.updateConnectionLines();
603
+ return win;
604
+ },
605
+
606
+ /** Resolve a human label for an agent from the run's fetched detail.agents[]. */
607
+ _ultracodeAgentLabel(agentId, runId) {
608
+ const detail = this.workflowRunDetails && runId ? this.workflowRunDetails.get(runId) : null;
609
+ const agents = detail && Array.isArray(detail.agents) ? detail.agents : null;
610
+ if (agents) {
611
+ const found = agents.find((a) => a.agentId === agentId);
612
+ if (found && found.label) return found.label;
613
+ }
614
+ return null;
615
+ },
616
+
617
+ /** Fill an agent window's body with the fetched transcript (or a friendly empty state). */
618
+ renderUltracodeAgentWindowContent(agentId, data) {
619
+ const info = this.ultracodeAgentWindows.get(agentId);
620
+ if (!info || !info.element) return;
621
+ const body = info.element.querySelector('.ultracode-window-body');
622
+ if (!body) return;
623
+ if (!data || !data.formatted || !data.entryCount) {
624
+ body.innerHTML =
625
+ '<div class="subagent-empty">No transcript available yet — the agent may be queued, aged out of tracking, or subagent tracking is disabled.</div>';
626
+ return;
627
+ }
628
+ const text = escapeHtml(data.formatted.join('\n'));
629
+ body.innerHTML = `<div class="uw-summary">${data.entryCount} entries</div><pre class="uw-transcript">${text}</pre>`;
630
+ },
631
+
632
+ /** Close one floating agent-transcript window. */
633
+ closeUltracodeAgentWindow(agentId) {
634
+ this._ensureUltracodeWindowState();
635
+ const info = this.ultracodeAgentWindows.get(agentId);
636
+ if (!info) return;
637
+ this._teardownUltracodeDrag(info.dragListeners);
638
+ if (info.element) info.element.remove();
639
+ this.ultracodeAgentWindows.delete(agentId);
640
+ this.updateConnectionLines();
641
+ },
642
+
263
643
  /** Tear down every floating window (called on SSE reconnect; keeps user dismissals). */
264
644
  removeAllUltracodeWindows() {
265
645
  this._ensureUltracodeWindowState();
266
- const had = this.ultracodeWindows.size > 0;
646
+ const hadMinimized = this.minimizedUltracodeRuns.size > 0 || this.minimizedUltracodeAgents.size > 0;
647
+ const had = this.ultracodeWindows.size > 0 || this.ultracodeAgentWindows.size > 0;
267
648
  for (const [, data] of this.ultracodeWindows) {
268
649
  this._teardownUltracodeDrag(data.dragListeners);
269
650
  if (data.element) data.element.remove();
270
651
  }
271
652
  this.ultracodeWindows.clear();
653
+ for (const [, info] of this.ultracodeAgentWindows) {
654
+ this._teardownUltracodeDrag(info.dragListeners);
655
+ if (info.element) info.element.remove();
656
+ }
657
+ this.ultracodeAgentWindows.clear();
272
658
  for (const t of this.ultracodeWindowCloseTimers.values()) clearTimeout(t);
273
659
  this.ultracodeWindowCloseTimers.clear();
660
+ this.minimizedUltracodeRuns.clear();
661
+ this.minimizedUltracodeAgents.clear();
274
662
  // Redraw so the now-orphaned connector lines are cleared from the shared SVG.
275
663
  if (had) this.updateConnectionLines();
664
+ // Drop any now-stale tab badges.
665
+ if (hadMinimized) this.renderSessionTabs();
276
666
  },
277
667
 
278
668
  /** When the feature is toggled on, pop windows for any currently-active runs. */
@@ -355,7 +745,7 @@ Object.assign(CodemanApp.prototype, {
355
745
  const header =
356
746
  `<div class="ultracode-phase-header"><span>${escapeHtml(title)}</span>` +
357
747
  `<span class="ultracode-phase-sub">${this._fmtNum(tok)} tok · ${tools} tools</span></div>`;
358
- return header + group.map((a) => this._workflowAgentCardHtml(a)).join('');
748
+ return header + group.map((a) => this._workflowAgentCardHtml(a, run.runId)).join('');
359
749
  })
360
750
  .join('');
361
751
  return head + grid;
@@ -408,4 +798,51 @@ Object.assign(CodemanApp.prototype, {
408
798
  svg.appendChild(line);
409
799
  }
410
800
  },
801
+
802
+ /**
803
+ * Append agent-window → parent connector lines into the shared SVG. Parent is the
804
+ * agent's run floating window when open, else the run's session tab. Called right
805
+ * after `_appendUltracodeConnectionLines` so it shares the same batched pass + the
806
+ * tab-rect cache.
807
+ */
808
+ _appendUltracodeAgentConnectionLines(svg, rects) {
809
+ this._ensureUltracodeWindowState();
810
+ if (!svg || !this.ultracodeAgentWindows.size) return;
811
+ if (!rects) rects = new Map();
812
+
813
+ for (const [agentId, info] of this.ultracodeAgentWindows) {
814
+ if (!info.element) continue;
815
+ const winRect = info.element.getBoundingClientRect();
816
+ // Anchor: parent run window bottom-center if open, else the run's tab.
817
+ let px, py;
818
+ const runWin = info.runId ? this.ultracodeWindows.get(info.runId) : null;
819
+ if (runWin && runWin.element) {
820
+ const pr = runWin.element.getBoundingClientRect();
821
+ px = pr.left + pr.width / 2;
822
+ py = pr.bottom;
823
+ } else {
824
+ const summary = info.runId && this.workflowRuns ? this.workflowRuns.get(info.runId) : null;
825
+ const parentSessionId = summary ? this._resolveUltracodeParentSession(summary) : null;
826
+ if (!parentSessionId) continue;
827
+ const tabKey = 'tab:' + parentSessionId;
828
+ if (!rects.has(tabKey)) {
829
+ const tab = document.querySelector(`.session-tab[data-id="${parentSessionId}"]`);
830
+ if (tab) rects.set(tabKey, tab.getBoundingClientRect());
831
+ }
832
+ const tabRect = rects.get(tabKey);
833
+ if (!tabRect) continue;
834
+ px = tabRect.left + tabRect.width / 2;
835
+ py = tabRect.bottom;
836
+ }
837
+ const x2 = winRect.left + winRect.width / 2;
838
+ const y2 = winRect.top;
839
+ const midY = (py + y2) / 2;
840
+ const path = `M ${px} ${py} C ${px} ${midY}, ${x2} ${midY}, ${x2} ${y2}`;
841
+ const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
842
+ line.setAttribute('d', path);
843
+ line.setAttribute('class', 'connection-line ultracode-connection ultracode-agent-connection');
844
+ line.setAttribute('data-agent-id', agentId);
845
+ svg.appendChild(line);
846
+ }
847
+ },
411
848
  });
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicodeman",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
4
4
  "description": "Mission control for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
Binary file
Binary file