@elench/testkit 0.1.89 → 0.1.90

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.
@@ -24,40 +24,13 @@ import {
24
24
 
25
25
  export function createInspectState({ dataSource = "live" } = {}) {
26
26
  const model = createEmptyInspectModel(dataSource);
27
- let mode = dataSource === "artifact" ? "complete" : "running";
28
27
  let notice = null;
29
- let agentSession = null;
30
28
  const listeners = new Set();
31
29
 
32
30
  function notify() {
33
31
  for (const callback of listeners) callback();
34
32
  }
35
33
 
36
- function appendTranscriptEntry(kind, text) {
37
- if (!agentSession || !text) return;
38
- const normalizedText = String(text);
39
- const entries = agentSession.transcriptEntries || [];
40
- const lastEntry = entries.at(-1);
41
- if (kind === "assistant" && lastEntry?.kind === "assistant") {
42
- lastEntry.text += normalizedText;
43
- agentSession.updatedAt = Date.now();
44
- return;
45
- }
46
- entries.push({ kind, text: normalizedText });
47
- agentSession.transcriptEntries = entries;
48
- agentSession.updatedAt = Date.now();
49
- }
50
-
51
- function finishAgentSession(status, extra = {}) {
52
- if (!agentSession) return;
53
- agentSession = {
54
- ...agentSession,
55
- ...extra,
56
- status,
57
- endedAt: Date.now(),
58
- };
59
- }
60
-
61
34
  function moveCursor(delta) {
62
35
  const snapshot = getSnapshot();
63
36
  if (snapshot.visibleEntries.length === 0) return;
@@ -70,9 +43,7 @@ export function createInspectState({ dataSource = "live" } = {}) {
70
43
  function getSnapshot() {
71
44
  return {
72
45
  ...buildSnapshot(model),
73
- mode,
74
46
  notice,
75
- agentSession,
76
47
  };
77
48
  }
78
49
 
@@ -84,15 +55,12 @@ export function createInspectState({ dataSource = "live" } = {}) {
84
55
 
85
56
  hydrateFromArtifact(artifact) {
86
57
  applyArtifactToModel(model, artifact);
87
- mode = model.finished ? "complete" : "running";
88
58
  notify();
89
59
  },
90
60
 
91
61
  resetForLive() {
92
62
  resetInspectModel(model, "live");
93
- mode = "running";
94
63
  notice = null;
95
- agentSession = null;
96
64
  notify();
97
65
  },
98
66
 
@@ -138,7 +106,6 @@ export function createInspectState({ dataSource = "live" } = {}) {
138
106
 
139
107
  finish(results, durationMs, regressionReport) {
140
108
  finishModel(model, results, durationMs, regressionReport);
141
- mode = "complete";
142
109
  notify();
143
110
  },
144
111
 
@@ -220,90 +187,6 @@ export function createInspectState({ dataSource = "live" } = {}) {
220
187
  notify();
221
188
  },
222
189
 
223
- beginInvestigation({ provider, userMessage } = {}) {
224
- mode = "investigating";
225
- notice = null;
226
- agentSession = {
227
- provider: provider || "auto",
228
- userMessage: userMessage || "",
229
- status: "starting",
230
- startedAt: Date.now(),
231
- updatedAt: Date.now(),
232
- rawEvents: [],
233
- transcriptEntries: [],
234
- timeline: [],
235
- summary: null,
236
- activePhase: "planning",
237
- activeStep: null,
238
- viewMode: "summary",
239
- };
240
- notify();
241
- },
242
-
243
- recordInvestigationProgress(event, presentation = null) {
244
- if (!agentSession || !event) return;
245
- agentSession.rawEvents.push({ ...event });
246
- if (event.type === "start") {
247
- agentSession.status = "running";
248
- } else if (event.type === "delta") {
249
- appendTranscriptEntry("assistant", event.text || "");
250
- } else if (event.type === "final") {
251
- if (event.text && !(agentSession.transcriptEntries || []).some((entry) => entry.kind === "assistant")) {
252
- appendTranscriptEntry("assistant", event.text);
253
- }
254
- agentSession.finalText = event.text || agentSession.finalText || "";
255
- } else if (event.type === "tool") {
256
- appendTranscriptEntry("tool", event.detail ? `${event.name}: ${event.detail}` : event.name);
257
- } else if (event.type === "status") {
258
- appendTranscriptEntry("status", event.message || "");
259
- } else if (event.type === "error") {
260
- appendTranscriptEntry("error", event.message || "Agent error");
261
- } else if (event.type === "exit") {
262
- agentSession.exitCode = event.code;
263
- }
264
-
265
- if (presentation) {
266
- agentSession.activePhase = presentation.phase || agentSession.activePhase || null;
267
- agentSession.activeStep = presentation.activeStep || null;
268
- agentSession.timeline = presentation.timeline || [];
269
- agentSession.summary = presentation.summary || null;
270
- }
271
- notify();
272
- },
273
-
274
- toggleInvestigationViewMode() {
275
- if (!agentSession) return;
276
- agentSession.viewMode = agentSession.viewMode === "summary" ? "transcript" : "summary";
277
- notify();
278
- },
279
-
280
- completeAgentSession(result = {}) {
281
- finishAgentSession("complete", {
282
- finalText: result.finalText || agentSession?.finalText || "",
283
- exitCode: result.exitCode ?? agentSession?.exitCode ?? 0,
284
- });
285
- notify();
286
- },
287
-
288
- failAgentSession(error) {
289
- finishAgentSession("error", {
290
- error: error instanceof Error ? error.message : String(error || "Agent error"),
291
- });
292
- notify();
293
- },
294
-
295
- cancelAgentSession(message = "Cancelled investigation.") {
296
- finishAgentSession("cancelled");
297
- mode = "complete";
298
- notice = message;
299
- notify();
300
- },
301
-
302
- returnToSummary() {
303
- mode = "complete";
304
- notify();
305
- },
306
-
307
190
  subscribe(callback) {
308
191
  listeners.add(callback);
309
192
  return () => listeners.delete(callback);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/next-analysis",
3
- "version": "0.1.89",
3
+ "version": "0.1.90",
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.89",
3
+ "version": "0.1.90",
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.89"
25
+ "@elench/testkit-protocol": "0.1.90"
26
26
  },
27
27
  "private": false
28
28
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.89",
3
+ "version": "0.1.90",
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.89",
3
+ "version": "0.1.90",
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.89",
3
+ "version": "0.1.90",
4
4
  "description": "CLI for discovering and running local HTTP, DAL, and Playwright test suites",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -82,10 +82,10 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "@babel/code-frame": "^7.29.0",
85
- "@elench/next-analysis": "0.1.89",
86
- "@elench/testkit-bridge": "0.1.89",
87
- "@elench/testkit-protocol": "0.1.89",
88
- "@elench/ts-analysis": "0.1.89",
85
+ "@elench/next-analysis": "0.1.90",
86
+ "@elench/testkit-bridge": "0.1.90",
87
+ "@elench/testkit-protocol": "0.1.90",
88
+ "@elench/ts-analysis": "0.1.90",
89
89
  "@oclif/core": "^4.10.6",
90
90
  "esbuild": "^0.25.11",
91
91
  "execa": "^9.5.0",
@@ -1,75 +0,0 @@
1
- import { loadInvestigationContext } from "./investigation-context.mjs";
2
- import { buildInvestigationPrompt } from "./prompt-builder.mjs";
3
- import { startAgentSession, startInteractiveAgentHandoff } from "./index.mjs";
4
-
5
- export function createInvestigationRequest({
6
- productDir,
7
- serviceName,
8
- filePath,
9
- provider = "auto",
10
- userMessage,
11
- } = {}) {
12
- const context = loadInvestigationContext({
13
- productDir,
14
- serviceName,
15
- filePath,
16
- });
17
- const prompt = buildInvestigationPrompt({ context, userMessage });
18
- return {
19
- provider,
20
- context,
21
- prompt,
22
- };
23
- }
24
-
25
- export function startHostedInvestigation({
26
- productDir,
27
- serviceName,
28
- filePath,
29
- provider = "auto",
30
- userMessage,
31
- onEvent,
32
- } = {}) {
33
- const request = createInvestigationRequest({
34
- productDir,
35
- serviceName,
36
- filePath,
37
- provider,
38
- userMessage,
39
- });
40
- const session = startAgentSession({
41
- provider: request.provider,
42
- cwd: productDir,
43
- prompt: request.prompt,
44
- onEvent,
45
- });
46
- return {
47
- ...session,
48
- request,
49
- };
50
- }
51
-
52
- export async function runInteractiveInvestigation({
53
- productDir,
54
- serviceName,
55
- filePath,
56
- provider = "auto",
57
- userMessage,
58
- } = {}) {
59
- const request = createInvestigationRequest({
60
- productDir,
61
- serviceName,
62
- filePath,
63
- provider,
64
- userMessage,
65
- });
66
- const result = await startInteractiveAgentHandoff({
67
- provider: request.provider,
68
- cwd: productDir,
69
- prompt: request.prompt,
70
- });
71
- return {
72
- ...result,
73
- request,
74
- };
75
- }
@@ -1,102 +0,0 @@
1
- import path from "path";
2
- import {
3
- collectArtifactEntries,
4
- formatArtifactPreview,
5
- formatFileDetail,
6
- getServiceLogRefs,
7
- getSetupOperationsForService,
8
- loadCurrentRunArtifact,
9
- resolveFileSubject,
10
- } from "../viewer.mjs";
11
- import { readLogTail } from "../../runner/logs.mjs";
12
-
13
- export function loadInvestigationContext({
14
- productDir,
15
- serviceName,
16
- filePath,
17
- logTail = 20,
18
- failureLimit = 5,
19
- previewLength = 6,
20
- } = {}) {
21
- if (!productDir) throw new Error("productDir is required");
22
- if (!serviceName) throw new Error("serviceName is required");
23
- if (!filePath) throw new Error("filePath is required");
24
-
25
- const runArtifact = loadCurrentRunArtifact(productDir);
26
- const subject = resolveFileSubject(runArtifact, filePath, serviceName);
27
- const detailLines = formatFileDetail(productDir, runArtifact, subject, {
28
- logTail,
29
- failureLimit,
30
- previewLength,
31
- });
32
- const artifacts = collectArtifactEntries(productDir, runArtifact, subject.file.path, subject.service.name).map((entry) => ({
33
- name: entry.artifactRef.name,
34
- kind: entry.artifactRef.kind || null,
35
- summary: entry.artifactRef.summary || null,
36
- path: entry.artifactRef.path,
37
- previewLines: formatArtifactPreview(entry.payload, previewLength),
38
- }));
39
- const setupOperations = getSetupOperationsForService(runArtifact, subject.service.name).slice(0, 8).map((operation) => ({
40
- stage: operation.stage,
41
- status: operation.status,
42
- summary: operation.summary || null,
43
- durationMs: operation.durationMs ?? null,
44
- logPath: operation.logRef?.path || null,
45
- error: operation.error || null,
46
- }));
47
- const backendLogs = getServiceLogRefs(runArtifact, subject.service.name).map((logRef) => ({
48
- runtimeLabel: logRef.runtimeLabel,
49
- path: logRef.path,
50
- lines: readLogTail(path.join(productDir, logRef.path), logTail),
51
- }));
52
-
53
- return {
54
- productDir,
55
- runArtifact,
56
- subject,
57
- detailLines,
58
- summary: {
59
- service: subject.service.name,
60
- suite: `${subject.suite.type}:${subject.suite.name}`,
61
- file: subject.file.path,
62
- status: subject.file.status,
63
- durationMs: subject.file.durationMs || 0,
64
- error: subject.file.error || null,
65
- failureCount: Array.isArray(subject.file.failureDetails) ? subject.file.failureDetails.length : 0,
66
- },
67
- setupOperations,
68
- artifacts,
69
- backendLogs,
70
- };
71
- }
72
-
73
- export function formatInvestigationTranscriptContext(context) {
74
- if (!context) return "";
75
- const artifactSummary = context.artifacts.length === 0
76
- ? "none"
77
- : context.artifacts
78
- .map((entry) => `${entry.name}${entry.kind ? ` [${entry.kind}]` : ""}: ${entry.path}`)
79
- .join("\n");
80
- const logSummary = context.backendLogs.length === 0
81
- ? "none"
82
- : context.backendLogs.map((entry) => `${entry.runtimeLabel}: ${entry.path}`).join("\n");
83
-
84
- return [
85
- `Service: ${context.summary.service}`,
86
- `Suite: ${context.summary.suite}`,
87
- `File: ${context.summary.file}`,
88
- `Status: ${context.summary.status}`,
89
- context.summary.error ? `Error: ${context.summary.error}` : null,
90
- "",
91
- "Artifacts:",
92
- artifactSummary,
93
- "",
94
- "Backend Logs:",
95
- logSummary,
96
- "",
97
- "Detailed Failure View:",
98
- ...context.detailLines,
99
- ]
100
- .filter(Boolean)
101
- .join("\n");
102
- }