The switch is off unless CONSULT_ANSWER_REASONING_EFFORT is set, so today's request body stays the same. A set value is sent as top-level reasoning_effort only on the answer step. The real A/B did not run: there is no model key. Request-body probe, the named frontend tests (114 pass, 0 fail), tsc, and lint (0 errors) were run. Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor <cursoragent@cursor.com>
59 lines
3.3 KiB
TypeScript
59 lines
3.3 KiB
TypeScript
// TASK-consult-answer-clock-20261001: the answer clock is a five-minute hang
|
|
// guard, the general / no-birth-minute loop runs on it from the start, and the
|
|
// domain budget does not move with it.
|
|
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
AGENT_TIMEOUT_MS,
|
|
CONSULTATION_ANSWER_TIMEOUT_MS,
|
|
CONSULTATION_DOMAIN_WALL_CLOCK_MS,
|
|
MAX_CONSULTATION_DOMAINS,
|
|
createConsultationRunClock,
|
|
} from "../src/mastra/consultation-tools.ts";
|
|
|
|
const route = readFileSync(new URL("../src/app/api/consult/route.ts", import.meta.url), "utf8");
|
|
const tools = readFileSync(new URL("../src/mastra/consultation-tools.ts", import.meta.url), "utf8");
|
|
|
|
test("the answer clock is five minutes and the tool phase stays 110s", () => {
|
|
assert.equal(CONSULTATION_ANSWER_TIMEOUT_MS, 300_000);
|
|
assert.equal(AGENT_TIMEOUT_MS, 110_000);
|
|
const maxDuration = Number(route.match(/export const maxDuration = (\d+);/)?.[1]);
|
|
assert.ok(AGENT_TIMEOUT_MS + CONSULTATION_ANSWER_TIMEOUT_MS + 60_000 <= maxDuration * 1000, "setup and settlement still fit");
|
|
});
|
|
|
|
test("the domain budget is unchanged by the answer clock", () => {
|
|
// Same numbers as before the change: 110s tool phase minus a 45s reserve,
|
|
// 31s per domain, so two domains run.
|
|
assert.equal(CONSULTATION_DOMAIN_WALL_CLOCK_MS, 65_000);
|
|
assert.equal(MAX_CONSULTATION_DOMAINS, 2);
|
|
const formula = tools.match(/export const CONSULTATION_DOMAIN_WALL_CLOCK_MS = [^;]+;/)?.[0] ?? "";
|
|
assert.match(formula, /AGENT_TIMEOUT_MS - CONSULTATION_ANSWER_RESERVE_MS/);
|
|
assert.doesNotMatch(formula, /CONSULTATION_ANSWER_TIMEOUT_MS/);
|
|
});
|
|
|
|
test("a loop with no calculation runs on the answer clock from the start", async () => {
|
|
const clock = createConsultationRunClock({ toolPhaseMs: 30, answerMs: 200, answerFromStart: true });
|
|
await new Promise((resolve) => setTimeout(resolve, 60));
|
|
assert.equal(clock.toolSignal.aborted, true, "the tool phase's timer still fires");
|
|
assert.equal(clock.loopSignal.aborted, false, "but it does not cut a loop that is already writing the answer");
|
|
await new Promise((resolve) => setTimeout(resolve, 220));
|
|
assert.equal(clock.loopSignal.aborted, true, "the answer clock still bounds it");
|
|
assert.equal(clock.answerSignal(), clock.answerSignal(), "retries and continuations share the same clock");
|
|
});
|
|
|
|
test("the consult route starts the general / no-birth-minute loop on the answer clock", () => {
|
|
assert.match(
|
|
route,
|
|
/const runClock = createConsultationRunClock\(\{[\s\S]*?answerFromStart: usesPublicDailyGeneralAgent\(consultationMode, generalDailyContext\),\s*\}\);/,
|
|
);
|
|
// That branch still streams on the shared loop signal and keeps its traces.
|
|
const general = route.match(/if \(usesPublicDailyGeneralAgent\(consultationMode, generalDailyContext\)\) \{[\s\S]*?\n {4}\}\n/)?.[0] ?? "";
|
|
// 原值:await streamWithOverflowRetry(agent)
|
|
// 新值:await streamWithOverflowRetry(agent, generalStreamOptions)
|
|
// 原因:公开日历从第一步就是答题。开关打开时这一步要带推理强度;不传选项就没有 prepareStep,强度加不上去。开关不设时这些选项不改请求体。
|
|
assert.match(general, /await streamWithOverflowRetry\(agent, generalStreamOptions\)/);
|
|
assert.match(general, /onError: \(error\) => settleRun\(\s*cancel,/);
|
|
});
|