@elench/testkit 0.1.89 → 0.1.91

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.
Files changed (31) hide show
  1. package/README.md +14 -7
  2. package/lib/cli/agents/index.mjs +27 -19
  3. package/lib/cli/agents/providers/claude.mjs +3 -3
  4. package/lib/cli/agents/providers/codex.mjs +3 -3
  5. package/lib/cli/assistant/app.mjs +210 -0
  6. package/lib/cli/assistant/context-pack.mjs +191 -0
  7. package/lib/cli/assistant/interactive.mjs +53 -0
  8. package/lib/cli/assistant/prompt-builder.mjs +7 -9
  9. package/lib/cli/assistant/session.mjs +6 -1
  10. package/lib/cli/assistant/state.mjs +134 -46
  11. package/lib/cli/assistant/tool-registry.mjs +220 -230
  12. package/lib/cli/commands/assistant.mjs +50 -34
  13. package/lib/cli/{tui/detail-pane.mjs → context-resources.mjs} +81 -21
  14. package/lib/cli/entrypoint.mjs +12 -4
  15. package/lib/cli/presentation/tree-reporter.mjs +0 -101
  16. package/lib/cli/tui/inspect-app.mjs +7 -88
  17. package/lib/cli/tui/inspect-state.mjs +0 -117
  18. package/node_modules/@elench/next-analysis/package.json +1 -1
  19. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  20. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  21. package/node_modules/@elench/ts-analysis/package.json +1 -1
  22. package/package.json +5 -5
  23. package/lib/cli/agents/investigate.mjs +0 -75
  24. package/lib/cli/agents/investigation-context.mjs +0 -102
  25. package/lib/cli/agents/investigation-interpreter.mjs +0 -320
  26. package/lib/cli/agents/investigation-log.mjs +0 -37
  27. package/lib/cli/agents/prompt-builder.mjs +0 -25
  28. package/lib/cli/assistant/content.mjs +0 -60
  29. package/lib/cli/assistant/tool-run-reporter.mjs +0 -80
  30. package/lib/cli/tui/assistant-app.mjs +0 -82
  31. package/lib/cli/tui/assistant-render.mjs +0 -99
@@ -7,32 +7,87 @@ import {
7
7
  getSetupOperationsForService,
8
8
  loadCurrentRunArtifact,
9
9
  resolveFileSubject,
10
- } from "../viewer.mjs";
11
- import { formatDuration } from "../../runner/formatting.mjs";
12
- import { readLogTail } from "../../runner/logs.mjs";
10
+ } from "./viewer.mjs";
11
+ import { formatDuration } from "../runner/formatting.mjs";
12
+ import { readLogTail } from "../runner/logs.mjs";
13
13
 
