Skip to content

Commit 20ab471

Browse files
authored
test: avoid bundled provider loading in session utils
1 parent bc8163c commit 20ab471

23 files changed

Lines changed: 327 additions & 168 deletions

extensions/llama-cpp/index.test.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ vi.mock("openclaw/plugin-sdk/memory-core-host-engine-embeddings", () => ({
2222
import llamaCppPlugin from "./index.js";
2323
import {
2424
DEFAULT_LLAMA_CPP_EMBEDDING_MODEL,
25-
createLlamaCppEmbeddingProvider,
2625
createLlamaCppMemoryEmbeddingProvider,
2726
formatLlamaCppSetupError,
2827
llamaCppEmbeddingProviderAdapter,
@@ -71,14 +70,16 @@ describe("llama.cpp provider plugin", () => {
7170
});
7271
const abortController = new AbortController();
7372

74-
const provider = await createLlamaCppEmbeddingProvider(
75-
{
76-
config: {},
77-
provider: "local",
78-
model: "text-embedding-3-small",
79-
},
80-
{ nodeLlamaCppImportUrl: "file:///plugin/node-llama-cpp.js" },
81-
);
73+
const result = await llamaCppEmbeddingProviderAdapter.create({
74+
config: {},
75+
provider: "local",
76+
model: "text-embedding-3-small",
77+
});
78+
const provider = result.provider;
79+
expect(provider).not.toBeNull();
80+
if (!provider) {
81+
throw new Error("expected llama.cpp provider");
82+
}
8283

8384
await expect(provider.embed("hello")).resolves.toEqual([0.6, 0.8]);
8485
await expect(
@@ -100,7 +101,7 @@ describe("llama.cpp provider plugin", () => {
100101
},
101102
},
102103
{
103-
nodeLlamaCppImportUrl: "file:///plugin/node-llama-cpp.js",
104+
nodeLlamaCppImportUrl: expect.stringContaining("node-llama-cpp"),
104105
},
105106
);
106107
const workerProvider =

extensions/llama-cpp/src/embedding-provider.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -182,17 +182,6 @@ function adaptMemoryEmbeddingProvider(provider: MemoryEmbeddingProvider): Embedd
182182
};
183183
}
184184

185-
export async function createLlamaCppEmbeddingProvider(
186-
options: EmbeddingProviderCreateOptions,
187-
runtimeOptions: LlamaCppEmbeddingProviderRuntimeOptions = {},
188-
): Promise<EmbeddingProvider> {
189-
const result = await createLlamaCppEmbeddingProviderResult(options, runtimeOptions);
190-
if (!result.provider) {
191-
throw new Error("llama.cpp local embedding provider was unavailable");
192-
}
193-
return result.provider;
194-
}
195-
196185
export async function createLlamaCppMemoryEmbeddingProvider(
197186
options: MemoryEmbeddingProviderCreateOptions,
198187
runtimeOptions: LlamaCppEmbeddingProviderRuntimeOptions = {},

extensions/msteams/src/monitor-handler.sso.test.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import {
99
type MSTeamsSsoFetch,
1010
handleSigninTokenExchangeInvoke,
1111
handleSigninVerifyStateInvoke,
12-
parseSigninTokenExchangeValue,
13-
parseSigninVerifyStateValue,
1412
} from "./sso.js";
1513

1614
function createMemorySsoTokenStore(): MSTeamsSsoTokenStore {
@@ -68,29 +66,6 @@ function createFakeFetch(handlers: Array<(url: string, init?: unknown) => unknow
6866
return { fetchImpl, calls };
6967
}
7068

71-
describe("msteams signin invoke value parsers", () => {
72-
it("parses signin/tokenExchange values", () => {
73-
expect(
74-
parseSigninTokenExchangeValue({
75-
id: "flow-1",
76-
connectionName: "Graph",
77-
token: "eyJ...",
78-
}),
79-
).toEqual({ id: "flow-1", connectionName: "Graph", token: "eyJ..." });
80-
});
81-
82-
it("rejects non-object signin/tokenExchange values", () => {
83-
expect(parseSigninTokenExchangeValue(null)).toBeNull();
84-
expect(parseSigninTokenExchangeValue("nope")).toBeNull();
85-
});
86-
87-
it("parses signin/verifyState values", () => {
88-
expect(parseSigninVerifyStateValue({ state: "123456" })).toEqual({ state: "123456" });
89-
expect(parseSigninVerifyStateValue({})).toEqual({ state: undefined });
90-
expect(parseSigninVerifyStateValue(null)).toBeNull();
91-
});
92-
});
93-
9469
describe("handleSigninTokenExchangeInvoke", () => {
9570
it("exchanges the Teams token and persists the result", async () => {
9671
const { fetchImpl, calls } = createFakeFetch([

extensions/msteams/src/sso.ts

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -96,32 +96,6 @@ type SigninVerifyStateValue = {
9696
state?: string;
9797
};
9898

99-
/**
100-
* Extract and validate the `signin/tokenExchange` activity value. Teams
101-
* delivers `{ id, connectionName, token }`; any field may be missing on
102-
* malformed invocations, so callers should check the parsed result.
103-
*/
104-
export function parseSigninTokenExchangeValue(value: unknown): SigninTokenExchangeValue | null {
105-
if (!value || typeof value !== "object") {
106-
return null;
107-
}
108-
const obj = value as Record<string, unknown>;
109-
const id = typeof obj.id === "string" ? obj.id : undefined;
110-
const connectionName = typeof obj.connectionName === "string" ? obj.connectionName : undefined;
111-
const token = typeof obj.token === "string" ? obj.token : undefined;
112-
return { id, connectionName, token };
113-
}
114-
115-
/** Extract the `signin/verifyState` activity value `{ state }`. */
116-
export function parseSigninVerifyStateValue(value: unknown): SigninVerifyStateValue | null {
117-
if (!value || typeof value !== "object") {
118-
return null;
119-
}
120-
const obj = value as Record<string, unknown>;
121-
const state = typeof obj.state === "string" ? obj.state : undefined;
122-
return { state };
123-
}
124-
12599
type UserTokenServiceCallParams = {
126100
baseUrl: string;
127101
path: string;

extensions/qa-lab/src/providers/mock-openai/server.test.ts

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ function explicitSessionsSpawnPrompt(token: string) {
122122
return [
123123
"Use sessions_spawn for this QA check.",
124124
`task="${threadSubagentTask(token)}"`,
125-
"label=qa-thread-subagent thread=true mode=session runTimeoutSeconds=30",
125+
"label=qa-thread-subagent thread=true mode=session",
126126
].join(" ");
127127
}
128128

@@ -219,6 +219,27 @@ describe("qa mock openai server", () => {
219219
expect(debugPayload.plannedToolName).toBe("read");
220220
});
221221

222+
it("returns a substantive private final fixture for the message-tool warning scenario", async () => {
223+
const server = await startMockServer();
224+
225+
const body = await expectResponsesJson<{
226+
output?: Array<{ content?: Array<{ text?: string }> }>;
227+
}>(server, {
228+
stream: false,
229+
model: "gpt-5.5",
230+
input: [
231+
makeUserInput(
232+
"qa private final reply warning check. Reply to me directly in two complete sentences with `QA-STRANDED-85714` in the first sentence and a short explanation in the second sentence. Do NOT call any tool. Do NOT use the message tool.",
233+
),
234+
],
235+
});
236+
237+
const text = body.output?.[0]?.content?.[0]?.text ?? "";
238+
expect(text).toContain("QA-STRANDED-85714");
239+
expect(text.length).toBeGreaterThanOrEqual(120);
240+
expect(text.match(/[.!?]+(?:\s|$)/g)).toHaveLength(2);
241+
});
242+
222243
it("emits deterministic text deltas for generic streaming QA prompts", async () => {
223244
const server = await startMockServer();
224245

@@ -642,6 +663,62 @@ describe("qa mock openai server", () => {
642663
expect(debugPayload.plannedToolName).toBe("read");
643664
});
644665

666+
it("reads unquoted fixture paths and honors exact replies after tool output", async () => {
667+
const server = await startMockServer();
668+
const prompt =
669+
"Read large-cache-fixture.txt, verify it contains CACHE-FIXTURE-1600, then reply exactly QA-LARGE-CACHE-WARMUP-OK.";
670+
671+
const toolPlan = await expectResponsesText(server, {
672+
stream: true,
673+
input: [makeUserInput(prompt)],
674+
});
675+
expect(toolPlan).toContain('"name":"read"');
676+
expect(toolPlan).toContain('"arguments":"{\\"path\\":\\"large-cache-fixture.txt\\"}"');
677+
678+
const completion = await expectResponsesJson<{
679+
output?: Array<{ content?: Array<{ text?: string }> }>;
680+
}>(server, {
681+
stream: false,
682+
input: [
683+
makeUserInput(prompt),
684+
{
685+
type: "function_call_output",
686+
call_id: "call_mock_read_1",
687+
output: "CACHE-FIXTURE-1600 stable tool-result evidence.",
688+
},
689+
],
690+
});
691+
692+
expect(outputText(completion)).toBe("QA-LARGE-CACHE-WARMUP-OK");
693+
});
694+
695+
it("preserves unquoted repo-scoped read targets", async () => {
696+
const server = await startMockServer();
697+
const toolPlan = await expectResponsesText(server, {
698+
stream: true,
699+
input: [makeUserInput("Read repo/qa/scenarios/index.yaml before continuing.")],
700+
});
701+
702+
expect(toolPlan).toContain('"name":"read"');
703+
expect(toolPlan).toContain('"arguments":"{\\"path\\":\\"repo/qa/scenarios/index.yaml\\"}"');
704+
});
705+
706+
it("does not treat natural reply-exactly-with phrasing as a marker token", async () => {
707+
const server = await startMockServer();
708+
const response = await expectResponsesJson<{
709+
output?: Array<{ content?: Array<{ text?: string }> }>;
710+
}>(server, {
711+
stream: false,
712+
input: [
713+
makeUserInput(
714+
"Use qa-visible-skill now. Reply exactly with the visible skill marker and nothing else.",
715+
),
716+
],
717+
});
718+
719+
expect(outputText(response)).toBe("VISIBLE-SKILL-OK");
720+
});
721+
645722
it("drives the Lobster Invaders write flow and memory recall responses", async () => {
646723
const server = await startQaMockOpenAiServer({
647724
host: "127.0.0.1",
@@ -1560,6 +1637,7 @@ describe("qa mock openai server", () => {
15601637
expect(spawnArgs.label).toBe("qa-direct-fallback-worker");
15611638
expect(spawnArgs.thread).toBe(false);
15621639
expect(spawnArgs.mode).toBe("run");
1640+
expect(spawnArgs).not.toHaveProperty("runTimeoutSeconds");
15631641

15641642
const body = await expectResponsesText(server, {
15651643
stream: true,
@@ -1626,7 +1704,6 @@ describe("qa mock openai server", () => {
16261704
label: "qa-thread-subagent",
16271705
thread: true,
16281706
mode: "session",
1629-
runTimeoutSeconds: 30,
16301707
}),
16311708
},
16321709
{
@@ -1663,7 +1740,6 @@ describe("qa mock openai server", () => {
16631740
label: "qa-thread-subagent",
16641741
thread: true,
16651742
mode: "session",
1666-
runTimeoutSeconds: 30,
16671743
}),
16681744
},
16691745
{
@@ -3866,7 +3942,7 @@ describe("qa mock openai server", () => {
38663942
expect(toolUseBlock?.input.label).toBe("qa-thread-subagent");
38673943
expect(toolUseBlock?.input.thread).toBe(true);
38683944
expect(toolUseBlock?.input.mode).toBe("session");
3869-
expect(toolUseBlock?.input.runTimeoutSeconds).toBe(30);
3945+
expect(toolUseBlock?.input).not.toHaveProperty("runTimeoutSeconds");
38703946

38713947
const debugResponse = await fetch(`${server.baseUrl}/debug/last-request`);
38723948
expect(debugResponse.status).toBe(200);

extensions/qa-lab/src/providers/mock-openai/server.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,13 @@ function readTargetFromPrompt(prompt: string) {
740740
return repoScoped;
741741
}
742742

743+
const loosePath = /\b[A-Za-z0-9._-]+\.(?:md|json|ts|tsx|js|mjs|cjs|txt|yaml|yml)\b/i
744+
.exec(prompt)?.[0]
745+
?.trim();
746+
if (loosePath) {
747+
return loosePath;
748+
}
749+
743750
if (/\bdocs?\b/i.test(prompt)) {
744751
return "repo/docs/help/testing.md";
745752
}
@@ -906,7 +913,6 @@ function buildQaToolSearchArgs(targetTool: string, failureMode: boolean): Record
906913
label: "runtime-tool-fixture",
907914
mode: "run",
908915
thread: false,
909-
runTimeoutSeconds: 30,
910916
};
911917
}
912918
if (targetTool === "memory_recall") {
@@ -959,7 +965,10 @@ function extractExactReplyDirective(text: string) {
959965
if (backtickedMatch) {
960966
return backtickedMatch;
961967
}
962-
return extractLastCapture(text, /reply(?: with)? exactly:\s*([^\n]+)/i);
968+
return (
969+
extractLastCapture(text, /reply(?: with)? exactly:\s*([^\n]+)/i) ??
970+
extractLastCapture(text, /reply(?: with)? exactly\s+(?!with\b)([^\s`.,;:!?]+)/i)
971+
);
963972
}
964973

965974
function extractFinishExactlyDirective(text: string) {
@@ -1107,18 +1116,12 @@ function buildExplicitSessionsSpawnArgs(text: string): Record<string, unknown> |
11071116
const label = extractQuotedToolArg(text, "label") ?? extractBareToolArg(text, "label");
11081117
const mode = extractBareToolArg(text, "mode")?.toLowerCase();
11091118
const context = extractBareToolArg(text, "context")?.toLowerCase();
1110-
const runTimeoutSecondsRaw = extractBareToolArg(text, "runTimeoutSeconds");
1111-
const runTimeoutSeconds =
1112-
runTimeoutSecondsRaw && /^\d+$/.test(runTimeoutSecondsRaw)
1113-
? Number(runTimeoutSecondsRaw)
1114-
: undefined;
11151119
return {
11161120
task,
11171121
...(label ? { label } : {}),
11181122
...(extractBareToolArg(text, "thread")?.toLowerCase() === "true" ? { thread: true } : {}),
11191123
...(mode === "session" || mode === "run" ? { mode } : {}),
11201124
...(context === "fork" || context === "isolated" ? { context } : {}),
1121-
...(runTimeoutSeconds !== undefined ? { runTimeoutSeconds } : {}),
11221125
};
11231126
}
11241127

@@ -1337,9 +1340,15 @@ function buildAssistantText(
13371340
if (/silent snack recall check/i.test(prompt)) {
13381341
return "Protocol note: I do not have enough context to say what you usually want for QA movie night.";
13391342
}
1343+
if (/qa private final reply warning check/i.test(prompt)) {
1344+
return "QA-STRANDED-85714 is present in this private final reply and I am not calling the message tool. This second sentence makes the omitted delivery substantive enough for the warning check.";
1345+
}
13401346
if (/tool continuity check/i.test(prompt) && toolOutput) {
13411347
return `Protocol note: model switch handoff confirmed on ${model || "the requested model"}. QA mission from QA_KICKOFF_TASK.md still applies: understand this OpenClaw repo from source + docs before acting.`;
13421348
}
1349+
if (toolOutput && promptExactReplyDirective) {
1350+
return promptExactReplyDirective;
1351+
}
13431352
if ((toolOutput || allInputText) && /repo contract followthrough check/i.test(allInputText)) {
13441353
const repoEvidenceText = [scenarioToolOutput, allInputText].filter(Boolean).join("\n");
13451354
if (
@@ -2075,7 +2084,6 @@ async function buildResponsesPayload(
20752084
label: "qa-direct-fallback-worker",
20762085
thread: false,
20772086
mode: "run",
2078-
runTimeoutSeconds: 30,
20792087
});
20802088
}
20812089
if (toolOutput && canCallSessionsYield && !/\byielded\b/i.test(toolOutput)) {

extensions/qa-lab/src/scenario-catalog.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ const qaTestFileScenarioExecutionSchema = z.discriminatedUnion("kind", [
7676
qaTestFileScenarioExecutionBaseSchema.extend({ kind: z.literal("playwright") }),
7777
qaTestFileScenarioExecutionBaseSchema.extend({
7878
kind: z.literal("script"),
79+
allowBlockedEvidence: z.boolean().optional(),
7980
args: z.array(z.string()).optional(),
8081
}),
8182
]);

0 commit comments

Comments
 (0)