@elench/testkit 0.1.102 → 0.1.103
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/lib/cli/assistant/providers/claude.mjs +1 -2
- package/lib/cli/assistant/providers/codex.mjs +45 -15
- package/lib/cli/assistant/providers/shared.mjs +9 -0
- package/lib/cli/assistant/session.mjs +6 -1
- package/lib/cli/assistant/state.mjs +6 -12
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/package.json +5 -5
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
buildToolEvent,
|
|
6
6
|
createHostedSessionRunner,
|
|
7
7
|
} from "./shared.mjs";
|
|
8
|
-
import { providerAssistantDelta, providerAssistantFinal,
|
|
8
|
+
import { providerAssistantDelta, providerAssistantFinal, providerToolStart } from "./events.mjs";
|
|
9
9
|
|
|
10
10
|
export function startClaudeHostedSession({
|
|
11
11
|
command = "claude",
|
|
@@ -130,7 +130,6 @@ export function parseClaudePayload(payload) {
|
|
|
130
130
|
} else if (typeof payload.result === "string") {
|
|
131
131
|
const event = providerAssistantFinal(payload.result);
|
|
132
132
|
if (event) events.push(event);
|
|
133
|
-
events.push(providerToolEnd("claude", { status: "ok", durationMs: payload.duration_ms || null }));
|
|
134
133
|
}
|
|
135
134
|
return events;
|
|
136
135
|
}
|
|
@@ -4,12 +4,18 @@ import path from "path";
|
|
|
4
4
|
import { execa } from "execa";
|
|
5
5
|
import {
|
|
6
6
|
buildErrorEvent,
|
|
7
|
-
buildStatusEvent,
|
|
8
7
|
buildToolEvent,
|
|
9
8
|
createHostedSessionRunner,
|
|
10
9
|
readTextFileIfPresent,
|
|
11
10
|
} from "./shared.mjs";
|
|
12
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
providerAssistantDelta,
|
|
13
|
+
providerAssistantFinal,
|
|
14
|
+
providerStatus,
|
|
15
|
+
providerToolEnd,
|
|
16
|
+
providerToolStart,
|
|
17
|
+
providerToolUpdate,
|
|
18
|
+
} from "./events.mjs";
|
|
13
19
|
|
|
14
20
|
export function startCodexHostedSession({
|
|
15
21
|
command = "codex",
|
|
@@ -99,19 +105,22 @@ export function parseCodexPayload(payload) {
|
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
if (type === "thread.started") {
|
|
102
|
-
const event =
|
|
108
|
+
const event = providerStatus(
|
|
109
|
+
payload.thread_id ? `Codex thread ${payload.thread_id} started` : "Codex thread started",
|
|
110
|
+
{ transient: true }
|
|
111
|
+
);
|
|
103
112
|
if (event) events.push(event);
|
|
104
113
|
return events;
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
if (type === "turn.started") {
|
|
108
|
-
const event =
|
|
117
|
+
const event = providerStatus("Codex turn started", { transient: true });
|
|
109
118
|
if (event) events.push(event);
|
|
110
119
|
return events;
|
|
111
120
|
}
|
|
112
121
|
|
|
113
122
|
if (type === "turn.completed") {
|
|
114
|
-
const event =
|
|
123
|
+
const event = providerStatus("Codex turn completed", { transient: true, usage: payload.usage || null });
|
|
115
124
|
if (event) events.push(event);
|
|
116
125
|
return events;
|
|
117
126
|
}
|
|
@@ -146,21 +155,27 @@ export function parseCodexPayload(payload) {
|
|
|
146
155
|
return events;
|
|
147
156
|
}
|
|
148
157
|
|
|
149
|
-
const statusEvent =
|
|
158
|
+
const statusEvent = providerStatus(type ? `Codex event: ${type}` : JSON.stringify(payload));
|
|
150
159
|
if (statusEvent) events.push(statusEvent);
|
|
151
160
|
return events;
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
function codexItemStartedEvent(item) {
|
|
155
164
|
if (!item || typeof item !== "object") return null;
|
|
156
|
-
if (item.type === "command_execution"
|
|
157
|
-
return providerToolStart(
|
|
165
|
+
if (item.type === "command_execution") {
|
|
166
|
+
return providerToolStart("command", {
|
|
167
|
+
id: item.id || null,
|
|
168
|
+
input: item.command || item.input || null,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (item.type === "tool_call") {
|
|
172
|
+
return providerToolStart(item.name || item.type, {
|
|
158
173
|
id: item.id || null,
|
|
159
174
|
input: item.arguments || item.input || null,
|
|
160
175
|
});
|
|
161
176
|
}
|
|
162
|
-
if (item.type === "agent_message") return
|
|
163
|
-
if (item.type) return
|
|
177
|
+
if (item.type === "agent_message") return providerStatus("Codex started response", { transient: true });
|
|
178
|
+
if (item.type) return providerStatus(`Codex started ${item.type}`);
|
|
164
179
|
return null;
|
|
165
180
|
}
|
|
166
181
|
|
|
@@ -169,8 +184,15 @@ function codexItemUpdatedEvent(item) {
|
|
|
169
184
|
if (item.type === "agent_message" && typeof item.text === "string") {
|
|
170
185
|
return providerAssistantDelta(item.text);
|
|
171
186
|
}
|
|
172
|
-
if (item.type === "command_execution"
|
|
173
|
-
return providerToolUpdate(
|
|
187
|
+
if (item.type === "command_execution") {
|
|
188
|
+
return providerToolUpdate("command", {
|
|
189
|
+
id: item.id || null,
|
|
190
|
+
text: item.output || item.status || null,
|
|
191
|
+
data: item,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (item.type === "tool_call") {
|
|
195
|
+
return providerToolUpdate(item.name || item.type, {
|
|
174
196
|
id: item.id || null,
|
|
175
197
|
text: item.output || item.status || null,
|
|
176
198
|
data: item,
|
|
@@ -184,14 +206,22 @@ function codexItemCompletedEvent(item) {
|
|
|
184
206
|
if (item.type === "agent_message" && typeof item.text === "string") {
|
|
185
207
|
return providerAssistantFinal(item.text);
|
|
186
208
|
}
|
|
187
|
-
if (item.type === "command_execution"
|
|
188
|
-
return providerToolEnd(
|
|
209
|
+
if (item.type === "command_execution") {
|
|
210
|
+
return providerToolEnd("command", {
|
|
211
|
+
id: item.id || null,
|
|
212
|
+
status: item.status === "failed" ? "error" : "ok",
|
|
213
|
+
output: item.output || null,
|
|
214
|
+
data: item,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
if (item.type === "tool_call") {
|
|
218
|
+
return providerToolEnd(item.name || item.type, {
|
|
189
219
|
id: item.id || null,
|
|
190
220
|
status: item.status === "failed" ? "error" : "ok",
|
|
191
221
|
output: item.output || null,
|
|
192
222
|
data: item,
|
|
193
223
|
});
|
|
194
224
|
}
|
|
195
|
-
if (item.type) return
|
|
225
|
+
if (item.type) return providerStatus(`Codex completed ${item.type}`);
|
|
196
226
|
return null;
|
|
197
227
|
}
|
|
@@ -40,6 +40,7 @@ export function createHostedSessionRunner({
|
|
|
40
40
|
emit(providerEvent("session-start"));
|
|
41
41
|
|
|
42
42
|
const stdoutReader = readline.createInterface({ input: child.stdout });
|
|
43
|
+
const stdoutClosed = waitForReaderClose(stdoutReader);
|
|
43
44
|
stdoutReader.on("line", (line) => {
|
|
44
45
|
onRawLine?.({ provider, stream: "stdout", line });
|
|
45
46
|
const parsed = tryParseJson(line);
|
|
@@ -53,6 +54,7 @@ export function createHostedSessionRunner({
|
|
|
53
54
|
});
|
|
54
55
|
|
|
55
56
|
const stderrReader = readline.createInterface({ input: child.stderr });
|
|
57
|
+
const stderrClosed = waitForReaderClose(stderrReader);
|
|
56
58
|
stderrReader.on("line", (line) => {
|
|
57
59
|
onRawLine?.({ provider, stream: "stderr", line });
|
|
58
60
|
if (shouldIgnoreStatus?.(line)) return;
|
|
@@ -61,6 +63,7 @@ export function createHostedSessionRunner({
|
|
|
61
63
|
|
|
62
64
|
const completion = (async () => {
|
|
63
65
|
const result = await child;
|
|
66
|
+
await Promise.all([stdoutClosed, stderrClosed]);
|
|
64
67
|
const fileFinalText = readFinalText ? readFinalText(result) : null;
|
|
65
68
|
const resolvedFinalText = fileFinalText || finalText || assistantText.trim() || null;
|
|
66
69
|
if ((result.exitCode ?? 0) !== 0) {
|
|
@@ -97,6 +100,12 @@ export function createHostedSessionRunner({
|
|
|
97
100
|
};
|
|
98
101
|
}
|
|
99
102
|
|
|
103
|
+
function waitForReaderClose(reader) {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
reader.once("close", resolve);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
100
109
|
export function tryParseJson(line) {
|
|
101
110
|
const normalized = String(line || "").trim();
|
|
102
111
|
if (!normalized) return null;
|
|
@@ -53,7 +53,12 @@ export async function runAssistantConversationTurn({
|
|
|
53
53
|
effort: runtimeSettings.effort || null,
|
|
54
54
|
});
|
|
55
55
|
onStatus?.(`Thinking with ${resolvedProvider}...`);
|
|
56
|
-
onProviderEvent?.({
|
|
56
|
+
onProviderEvent?.({
|
|
57
|
+
type: "status",
|
|
58
|
+
provider: resolvedProvider,
|
|
59
|
+
text: `Thinking with ${resolvedProvider}...`,
|
|
60
|
+
transient: true,
|
|
61
|
+
});
|
|
57
62
|
|
|
58
63
|
observer.start();
|
|
59
64
|
try {
|
|
@@ -641,6 +641,10 @@ function createProviderTurnState() {
|
|
|
641
641
|
|
|
642
642
|
function handleProviderEvent(turn, event, { appendMessage, updateMessage, setStatus } = {}) {
|
|
643
643
|
if (!event) return;
|
|
644
|
+
if (event.transient || event.display === false) {
|
|
645
|
+
if (event.type === "status" && event.text) setStatus?.(event.text);
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
644
648
|
if (event.type === "assistant-delta") {
|
|
645
649
|
appendAssistantDelta(turn, event, { appendMessage, updateMessage });
|
|
646
650
|
setStatus?.(`${event.provider || "provider"} responding`);
|
|
@@ -652,21 +656,11 @@ function handleProviderEvent(turn, event, { appendMessage, updateMessage, setSta
|
|
|
652
656
|
return;
|
|
653
657
|
}
|
|
654
658
|
if (event.type === "session-start") {
|
|
655
|
-
|
|
656
|
-
role: "provider-activity",
|
|
657
|
-
title: formatProviderName(event.provider),
|
|
658
|
-
text: "Session started",
|
|
659
|
-
data: event,
|
|
660
|
-
}, { appendMessage, setStatus });
|
|
659
|
+
setStatus?.(`${event.provider || "provider"} session started`);
|
|
661
660
|
return;
|
|
662
661
|
}
|
|
663
662
|
if (event.type === "session-end") {
|
|
664
|
-
|
|
665
|
-
role: "provider-activity",
|
|
666
|
-
title: formatProviderName(event.provider),
|
|
667
|
-
text: Number.isInteger(event.exitCode) ? `Session ended with exit code ${event.exitCode}` : "Session ended",
|
|
668
|
-
data: event,
|
|
669
|
-
}, { appendMessage, setStatus });
|
|
663
|
+
setStatus?.(`${event.provider || "provider"} session ended`);
|
|
670
664
|
return;
|
|
671
665
|
}
|
|
672
666
|
if (event.type === "status") {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elench/testkit-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.103",
|
|
4
4
|
"description": "Browser bridge helpers for testkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@elench/testkit-protocol": "0.1.
|
|
25
|
+
"@elench/testkit-protocol": "0.1.103"
|
|
26
26
|
},
|
|
27
27
|
"private": false
|
|
28
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elench/testkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.103",
|
|
4
4
|
"description": "Assistant-first CLI for running, inspecting, and debugging local testkit suites",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -90,10 +90,10 @@
|
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
92
|
"@babel/code-frame": "^7.29.0",
|
|
93
|
-
"@elench/next-analysis": "0.1.
|
|
94
|
-
"@elench/testkit-bridge": "0.1.
|
|
95
|
-
"@elench/testkit-protocol": "0.1.
|
|
96
|
-
"@elench/ts-analysis": "0.1.
|
|
93
|
+
"@elench/next-analysis": "0.1.103",
|
|
94
|
+
"@elench/testkit-bridge": "0.1.103",
|
|
95
|
+
"@elench/testkit-protocol": "0.1.103",
|
|
96
|
+
"@elench/ts-analysis": "0.1.103",
|
|
97
97
|
"@oclif/core": "^4.10.6",
|
|
98
98
|
"esbuild": "^0.25.11",
|
|
99
99
|
"execa": "^9.5.0",
|