14
- export function buildInspectPaneContent({ productDir, snapshot, paneMode = "detail", logTail = 12 } = {}) {
15
- const selectedEntry = snapshot.selectedEntry || null;
14
+ export function readContextContent({
15
+ productDir,
16
+ snapshot,
17
+ mode = "detail",
18
+ logTail = 12,
19
+ previewLength = 6,
20
+ } = {}) {
21
+ const normalizedMode = normalizeMode(mode);
22
+ const selectedEntry = snapshot?.selectedEntry || null;
23
+ const summaryRows = snapshot?.summaryData?.rows || [];
16
24
  if (!selectedEntry) {
17
- return { title: "Selection", lines: ["No entry selected."], data: null };
25
+ return {
26
+ mode: normalizedMode,
27
+ title: "Selection",
28
+ lines: ["No entry selected."],
29
+ data: null,
30
+ selection: null,
31
+ summaryRows,
32
+ };
18
33
  }
19
34
 
20
35
  const runArtifact = resolveArtifact(productDir, snapshot);
21
36
  const subject = resolveSubject(runArtifact, selectedEntry);
22
37
 
23
- if (paneMode === "artifacts") {
24
- return buildArtifactsPane(productDir, runArtifact, selectedEntry, subject);
25
- }
26
- if (paneMode === "logs") {
27
- return buildLogsPane(productDir, runArtifact, selectedEntry, logTail);
28
- }
29
- if (paneMode === "setup") {
30
- return buildSetupPane(productDir, runArtifact, selectedEntry);
38
+ let content = null;
39
+ if (normalizedMode === "artifacts") {
40
+ content = buildArtifactsContent(productDir, runArtifact, selectedEntry, subject, previewLength);
41
+ } else if (normalizedMode === "logs") {
42
+ content = buildLogsContent(productDir, runArtifact, selectedEntry, logTail);
43
+ } else if (normalizedMode === "setup") {
44
+ content = buildSetupContent(runArtifact, selectedEntry);
45
+ } else {
46
+ content = buildDetailContent(productDir, runArtifact, selectedEntry, subject, logTail);
31
47
  }
32
- return buildDetailPane(productDir, runArtifact, selectedEntry, subject, logTail);
48
+
49
+ return {
50
+ mode: normalizedMode,
51
+ title: content.title,
52
+ lines: content.lines || [],
53
+ data: content.data ?? null,
54
+ selection: selectedEntry,
55
+ summaryRows,
56
+ };
33
57
  }
34
58
 
35
- function buildDetailPane(productDir, runArtifact, entry, subject, logTail) {
59
+ export function buildContextSelection(snapshot) {
60
+ const selectedEntry = snapshot?.selectedEntry || null;
61
+ const summaryRows = snapshot?.summaryData?.rows || [];
62
+ return {
63
+ selection: selectedEntry
64
+ ? {
65
+ kind: selectedEntry.kind,
66
+ label: selectedEntry.label || selectedEntry.filePath || selectedEntry.serviceName || "selection",
67
+ serviceName: selectedEntry.serviceName || null,
68
+ type: selectedEntry.type || null,
69
+ suiteName: selectedEntry.suiteName || null,
70
+ filePath: selectedEntry.filePath || null,
71
+ status: selectedEntry.status || null,
72
+ }
73
+ : null,
74
+ summaryRows,
75
+ phase: snapshot?.phase || null,
76
+ hasArtifact: Boolean(snapshot?.runArtifact),
77
+ };
78
+ }
79
+
80
+ export function formatContextToolText(title, lines) {
81
+ const normalizedTitle = String(title || "").trim();
82
+ const normalizedLines = Array.isArray(lines) ? lines.filter((line) => String(line).length > 0) : [];
83
+ if (normalizedTitle && normalizedLines.length > 0) {
84
+ return [normalizedTitle, ...normalizedLines].join("\n");
85
+ }
86
+ if (normalizedTitle) return normalizedTitle;
87
+ return normalizedLines.join("\n");
88
+ }
89
+
90
+ function buildDetailContent(productDir, runArtifact, entry, subject, logTail) {
36
91
  if (subject && runArtifact) {
37
92
  return {
38
93
  title: "Detail",
@@ -48,7 +103,7 @@ function buildDetailPane(productDir, runArtifact, entry, subject, logTail) {
48
103
  };
49
104
  }
50
105
 
51
- function buildArtifactsPane(productDir, runArtifact, entry, subject) {
106
+ function buildArtifactsContent(productDir, runArtifact, entry, subject, previewLength) {
52
107
  if (!runArtifact || !subject) {
53
108
  return {
54
109
  title: "Artifacts",
@@ -65,7 +120,7 @@ function buildArtifactsPane(productDir, runArtifact, entry, subject) {
65
120
  kind: item.artifactRef.kind,
66
121
  summary: item.artifactRef.summary,
67
122
  path: item.artifactRef.path,
68
- preview: formatArtifactPreview(item.payload, 6),
123
+ preview: formatArtifactPreview(item.payload, previewLength),
69
124
  }));
70
125
 
71
126
  if (entries.length === 0) {
@@ -82,7 +137,7 @@ function buildArtifactsPane(productDir, runArtifact, entry, subject) {
82
137
  return { title: "Artifacts", lines, data: entries };
83
138
  }
84
139
 
85
- function buildLogsPane(productDir, runArtifact, entry, tail) {
140
+ function buildLogsContent(productDir, runArtifact, entry, tail) {
86
141
  if (!runArtifact) {
87
142
  return { title: "Logs", lines: ["Backend logs are available only from persisted run artifacts."], data: [] };
88
143
  }
@@ -103,7 +158,7 @@ function buildLogsPane(productDir, runArtifact, entry, tail) {
103
158
  return { title: "Logs", lines, data: logs };
104
159
  }
105
160
 
106
- function buildSetupPane(productDir, runArtifact, entry) {
161
+ function buildSetupContent(runArtifact, entry) {
107
162
  if (!runArtifact) {
108
163
  return { title: "Setup", lines: ["Setup operations are available only from persisted run artifacts."], data: [] };
109
164
  }
@@ -123,7 +178,7 @@ function buildSetupPane(productDir, runArtifact, entry) {
123
178
  }
124
179
 
125
180
  function resolveArtifact(productDir, snapshot) {
126
- if (snapshot.runArtifact) return snapshot.runArtifact;
181
+ if (snapshot?.runArtifact) return snapshot.runArtifact;
127
182
  try {
128
183
  return loadCurrentRunArtifact(productDir);
129
184
  } catch {
@@ -159,3 +214,8 @@ function formatAggregateDetail(entry) {
159
214
  if (entry.error) lines.push(`Error: ${entry.error}`);
160
215
  return lines;
161
216
  }
217
+
218
+ function normalizeMode(mode) {
219
+ if (mode === "logs" || mode === "artifacts" || mode === "setup") return mode;
220
+ return "detail";
221
+ }
@@ -36,10 +36,13 @@ export function normalizeCliArgs(argv) {
36
36
  "--log-tail",
37
37
  "--provider",
38
38
  "--message",
39
+ "--prompt",
39
40
  ]);
40
41
  const positionals = findPositionals(argv, valueFlags);
41
42
  const firstPositional = positionals[0] || null;
42
- const interactiveTty = process.stdout.isTTY;
43
+ const forcedInteractiveAssistant = process.env.TESTKIT_FORCE_INTERACTIVE_ASSISTANT === "1";
44
+ const interactiveTty = process.stdout.isTTY || forcedInteractiveAssistant;
45
+ const assistantDefaultDisabled = process.env.TESTKIT_NO_ASSISTANT_DEFAULT === "1";
43
46
  const runFlagPresent = argv.some((value) =>
44
47
  [
45
48
  "--type",
@@ -65,7 +68,7 @@ export function normalizeCliArgs(argv) {
65
68
  runFlagPresent ||
66
69
  !topLevelCommands.has(firstPositional?.value);
67
70
 
68
- if (!firstPositional && interactiveTty && !runFlagPresent) {
71
+ if (!firstPositional && interactiveTty && !runFlagPresent && !assistantDefaultDisabled) {
69
72
  return ["assistant", ...argv];
70
73
  }
71
74
 
@@ -73,8 +76,13 @@ export function normalizeCliArgs(argv) {
73
76
  return reorderCommandArgs(argv, positionals);
74
77
  }
75
78
 
76
- if (!topLevelCommands.has(firstPositional?.value) && interactiveTty && !runFlagPresent) {
77
- return ["assistant", "--message", argv.join(" ")];
79
+ if (!topLevelCommands.has(firstPositional?.value) && interactiveTty && !assistantDefaultDisabled) {
80
+ const shouldOpenAssistantPrompt =
81
+ forcedInteractiveAssistant ||
82
+ (!runFlagPresent && !runTypeShortcuts.has(firstPositional?.value));
83
+ if (shouldOpenAssistantPrompt) {
84
+ return ["assistant", "--prompt", argv.join(" ")];
85
+ }
78
86
  }
79
87
 
80
88
  if (shouldPrefixRun) {
@@ -9,24 +9,16 @@ import {
9
9
  applyReporterTaskStarted,
10
10
  } from "../tui/inspect-live-adapter.mjs";
11
11
  import { suiteSelectionType } from "../../runner/suite-selection.mjs";
12
- import { startHostedInvestigation } from "../agents/investigate.mjs";
13
- import { createInvestigationInterpreter } from "../agents/investigation-interpreter.mjs";
14
- import { writeInvestigationLog } from "../agents/investigation-log.mjs";
15
12
 
16
13
  export function createTreeReporter({ stdout = process.stdout, stderr = process.stderr, productDir } = {}) {
17
14
  const inspectState = createInspectState({ dataSource: "live" });
18
- let activeAgentSession = null;
19
- let activeInterpreter = null;
20
- let investigationToken = 0;
21
15
 
22
16
  const app = render(
23
17
  createElement(InspectApp, {
24
18
  inspectState,
25
19
  stdout,
26
20
  productDir,
27
- onInvestigate: startInvestigation,
28
21
  onRequestClose: close,
29
- onCancelInvestigation: cancelInvestigation,
30
22
  }),
31
23
  { stdout, exitOnCtrlC: false }
32
24
  );
@@ -98,100 +90,7 @@ export function createTreeReporter({ stdout = process.stdout, stderr = process.s
98
90
  close,
99
91
  };
100
92
 
101
- async function startInvestigation({ provider = "auto", userMessage } = {}) {
102
- const snapshot = inspectState.getSnapshot();
103
- if (!snapshot.selectedFailure) {
104
- inspectState.setNotice("No failed file is selected for investigation.");
105
- return;
106
- }
107
- if (activeAgentSession) {
108
- inspectState.setNotice("An investigation is already running.");
109
- return;
110
- }
111
-
112
- const token = ++investigationToken;
113
- let finalDelivered = false;
114
- inspectState.beginInvestigation({ provider, userMessage });
115
-
116
- try {
117
- activeInterpreter = createInvestigationInterpreter();
118
- activeAgentSession = startHostedInvestigation({
119
- productDir,
120
- serviceName: snapshot.selectedFailure.serviceName,
121
- filePath: snapshot.selectedFailure.filePath,
122
- provider,
123
- userMessage,
124
- onEvent(event) {
125
- if (token !== investigationToken) return;
126
- if (event.type === "final") finalDelivered = true;
127
- const presentation = activeInterpreter?.consumeProviderEvent(event) || null;
128
- inspectState.recordInvestigationProgress(event, presentation);
129
- },
130
- });
131
- const result = await activeAgentSession.completion;
132
- if (token !== investigationToken) return;
133
- activeAgentSession = null;
134
- if (result.finalText && !finalDelivered) {
135
- const finalEvent = { type: "final", text: result.finalText };
136
- const presentation = activeInterpreter?.consumeProviderEvent(finalEvent) || null;
137
- inspectState.recordInvestigationProgress(finalEvent, presentation);
138
- }
139
- if (result.cancelled) {
140
- inspectState.cancelAgentSession("Cancelled investigation.");
141
- persistInvestigationLog();
142
- activeInterpreter = null;
143
- return;
144
- }
145
- if (result.exitCode !== 0 && !result.finalText) {
146
- inspectState.failAgentSession(result.stderr || `Agent exited with code ${result.exitCode}`);
147
- persistInvestigationLog();
148
- activeInterpreter = null;
149
- return;
150
- }
151
- inspectState.completeAgentSession({
152
- finalText: result.finalText,
153
- exitCode: result.exitCode,
154
- });
155
- persistInvestigationLog();
156
- activeInterpreter = null;
157
- } catch (error) {
158
- if (token !== investigationToken) return;
159
- activeAgentSession = null;
160
- inspectState.failAgentSession(error);
161
- persistInvestigationLog();
162
- activeInterpreter = null;
163
- }
164
- }
165
-
166
- function cancelInvestigation() {
167
- if (!activeAgentSession) {
168
- inspectState.returnToSummary();
169
- return;
170
- }
171
- investigationToken += 1;
172
- activeAgentSession.cancel();
173
- activeAgentSession = null;
174
- inspectState.cancelAgentSession("Cancelled investigation.");
175
- persistInvestigationLog();
176
- activeInterpreter = null;
177
- }
178
-
179
93
  function close() {
180
- if (activeAgentSession) {
181
- investigationToken += 1;
182
- activeAgentSession.cancel();
183
- activeAgentSession = null;
184
- }
185
94
  app.unmount();
186
95
  }
187
-
188
- function persistInvestigationLog() {
189
- const snapshot = inspectState.getSnapshot();
190
- if (!snapshot.selectedFailure || !snapshot.agentSession) return;
191
- writeInvestigationLog({
192
- productDir,
193
- selectedFailure: snapshot.selectedFailure,
194
- agentSession: snapshot.agentSession,
195
- });
196
- }
197
96
  }
@@ -13,9 +13,8 @@ import {
13
13
  } from "../presentation/colors.mjs";
14
14
  import { renderSummaryBox } from "../presentation/summary-box.mjs";
15
15
  import { getTerminalWidth } from "../presentation/terminal-layout.mjs";
16
- import { defaultInvestigationMessage } from "../agents/prompt-builder.mjs";
16
+ import { readContextContent } from "../context-resources.mjs";
17
17
  import { applyHighlight } from "./fuzzy-match.mjs";
18
- import { buildInspectPaneContent } from "./detail-pane.mjs";
19
18
  import { FilterBar } from "./filter-bar.mjs";
20
19
 
21
20
  const SPINNER_FRAMES = ["|", "/", "-", "\\"];
@@ -24,9 +23,7 @@ export function InspectApp({
24
23
  inspectState,
25
24
  stdout,
26
25
  productDir,
27
- onInvestigate,
28
26
  onRequestClose,
29
- onCancelInvestigation,
30
27
  } = {}) {
31
28
  const { exit } = useApp();
32
29
  const [snapshot, setSnapshot] = useState(() => inspectState.getSnapshot());
@@ -41,25 +38,6 @@ export function InspectApp({
41
38
  }, [inspectState]);
42
39
 
43
40
  useInput((input, key) => {
44
- if (snapshot.mode === "investigating") {
45
- if (input === "q") {
46
- (onRequestClose || exit)();
47
- return;
48
- }
49
- if (input === "t") {
50
- inspectState.toggleInvestigationViewMode();
51
- return;
52
- }
53
- if (input === "x") {
54
- onCancelInvestigation?.();
55
- return;
56
- }
57
- if (input === "b" && snapshot.agentSession?.status !== "running" && snapshot.agentSession?.status !== "starting") {
58
- inspectState.returnToSummary();
59
- }
60
- return;
61
- }
62
-
63
41
  if (!snapshot.finished) {
64
42
  if (input === "q") {
65
43
  inspectState.setNotice("Run is still in progress. Wait for completion before closing.");
@@ -115,20 +93,6 @@ export function InspectApp({
115
93
  inspectState.cyclePaneMode();
116
94
  return;
117
95
  }
118
- if ((input === "y" || input === "i") && snapshot.selectedFailure) {
119
- onInvestigate?.({
120
- provider: "auto",
121
- userMessage: defaultInvestigationMessage(),
122
- });
123
- return;
124
- }
125
- if (input === "c" && snapshot.selectedFailure) {
126
- onInvestigate?.({ provider: "claude", userMessage: defaultInvestigationMessage() });
127
- return;
128
- }
129
- if (input === "o" && snapshot.selectedFailure) {
130
- onInvestigate?.({ provider: "codex", userMessage: defaultInvestigationMessage() });
131
- }
132
96
  });
133
97
 
134
98
  const terminalWidth = getTerminalWidth(stdout, 100);
@@ -140,13 +104,11 @@ export function InspectApp({
140
104
  );
141
105
  const paneContent = useMemo(
142
106
  () =>
143
- snapshot.mode === "investigating"
144
- ? { title: "Investigation", lines: buildInvestigationLines(snapshot) }
145
- : buildInspectPaneContent({
146
- productDir,
147
- snapshot,
148
- paneMode: snapshot.paneMode,
149
- }),
107
+ readContextContent({
108
+ productDir,
109
+ snapshot,
110
+ mode: snapshot.paneMode,
111
+ }),
150
112
  [productDir, snapshot]
151
113
  );
152
114
  const summaryLines = snapshot.finished && snapshot.summaryData
@@ -188,55 +150,12 @@ export function buildHeaderText(snapshot) {
188
150
  }
189
151
 
190
152
  export function buildFooterText(snapshot) {
191
- if (snapshot.mode === "investigating") {
192
- if (snapshot.agentSession?.status === "running" || snapshot.agentSession?.status === "starting") {
193
- return "t transcript · x cancel investigation · q quit";
194
- }
195
- return "t transcript · b back · q quit";
196
- }
197
153
  if (!snapshot.finished) return "Run in progress";
198
154
  if (snapshot.filter.active) {
199
155
  return "type to filter · ↑/↓ move · Esc clear filter · q quit";
200
156
  }
201
157
  const inspectKeys = "↑/↓ move · Enter collapse/expand · Tab cycle pane · / filter";
202
- if (!snapshot.selectedFailure) return `${inspectKeys} · q quit`;
203
- return `${inspectKeys} · y investigate · c Claude · o Codex · q quit`;
204
- }
205
-
206
- export function formatAgentEntry(entry) {
207
- if (!entry) return "";
208
- if (entry.kind === "status") return `[status] ${entry.text}`;
209
- if (entry.kind === "tool") return `[tool] ${entry.text}`;
210
- if (entry.kind === "error") return `[error] ${entry.text}`;
211
- return entry.text;
212
- }
213
-
214
- export function formatTimelineEntry(entry) {
215
- if (!entry) return "";
216
- if (entry.kind === "result") return entry.summary || "";
217
- if (entry.kind === "notice") return `${entry.severity || "info"}: ${entry.message}`;
218
- return entry.message || entry.summary || "";
219
- }
220
-
221
- function buildInvestigationLines(snapshot) {
222
- const transcriptEntries = snapshot.agentSession?.transcriptEntries || [];
223
- const timeline = snapshot.agentSession?.timeline || [];
224
- const viewMode = snapshot.agentSession?.viewMode || "summary";
225
- const status = snapshot.agentSession?.status || "idle";
226
- const lines = [
227
- `Status: ${status}`,
228
- `Phase: ${snapshot.agentSession?.activePhase || "planning"}`,
229
- "",
230
- ];
231
- if (viewMode === "transcript") {
232
- for (const entry of transcriptEntries.slice(-28)) lines.push(formatAgentEntry(entry));
233
- return lines;
234
- }
235
- for (const entry of timeline.slice(-16)) lines.push(formatTimelineEntry(entry));
236
- if (timeline.length === 0 && snapshot.agentSession?.finalText) {
237
- lines.push(snapshot.agentSession.finalText);
238
- }
239
- return lines;
158
+ return `${inspectKeys} · q quit`;
240
159
  }
241
160
 
242
161
  function buildTreeViewport(entries, selectedEntryId, radius) {
@@ -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.91",
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.91",
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.91"
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.91",
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.91",
4
4
  "description": "TypeScript compiler-backed source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {