mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-10 22:20:27 +00:00
compaction: live progress signal + user-message cap
COMPACTING event drives a 'Compacting context…' transient in the GUI. Cap the compacted block's user-message list at 40 with an honest omitted count.
This commit is contained in:
@@ -64,9 +64,14 @@ test("the compacted divider renders mid-session and the transcript stays intact"
|
||||
|
||||
await box.fill("compact the context");
|
||||
await box.press("Enter");
|
||||
// The transient signal shows while the summarizer runs, then yields to the divider.
|
||||
await expect(page.getByText("Compacting context…").first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
});
|
||||
await expect(
|
||||
page.getByText("Context compacted — earlier turns were summarized").first(),
|
||||
).toBeVisible({ timeout: 10_000 });
|
||||
await expect(page.getByText("Compacting context…")).toHaveCount(0);
|
||||
await expect(
|
||||
page.getByText("Still on it — continuing where I left off.").first(),
|
||||
).toBeVisible();
|
||||
|
||||
@@ -681,12 +681,16 @@ export async function mockApi(page: import("@playwright/test").Page) {
|
||||
}, 120);
|
||||
return;
|
||||
}
|
||||
// Auto-compaction (OPE-27): the server compacts mid-run and emits the marker,
|
||||
// then the turn continues normally — the divider must render inline.
|
||||
// Auto-compaction (OPE-27): the server signals `compacting` (the transient
|
||||
// spinner label), summarizes for a beat, then emits the marker and the turn
|
||||
// continues normally — the divider must render inline.
|
||||
if (/compact the context/i.test(msg.text)) {
|
||||
send("compacted", { text: "Context compacted — earlier turns were summarized" });
|
||||
send("assistant_message", { text: "Still on it — continuing where I left off." });
|
||||
send("turn_done");
|
||||
send("compacting", {});
|
||||
setTimeout(() => {
|
||||
send("compacted", { text: "Context compacted — earlier turns were summarized" });
|
||||
send("assistant_message", { text: "Still on it — continuing where I left off." });
|
||||
send("turn_done");
|
||||
}, 400);
|
||||
return;
|
||||
}
|
||||
// A turn that dies on a provider error; the follow-up {type:"retry"} recovers.
|
||||
|
||||
@@ -172,6 +172,10 @@ export function App() {
|
||||
const [mode, setMode] = useState("interactive");
|
||||
const [connected, setConnected] = useState(false);
|
||||
const [running, setRunning] = useState(false);
|
||||
// Transient "Compacting context…" indicator (OPE-27): set by the `compacting` event,
|
||||
// cleared by whatever the engine emits next — the summarizer call is otherwise a
|
||||
// multi-second silent stall mid-turn.
|
||||
const [compacting, setCompacting] = useState(false);
|
||||
const [items, setItems] = useState<Item[]>([]);
|
||||
const [streaming, setStreamingState] = useState("");
|
||||
// Ref mirror of `streaming`: the WS handler closure is built once per socket and can't read
|
||||
@@ -576,6 +580,9 @@ export function App() {
|
||||
},
|
||||
]);
|
||||
};
|
||||
// Any engine event after `compacting` means the summarizer finished (compacted /
|
||||
// silent no-op / failure prompt) — the transient must never outlive it.
|
||||
if (ev.type !== "compacting") setCompacting(false);
|
||||
switch (ev.type) {
|
||||
case "ready":
|
||||
setConnected(true);
|
||||
@@ -709,6 +716,9 @@ export function App() {
|
||||
if (d.model) setModel(d.model);
|
||||
setItems((p) => [...p, { kind: "notice", tone: "info", text: d.text || "Model switched" }]);
|
||||
break;
|
||||
case "compacting":
|
||||
setCompacting(true);
|
||||
break;
|
||||
case "compacted":
|
||||
// Auto-compaction marker (OPE-27): outbound-only — the transcript stays intact,
|
||||
// this divider just shows where the model's memory was summarized.
|
||||
@@ -1519,7 +1529,11 @@ export function App() {
|
||||
<ThinkingBlock text={reasoningStream} live />
|
||||
</div>
|
||||
)}
|
||||
{/* Compaction runs between provider turns (nothing streams during it), so
|
||||
the transient takes over the waiting slot with a specific label. */}
|
||||
{running && compacting && <WaitingForAgent label="Compacting context…" />}
|
||||
{running &&
|
||||
!compacting &&
|
||||
!reasoningStream &&
|
||||
(!streaming || streamMode(streaming, items, running) === "hold") &&
|
||||
!lastItemIsAssistant(items) && <WaitingForAgent />}
|
||||
@@ -1685,12 +1699,12 @@ function lastItemIsAssistant(items: Item[]): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
function WaitingForAgent() {
|
||||
function WaitingForAgent({ label }: { label?: string }) {
|
||||
return (
|
||||
<div className="waiting-transcript">
|
||||
<div className="waiting-row" aria-live="polite">
|
||||
<span className="waiting-spinner" />
|
||||
<span>Waiting for agent...</span>
|
||||
<span>{label || "Waiting for agent..."}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -18,6 +18,7 @@ export type EventType =
|
||||
| "input_rejected"
|
||||
| "interrupted"
|
||||
| "model_changed"
|
||||
| "compacting"
|
||||
| "compacted"
|
||||
| "turn_done";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user