@emseepea/testing 0.26.13 → 0.26.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/package.json +2 -2
- package/semantic/provider.mjs +29 -6
- package/semantic/test.d.mts +3 -0
- package/semantic/test.mjs +6 -0
package/README.md
CHANGED
|
@@ -109,6 +109,14 @@ argument correctness, persisted effects, response meaning, or recovery from an
|
|
|
109
109
|
error. Keep an exact assertion when order, count, and arguments are
|
|
110
110
|
deterministic, and add the separate assertions your application outcome needs.
|
|
111
111
|
|
|
112
|
+
Each turn exposes `assistantMessages`, an immutable array of visible native
|
|
113
|
+
Claude messages for each trial, in event order. Other providers currently return
|
|
114
|
+
empty arrays. These messages may include the final message; `responses` and
|
|
115
|
+
response assertions continue to use the separate terminal result.
|
|
116
|
+
|
|
117
|
+
Saved evidence and meaning-judge history retain these messages without hidden
|
|
118
|
+
reasoning. Earlier text does not satisfy an assertion about the final response.
|
|
119
|
+
|
|
112
120
|
Each turn also exposes `toolOutcomes`, aligned by trial and call order with
|
|
113
121
|
`toolCalls`. Each outcome is a read-only snapshot of what the model saw. It
|
|
114
122
|
contains only `name`, `arguments`, the model-visible `result`, and `isError`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emseepea/testing",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.15",
|
|
4
4
|
"description": "MCP integration and semantic testing helpers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"test:llm:built": "emseepea-test eval"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@emseepea/server": "0.
|
|
60
|
+
"@emseepea/server": "0.26.0",
|
|
61
61
|
"@modelcontextprotocol/client": "2.3.1",
|
|
62
62
|
"@modelcontextprotocol/server": "2.3.1",
|
|
63
63
|
"@modelcontextprotocol/core": "2.3.1"
|
package/semantic/provider.mjs
CHANGED
|
@@ -190,6 +190,12 @@ export function parseNativeClaudeEvents(stdout, advertisedTools, requireInit = f
|
|
|
190
190
|
}
|
|
191
191
|
return {
|
|
192
192
|
answer: result.result,
|
|
193
|
+
assistantMessages: events.flatMap(({ type, message }) => {
|
|
194
|
+
if (type !== "assistant" || !Array.isArray(message?.content)
|
|
195
|
+
|| (message.role !== undefined && message.role !== "assistant")) return [];
|
|
196
|
+
const text = message.content.filter((block) => block.type === "text" && typeof block.text === "string");
|
|
197
|
+
return text.length ? [text.map((block) => block.text).join("")] : [];
|
|
198
|
+
}),
|
|
193
199
|
calls,
|
|
194
200
|
toolResults: toolUses.map(({ id }) => {
|
|
195
201
|
const toolResult = toolResults.get(id);
|
|
@@ -448,9 +454,6 @@ export function parseCodexEvents(stdout, advertisedTools, expectedThreadId, proc
|
|
|
448
454
|
if (authentication.test(errorText)) {
|
|
449
455
|
throw codexEvidenceError("Model command is not signed in", "authentication");
|
|
450
456
|
}
|
|
451
|
-
const failedTool = events.some(({ item }) => item?.type === "mcp_tool_call"
|
|
452
|
-
&& (item.status === "failed" || item.result?.isError === true || item.result?.is_error === true));
|
|
453
|
-
if (failedTool) throw new Error("Model command reported a failed MCP tool call");
|
|
454
457
|
if (processExitCode !== 0) throw codexProcessError(processExitCode, providerFailureCategory);
|
|
455
458
|
if (events.some(({ type }) => type === "turn.failed" || type === "error")) {
|
|
456
459
|
throw new Error("Model command reported an error");
|
|
@@ -473,6 +476,20 @@ export function parseCodexEvents(stdout, advertisedTools, expectedThreadId, proc
|
|
|
473
476
|
const forbidden = completed.filter((item) => !["agent_message", "reasoning", "mcp_tool_call"].includes(item?.type));
|
|
474
477
|
if (forbidden.length) throw forbiddenItemError(forbidden);
|
|
475
478
|
const toolItems = completed.filter(({ type }) => type === "mcp_tool_call");
|
|
479
|
+
const terminalAnswerIndex = completed.findLastIndex(({ type }) => type === "agent_message");
|
|
480
|
+
const codexApplicationError = (item) => item.status === "failed"
|
|
481
|
+
&& item.server === mcpServerName && advertised.has(item.tool)
|
|
482
|
+
&& item.arguments && typeof item.arguments === "object" && !Array.isArray(item.arguments)
|
|
483
|
+
&& item.result && typeof item.result === "object" && !Array.isArray(item.result)
|
|
484
|
+
&& Object.keys(item.result).sort().join(",") === "content,structured_content"
|
|
485
|
+
&& item.result.structured_content === null
|
|
486
|
+
&& Array.isArray(item.result.content) && item.result.content.length > 0
|
|
487
|
+
&& item.result.content.every((block) => block && block.type === "text"
|
|
488
|
+
&& typeof block.text === "string" && block.text.length > 0)
|
|
489
|
+
&& completed.indexOf(item) < terminalAnswerIndex;
|
|
490
|
+
if (toolItems.some((item) => item.status === "failed" && !codexApplicationError(item))) {
|
|
491
|
+
throw new Error("Model command reported a failed MCP tool call");
|
|
492
|
+
}
|
|
476
493
|
const resourceItems = resourceCatalogue ? toolItems.flatMap((item) => {
|
|
477
494
|
const operation = resourceHelperOperation(item, resourceCatalogue);
|
|
478
495
|
return operation ? [{ item, operation }] : [];
|
|
@@ -489,8 +506,10 @@ export function parseCodexEvents(stdout, advertisedTools, expectedThreadId, proc
|
|
|
489
506
|
|| !item.arguments || typeof item.arguments !== "object" || Array.isArray(item.arguments)) {
|
|
490
507
|
throw forbiddenItemError([item]);
|
|
491
508
|
}
|
|
492
|
-
|
|
493
|
-
|| item.result
|
|
509
|
+
const applicationError = codexApplicationError(item)
|
|
510
|
+
|| item.result?.isError === true || item.result?.is_error === true;
|
|
511
|
+
if (!item.result || typeof item.result !== "object"
|
|
512
|
+
|| (item.status !== "completed" && !(item.status === "failed" && applicationError))) {
|
|
494
513
|
throw new Error("Model command reported a failed MCP tool call");
|
|
495
514
|
}
|
|
496
515
|
return { name: item.tool, arguments: item.arguments };
|
|
@@ -498,7 +517,11 @@ export function parseCodexEvents(stdout, advertisedTools, expectedThreadId, proc
|
|
|
498
517
|
const messages = completed.filter(({ type }) => type === "agent_message");
|
|
499
518
|
const answer = messages.at(-1)?.text;
|
|
500
519
|
if (typeof answer !== "string") throw new Error("Model command returned no answer");
|
|
501
|
-
const toolResults = applicationToolItems.map((
|
|
520
|
+
const toolResults = applicationToolItems.map((item) => ({
|
|
521
|
+
content: item.result.content ?? item.result,
|
|
522
|
+
isError: item.result.isError === true || item.result.is_error === true
|
|
523
|
+
|| codexApplicationError(item),
|
|
524
|
+
}));
|
|
502
525
|
return {
|
|
503
526
|
answer,
|
|
504
527
|
calls,
|
package/semantic/test.d.mts
CHANGED
|
@@ -52,6 +52,9 @@ export interface ResourceOperation {
|
|
|
52
52
|
export interface ConversationTurn {
|
|
53
53
|
readonly resourceOperations: readonly (readonly ResourceOperation[])[];
|
|
54
54
|
readonly responses: readonly string[];
|
|
55
|
+
/** Ordered native Claude visible messages by trial. Other providers currently return empty arrays.
|
|
56
|
+
* May include the final message; responses remains the separate terminal result. Excludes hidden reasoning. */
|
|
57
|
+
readonly assistantMessages: readonly (readonly string[])[];
|
|
55
58
|
readonly toolCalls: readonly (readonly ToolCall[])[];
|
|
56
59
|
readonly toolOutcomes: readonly (readonly ToolOutcome[])[];
|
|
57
60
|
readonly elicitations: readonly (readonly ElicitationEvidence[])[];
|
package/semantic/test.mjs
CHANGED
|
@@ -148,6 +148,7 @@ export async function createConversation(testContext, options) {
|
|
|
148
148
|
toolCalls: recordedToolCalls,
|
|
149
149
|
resourceOperations: answer.resourceOperations ?? [],
|
|
150
150
|
assistant: answer.answer,
|
|
151
|
+
...(answer.assistantMessages ? { assistantMessages: answer.assistantMessages } : {}),
|
|
151
152
|
});
|
|
152
153
|
const record = {
|
|
153
154
|
turn: trial.record.turns.length + 1,
|
|
@@ -155,6 +156,10 @@ export async function createConversation(testContext, options) {
|
|
|
155
156
|
interactionMode: "native-mcp",
|
|
156
157
|
prompt,
|
|
157
158
|
response: answer.answer,
|
|
159
|
+
...(answer.assistantMessages ? {
|
|
160
|
+
assistantMessages: answer.assistantMessages,
|
|
161
|
+
assistantMessagesSha256: hash(JSON.stringify(answer.assistantMessages)),
|
|
162
|
+
} : {}),
|
|
158
163
|
elicitations: answer.elicitations,
|
|
159
164
|
toolCalls: recordedToolCalls,
|
|
160
165
|
resourceOperations: answer.resourceOperations ?? [],
|
|
@@ -213,6 +218,7 @@ export async function createConversation(testContext, options) {
|
|
|
213
218
|
}
|
|
214
219
|
return Object.freeze({
|
|
215
220
|
responses: Object.freeze(trials.map(({ answer }) => answer)),
|
|
221
|
+
assistantMessages: immutableSnapshot(trials.map(({ record }) => record.assistantMessages ?? [])),
|
|
216
222
|
toolCalls: Object.freeze(trials.map(({ calls }) => Object.freeze(calls))),
|
|
217
223
|
toolOutcomes: Object.freeze(trials.map(({ record }) => immutableSnapshot(record.toolCalls.map(
|
|
218
224
|
({ name, arguments: args, result, isError }) => ({ name, arguments: args, result, isError }),
|