aicodeman 1.1.13 → 1.1.14
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.
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/{app.a24e7b65.js → app.160e8c7d.js} +10 -9
- package/dist/web/public/app.160e8c7d.js.br +0 -0
- package/dist/web/public/app.160e8c7d.js.gz +0 -0
- package/dist/web/public/constants.1c779517.js.gz +0 -0
- package/dist/web/public/image-input.0ea86695.js.gz +0 -0
- package/dist/web/public/index.html +4 -4
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.4e950e0a.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
- package/dist/web/public/mobile-handlers.b5a667ef.js.gz +0 -0
- package/dist/web/public/mobile.38346326.css.gz +0 -0
- package/dist/web/public/notification-manager.9c984ac2.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/panels-ui.f3f08e26.js.gz +0 -0
- package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
- package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
- package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-ui.1463b824.js.gz +0 -0
- package/dist/web/public/settings-ui.38feb956.js.gz +0 -0
- package/dist/web/public/{styles.e0388c26.css → styles.4d2984bd.css} +1 -1
- package/dist/web/public/styles.4d2984bd.css.br +0 -0
- package/dist/web/public/styles.4d2984bd.css.gz +0 -0
- package/dist/web/public/{subagent-windows.07e139f2.js → subagent-windows.e3207234.js} +4 -0
- package/dist/web/public/subagent-windows.e3207234.js.br +0 -0
- package/dist/web/public/subagent-windows.e3207234.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.70596bbd.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js +12 -22
- package/dist/web/public/ultracode-panel.js.br +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js +449 -12
- package/dist/web/public/ultracode-windows.js.br +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
- package/package.json +1 -1
- package/dist/web/public/app.a24e7b65.js.br +0 -0
- package/dist/web/public/app.a24e7b65.js.gz +0 -0
- package/dist/web/public/styles.e0388c26.css.br +0 -0
- package/dist/web/public/styles.e0388c26.css.gz +0 -0
- package/dist/web/public/subagent-windows.07e139f2.js.br +0 -0
- package/dist/web/public/subagent-windows.07e139f2.js.gz +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -461,6 +461,10 @@ Object.assign(CodemanApp.prototype, {
|
|
|
461
461
|
if (typeof this._appendUltracodeConnectionLines === 'function') {
|
|
462
462
|
this._appendUltracodeConnectionLines(svg, rects);
|
|
463
463
|
}
|
|
464
|
+
// Agent-transcript windows → their run window / tab (ultracode-windows.js).
|
|
465
|
+
if (typeof this._appendUltracodeAgentConnectionLines === 'function') {
|
|
466
|
+
this._appendUltracodeAgentConnectionLines(svg, rects);
|
|
467
|
+
}
|
|
464
468
|
},
|
|
465
469
|
|
|
466
470
|
// ═══════════════════════════════════════════════════════════════
|
|
Binary file
|
|
Binary file
|
package/dist/web/public/sw.js.gz
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -131,12 +131,15 @@ Object.assign(CodemanApp.prototype, {
|
|
|
131
131
|
}
|
|
132
132
|
},
|
|
133
133
|
|
|
134
|
-
// Phase 4:
|
|
134
|
+
// Phase 4: fetch an agent's live transcript by agentId. The workflow agent's
|
|
135
135
|
// agentId is byte-identical to the agent-<id>.jsonl stem already tracked by
|
|
136
136
|
// subagent-watcher, so we reuse the existing transcript route — no watcher edits.
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
// Returns { formatted: string[], entryCount } or null when nothing is available
|
|
138
|
+
// (queued / aged out of tracking / tracking disabled). Rendering into a connected
|
|
139
|
+
// in-page floating window lives in ultracode-windows.js (openUltracodeAgentWindow) —
|
|
140
|
+
// we no longer spawn a detached browser popup.
|
|
141
|
+
async _fetchWorkflowAgentTranscript(agentId) {
|
|
142
|
+
if (!agentId) return null;
|
|
140
143
|
let data = null;
|
|
141
144
|
try {
|
|
142
145
|
const res = await fetch(`/api/subagents/${encodeURIComponent(agentId)}/transcript?format=formatted`);
|
|
@@ -147,21 +150,8 @@ Object.assign(CodemanApp.prototype, {
|
|
|
147
150
|
const ok = data && data.success && data.data;
|
|
148
151
|
const formatted = ok ? data.data.formatted : null;
|
|
149
152
|
const entryCount = ok ? data.data.entryCount || 0 : 0;
|
|
150
|
-
if (!formatted || !entryCount)
|
|
151
|
-
|
|
152
|
-
'No transcript available for this agent yet — it may be queued, aged out of tracking, or subagent tracking is disabled.'
|
|
153
|
-
);
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
const win = window.open('', '_blank', 'width=860,height=640');
|
|
157
|
-
if (!win) return; // popup blocked
|
|
158
|
-
win.document.write(
|
|
159
|
-
`<html><head><title>Workflow agent ${escapeHtml(agentId)} transcript</title>` +
|
|
160
|
-
`<style>body{background:#1a1a2e;color:#eee;font-family:monospace;padding:20px}pre{white-space:pre-wrap;word-wrap:break-word}</style>` +
|
|
161
|
-
`</head><body><h2>Workflow agent ${escapeHtml(agentId)} (${entryCount} entries)</h2>` +
|
|
162
|
-
`<pre>${escapeHtml(formatted.join('\n'))}</pre></body></html>`
|
|
163
|
-
);
|
|
164
|
-
win.document.close();
|
|
153
|
+
if (!formatted || !entryCount) return null;
|
|
154
|
+
return { formatted, entryCount };
|
|
165
155
|
},
|
|
166
156
|
|
|
167
157
|
// ----- Render (debounced) -----
|
|
@@ -262,13 +252,13 @@ Object.assign(CodemanApp.prototype, {
|
|
|
262
252
|
const header =
|
|
263
253
|
`<div class="ultracode-phase-header"><span>${escapeHtml(title)}</span>` +
|
|
264
254
|
`<span class="ultracode-phase-sub">${this._fmtNum(tok)} tok · ${tools} tools</span></div>`;
|
|
265
|
-
return header + group.map((a) => this._workflowAgentCardHtml(a)).join('');
|
|
255
|
+
return header + group.map((a) => this._workflowAgentCardHtml(a, runId)).join('');
|
|
266
256
|
})
|
|
267
257
|
.join('');
|
|
268
258
|
detail.innerHTML = html;
|
|
269
259
|
},
|
|
270
260
|
|
|
271
|
-
_workflowAgentCardHtml(a) {
|
|
261
|
+
_workflowAgentCardHtml(a, runId) {
|
|
272
262
|
const state = String(a.state || 'start');
|
|
273
263
|
const stateCls = this._workflowAgentStateClass(state);
|
|
274
264
|
const stateLabel = state === 'start' ? 'queued' : state === 'progress' ? 'running' : state;
|
|
@@ -289,7 +279,7 @@ Object.assign(CodemanApp.prototype, {
|
|
|
289
279
|
const cardStateCls = state === 'done' ? ' uw-state-done' : state === 'progress' ? ' uw-state-working' : '';
|
|
290
280
|
const cardAttrs = clickable
|
|
291
281
|
? ` class="ultracode-agent-card ultracode-agent-card--clickable${cardStateCls}" role="button" tabindex="0"` +
|
|
292
|
-
` title="View transcript" onclick="app.
|
|
282
|
+
` title="View transcript" onclick="app.openUltracodeAgentWindow('${escapeHtml(a.agentId)}','${escapeHtml(runId || '')}')"`
|
|
293
283
|
: ` class="ultracode-agent-card${cardStateCls}"`;
|
|
294
284
|
return (
|
|
295
285
|
`<div${cardAttrs}>` +
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
|
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
|
|
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="
|
|
188
|
+
<button class="uw-min" type="button" title="Minimize to tab">─</button>
|
|
171
189
|
<button class="uw-close" type="button" title="Close">×</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.
|
|
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
|
|
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
|
-
|
|
222
|
-
|
|
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
|
-
|
|
226
|
-
|
|
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(runId)}','${escapeHtml(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(runId)}','${escapeHtml(sessionId)}')" title="Dismiss">×</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(agentId)}','${escapeHtml(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(agentId)}','${escapeHtml(sessionId)}')" title="Dismiss">×</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">×</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
|
|
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|