@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.
@@ -5,7 +5,7 @@ import {
5
5
  buildToolEvent,
6
6
  createHostedSessionRunner,
7
7
  } from "./shared.mjs";
8
- import { providerAssistantDelta, providerAssistantFinal, providerToolEnd, providerToolStart } from "./events.mjs";
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 { providerAssistantDelta, providerAssistantFinal, providerToolEnd, providerToolStart, providerToolUpdate } from "./events.mjs";
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 = buildStatusEvent(payload.thread_id ? `Codex thread ${payload.thread_id} started` : "Codex thread started");
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 = buildStatusEvent("Codex turn started");
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 = providerToolEnd("codex", { status: "ok", usage: payload.usage || null });
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 = buildStatusEvent(type ? `Codex event: ${type}` : JSON.stringify(payload));
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" || item.type === "tool_call") {
157
- return providerToolStart(item.command || item.name || item.type, {
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 buildStatusEvent("Codex started response");
163
- if (item.type) return buildStatusEvent(`Codex started ${item.type}`);
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" || item.type === "tool_call") {
173
- return providerToolUpdate(item.command || item.name || item.type, {
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" || item.type === "tool_call") {
188
- return providerToolEnd(item.command || item.name || item.type, {
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 buildStatusEvent(`Codex completed ${item.type}`);
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?.({ type: "status", provider: resolvedProvider, text: `Thinking with ${resolvedProvider}...` });
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
- appendProviderActivity(turn, {
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
- appendProviderActivity(turn, {
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/next-analysis",
3
- "version": "0.1.102",
3
+ "version": "0.1.103",
4
4
  "description": "SWC-backed Next.js source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-bridge",
3
- "version": "0.1.102",
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.102"
25
+ "@elench/testkit-protocol": "0.1.103"
26
26
  },
27
27
  "private": false
28
28
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.102",
3
+ "version": "0.1.103",
4
4
  "description": "Shared browser protocol for testkit bridge and extension consumers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/ts-analysis",
3
- "version": "0.1.102",
3
+ "version": "0.1.103",
4
4
  "description": "TypeScript compiler-backed source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit",
3
- "version": "0.1.102",
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.102",
94
- "@elench/testkit-bridge": "0.1.102",
95
- "@elench/testkit-protocol": "0.1.102",
96
- "@elench/ts-analysis": "0.1.102",
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",