diff --git a/src/app.ts b/src/app.ts index 79c25f0..5548053 100644 --- a/src/app.ts +++ b/src/app.ts @@ -752,7 +752,11 @@ export function createApp(o: AppOptions): App { teamRt.memberDone(member.name, "", `error — ${(e as Error).message}`, []) }) }, - lead: { note: (text) => engine.note(text), wake: (text) => wake(text, shownLine(text)) }, + lead: { + note: (text) => engine.note(text), + wake: (text) => wake(text, shownLine(text)), + waiting: (model) => engine.progress.set({ phase: "waiting_for_model", model: engine.model.spec.name ?? model, reason: "slots" }), + }, tools: () => tools, resolve: (agent, model) => { if (!agentList.some((a) => a.name === agent)) return { error: `There is no agent "${agent}". The agents are: ${agentList.map((a) => a.name).join(", ")}.` } diff --git a/src/team/runtime.ts b/src/team/runtime.ts index 66ed872..ad2e0c7 100644 --- a/src/team/runtime.ts +++ b/src/team/runtime.ts @@ -29,7 +29,7 @@ export interface RuntimeOptions { /** Make a member's engine (attached here with `attach`) and start its first turn with `text`. */ spawnMember(member: TeamAgent, text: string): void /** The lead: a message at its next step (`note`), or — idle — a turn of its own (`wake`). */ - lead: { note(text: string): void; wake(text: string): void } + lead: { note(text: string): void; wake(text: string): void; /** Spec 3.4.0: its progress while it waits for a slot. */ waiting?(model: string): void } /** An unknown agent or model, answered as task answers it; undefined when both are known. The * model's ref as the session knows it. */ resolve?(agent: string, model: string | undefined): { error: string } | { model: string } @@ -74,17 +74,24 @@ export class TeamRuntime { /** Said on the lead's bus as `team`, at most four times a second (acp.md `team` event). */ private changed() { - if (this.pace) return + // A change inside the pace window is said once at its end, so the last state always goes out. + if (this.pace) return void (this.trailing = true) this.o.bus.emit({ type: "team" }) this.pace = setTimeout(() => { this.pace = undefined + if (this.trailing) { + this.trailing = false + this.changed() + } }, 1000 / 4) } + private trailing = false /** Something changed that a reader must see now (the tools' answers, tests): said at once. */ private changedNow() { if (this.pace) clearTimeout(this.pace) this.pace = undefined + this.trailing = false this.o.bus.emit({ type: "team" }) } @@ -206,6 +213,7 @@ export class TeamRuntime { return true } this.o.bus.emit({ type: "notice", message: `waiting for a free slot: the model serves ${parallelFrom(this.team.capacityOf(this.team.model))} requests at once and the team's members hold them` }) + this.o.lead.waiting?.(this.team.model) this.changed() return await new Promise((resolve) => { const onAbort = () => {