feat: show owners a live worker status snapshot
While a worker turn is running, the assistant prompt for the working proposal's owner includes a compact status card (elapsed time, last tool activity category with age, per-category counts) built from the ACP session/update stream the runtime already receives. Other users still see only the desensitized busy state, and raw update content never enters the assistant session.
This commit is contained in:
@@ -722,6 +722,53 @@ test("image attachments degrade to a text note when the agent has no image capab
|
||||
}
|
||||
});
|
||||
|
||||
test("the owner sees a live worker activity card while others get only a sanitized busy state", async () => {
|
||||
const harness = await createHarness();
|
||||
try {
|
||||
await harness.manager.prompt(request("create proposal: toolhang", "user-a"));
|
||||
const proposal = harness.proposals.list()[0]!;
|
||||
await harness.manager.prompt(request("confirm", "user-a"));
|
||||
const internals = harness.manager as unknown as {
|
||||
active?: { activity: { turnStartedAt?: number; lastActivityAt?: number; lastCategory?: string; counts: Record<string, number> } };
|
||||
};
|
||||
await waitFor(() => (internals.active?.activity.counts.read || 0) === 2);
|
||||
|
||||
const activity = internals.active!.activity;
|
||||
assert.equal(activity.lastCategory, "execute");
|
||||
assert.equal(activity.counts.read, 2);
|
||||
assert.equal(activity.counts.execute, 1);
|
||||
assert.ok(activity.turnStartedAt);
|
||||
assert.ok(activity.lastActivityAt);
|
||||
|
||||
await harness.manager.prompt(request("进度如何", "user-a"));
|
||||
await harness.manager.prompt(request("进度如何", "user-b"));
|
||||
const progressPrompts = readLog(harness.logFile).filter((entry) => entry.method === "session/prompt"
|
||||
&& entry.text?.startsWith("[User message]\n进度如何"));
|
||||
assert.equal(progressPrompts.length, 2);
|
||||
const ownerPrompt = progressPrompts[0]!.text!;
|
||||
assert.match(ownerPrompt, /title="Test proposal" running=\d{2}:\d{2}/);
|
||||
assert.match(ownerPrompt, /lastActivity=execute \(\d+s ago\)/);
|
||||
assert.match(ownerPrompt, /counts: read×2 execute×1/);
|
||||
assert.match(ownerPrompt, /real-time snapshot of the worker/);
|
||||
const otherPrompt = progressPrompts[1]!.text!;
|
||||
assert.match(otherPrompt, /details hidden/);
|
||||
assert.doesNotMatch(otherPrompt, /lastActivity=/);
|
||||
assert.doesNotMatch(otherPrompt, /Test proposal/);
|
||||
|
||||
// Once the worker is no longer working, the card disappears.
|
||||
assert.equal(await harness.manager.stop("webhook", "chat-1", "user-a"), true);
|
||||
assert.equal(harness.proposals.get(proposal.id)!.status, "pending");
|
||||
await harness.manager.prompt(request("hello", "user-a"));
|
||||
const idlePrompts = readLog(harness.logFile).filter((entry) => entry.method === "session/prompt"
|
||||
&& entry.text?.startsWith("[User message]\nhello"));
|
||||
assert.equal(idlePrompts.length, 1);
|
||||
assert.match(idlePrompts[0]!.text!, /no active worker/);
|
||||
assert.doesNotMatch(idlePrompts[0]!.text!, /lastActivity=/);
|
||||
} finally {
|
||||
await closeHarness(harness);
|
||||
}
|
||||
});
|
||||
|
||||
test("an assistant session idle beyond the reset threshold is dropped when the owner has no unfinished proposal", async () => {
|
||||
const harness = await createHarness();
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user