mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 05:01:15 +00:00
fix(ci): restore build and typecheck on main
This commit is contained in:
@@ -104,7 +104,7 @@ function parseProviderModelRef(
|
||||
|
||||
function isAnthropicCacheRetentionTarget(
|
||||
parsed: { provider: string; model: string } | null | undefined,
|
||||
) {
|
||||
): parsed is { provider: string; model: string } {
|
||||
return Boolean(
|
||||
parsed &&
|
||||
(parsed.provider === "anthropic" ||
|
||||
|
||||
@@ -96,7 +96,7 @@ const resolveAcpSpawnStreamLogPathSpy = vi.spyOn(
|
||||
"resolveAcpSpawnStreamLogPath",
|
||||
);
|
||||
|
||||
const { spawnAcpDirect } = await import("./acp-spawn.js");
|
||||
const { isSpawnAcpAcceptedResult, spawnAcpDirect } = await import("./acp-spawn.js");
|
||||
type SpawnRequest = Parameters<typeof spawnAcpDirect>[0];
|
||||
type SpawnContext = Parameters<typeof spawnAcpDirect>[1];
|
||||
type SpawnResult = Awaited<ReturnType<typeof spawnAcpDirect>>;
|
||||
@@ -254,6 +254,14 @@ function expectFailedSpawn(
|
||||
return result;
|
||||
}
|
||||
|
||||
function expectAcceptedSpawn(result: SpawnResult): Extract<SpawnResult, { status: "accepted" }> {
|
||||
expect(result.status).toBe("accepted");
|
||||
if (!isSpawnAcpAcceptedResult(result)) {
|
||||
throw new Error("Expected ACP spawn to be accepted");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function expectAgentGatewayCall(overrides: AgentCallParams): void {
|
||||
const agentCall = findAgentGatewayCall();
|
||||
expect(agentCall?.params?.deliver).toBe(overrides.deliver);
|
||||
@@ -504,15 +512,15 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.childSessionKey).toMatch(/^agent:codex:acp:/);
|
||||
expect(result.runId).toBe("run-1");
|
||||
expect(result.mode).toBe("session");
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.childSessionKey).toMatch(/^agent:codex:acp:/);
|
||||
expect(accepted.runId).toBe("run-1");
|
||||
expect(accepted.mode).toBe("session");
|
||||
const patchCalls = hoisted.callGatewayMock.mock.calls
|
||||
.map((call: unknown[]) => call[0] as { method?: string; params?: Record<string, unknown> })
|
||||
.filter((request) => request.method === "sessions.patch");
|
||||
expect(patchCalls[0]?.params).toMatchObject({
|
||||
key: result.childSessionKey,
|
||||
key: accepted.childSessionKey,
|
||||
spawnedBy: "agent:main:main",
|
||||
});
|
||||
expect(hoisted.sessionBindingBindMock).toHaveBeenCalledWith(
|
||||
@@ -954,9 +962,9 @@ describe("spawnAcpDirect", () => {
|
||||
])("$name", async ({ ctx, expectedAgentCall, expectTranscriptPersistence }) => {
|
||||
const result = await spawnAcpDirect(createSpawnRequest(), ctx);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBeUndefined();
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBeUndefined();
|
||||
expect(hoisted.startAcpSpawnParentStreamRelayMock).not.toHaveBeenCalled();
|
||||
if (expectTranscriptPersistence) {
|
||||
expect(hoisted.resolveSessionTranscriptFileMock).toHaveBeenCalledWith(
|
||||
@@ -1157,8 +1165,8 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.streamLogPath).toBe("/tmp/sess-main.acp-stream.jsonl");
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.streamLogPath).toBe("/tmp/sess-main.acp-stream.jsonl");
|
||||
const agentCall = hoisted.callGatewayMock.mock.calls
|
||||
.map((call: unknown[]) => call[0] as { method?: string; params?: Record<string, unknown> })
|
||||
.find((request) => request.method === "agent");
|
||||
@@ -1183,7 +1191,7 @@ describe("spawnAcpDirect", () => {
|
||||
(call: unknown[]) => (call[0] as { runId?: string }).runId,
|
||||
);
|
||||
expect(relayRuns).toContain(agentCall?.params?.idempotencyKey);
|
||||
expect(relayRuns).toContain(result.runId);
|
||||
expect(relayRuns).toContain(accepted.runId);
|
||||
expect(hoisted.resolveAcpSpawnStreamLogPathMock).toHaveBeenCalledWith({
|
||||
childSessionKey: expect.stringMatching(/^agent:codex:acp:/),
|
||||
});
|
||||
@@ -1248,9 +1256,9 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBe("/tmp/sess-main.acp-stream.jsonl");
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBe("/tmp/sess-main.acp-stream.jsonl");
|
||||
const agentCall = hoisted.callGatewayMock.mock.calls
|
||||
.map((call: unknown[]) => call[0] as { method?: string; params?: Record<string, unknown> })
|
||||
.find((request) => request.method === "agent");
|
||||
@@ -1294,9 +1302,9 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBeUndefined();
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBeUndefined();
|
||||
expect(hoisted.startAcpSpawnParentStreamRelayMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1327,9 +1335,9 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBeUndefined();
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBeUndefined();
|
||||
expect(hoisted.startAcpSpawnParentStreamRelayMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1351,9 +1359,9 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBeUndefined();
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBeUndefined();
|
||||
expect(hoisted.startAcpSpawnParentStreamRelayMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1380,9 +1388,9 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBeUndefined();
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBeUndefined();
|
||||
expect(hoisted.startAcpSpawnParentStreamRelayMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1399,9 +1407,9 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBeUndefined();
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBeUndefined();
|
||||
expect(hoisted.startAcpSpawnParentStreamRelayMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1416,9 +1424,9 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBeUndefined();
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBeUndefined();
|
||||
expect(hoisted.startAcpSpawnParentStreamRelayMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1437,9 +1445,9 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBeUndefined();
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBeUndefined();
|
||||
expect(hoisted.startAcpSpawnParentStreamRelayMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1470,9 +1478,9 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("run");
|
||||
expect(result.streamLogPath).toBeUndefined();
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("run");
|
||||
expect(accepted.streamLogPath).toBeUndefined();
|
||||
expect(hoisted.startAcpSpawnParentStreamRelayMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1548,8 +1556,8 @@ describe("spawnAcpDirect", () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe("accepted");
|
||||
expect(result.mode).toBe("session");
|
||||
const accepted = expectAcceptedSpawn(result);
|
||||
expect(accepted.mode).toBe("session");
|
||||
expect(hoisted.sessionBindingBindMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
placement: "current",
|
||||
|
||||
@@ -126,6 +126,10 @@ type SpawnAcpFailedResult = {
|
||||
|
||||
export type SpawnAcpResult = SpawnAcpAcceptedResult | SpawnAcpFailedResult;
|
||||
|
||||
export function isSpawnAcpAcceptedResult(result: SpawnAcpResult): result is SpawnAcpAcceptedResult {
|
||||
return result.status === "accepted";
|
||||
}
|
||||
|
||||
export const ACP_SPAWN_ACCEPTED_NOTE =
|
||||
"initial ACP task queued in isolated session; follow-ups continue in the bound thread.";
|
||||
export const ACP_SPAWN_SESSION_ACCEPTED_NOTE =
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import fs from "node:fs/promises";
|
||||
import type { AssistantMessage, Message, Tool } from "@mariozechner/pi-ai";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { LIVE_CACHE_REGRESSION_BASELINE } from "./live-cache-regression-baseline.js";
|
||||
import {
|
||||
LIVE_CACHE_REGRESSION_BASELINE,
|
||||
type LiveCacheFloor,
|
||||
} from "./live-cache-regression-baseline.js";
|
||||
import {
|
||||
buildAssistantHistoryTurn,
|
||||
buildStableCachePrefix,
|
||||
@@ -25,11 +28,16 @@ const LIVE_TEST_PNG_URL = new URL(
|
||||
type LiveResolvedModel = Awaited<ReturnType<typeof resolveLiveDirectModel>>;
|
||||
type ProviderKey = keyof typeof LIVE_CACHE_REGRESSION_BASELINE;
|
||||
type CacheLane = "image" | "mcp" | "stable" | "tool";
|
||||
type CacheUsage = {
|
||||
input?: number;
|
||||
cacheRead?: number;
|
||||
cacheWrite?: number;
|
||||
};
|
||||
type CacheRun = {
|
||||
hitRate: number;
|
||||
suffix: string;
|
||||
text: string;
|
||||
usage: AssistantMessage["usage"];
|
||||
usage: CacheUsage;
|
||||
};
|
||||
type LaneResult = {
|
||||
best?: CacheRun;
|
||||
@@ -88,6 +96,23 @@ function extractFirstToolCall(message: AssistantMessage) {
|
||||
return message.content.find((block) => block.type === "toolCall");
|
||||
}
|
||||
|
||||
function normalizeCacheUsage(usage: AssistantMessage["usage"] | undefined): CacheUsage {
|
||||
const value = usage as Record<string, unknown> | null | undefined;
|
||||
const readNumber = (key: keyof CacheUsage): number | undefined =>
|
||||
typeof value?.[key] === "number" ? value[key] : undefined;
|
||||
return {
|
||||
input: readNumber("input"),
|
||||
cacheRead: readNumber("cacheRead"),
|
||||
cacheWrite: readNumber("cacheWrite"),
|
||||
};
|
||||
}
|
||||
|
||||
function resolveBaselineFloor(provider: ProviderKey, lane: string): LiveCacheFloor | undefined {
|
||||
return LIVE_CACHE_REGRESSION_BASELINE[provider][
|
||||
lane as keyof (typeof LIVE_CACHE_REGRESSION_BASELINE)[typeof provider]
|
||||
] as LiveCacheFloor | undefined;
|
||||
}
|
||||
|
||||
function assert(condition: unknown, message: string): asserts condition {
|
||||
if (!condition) {
|
||||
throw new Error(message);
|
||||
@@ -194,11 +219,12 @@ async function completeCacheProbe(params: {
|
||||
text.toLowerCase().includes(params.suffix.toLowerCase()),
|
||||
`expected response to contain ${params.suffix}, got ${JSON.stringify(text)}`,
|
||||
);
|
||||
const usage = normalizeCacheUsage(response.usage);
|
||||
return {
|
||||
suffix: params.suffix,
|
||||
text,
|
||||
usage: response.usage,
|
||||
hitRate: computeCacheHitRate(response.usage),
|
||||
usage,
|
||||
hitRate: computeCacheHitRate(usage),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -318,8 +344,8 @@ async function runAnthropicDisabledLane(params: {
|
||||
return { disabled };
|
||||
}
|
||||
|
||||
function formatUsage(usage: AssistantMessage["usage"]) {
|
||||
return `cacheRead=${usage.cacheRead ?? 0} cacheWrite=${usage.cacheWrite ?? 0} input=${usage.input ?? 0}`;
|
||||
function formatUsage(usage: CacheUsage | undefined) {
|
||||
return `cacheRead=${usage?.cacheRead ?? 0} cacheWrite=${usage?.cacheWrite ?? 0} input=${usage?.input ?? 0}`;
|
||||
}
|
||||
|
||||
function assertAgainstBaseline(params: {
|
||||
@@ -328,10 +354,7 @@ function assertAgainstBaseline(params: {
|
||||
result: LaneResult;
|
||||
regressions: string[];
|
||||
}) {
|
||||
const floor =
|
||||
LIVE_CACHE_REGRESSION_BASELINE[params.provider][
|
||||
params.lane as keyof (typeof LIVE_CACHE_REGRESSION_BASELINE)[typeof params.provider]
|
||||
];
|
||||
const floor = resolveBaselineFloor(params.provider, params.lane);
|
||||
if (!floor) {
|
||||
params.regressions.push(`${params.provider}:${params.lane} missing baseline entry`);
|
||||
return;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { loadConfig } from "../../config/config.js";
|
||||
import { callGateway } from "../../gateway/call.js";
|
||||
import { normalizeDeliveryContext } from "../../utils/delivery-context.js";
|
||||
import type { GatewayMessageChannel } from "../../utils/message-channel.js";
|
||||
import { spawnAcpDirect } from "../acp-spawn.js";
|
||||
import { isSpawnAcpAcceptedResult, spawnAcpDirect } from "../acp-spawn.js";
|
||||
import { optionalStringEnum } from "../schema/typebox.js";
|
||||
import type { SpawnedToolContext } from "../spawned-context.js";
|
||||
import { registerSubagentRun } from "../subagent-registry.js";
|
||||
@@ -223,7 +223,7 @@ export function createSessionsSpawnTool(
|
||||
},
|
||||
);
|
||||
const childSessionKey = result.childSessionKey?.trim();
|
||||
const childRunId = result.runId?.trim();
|
||||
const childRunId = isSpawnAcpAcceptedResult(result) ? result.runId?.trim() : undefined;
|
||||
const shouldTrackViaRegistry =
|
||||
result.status === "accepted" &&
|
||||
Boolean(childSessionKey) &&
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
BlockStreamingChunkConfig,
|
||||
BlockStreamingCoalesceConfig,
|
||||
ContextVisibilityMode,
|
||||
DmPolicy,
|
||||
@@ -159,6 +160,8 @@ export type TelegramAccountConfig = {
|
||||
streaming?: TelegramStreamingMode;
|
||||
/** Disable block streaming for this account. */
|
||||
blockStreaming?: boolean;
|
||||
/** Draft block-stream chunking thresholds for Telegram preview edits. */
|
||||
draftChunk?: BlockStreamingChunkConfig;
|
||||
/** Merge streamed block replies before sending. */
|
||||
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
|
||||
mediaMaxMb?: number;
|
||||
|
||||
Reference in New Issue
Block a user