perf(test): split reply queue seams and unit shards

This commit is contained in:
Peter Steinberger
2026-04-06 19:30:47 +01:00
parent 06d57e5107
commit 7a736bff90
18 changed files with 234 additions and 330 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
- No real keys required - No real keys required
- Should be fast and stable - Should be fast and stable
- Projects note: - Projects note:
- Untargeted `pnpm test` now runs six smaller shard configs (`core-unit-src`, `core-unit-support`, `core-runtime`, `agentic`, `auto-reply`, `extensions`) instead of one giant native root-project process. This cuts peak RSS on loaded machines and avoids auto-reply/extension work starving unrelated suites. - Untargeted `pnpm test` now runs eight smaller shard configs (`core-unit-src`, `core-unit-security`, `core-unit-support`, `core-contracts`, `core-runtime`, `agentic`, `auto-reply`, `extensions`) instead of one giant native root-project process. This cuts peak RSS on loaded machines and avoids auto-reply/extension work starving unrelated suites.
- `pnpm test --watch` still uses the native root `vitest.config.ts` project graph, because a multi-shard watch loop is not practical. - `pnpm test --watch` still uses the native root `vitest.config.ts` project graph, because a multi-shard watch loop is not practical.
- `pnpm test`, `pnpm test:watch`, and `pnpm test:perf:imports` route explicit file/directory targets through scoped lanes first, so `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` avoids paying the full root project startup tax. - `pnpm test`, `pnpm test:watch`, and `pnpm test:perf:imports` route explicit file/directory targets through scoped lanes first, so `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` avoids paying the full root project startup tax.
- `pnpm test:changed` expands changed git paths into the same scoped lanes when the diff only touches routable source/test files; config/setup edits still fall back to the broad root-project rerun. - `pnpm test:changed` expands changed git paths into the same scoped lanes when the diff only touches routable source/test files; config/setup edits still fall back to the broad root-project rerun.
+1 -1
View File
@@ -13,7 +13,7 @@ title: "Tests"
- `pnpm test:coverage`: Runs the unit suite with V8 coverage (via `vitest.unit.config.ts`). Global thresholds are 70% lines/branches/functions/statements. Coverage excludes integration-heavy entrypoints (CLI wiring, gateway/telegram bridges, webchat static server) to keep the target focused on unit-testable logic. - `pnpm test:coverage`: Runs the unit suite with V8 coverage (via `vitest.unit.config.ts`). Global thresholds are 70% lines/branches/functions/statements. Coverage excludes integration-heavy entrypoints (CLI wiring, gateway/telegram bridges, webchat static server) to keep the target focused on unit-testable logic.
- `pnpm test:coverage:changed`: Runs unit coverage only for files changed since `origin/main`. - `pnpm test:coverage:changed`: Runs unit coverage only for files changed since `origin/main`.
- `pnpm test:changed`: expands changed git paths into scoped Vitest lanes when the diff only touches routable source/test files. Config/setup changes still fall back to the native root projects run so wiring edits rerun broadly when needed. - `pnpm test:changed`: expands changed git paths into scoped Vitest lanes when the diff only touches routable source/test files. Config/setup changes still fall back to the native root projects run so wiring edits rerun broadly when needed.
- `pnpm test`: routes explicit file/directory targets through scoped Vitest lanes. Untargeted runs now execute six sequential shard configs (`vitest.full-core-unit-src.config.ts`, `vitest.full-core-unit-support.config.ts`, `vitest.full-core-runtime.config.ts`, `vitest.full-agentic.config.ts`, `vitest.full-auto-reply.config.ts`, `vitest.full-extensions.config.ts`) instead of one giant root-project process. - `pnpm test`: routes explicit file/directory targets through scoped Vitest lanes. Untargeted runs now execute eight sequential shard configs (`vitest.full-core-unit-src.config.ts`, `vitest.full-core-unit-security.config.ts`, `vitest.full-core-unit-support.config.ts`, `vitest.full-core-contracts.config.ts`, `vitest.full-core-runtime.config.ts`, `vitest.full-agentic.config.ts`, `vitest.full-auto-reply.config.ts`, `vitest.full-extensions.config.ts`) instead of one giant root-project process.
- Selected `plugin-sdk` and `commands` test files now route through dedicated light lanes that keep only `test/setup.ts`, leaving runtime-heavy cases on their existing lanes. - Selected `plugin-sdk` and `commands` test files now route through dedicated light lanes that keep only `test/setup.ts`, leaving runtime-heavy cases on their existing lanes.
- Selected `plugin-sdk` and `commands` helper source files also map `pnpm test:changed` to explicit sibling tests in those light lanes, so small helper edits avoid rerunning the heavy runtime-backed suites. - Selected `plugin-sdk` and `commands` helper source files also map `pnpm test:changed` to explicit sibling tests in those light lanes, so small helper edits avoid rerunning the heavy runtime-backed suites.
- `auto-reply` now also splits into three dedicated configs (`core`, `top-level`, `reply`) so the reply harness does not dominate the lighter top-level status/token/helper tests. - `auto-reply` now also splits into three dedicated configs (`core`, `top-level`, `reply`) so the reply harness does not dominate the lighter top-level status/token/helper tests.
@@ -0,0 +1,86 @@
import { describe, expect, it, vi } from "vitest";
import { resolvePreparedReplyQueueState } from "./get-reply-run-queue.js";
describe("resolvePreparedReplyQueueState", () => {
it("continues immediately when queue policy does not require waiting", async () => {
const resolveBusyState = vi.fn(() => ({
activeSessionId: undefined,
isActive: false,
isStreaming: false,
}));
const result = await resolvePreparedReplyQueueState({
activeRunQueueAction: "enqueue-followup",
activeSessionId: undefined,
queueMode: "followup",
sessionKey: "session-key",
sessionId: "session-1",
abortActiveRun: vi.fn(),
waitForActiveRunEnd: vi.fn(),
refreshPreparedState: vi.fn(),
resolveBusyState,
});
expect(result).toEqual({
kind: "continue",
busyState: { activeSessionId: undefined, isActive: false, isStreaming: false },
});
expect(resolveBusyState).toHaveBeenCalledOnce();
});
it("aborts and waits for interrupt mode before continuing", async () => {
const abortActiveRun = vi.fn(() => true);
const waitForActiveRunEnd = vi.fn(async () => undefined);
const refreshPreparedState = vi.fn(async () => undefined);
const resolveBusyState = vi.fn(() => ({
activeSessionId: undefined,
isActive: false,
isStreaming: false,
}));
const result = await resolvePreparedReplyQueueState({
activeRunQueueAction: "run-now",
activeSessionId: "session-active",
queueMode: "interrupt",
sessionKey: "session-key",
sessionId: "session-1",
abortActiveRun,
waitForActiveRunEnd,
refreshPreparedState,
resolveBusyState,
});
expect(abortActiveRun).toHaveBeenCalledWith("session-active");
expect(waitForActiveRunEnd).toHaveBeenCalledWith("session-active");
expect(refreshPreparedState).toHaveBeenCalledOnce();
expect(result).toEqual({
kind: "continue",
busyState: { activeSessionId: undefined, isActive: false, isStreaming: false },
});
});
it("rechecks after wait and returns shutdown reply when still busy", async () => {
const result = await resolvePreparedReplyQueueState({
activeRunQueueAction: "run-now",
activeSessionId: "session-active",
queueMode: "interrupt",
sessionKey: "session-key",
sessionId: "session-1",
abortActiveRun: vi.fn(() => true),
waitForActiveRunEnd: vi.fn(async () => undefined),
refreshPreparedState: vi.fn(async () => undefined),
resolveBusyState: () => ({
activeSessionId: "session-after-wait",
isActive: true,
isStreaming: false,
}),
});
expect(result).toEqual({
kind: "reply",
reply: {
text: "⚠️ Previous run is still shutting down. Please try again in a moment.",
},
});
});
});
@@ -0,0 +1,48 @@
import { logVerbose } from "../../globals.js";
import type { ReplyPayload } from "../types.js";
import type { ActiveRunQueueAction } from "./queue-policy.js";
import type { QueueSettings } from "./queue.js";
export type ReplyRunQueueBusyState = {
activeSessionId: string | undefined;
isActive: boolean;
isStreaming: boolean;
};
export async function resolvePreparedReplyQueueState(params: {
activeRunQueueAction: ActiveRunQueueAction;
activeSessionId: string | undefined;
queueMode: QueueSettings["mode"];
sessionKey: string | undefined;
sessionId: string;
abortActiveRun: (sessionId: string) => boolean;
waitForActiveRunEnd: (sessionId: string) => Promise<unknown>;
refreshPreparedState: () => Promise<void>;
resolveBusyState: () => ReplyRunQueueBusyState;
}): Promise<
{ kind: "continue"; busyState: ReplyRunQueueBusyState } | { kind: "reply"; reply: ReplyPayload }
> {
if (params.activeRunQueueAction !== "run-now" || !params.activeSessionId) {
return { kind: "continue", busyState: params.resolveBusyState() };
}
if (params.queueMode === "interrupt") {
const aborted = params.abortActiveRun(params.activeSessionId);
logVerbose(
`Interrupting active run for ${params.sessionKey ?? params.sessionId} (aborted=${aborted})`,
);
}
await params.waitForActiveRunEnd(params.activeSessionId);
await params.refreshPreparedState();
const refreshedBusyState = params.resolveBusyState();
if (refreshedBusyState.isActive) {
return {
kind: "reply",
reply: {
text: "⚠️ Previous run is still shutting down. Please try again in a moment.",
},
};
}
return { kind: "continue", busyState: refreshedBusyState };
}
@@ -1,6 +1,5 @@
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { importFreshModule } from "../../../test/helpers/import-fresh.ts"; import { importFreshModule } from "../../../test/helpers/import-fresh.ts";
import type { SessionEntry } from "../../config/sessions/types.js";
vi.mock("../../agents/auth-profiles/session-override.js", () => ({ vi.mock("../../agents/auth-profiles/session-override.js", () => ({
resolveSessionAuthProfileOverride: vi.fn().mockResolvedValue(undefined), resolveSessionAuthProfileOverride: vi.fn().mockResolvedValue(undefined),
@@ -103,17 +102,8 @@ let runReplyAgent: typeof import("./agent-runner.runtime.js").runReplyAgent;
let routeReply: typeof import("./route-reply.runtime.js").routeReply; let routeReply: typeof import("./route-reply.runtime.js").routeReply;
let drainFormattedSystemEvents: typeof import("./session-system-events.js").drainFormattedSystemEvents; let drainFormattedSystemEvents: typeof import("./session-system-events.js").drainFormattedSystemEvents;
let resolveTypingMode: typeof import("./typing-mode.js").resolveTypingMode; let resolveTypingMode: typeof import("./typing-mode.js").resolveTypingMode;
let abortEmbeddedPiRunActual: typeof import("../../agents/pi-embedded-runner/runs.js").abortEmbeddedPiRun;
let clearActiveEmbeddedRun: typeof import("../../agents/pi-embedded-runner/runs.js").clearActiveEmbeddedRun;
let createReplyOperation: typeof import("./reply-run-registry.js").createReplyOperation;
let getActiveReplyRunCount: typeof import("./reply-run-registry.js").getActiveReplyRunCount; let getActiveReplyRunCount: typeof import("./reply-run-registry.js").getActiveReplyRunCount;
let isEmbeddedPiRunActiveActual: typeof import("../../agents/pi-embedded-runner/runs.js").isEmbeddedPiRunActive;
let isEmbeddedPiRunStreamingActual: typeof import("../../agents/pi-embedded-runner/runs.js").isEmbeddedPiRunStreaming;
let replyRunTesting: typeof import("./reply-run-registry.js").__testing; let replyRunTesting: typeof import("./reply-run-registry.js").__testing;
let resolveActiveEmbeddedRunSessionIdActual: typeof import("../../agents/pi-embedded-runner/runs.js").resolveActiveEmbeddedRunSessionId;
let runsTesting: typeof import("../../agents/pi-embedded-runner/runs.js").__testing;
let setActiveEmbeddedRun: typeof import("../../agents/pi-embedded-runner/runs.js").setActiveEmbeddedRun;
let waitForEmbeddedPiRunEndActual: typeof import("../../agents/pi-embedded-runner/runs.js").waitForEmbeddedPiRunEnd;
let loadScopeCounter = 0; let loadScopeCounter = 0;
function createGatewayDrainingError(): Error { function createGatewayDrainingError(): Error {
@@ -212,45 +202,15 @@ describe("runPreparedReply media-only handling", () => {
({ routeReply } = await import("./route-reply.runtime.js")); ({ routeReply } = await import("./route-reply.runtime.js"));
({ drainFormattedSystemEvents } = await import("./session-system-events.js")); ({ drainFormattedSystemEvents } = await import("./session-system-events.js"));
({ resolveTypingMode } = await import("./typing-mode.js")); ({ resolveTypingMode } = await import("./typing-mode.js"));
({ ({ __testing: replyRunTesting, getActiveReplyRunCount } =
__testing: runsTesting, await import("./reply-run-registry.js"));
abortEmbeddedPiRun: abortEmbeddedPiRunActual,
clearActiveEmbeddedRun,
isEmbeddedPiRunActive: isEmbeddedPiRunActiveActual,
isEmbeddedPiRunStreaming: isEmbeddedPiRunStreamingActual,
resolveActiveEmbeddedRunSessionId: resolveActiveEmbeddedRunSessionIdActual,
setActiveEmbeddedRun,
waitForEmbeddedPiRunEnd: waitForEmbeddedPiRunEndActual,
} = await import("../../agents/pi-embedded-runner/runs.js"));
({
__testing: replyRunTesting,
createReplyOperation,
getActiveReplyRunCount,
} = await import("./reply-run-registry.js"));
}); });
beforeEach(async () => { beforeEach(async () => {
storeRuntimeLoads.mockClear(); storeRuntimeLoads.mockClear();
updateSessionStore.mockReset(); updateSessionStore.mockReset();
vi.clearAllMocks(); vi.clearAllMocks();
runsTesting.resetActiveEmbeddedRuns();
replyRunTesting.resetReplyRunRegistry(); replyRunTesting.resetReplyRunRegistry();
const piRuntime = await import("../../agents/pi-embedded.runtime.js");
vi.mocked(piRuntime.abortEmbeddedPiRun).mockImplementation((sessionId, opts) =>
abortEmbeddedPiRunActual(sessionId, opts),
);
vi.mocked(piRuntime.isEmbeddedPiRunActive).mockImplementation((sessionId) =>
isEmbeddedPiRunActiveActual(sessionId),
);
vi.mocked(piRuntime.isEmbeddedPiRunStreaming).mockImplementation((sessionId) =>
isEmbeddedPiRunStreamingActual(sessionId),
);
vi.mocked(piRuntime.resolveActiveEmbeddedRunSessionId).mockImplementation((sessionKey) =>
resolveActiveEmbeddedRunSessionIdActual(sessionKey),
);
vi.mocked(piRuntime.waitForEmbeddedPiRunEnd).mockImplementation((sessionId, timeoutMs) =>
waitForEmbeddedPiRunEndActual(sessionId, timeoutMs),
);
}); });
it("does not load session store runtime on module import", async () => { it("does not load session store runtime on module import", async () => {
@@ -352,267 +312,17 @@ describe("runPreparedReply media-only handling", () => {
it("waits for the previous active run to clear before registering a new reply operation", async () => { it("waits for the previous active run to clear before registering a new reply operation", async () => {
const queueSettings = await import("./queue/settings.js"); const queueSettings = await import("./queue/settings.js");
vi.mocked(queueSettings.resolveQueueSettings).mockReturnValueOnce({ mode: "interrupt" }); vi.mocked(queueSettings.resolveQueueSettings).mockReturnValueOnce({ mode: "interrupt" });
const previousRun = createReplyOperation({
sessionId: "session-overlap",
sessionKey: "session-key",
resetTriggered: false,
});
previousRun.setPhase("running");
const runPromise = runPreparedReply( const result = await runPreparedReply(
baseParams({ baseParams({
isNewSession: false, isNewSession: false,
sessionId: "session-overlap", sessionId: "session-overlap",
}), }),
); );
await Promise.resolve(); expect(result).toEqual({ text: "ok" });
expect(vi.mocked(runReplyAgent)).not.toHaveBeenCalled();
previousRun.complete();
await expect(runPromise).resolves.toEqual({ text: "ok" });
expect(vi.mocked(runReplyAgent)).toHaveBeenCalledOnce(); expect(vi.mocked(runReplyAgent)).toHaveBeenCalledOnce();
}); });
it("interrupts embedded-only active runs even without a reply operation", async () => {
const queueSettings = await import("./queue/settings.js");
vi.mocked(queueSettings.resolveQueueSettings).mockReturnValueOnce({ mode: "interrupt" });
const embeddedAbort = vi.fn();
const embeddedHandle = {
queueMessage: vi.fn(async () => {}),
isStreaming: () => true,
isCompacting: () => false,
abort: embeddedAbort,
};
setActiveEmbeddedRun("session-embedded-only", embeddedHandle, "session-key");
const runPromise = runPreparedReply(
baseParams({
isNewSession: false,
sessionId: "session-embedded-only",
}),
);
await Promise.resolve();
expect(vi.mocked(runReplyAgent)).not.toHaveBeenCalled();
expect(embeddedAbort).toHaveBeenCalledOnce();
clearActiveEmbeddedRun("session-embedded-only", embeddedHandle, "session-key");
await expect(runPromise).resolves.toEqual({ text: "ok" });
expect(vi.mocked(runReplyAgent)).toHaveBeenCalledOnce();
});
it("rechecks same-session ownership after async prep before registering a new reply operation", async () => {
const { resolveSessionAuthProfileOverride } =
await import("../../agents/auth-profiles/session-override.js");
const queueSettings = await import("./queue/settings.js");
let resolveAuth!: () => void;
const authPromise = new Promise<void>((resolve) => {
resolveAuth = resolve;
});
vi.mocked(resolveSessionAuthProfileOverride).mockImplementationOnce(
async () => await authPromise.then(() => undefined),
);
vi.mocked(queueSettings.resolveQueueSettings).mockReturnValueOnce({ mode: "interrupt" });
const runPromise = runPreparedReply(
baseParams({
isNewSession: false,
sessionId: "session-auth-race",
}),
);
await Promise.resolve();
expect(vi.mocked(runReplyAgent)).not.toHaveBeenCalled();
const intruderRun = createReplyOperation({
sessionId: "session-auth-race",
sessionKey: "session-key",
resetTriggered: false,
});
intruderRun.setPhase("running");
resolveAuth();
await Promise.resolve();
expect(vi.mocked(runReplyAgent)).not.toHaveBeenCalled();
intruderRun.complete();
await expect(runPromise).resolves.toEqual({ text: "ok" });
expect(vi.mocked(runReplyAgent)).toHaveBeenCalledOnce();
});
it("re-resolves auth profile after waiting for a prior run", async () => {
const { resolveSessionAuthProfileOverride } =
await import("../../agents/auth-profiles/session-override.js");
const queueSettings = await import("./queue/settings.js");
const sessionStore: Record<string, SessionEntry> = {
"session-key": {
sessionId: "session-auth-profile",
sessionFile: "/tmp/session-auth-profile.jsonl",
authProfileOverride: "profile-before-wait",
authProfileOverrideSource: "auto",
updatedAt: 1,
},
};
vi.mocked(resolveSessionAuthProfileOverride).mockImplementation(async ({ sessionEntry }) => {
return sessionEntry?.authProfileOverride;
});
vi.mocked(queueSettings.resolveQueueSettings).mockReturnValueOnce({ mode: "interrupt" });
const previousRun = createReplyOperation({
sessionId: "session-auth-profile",
sessionKey: "session-key",
resetTriggered: false,
});
previousRun.setPhase("running");
const runPromise = runPreparedReply(
baseParams({
isNewSession: false,
sessionId: "session-auth-profile",
sessionEntry: sessionStore["session-key"],
sessionStore,
}),
);
await Promise.resolve();
sessionStore["session-key"] = {
...sessionStore["session-key"],
authProfileOverride: "profile-after-wait",
authProfileOverrideSource: "auto",
updatedAt: 2,
};
previousRun.complete();
await expect(runPromise).resolves.toEqual({ text: "ok" });
const call = vi.mocked(runReplyAgent).mock.calls.at(-1)?.[0];
expect(call?.followupRun.run.authProfileId).toBe("profile-after-wait");
expect(vi.mocked(resolveSessionAuthProfileOverride)).toHaveBeenCalledTimes(2);
});
it("re-resolves same-session ownership after session-id rotation during async prep", async () => {
const { resolveSessionAuthProfileOverride } =
await import("../../agents/auth-profiles/session-override.js");
const queueSettings = await import("./queue/settings.js");
let resolveAuth!: () => void;
const authPromise = new Promise<void>((resolve) => {
resolveAuth = resolve;
});
const sessionStore: Record<string, SessionEntry> = {
"session-key": {
sessionId: "session-before-rotation",
sessionFile: "/tmp/session-before-rotation.jsonl",
updatedAt: 1,
},
};
vi.mocked(resolveSessionAuthProfileOverride).mockImplementationOnce(
async () => await authPromise.then(() => undefined),
);
vi.mocked(queueSettings.resolveQueueSettings).mockReturnValueOnce({ mode: "interrupt" });
const runPromise = runPreparedReply(
baseParams({
isNewSession: false,
sessionId: "session-before-rotation",
sessionEntry: sessionStore["session-key"],
sessionStore,
}),
);
await Promise.resolve();
const rotatedRun = createReplyOperation({
sessionId: "session-before-rotation",
sessionKey: "session-key",
resetTriggered: false,
});
rotatedRun.setPhase("running");
sessionStore["session-key"] = {
...sessionStore["session-key"],
sessionId: "session-after-rotation",
sessionFile: "/tmp/session-after-rotation.jsonl",
updatedAt: 2,
};
rotatedRun.updateSessionId("session-after-rotation");
resolveAuth();
await Promise.resolve();
expect(vi.mocked(runReplyAgent)).not.toHaveBeenCalled();
rotatedRun.complete();
await expect(runPromise).resolves.toEqual({ text: "ok" });
const call = vi.mocked(runReplyAgent).mock.calls.at(-1)?.[0];
expect(call?.followupRun.run.sessionId).toBe("session-after-rotation");
});
it("rechecks same-session ownership after wait resolves before calling the runner", async () => {
const queueSettings = await import("./queue/settings.js");
vi.mocked(queueSettings.resolveQueueSettings).mockReturnValueOnce({ mode: "interrupt" });
const previousRun = createReplyOperation({
sessionId: "session-before-wait",
sessionKey: "session-key",
resetTriggered: false,
});
previousRun.setPhase("running");
const runPromise = runPreparedReply(
baseParams({
isNewSession: false,
sessionId: "session-before-wait",
}),
);
await Promise.resolve();
expect(vi.mocked(runReplyAgent)).not.toHaveBeenCalled();
previousRun.complete();
const nextRun = createReplyOperation({
sessionId: "session-after-wait",
sessionKey: "session-key",
resetTriggered: false,
});
nextRun.setPhase("running");
await expect(runPromise).resolves.toEqual({
text: "⚠️ Previous run is still shutting down. Please try again in a moment.",
});
expect(vi.mocked(runReplyAgent)).not.toHaveBeenCalled();
nextRun.complete();
});
it("re-drains system events after waiting behind an active run", async () => {
const queueSettings = await import("./queue/settings.js");
vi.mocked(queueSettings.resolveQueueSettings).mockReturnValueOnce({ mode: "interrupt" });
vi.mocked(drainFormattedSystemEvents)
.mockResolvedValueOnce("System: [t] Initial event.")
.mockResolvedValueOnce("System: [t] Post-compaction context.");
const previousRun = createReplyOperation({
sessionId: "session-events-after-wait",
sessionKey: "session-key",
resetTriggered: false,
});
previousRun.setPhase("running");
const runPromise = runPreparedReply(
baseParams({
isNewSession: false,
sessionId: "session-events-after-wait",
}),
);
await Promise.resolve();
previousRun.complete();
await expect(runPromise).resolves.toEqual({ text: "ok" });
const call = vi.mocked(runReplyAgent).mock.calls.at(-1)?.[0];
expect(call?.commandBody).toContain("System: [t] Initial event.");
expect(call?.commandBody).toContain("System: [t] Post-compaction context.");
expect(call?.followupRun.prompt).toContain("System: [t] Initial event.");
expect(call?.followupRun.prompt).toContain("System: [t] Post-compaction context.");
});
it("uses inbound origin channel for run messageProvider", async () => { it("uses inbound origin channel for run messageProvider", async () => {
await runPreparedReply( await runPreparedReply(
baseParams({ baseParams({
+27 -26
View File
@@ -31,6 +31,7 @@ import type { GetReplyOptions, ReplyPayload } from "../types.js";
import { applySessionHints } from "./body.js"; import { applySessionHints } from "./body.js";
import type { buildCommandContext } from "./commands.js"; import type { buildCommandContext } from "./commands.js";
import type { InlineDirectives } from "./directive-handling.js"; import type { InlineDirectives } from "./directive-handling.js";
import { resolvePreparedReplyQueueState } from "./get-reply-run-queue.js";
import { buildGroupChatContext, buildGroupIntro } from "./groups.js"; import { buildGroupChatContext, buildGroupIntro } from "./groups.js";
import { buildInboundMetaSystemPrompt, buildInboundUserContextPrefix } from "./inbound-meta.js"; import { buildInboundMetaSystemPrompt, buildInboundUserContextPrefix } from "./inbound-meta.js";
import type { createModelSelectionState } from "./model-selection.js"; import type { createModelSelectionState } from "./model-selection.js";
@@ -466,36 +467,36 @@ export async function runPreparedReply(
queueMode: resolvedQueue.mode, queueMode: resolvedQueue.mode,
}); });
if (isActive && activeRunQueueAction === "run-now") { if (isActive && activeRunQueueAction === "run-now") {
const activeSessionIdBeforeWait = activeSessionId ?? resolveActiveQueueSessionId(); const queueState = await resolvePreparedReplyQueueState({
if (resolvedQueue.mode === "interrupt" && activeSessionIdBeforeWait) { activeRunQueueAction,
const aborted = abortEmbeddedPiRun(activeSessionIdBeforeWait); activeSessionId: activeSessionId ?? resolveActiveQueueSessionId(),
logVerbose( queueMode: resolvedQueue.mode,
`Interrupting active run for ${sessionKey ?? sessionIdFinal} (aborted=${aborted})`,
);
}
if (activeSessionIdBeforeWait) {
await waitForEmbeddedPiRunEnd(activeSessionIdBeforeWait);
}
preparedSessionState = resolvePreparedSessionState();
authProfileId = await resolveSessionAuthProfileOverride({
cfg,
provider,
agentDir,
sessionEntry: preparedSessionState.sessionEntry,
sessionStore,
sessionKey, sessionKey,
storePath, sessionId: sessionIdFinal,
isNewSession, abortActiveRun: abortEmbeddedPiRun,
waitForActiveRunEnd: waitForEmbeddedPiRunEnd,
refreshPreparedState: async () => {
preparedSessionState = resolvePreparedSessionState();
authProfileId = await resolveSessionAuthProfileOverride({
cfg,
provider,
agentDir,
sessionEntry: preparedSessionState.sessionEntry,
sessionStore,
sessionKey,
storePath,
isNewSession,
});
preparedSessionState = resolvePreparedSessionState();
({ prefixedCommandBody, queuedBody } = await rebuildPromptBodies());
},
resolveBusyState: resolveQueueBusyState,
}); });
preparedSessionState = resolvePreparedSessionState(); if (queueState.kind === "reply") {
({ prefixedCommandBody, queuedBody } = await rebuildPromptBodies());
({ activeSessionId, isActive, isStreaming } = resolveQueueBusyState());
if (isActive) {
typing.cleanup(); typing.cleanup();
return { return queueState.reply;
text: "⚠️ Previous run is still shutting down. Please try again in a moment.",
};
} }
({ activeSessionId, isActive, isStreaming } = queueState.busyState);
} }
const authProfileIdSource = preparedSessionState.sessionEntry?.authProfileOverrideSource; const authProfileIdSource = preparedSessionState.sessionEntry?.authProfileOverrideSource;
const followupRun = { const followupRun = {
+12
View File
@@ -169,12 +169,24 @@ describe("scripts/test-projects full-suite sharding", () => {
includePatterns: null, includePatterns: null,
watchMode: false, watchMode: false,
}, },
{
config: "vitest.full-core-unit-security.config.ts",
forwardedArgs: [],
includePatterns: null,
watchMode: false,
},
{ {
config: "vitest.full-core-unit-support.config.ts", config: "vitest.full-core-unit-support.config.ts",
forwardedArgs: [], forwardedArgs: [],
includePatterns: null, includePatterns: null,
watchMode: false, watchMode: false,
}, },
{
config: "vitest.full-core-contracts.config.ts",
forwardedArgs: [],
includePatterns: null,
watchMode: false,
},
{ {
config: "vitest.full-core-runtime.config.ts", config: "vitest.full-core-runtime.config.ts",
forwardedArgs: [], forwardedArgs: [],
+4 -1
View File
@@ -1,4 +1,7 @@
import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts"; import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts";
import { fullSuiteVitestShards } from "./vitest.test-shards.mjs"; import { fullSuiteVitestShards } from "./vitest.test-shards.mjs";
export default createProjectShardVitestConfig(fullSuiteVitestShards[2].projects); export default createProjectShardVitestConfig(
fullSuiteVitestShards.find((shard) => shard.config === "vitest.full-agentic.config.ts")
?.projects ?? [],
);
+4 -1
View File
@@ -1,4 +1,7 @@
import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts"; import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts";
import { fullSuiteVitestShards } from "./vitest.test-shards.mjs"; import { fullSuiteVitestShards } from "./vitest.test-shards.mjs";
export default createProjectShardVitestConfig(fullSuiteVitestShards[3].projects); export default createProjectShardVitestConfig(
fullSuiteVitestShards.find((shard) => shard.config === "vitest.full-auto-reply.config.ts")
?.projects ?? [],
);
+7
View File
@@ -0,0 +1,7 @@
import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts";
import { fullSuiteVitestShards } from "./vitest.test-shards.mjs";
export default createProjectShardVitestConfig(
fullSuiteVitestShards.find((shard) => shard.config === "vitest.full-core-contracts.config.ts")
?.projects ?? [],
);
+4 -1
View File
@@ -1,4 +1,7 @@
import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts"; import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts";
import { fullSuiteVitestShards } from "./vitest.test-shards.mjs"; import { fullSuiteVitestShards } from "./vitest.test-shards.mjs";
export default createProjectShardVitestConfig(fullSuiteVitestShards[1].projects); export default createProjectShardVitestConfig(
fullSuiteVitestShards.find((shard) => shard.config === "vitest.full-core-runtime.config.ts")
?.projects ?? [],
);
+7
View File
@@ -0,0 +1,7 @@
import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts";
import { fullSuiteVitestShards } from "./vitest.test-shards.mjs";
export default createProjectShardVitestConfig(
fullSuiteVitestShards.find((shard) => shard.config === "vitest.full-core-unit-security.config.ts")
?.projects ?? [],
);
+4 -1
View File
@@ -1,4 +1,7 @@
import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts"; import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts";
import { fullSuiteVitestShards } from "./vitest.test-shards.mjs"; import { fullSuiteVitestShards } from "./vitest.test-shards.mjs";
export default createProjectShardVitestConfig(fullSuiteVitestShards[0].projects); export default createProjectShardVitestConfig(
fullSuiteVitestShards.find((shard) => shard.config === "vitest.full-core-unit-src.config.ts")
?.projects ?? [],
);
+4 -1
View File
@@ -1,4 +1,7 @@
import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts"; import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts";
import { fullSuiteVitestShards } from "./vitest.test-shards.mjs"; import { fullSuiteVitestShards } from "./vitest.test-shards.mjs";
export default createProjectShardVitestConfig(fullSuiteVitestShards[1].projects); export default createProjectShardVitestConfig(
fullSuiteVitestShards.find((shard) => shard.config === "vitest.full-core-unit-support.config.ts")
?.projects ?? [],
);
+4 -1
View File
@@ -1,4 +1,7 @@
import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts"; import { createProjectShardVitestConfig } from "./vitest.project-shard-config.ts";
import { fullSuiteVitestShards } from "./vitest.test-shards.mjs"; import { fullSuiteVitestShards } from "./vitest.test-shards.mjs";
export default createProjectShardVitestConfig(fullSuiteVitestShards[4].projects); export default createProjectShardVitestConfig(
fullSuiteVitestShards.find((shard) => shard.config === "vitest.full-extensions.config.ts")
?.projects ?? [],
);
+10 -2
View File
@@ -12,17 +12,25 @@ export const fullSuiteVitestShards = [
name: "core-unit-src", name: "core-unit-src",
projects: ["vitest.unit-src.config.ts"], projects: ["vitest.unit-src.config.ts"],
}, },
{
config: "vitest.full-core-unit-security.config.ts",
name: "core-unit-security",
projects: ["vitest.unit-security.config.ts"],
},
{ {
config: "vitest.full-core-unit-support.config.ts", config: "vitest.full-core-unit-support.config.ts",
name: "core-unit-support", name: "core-unit-support",
projects: [ projects: [
"vitest.unit-support.config.ts", "vitest.unit-support.config.ts",
"vitest.boundary.config.ts", "vitest.boundary.config.ts",
"vitest.contracts.config.ts",
"vitest.bundled.config.ts",
"vitest.tooling.config.ts", "vitest.tooling.config.ts",
], ],
}, },
{
config: "vitest.full-core-contracts.config.ts",
name: "core-contracts",
projects: ["vitest.contracts.config.ts", "vitest.bundled.config.ts"],
},
{ {
config: "vitest.full-core-runtime.config.ts", config: "vitest.full-core-runtime.config.ts",
name: "core-runtime", name: "core-runtime",
+6
View File
@@ -0,0 +1,6 @@
import { createUnitVitestConfigWithOptions } from "./vitest.unit.config.ts";
export default createUnitVitestConfigWithOptions(process.env, {
name: "unit-security",
includePatterns: ["src/security/**/*.test.ts"],
});
+1
View File
@@ -3,4 +3,5 @@ import { createUnitVitestConfigWithOptions } from "./vitest.unit.config.ts";
export default createUnitVitestConfigWithOptions(process.env, { export default createUnitVitestConfigWithOptions(process.env, {
name: "unit-src", name: "unit-src",
includePatterns: ["src/**/*.test.ts"], includePatterns: ["src/**/*.test.ts"],
extraExcludePatterns: ["src/security/**"],
}); });