Q25: runs leave outcomes and failure causes as searchable memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-08 05:34:17 -04:00
1 parent 12e2d9dcd5
commit 8c70111c93
4 files changed
+136

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
import type { DatabaseSync } from "node:sqlite";
import { KnowledgeService } from "./knowledge-service";
const MEMORY_CHARS = 2_000;
/** Keeps what a finished or failed run of real work concluded, so later runs can find it. */
export function recordRunMemory(
db: DatabaseSync,
input: { orgId: string; runId: string; taskTitle: string; outcome: "completed" | "failed"; text: string },
): void {
const text = input.text.trim();
if (!text) return;
new KnowledgeService(db).create({
orgId: input.orgId,
topic: `${input.outcome === "completed" ? "Outcome" : "Failure"}: ${input.taskTitle}`,
category: input.outcome === "completed" ? "run_outcome" : "failure_cause",
content: text.length > MEMORY_CHARS ? `${text.slice(0, MEMORY_CHARS)}…` : text,
importance: "normal",
sourceType: "task_run",
sourceId: input.runId,
});
}
@@ -23,6 +23,7 @@ import {
} from "./task-prompts";
import { renderTaskHandoff } from "./agent-task-handoff";
import { relevantKnowledgeLines } from "./agent-knowledge-tools";
import { recordRunMemory } from "./run-memory";
import { createRunContextRecorder, createRunMessagePoller, loadChatWakeContext } from "./chat-wake-context";
import { runningWorkspaceWriter } from "./workspace-write-lock";
import { inputBudgetTokens, skillBudgetTokens } from "./context-budget";
@@ -126,6 +127,8 @@ export class TaskExecutionService {
action: "failed",
payload: { taskId: run.task_id, agentId: run.agent_id, providerId: run.provider_id, stage: "preparation", errorMessage: message },
});
const task = this.db.prepare("select title, origin from tasks where id = ?").get(run.task_id) as { title: string; origin: string | null } | undefined;
if (task && task.origin === null) recordRunMemory(this.db, { orgId, runId, taskTitle: task.title, outcome: "failed", text: message });
throw error;
}
return this.executeAgentRun(prepared);
@@ -220,6 +223,7 @@ export class TaskExecutionService {
}
this.completeRun(orgId, runId, result.lastRequestId);
this.cooldowns.clear(provider);
if (task.origin === null) recordRunMemory(this.db, { orgId, runId, taskTitle: task.title, outcome: "completed", text: result.finalText });
this.audit.record({
orgId,
entityType: "task_run",
@@ -244,6 +248,7 @@ export class TaskExecutionService {
const message = error instanceof Error ? error.message : String(error);
this.failRun(orgId, runId, providerRequestId, message);
if (isTransientProviderFailure(message)) this.cooldowns.park(provider, message);
else if (task.origin === null) recordRunMemory(this.db, { orgId, runId, taskTitle: task.title, outcome: "failed", text: message });
this.audit.record({
orgId,
entityType: "task_run",
+6
View File
@@ -1,5 +1,8 @@
import { canonicalMentionHandle } from "../../shared/mention-handles";
const RECORD_LEARNINGS_INSTRUCTION =
"Before you finish, record anything the company should remember — decisions, preferences, why something failed, conclusions you verified — with record_knowledge.";
export interface TaskExecutionTaskRow {
id: string;
org_id: string;
@@ -226,6 +229,8 @@ export function buildTaskPrompt(input: {
input.chatContext.fresh || "(nothing new since your last reply)",
"",
`You have been brought into this thread. Reply to the people here in your own voice — answer what they asked, give your honest take, push back if you disagree, or hand work over. Stay in the thread by passing thread_id="${input.chatContext.threadId}" to send_message. This is a conversation between colleagues, not a status report; only change a task's status if the work itself actually moved.`,
"",
RECORD_LEARNINGS_INSTRUCTION,
);
return lines.join("\n");
}
@@ -254,6 +259,7 @@ export function buildTaskPrompt(input: {
`First, read_messages to check if anyone has said something you should respond to — reply to them like a colleague, not a report. Then do the work: write real output with write_deliverable, raise decisions and blockers with ${managerReference} in a direct message (send_message with to=), and keep team-wide topics in channels. Hand files over with /handoff and attachment_file_ids. Keep conversation going — ask questions, share your thinking, push back if something seems wrong. Before you finish: if the work is genuinely done, call update_task_status(task_id="${input.task.id}", status="done"); if it needs another session, move it to "in_progress" and say in the thread what remains — an open task will be run again.`,
);
}
lines.push("", RECORD_LEARNINGS_INSTRUCTION);
return lines.join("\n");
}