mirror of
https://github.com/wassname/pi-goals.git
synced 2026-09-21 13:20:15 +08:00
Reduce repeated worker notices and shrinking-backlog reminders
This commit is contained in:
+7
-6
@@ -357,18 +357,19 @@ export default function mainSupervisor(pi: ExtensionAPI) {
|
||||
}
|
||||
const records = <T,>(ctx: ExtensionContext, type: string): T[] => ctx.sessionManager.getBranch().flatMap(entry => entry.type === "custom" && entry.customType === type ? [entry.data as T] : []);
|
||||
const pendingReports = (ctx: ExtensionContext) => records<Report>(ctx, REPORT).filter(report => !records<ReportReview>(ctx, REVIEW).some(review => review.report === report.id) && !records<Report>(ctx, REPORT).some(newer => newer.session === report.session && newer.supersedes === report.id));
|
||||
function recordReport(ctx: ExtensionContext, report: Report, wake = true) {
|
||||
function recordReport(ctx: ExtensionContext, report: Report, wake = true, show = true) {
|
||||
if (records<Report>(ctx, REPORT).some(saved => saved.id === report.id)) return;
|
||||
pi.appendEntry(REPORT, report);
|
||||
send(workerReview(report.plan, report.session, `${report.id}\n${report.text}`), false);
|
||||
if (show) send(workerReview(report.id, report.text), false);
|
||||
if (wake && ctx.isIdle()) remindReports(ctx);
|
||||
}
|
||||
function remindReports(ctx: ExtensionContext) {
|
||||
if (state.child || state.mode !== "supervising") return;
|
||||
const ids = pendingReports(ctx).map(report => report.id);
|
||||
const fingerprint = digest(JSON.stringify(ids));
|
||||
if (!ids.length || records<string>(ctx, REVIEW_REMINDER).at(-1) === fingerprint) return;
|
||||
pi.appendEntry(REVIEW_REMINDER, fingerprint);
|
||||
const branch = ctx.sessionManager.getBranch();
|
||||
const sinceReminder = branch.slice(branch.map(entry => entry.type === "custom" ? entry.customType : "").lastIndexOf(REVIEW_REMINDER) + 1);
|
||||
if (!sinceReminder.some(entry => entry.type === "custom" && entry.customType === REPORT && ids.includes((entry.data as Report).id))) return;
|
||||
pi.appendEntry(REVIEW_REMINDER);
|
||||
send(pendingReportReviews(ids));
|
||||
}
|
||||
pi.registerEntryRenderer(REVIEW, entry => new Text((entry.data as ReportReview).content, 0, 0));
|
||||
@@ -458,7 +459,7 @@ export default function mainSupervisor(pi: ExtensionAPI) {
|
||||
if (retry && records<Report>(ctx, REPORT).some(report => report.id === retry && report.text === message.content!.text)) { aliases.set(id, retry); continue; }
|
||||
aliases.set(id, id);
|
||||
const supersedes = message.supersedes ? aliases.get(`${sender}:${message.supersedes}`) || `${sender}:${message.supersedes}` : undefined;
|
||||
recordReport(ctx, { id, session: sender, sessionFile: owner.worker.sessionFile, plan: owner.plan, requestId: owner.worker.requestId, text: message.content.text, supersedes }, false);
|
||||
recordReport(ctx, { id, session: sender, sessionFile: owner.worker.sessionFile, plan: owner.plan, requestId: owner.worker.requestId, text: message.content.text, supersedes }, false, false); // Intercom already displays and saves this report.
|
||||
}
|
||||
if (ctx.isIdle()) remindReports(ctx);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export function noticeDisplay(pi: ExtensionAPI) {
|
||||
context.messageType === "user" && mirrored.has(markdown) ? "" : markdown);
|
||||
pi.registerEntryRenderer(NOTICE, (entry, { expanded }, theme) => {
|
||||
const { content } = entry.data as { content: string };
|
||||
const label = content.includes("\nPlan changed.") ? "Plan changed · review requested" : "Goal instructions";
|
||||
const label = content.includes("\nPlan changed") ? "Plan changed · review requested" : content.includes("\nPending worker reviews:") ? "Worker reviews pending" : "Goal instructions";
|
||||
if (expanded) return new Markdown(content, 0, 0, getMarkdownTheme());
|
||||
return {
|
||||
render: (width) => [truncateToWidth(theme.fg("muted", `[pi-goals] ${label} · ${keyHint("app.tools.expand", "expand")}`), width)],
|
||||
|
||||
+3
-3
@@ -193,9 +193,9 @@ export function workerAttachment(plan: string, session: string, text: string): s
|
||||
// Pi/OpenAI: supervisor-authored report reviews, separate from goal completion.
|
||||
export const reportReviewDescription = "Review an owned worker report after inspecting its actual artifacts. Quote the assigned goal/task and evidence from files (optional saved-session entryId selects decoded message text). State observations and unmet requirements; use accepted, changes_requested or blocked. Changes requested need a concrete continuation. Text quotes are checked, not their relevance or quality. Non-text evidence needs a nonempty capture and specific observation. Delivery stays pending until the worker saves the visible review. Acceptance never completes a goal or wakes/closes the worker.";
|
||||
export const reportReviewContent = (report: string, sessionFile: string, sources: string[], observation: string, unmet: string, verdict: string, continuation: string) => `Worker review: ${verdict}\nReport: ${report}\nSaved session: ${sessionFile}\n\nAssigned goal/task:\n${sources[0]}\n\nEvidence:\n${sources.slice(1).join("\n\n")}\n\nInspected: ${observation}\nUnmet: ${unmet}\nContinuation: ${continuation || "none"}\nThis is a report review, not CompleteGoal.\n— Pi supervisor`;
|
||||
export const pendingReportReviews = (reports: string[]) => `Pending worker reviews: ${reports.join(", ")}. Inspect their saved reports and actual artifacts, then use review_subagent. Independent authorized work may continue; receipts and generic replies do not resolve reviews.`;
|
||||
export function workerReview(plan: string, session: string, text: string): string {
|
||||
return `Worker event for ${plan}, exact Intercom session ${session}:\n${text}\nThis is a report, not completion approval. Inspect actual artifacts and saved messages; if correction is needed, send it to the same session. Preserve its visible review conversation. Respect pauses; do not reply merely to acknowledge.`;
|
||||
export const pendingReportReviews = (reports: string[]) => `Pending worker reviews: ${reports.join(", ")}. Inspect reports and artifacts; use review_subagent. Independent authorized work may continue.`;
|
||||
export function workerReview(report: string, text: string): string {
|
||||
return `Worker report ${report}:\n${text}`;
|
||||
}
|
||||
|
||||
export function manualReview(planPath: string, text: string): string {
|
||||
|
||||
+12
-1
@@ -1290,21 +1290,32 @@ it("reviews a saved worker revision through inspection, silent delivery, retry a
|
||||
parent.hooks.get("session_start")({}, parent.ctx); // Reconcile the missed stop from real saved worker history.
|
||||
await parent.command("status");
|
||||
expect(parent.ctx.ui.notify.mock.lastCall?.[0]).toContain(missed);
|
||||
await parent.hooks.get("agent_settled")({}, parent.ctx);
|
||||
form.report = missed; await review(); // Old-plan blocked review remains deliverable after retargeting.
|
||||
await parent.command("status");
|
||||
expect(parent.ctx.ui.notify.mock.lastCall?.[0]).not.toContain(missed);
|
||||
expect(parent.ctx.ui.notify.mock.lastCall?.[0]).toContain(nextReport);
|
||||
const afterReview = parent.messages.length;
|
||||
await parent.hooks.get("agent_settled")({}, parent.ctx);
|
||||
parent.hooks.get("session_start")({}, parent.ctx);
|
||||
await parent.hooks.get("agent_settled")({}, parent.ctx);
|
||||
expect(parent.messages).toHaveLength(afterReview); // Shrinking/restoring the same backlog does not wake again.
|
||||
const ordinary = (id: string, text: string, links = {}, sender = workerId) => {
|
||||
const details = { from: { id: sender }, message: { id, timestamp: Date.now(), content: { text }, ...links } };
|
||||
parent.ctx.sessionManager.getBranch().push({ type: "custom_message", id, customType: "intercom_message", content: text, details });
|
||||
parent.hooks.get("message_end")({ message: { role: "custom", customType: "intercom_message", content: text, details } }, parent.ctx);
|
||||
};
|
||||
const beforeOrdinary = parent.messages.length;
|
||||
ordinary("ordinary-a", "Partial output needs review");
|
||||
ordinary("ordinary-b", "Corrected output needs review", { supersedes: "ordinary-a" });
|
||||
ordinary("retry-b", "Corrected output needs review", { retryOf: "ordinary-b" });
|
||||
ordinary("retry-again", "Corrected output needs review", { retryOf: "retry-b" });
|
||||
ordinary("ack-only", "OK"); ordinary("foreign", "Unowned report", {}, "foreign-peer");
|
||||
parent.hooks.get("session_start")({}, parent.ctx); await parent.command("status");
|
||||
expect(parent.messages).toHaveLength(beforeOrdinary); // No second body beside Intercom's saved/displayed original.
|
||||
parent.hooks.get("session_start")({}, parent.ctx);
|
||||
await parent.hooks.get("agent_settled")({}, parent.ctx);
|
||||
expect(parent.messages).toHaveLength(beforeOrdinary + 1); // The new/revised obligation still wakes once.
|
||||
await parent.command("status");
|
||||
const pending = parent.ctx.ui.notify.mock.lastCall?.[0];
|
||||
expect(pending).toContain(`${workerId}:ordinary-b`);
|
||||
for (const excluded of ["ordinary-a", "retry-b", "retry-again", "ack-only", "foreign-peer"]) expect(pending).not.toContain(excluded);
|
||||
|
||||
Reference in New Issue
Block a user