fix(consult): five-minute answer clock as a hang guard; general mode runs on it from the start (BUG-1142)
Independent Staging Quality Gate / validate (push) Successful in 13m22s
Independent Staging Quality Gate / publish (push) Successful in 3m46s

The 70s answer clock was sized for a non-reasoning writer; thinking tokens
come out of the same clock and deepseek-v4-pro took 77s on a parents answer.
Product 2026-10-01 chose five minutes. The general / no-birth-minute loop has
no tools, so it now starts on the answer clock instead of the 110s tool clock.
Tool phase and domain budget unchanged; maxDuration 240 -> 480.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-10-01 11:12:01 +08:00
co-authored by Claude Opus 5.5
parent 8912ba685a
commit 04c09cbf4d
11 changed files with 185 additions and 18 deletions
+6 -3
View File
@@ -115,14 +115,14 @@ import { generateSessionTitle, shouldGenerateSessionTitle } from "@/lib/session-
import { z } from "zod";
export const runtime = "nodejs";
export const maxDuration = 240;
export const maxDuration = 480;
// The step budget, the wall-clock budget and the domain cap all bound this same
// run, so they are declared as one group in @/mastra/consultation-tools with the
// reasoning that ties them together. maxDuration above is the ceiling they must
// stay under: the tool phase (AGENT_TIMEOUT_MS) plus the answer phase's own
// clock (CONSULTATION_ANSWER_TIMEOUT_MS), 180s, plus setup and settlement
// (BUG-1051, BUG-1053). Self-hosted `node server.js` does not enforce it; it
// clock (CONSULTATION_ANSWER_TIMEOUT_MS), 410s, plus setup and settlement
// (BUG-1051, BUG-1053, TASK-consult-answer-clock-20261001). Self-hosted `node server.js` does not enforce it; it
// documents the ceiling.
const chatRequestMetadataSchema = z.object({
@@ -1103,10 +1103,13 @@ export async function POST(request: Request) {
// that deadline until the calculation result is in hand, then on the answer
// clock (CONSULTATION_ANSWER_TIMEOUT_MS), because its next step writes the
// answer. Continuation and answer retries share the same answer clock.
// The general / no-birth-minute agent has no tools, so its loop is the
// answer from the first step and runs on the answer clock from the start.
const runClock = createConsultationRunClock({
toolPhaseMs: AGENT_TIMEOUT_MS,
answerMs: CONSULTATION_ANSWER_TIMEOUT_MS,
answerReady: () => state.consultationToolCompleted,
answerFromStart: usesPublicDailyGeneralAgent(consultationMode, generalDailyContext),
});
const agentAbortSignal = runClock.toolSignal;
const streamOptions = {