Show live and historical turn activity trace (model, pack, steps) (0.15.17).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-23 22:23:26 +03:00
co-authored by Cursor
parent 649c64a587
commit baa75f2858
7 changed files with 731 additions and 118 deletions
+328 -56
View File
@@ -1040,10 +1040,179 @@
park: "\u25BC",
inventory: "\u25A4",
compress: "\u25A4",
ctx: "\u2699",
model: "\u25C9",
done: "\u2713",
skip: "\u2013",
error: "!"
};
function trimDetail(s, max = 220) {
const t = String(s || "").trim();
if (t.length <= max) {
return t;
}
return `${t.slice(0, max)}\u2026`;
}
function shortModelName(model) {
const s = String(model || "").trim();
if (!s) {
return "";
}
const slash = s.lastIndexOf("/");
return slash >= 0 ? s.slice(slash + 1) : s;
}
function buildContextMeta(context = {}) {
const parts = [];
const model = shortModelName(context.model);
if (model) {
parts.push(model);
}
if (context.pack) {
parts.push(String(context.pack).replace(/_/g, " "));
}
if (context.persona && context.persona !== "neutral") {
parts.push(String(context.persona));
}
if (Array.isArray(context.skills) && context.skills.length) {
parts.push(`skills: ${context.skills.slice(0, 4).join(", ")}`);
}
if (context.hop) {
parts.push(String(context.hop));
}
return parts.join(" \xB7 ");
}
function renderMetaRow(context) {
const meta = buildContextMeta(context);
if (!meta) {
return null;
}
const row = document.createElement("div");
row.className = "sa-activity-meta";
row.textContent = meta;
row.title = meta;
return row;
}
function renderSteps(listEl, steps) {
if (!listEl) {
return;
}
listEl.replaceChildren(...steps.map(renderStep));
}
function renderStep(step) {
const row = document.createElement("div");
row.className = `sa-activity-step sa-activity-${step.status || "done"}`;
row.dataset.id = step.id;
const icon = document.createElement("span");
icon.className = "sa-activity-icon";
icon.setAttribute("aria-hidden", "true");
icon.textContent = STEP_ICONS[step.kind] || STEP_ICONS.think;
const body = document.createElement("div");
body.className = "sa-activity-body";
const label = document.createElement("div");
label.className = "sa-activity-label";
label.textContent = step.label || step.id;
body.appendChild(label);
if (step.detail) {
const detail = document.createElement("div");
detail.className = "sa-activity-detail";
detail.textContent = step.detail;
body.appendChild(detail);
}
row.appendChild(icon);
row.appendChild(body);
return row;
}
function buildActivityCard(trace, { live = false, collapsed = false } = {}) {
const card = document.createElement("div");
card.className = "sa-activity" + (live ? " sa-activity-live" : " sa-activity-done sa-activity-historical");
const steps = Array.isArray(trace?.steps) ? trace.steps : [];
const running = steps.some((s) => s.status === "running");
if (!live && !running) {
card.classList.add("sa-activity-done");
}
if (running) {
card.classList.add("sa-activity-live");
}
card.setAttribute("role", "status");
card.setAttribute("aria-live", live ? "polite" : "off");
const head = document.createElement("button");
head.type = "button";
head.className = "sa-activity-head";
head.setAttribute("aria-expanded", collapsed ? "false" : "true");
const spin = document.createElement("span");
spin.className = "sa-activity-spin";
spin.setAttribute("aria-hidden", "true");
const titleEl = document.createElement("span");
titleEl.className = "sa-activity-title";
const runningStep = steps.find((s) => s.status === "running");
const last = steps[steps.length - 1];
titleEl.textContent = trace?.title || runningStep?.label || last?.label || "Assistent";
const chev = document.createElement("span");
chev.className = "sa-activity-chev";
chev.setAttribute("aria-hidden", "true");
chev.textContent = "\u25BE";
head.appendChild(spin);
head.appendChild(titleEl);
head.appendChild(chev);
let open = !collapsed;
head.addEventListener("click", () => {
open = !open;
card.classList.toggle("sa-activity-collapsed", !open);
head.setAttribute("aria-expanded", open ? "true" : "false");
});
if (collapsed) {
card.classList.add("sa-activity-collapsed");
}
const meta = renderMetaRow(trace?.context || {});
const listEl = document.createElement("div");
listEl.className = "sa-activity-steps";
renderSteps(listEl, steps);
card.appendChild(head);
if (meta) {
card.appendChild(meta);
}
card.appendChild(listEl);
return card;
}
function mountActivityTrace(trace, { box, insertBefore = null, collapsed = true } = {}) {
const host = box || null;
if (!host || !trace || !Array.isArray(trace.steps) || !trace.steps.length) {
return null;
}
const card = buildActivityCard(trace, { live: false, collapsed });
if (insertBefore && insertBefore.parentNode === host) {
host.insertBefore(card, insertBefore);
} else {
host.appendChild(card);
}
return card;
}
function slimActivityTrace(trace) {
if (!trace || typeof trace !== "object") {
return void 0;
}
const steps = (trace.steps || []).slice(0, 32).map((s) => ({
id: String(s.id || "").slice(0, 40),
kind: s.kind || "think",
label: String(s.label || s.id || "").slice(0, 120),
detail: trimDetail(s.detail, 180),
status: s.status || "done"
}));
if (!steps.length) {
return void 0;
}
return {
title: String(trace.title || "Assistent").slice(0, 80),
context: trace.context ? {
model: trace.context.model || void 0,
pack: trace.context.pack || void 0,
persona: trace.context.persona || void 0,
skills: Array.isArray(trace.context.skills) ? trace.context.skills.slice(0, 8) : void 0,
hop: trace.context.hop || void 0
} : void 0,
steps
};
}
function createActivityController(opts = {}) {
const {
getMessagesEl,
@@ -1053,8 +1222,27 @@
let card = null;
let listEl = null;
let titleEl = null;
let metaEl = null;
let steps = [];
let context = {};
let title = "Assistent";
let open = true;
function insertCard(el) {
const box = typeof getMessagesEl === "function" ? getMessagesEl() : null;
if (!box || !el) {
return;
}
const lastUser = box.querySelector(".sa-msg.user:last-of-type");
if (lastUser && lastUser.parentNode === box) {
if (lastUser.nextSibling) {
box.insertBefore(el, lastUser.nextSibling);
} else {
box.appendChild(el);
}
return;
}
box.appendChild(el);
}
function ensureCard() {
const box = typeof getMessagesEl === "function" ? getMessagesEl() : null;
if (!box) {
@@ -1079,7 +1267,7 @@
spin.setAttribute("aria-hidden", "true");
titleEl = document.createElement("span");
titleEl.className = "sa-activity-title";
titleEl.textContent = "Assistent";
titleEl.textContent = title || "Assistent";
const chev = document.createElement("span");
chev.className = "sa-activity-chev";
chev.setAttribute("aria-hidden", "true");
@@ -1095,46 +1283,32 @@
listEl = document.createElement("div");
listEl.className = "sa-activity-steps";
card.appendChild(head);
metaEl = renderMetaRow(context);
if (metaEl) {
card.appendChild(metaEl);
}
card.appendChild(listEl);
box.appendChild(card);
insertCard(card);
if (typeof scrollToBottom === "function") {
scrollToBottom();
}
return card;
}
function renderStep(step) {
const row = document.createElement("div");
row.className = `sa-activity-step sa-activity-${step.status || "running"}`;
row.dataset.id = step.id;
const icon = document.createElement("span");
icon.className = "sa-activity-icon";
icon.setAttribute("aria-hidden", "true");
icon.textContent = STEP_ICONS[step.kind] || STEP_ICONS.think;
const body = document.createElement("div");
body.className = "sa-activity-body";
const label = document.createElement("div");
label.className = "sa-activity-label";
label.textContent = step.label || step.id;
body.appendChild(label);
if (step.detail) {
const detail = document.createElement("div");
detail.className = "sa-activity-detail";
detail.textContent = step.detail;
body.appendChild(detail);
}
row.appendChild(icon);
row.appendChild(body);
return row;
}
function paint() {
if (!ensureCard() || !listEl) {
return;
}
listEl.replaceChildren(...steps.map(renderStep));
if (metaEl) {
const nextMeta = buildContextMeta(context);
metaEl.textContent = nextMeta;
metaEl.title = nextMeta;
metaEl.hidden = !nextMeta;
}
renderSteps(listEl, steps);
const running = steps.find((s) => s.status === "running");
const last = steps[steps.length - 1];
if (titleEl) {
titleEl.textContent = running ? running.label : last?.label || "Assistent";
titleEl.textContent = running ? running.label : last?.label || title || "Assistent";
}
card.classList.toggle("sa-activity-live", steps.some((s) => s.status === "running"));
card.classList.toggle("sa-activity-done", steps.length > 0 && steps.every((s) => s.status === "done" || s.status === "skip"));
@@ -1142,15 +1316,44 @@
scrollToBottom();
}
}
function begin(title) {
function setContext(patch = {}) {
context = { ...context, ...patch };
if (card && !metaEl) {
metaEl = renderMetaRow(context);
if (metaEl && card.firstChild) {
card.insertBefore(metaEl, card.firstChild);
} else if (metaEl) {
card.prepend(metaEl);
}
}
paint();
}
function begin(nextTitle, nextContext = {}) {
steps = [];
card = null;
listEl = null;
titleEl = null;
metaEl = null;
open = true;
title = nextTitle || "Assistent";
context = nextContext && typeof nextContext === "object" ? { ...nextContext } : {};
ensureCard();
if (titleEl && title) {
titleEl.textContent = title;
const meta = buildContextMeta(context);
if (meta) {
upsert("ctx", {
kind: "ctx",
label: "\u041A\u043E\u043D\u0442\u0435\u043A\u0441\u0442 \u0445\u043E\u0434\u0430",
detail: meta,
status: "done"
});
}
if (context.model) {
upsert("model", {
kind: "model",
label: "\u041C\u043E\u0434\u0435\u043B\u044C",
detail: String(context.model),
status: "done"
});
}
paint();
}
@@ -1183,6 +1386,9 @@
s.status = "done";
}
});
if (summary) {
title = summary;
}
if (summary && titleEl) {
titleEl.textContent = summary;
}
@@ -1192,6 +1398,13 @@
card.classList.add("sa-activity-done");
}
}
function snapshot() {
return slimActivityTrace({
title: titleEl?.textContent || title || "Assistent",
context,
steps: steps.slice()
});
}
function noteModelCommands(patch) {
if (!patch || typeof patch !== "object") {
return;
@@ -1200,16 +1413,16 @@
if (keys.length) {
done("delta", {
kind: "delta",
label: "\u041E\u0431\u043D\u043E\u0432\u0438\u043B \u0441\u0435\u0441\u0441\u0438\u044E",
detail: keys.slice(0, 10).join(", ")
label: "\u041F\u0430\u0442\u0447 \u0441\u0435\u0441\u0441\u0438\u0438",
detail: keys.slice(0, 12).join(", ")
});
}
const ask = Array.isArray(patch.ask) ? patch.ask.map(String) : patch.ask ? [String(patch.ask)] : [];
if (ask.length) {
upsert("ask", {
kind: "ask",
label: `\u0417\u0430\u043F\u0440\u043E\u0441\u0438\u043B ${ask.join(", ")}`,
detail: "\u043F\u043E\u0434\u0433\u0440\u0443\u0436\u0430\u044E \u0434\u0435\u0442\u0430\u043B\u0438\u2026",
label: `ask: ${ask.join(", ")}`,
detail: "\u0437\u0430\u043F\u0440\u043E\u0441 \u0434\u0435\u0442\u0430\u043B\u0435\u0439 \u0443 SwarmUI",
status: "running"
});
}
@@ -1217,7 +1430,7 @@
const slots = [].concat(patch.look_at || patch.vision_from || patch.vision_slots || []);
upsert("look", {
kind: "look",
label: "\u0421\u043C\u043E\u0442\u0440\u0438\u0442 \u043D\u0430 \u043A\u0430\u0434\u0440",
label: "look_at",
detail: slots.map(String).slice(0, 4).join(", "),
status: "running"
});
@@ -1226,25 +1439,27 @@
upsert("generate", {
kind: "generate",
label: "Generate",
detail: "\u0436\u0434\u0451\u0442 \u043F\u0430\u0439\u043F\u043B\u0430\u0439\u043D\u2026",
detail: "\u043A\u043B\u0438\u0435\u043D\u0442 \u2192 SwarmUI",
status: "running"
});
}
if (Array.isArray(patch.variants) && patch.variants.length) {
upsert("variants", {
kind: "generate",
label: `\u0412\u0430\u0440\u0438\u0430\u043D\u0442\u044B \xD7${patch.variants.length}`,
label: `variants \xD7${patch.variants.length}`,
status: "running"
});
}
}
return {
begin,
setContext,
upsert,
done,
skip,
fail,
finish,
snapshot,
noteModelCommands,
get steps() {
return steps.slice();
@@ -1253,6 +1468,8 @@
}
function attachActivity(SA2) {
SA2.createActivityController = createActivityController;
SA2.mountActivityTrace = mountActivityTrace;
SA2.slimActivityTrace = slimActivityTrace;
}
// src/kreaProfile.js
@@ -1566,7 +1783,8 @@
settingsPersonaId: null,
ollamaHealth: "unknown",
trainingLock: false,
activity: null
activity: null,
pendingActivityTrace: null
};
function getActivity() {
if (state.activity) {
@@ -1581,12 +1799,24 @@
}
return state.activity;
}
function activityBegin(title) {
function activityBegin(title, context) {
const a = getActivity();
if (a) {
a.begin(title || "Assistent");
a.begin(title || "Assistent", context || {});
}
}
function captureActivityTrace() {
const a = getActivity();
if (!a || typeof a.snapshot !== "function") {
return null;
}
const snap = a.snapshot();
if (snap && snap.steps && snap.steps.length) {
state.pendingActivityTrace = snap;
return snap;
}
return null;
}
function activityStep(id, patch) {
const a = getActivity();
if (a) {
@@ -1603,6 +1833,7 @@
const a = getActivity();
if (a) {
a.finish(summary);
captureActivityTrace();
}
}
const HOP_BUDGET = 4;
@@ -4364,7 +4595,8 @@ ${patch.prompt}`;
role: m.role,
content: content.slice(0, 4e3),
persona: m.persona || void 0,
pack: m.pack || void 0
pack: m.pack || void 0,
activity: window.SA && typeof SA.slimActivityTrace === "function" ? SA.slimActivityTrace(m.activity) : m.activity || void 0
};
});
}
@@ -4725,7 +4957,8 @@ ${patch.prompt}`;
appendMessage("assistant", m.content, null, null, {
persona: m.persona ? { id: m.persona, title: m.persona } : null,
pack: m.pack,
historical: true
historical: true,
activity: m.activity
});
}
}
@@ -6162,7 +6395,14 @@ ${patch.prompt}`;
} else {
appendMessage("assistant", prose);
}
state.history.push({ role: "assistant", content: prose, persona, pack });
state.history.push({
role: "assistant",
content: prose,
persona,
pack,
activity: captureActivityTrace() || state.pendingActivityTrace || void 0
});
state.pendingActivityTrace = null;
persistHistory();
state.turnSettled = true;
}
@@ -6711,6 +6951,9 @@ ${patch.prompt}`;
if (role === "assistant" && !(meta && meta.historical)) {
mountCurateButtons(div, meta);
}
if (role === "assistant" && meta?.historical && meta?.activity && window.SA && typeof SA.mountActivityTrace === "function") {
SA.mountActivityTrace(meta.activity, { box, insertBefore: div, collapsed: true });
}
box.appendChild(div);
scrollMessagesToBottom({ force: true });
return div;
@@ -9725,16 +9968,6 @@ ${HELP_TEXT}`);
state.turnSettled = false;
state.llmParked = false;
setInterruptVisible(true);
if (!isContinuationTurn(opts) && !opts.fromAskHop) {
activityBegin(opts.fromDebug ? "Debug" : "\u0425\u043E\u0434 Assistent");
activityStep("think", { kind: "think", label: "\u0414\u0443\u043C\u0430\u044E\u2026", status: "running" });
} else if (opts.fromAskHop) {
activityStep("think", { kind: "think", label: "\u041E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0441 \u0434\u0435\u0442\u0430\u043B\u044F\u043C\u0438\u2026", status: "running" });
} else if (opts.fromVisionHop) {
activityStep("look", { kind: "look", label: "\u0421\u043C\u043E\u0442\u0440\u0438\u0442 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435\u2026", status: "running" });
} else if (opts.fromPromptEnRetry) {
activityStep("prep", { kind: "prep", label: "\u0414\u043E\u043F\u0438\u0441\u044B\u0432\u0430\u044E EN-\u043F\u0440\u043E\u043C\u043F\u0442\u2026", status: "running" });
}
if (state.expectColdLoad && !isContinuationTurn(opts)) {
startBusyUi("warming");
setStatus("\u0412\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u044E LLM \u0432 GPU\u2026");
@@ -9811,6 +10044,27 @@ ${HELP_TEXT}`);
persistHistory();
}
}
if (!isContinuationTurn(opts) && !opts.fromAskHop) {
state.pendingActivityTrace = null;
const hopLabel = opts.fromVisionHop ? "vision hop" : opts.fromPromptEnRetry ? "krea prep" : opts.fromAutoCritique ? "critique" : opts.fromDebug ? "debug" : "";
activityBegin(opts.fromDebug ? "Debug" : "\u0425\u043E\u0434 Assistent", {
model,
pack,
persona,
skills: opts.fromDebug ? [] : state.enabledSkills || [],
hop: hopLabel || void 0
});
activityStep("think", { kind: "think", label: "\u0414\u0443\u043C\u0430\u044E\u2026", status: "running" });
} else if (opts.fromAskHop) {
getActivity()?.setContext?.({ hop: "ask hop", pack, model });
activityStep("think", { kind: "think", label: "\u041E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0441 \u0434\u0435\u0442\u0430\u043B\u044F\u043C\u0438\u2026", status: "running" });
} else if (opts.fromVisionHop) {
getActivity()?.setContext?.({ hop: "vision hop" });
activityStep("look", { kind: "look", label: "\u0421\u043C\u043E\u0442\u0440\u0438\u0442 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435\u2026", status: "running" });
} else if (opts.fromPromptEnRetry) {
getActivity()?.setContext?.({ hop: "krea prep" });
activityStep("prep", { kind: "prep", label: "\u0414\u043E\u043F\u0438\u0441\u044B\u0432\u0430\u044E EN-\u043F\u0440\u043E\u043C\u043F\u0442\u2026", status: "running" });
}
if (!opts.fromDebug && !opts.fromCompress && !opts.fromAskHop && !opts.fromVisionHop && !opts.fromAutoCritique && !isContinuationTurn(opts)) {
try {
await maybeAutoCompressBeforeSend(chatEpoch);
@@ -9885,8 +10139,6 @@ ${HELP_TEXT}`);
}
updateCtxChip();
const prose = visibleAssistantProse(reply);
state.history.push({ role: "assistant", content: prose, persona, pack });
persistHistory();
setBusyPhase(state.pendingSilentGen ? "silent_gen" : "thinking");
try {
await handleReplySideEffects(reply, civitaiResults, {
@@ -9895,6 +10147,26 @@ ${HELP_TEXT}`);
userWantsGenerate: !!opts.userWantsGenerate || !!state.turnUserWantsGenerate || !isMachineTurn(opts) && state.pendingSilentGen,
attachedSlotIds: visionSlots.map((s) => s.id)
});
if (meta.system_layers && typeof meta.system_layers === "object") {
const layers = Object.keys(meta.system_layers).filter((k) => meta.system_layers[k]);
if (layers.length) {
activityDone("layers", {
kind: "ctx",
label: "system_layers",
detail: layers.slice(0, 12).join(", ")
});
}
}
captureActivityTrace();
state.history.push({
role: "assistant",
content: prose,
persona,
pack,
activity: state.pendingActivityTrace || void 0
});
state.pendingActivityTrace = null;
persistHistory();
} finally {
if (chatEpoch !== state.chatEpoch) {
return;
+18
View File
@@ -3081,6 +3081,24 @@
word-break: break-word;
}
.sa-activity-meta {
padding: 0.15rem 0.55rem 0.25rem;
font-size: 0.72rem;
line-height: 1.35;
opacity: 0.78;
word-break: break-word;
border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}
.sa-activity-historical {
margin: 0.25rem 0 0.35rem;
opacity: 0.95;
}
.sa-activity-historical .sa-activity-head {
cursor: pointer;
}
@keyframes sa-spin {
to { transform: rotate(360deg); }
}