@vitest-agent/sdk 6.1.0 → 7.0.1
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/index.d.ts +186 -12
- package/index.js +4 -2
- package/package.json +3 -3
- package/schemas/5.0/run.json +81 -0
- package/schemas/AgentReport.js +3 -1
- package/schemas/Common.js +1 -13
- package/schemas/RenderState.js +8 -1
- package/schemas/RunEvent.js +8 -1
- package/schemas/StrayOutput.js +38 -0
- package/utils/stray-output.js +143 -0
- package/version.js +1 -1
package/index.d.ts
CHANGED
|
@@ -495,6 +495,21 @@ export declare const RenderState: Schema.Struct<{
|
|
|
495
495
|
readonly expected: Schema.optional<Schema.String>;
|
|
496
496
|
readonly received: Schema.optional<Schema.String>;
|
|
497
497
|
}>>;
|
|
498
|
+
/**
|
|
499
|
+
* What the run wrote straight to the terminal, past Vitest's console
|
|
500
|
+
* capture, folded from `RunFinished.strayOutput`. Absent when the run
|
|
501
|
+
* wrote nothing stray.
|
|
502
|
+
*/
|
|
503
|
+
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
504
|
+
readonly total: Schema.Number;
|
|
505
|
+
readonly stdout: Schema.Number;
|
|
506
|
+
readonly stderr: Schema.Number;
|
|
507
|
+
readonly bytes: Schema.Number;
|
|
508
|
+
readonly samples: Schema.$Array<Schema.Struct<{
|
|
509
|
+
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
510
|
+
readonly text: Schema.String;
|
|
511
|
+
}>>;
|
|
512
|
+
}>>;
|
|
498
513
|
}>;
|
|
499
514
|
/** @public */
|
|
500
515
|
export type RenderState = typeof RenderState.Type;
|
|
@@ -930,6 +945,16 @@ export declare const AgentReport: Schema.Struct<{
|
|
|
930
945
|
readonly files: Schema.Number;
|
|
931
946
|
}>>;
|
|
932
947
|
}>>;
|
|
948
|
+
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
949
|
+
readonly total: Schema.Number;
|
|
950
|
+
readonly stdout: Schema.Number;
|
|
951
|
+
readonly stderr: Schema.Number;
|
|
952
|
+
readonly bytes: Schema.Number;
|
|
953
|
+
readonly samples: Schema.$Array<Schema.Struct<{
|
|
954
|
+
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
955
|
+
readonly text: Schema.String;
|
|
956
|
+
}>>;
|
|
957
|
+
}>>;
|
|
933
958
|
}>;
|
|
934
959
|
/** @public */
|
|
935
960
|
export type AgentReport = typeof AgentReport.Type;
|
|
@@ -1021,13 +1046,6 @@ export type Environment = typeof Environment.Type;
|
|
|
1021
1046
|
export declare const Executor: Schema.Literals<readonly ["human", "agent", "ci"]>;
|
|
1022
1047
|
/** @public */
|
|
1023
1048
|
export type Executor = typeof Executor.Type;
|
|
1024
|
-
/**
|
|
1025
|
-
* Output format for the reporter pipeline.
|
|
1026
|
-
* @public
|
|
1027
|
-
*/
|
|
1028
|
-
export declare const OutputFormat: Schema.Literals<readonly ["terminal", "markdown", "json", "vitest-bypass", "silent", "ci-annotations"]>;
|
|
1029
|
-
/** @public */
|
|
1030
|
-
export type OutputFormat = typeof OutputFormat.Type;
|
|
1031
1049
|
/**
|
|
1032
1050
|
* Level of detail in reporter output.
|
|
1033
1051
|
* @public
|
|
@@ -1322,6 +1340,21 @@ export declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunSt
|
|
|
1322
1340
|
readonly expected: Schema.optional<Schema.String>;
|
|
1323
1341
|
readonly received: Schema.optional<Schema.String>;
|
|
1324
1342
|
}>>>;
|
|
1343
|
+
/**
|
|
1344
|
+
* What the run wrote straight to the terminal, past Vitest's console
|
|
1345
|
+
* capture (`AgentReport.strayOutput`). Optional: absent when the run
|
|
1346
|
+
* wrote nothing stray, and in older replay data.
|
|
1347
|
+
*/
|
|
1348
|
+
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
1349
|
+
readonly total: Schema.Number;
|
|
1350
|
+
readonly stdout: Schema.Number;
|
|
1351
|
+
readonly stderr: Schema.Number;
|
|
1352
|
+
readonly bytes: Schema.Number;
|
|
1353
|
+
readonly samples: Schema.$Array<Schema.Struct<{
|
|
1354
|
+
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
1355
|
+
readonly text: Schema.String;
|
|
1356
|
+
}>>;
|
|
1357
|
+
}>>;
|
|
1325
1358
|
}>]>;
|
|
1326
1359
|
/** @public */
|
|
1327
1360
|
export type RunEvent = typeof RunEvent.Type;
|
|
@@ -1461,10 +1494,10 @@ export type Transport = typeof Transport.Type;
|
|
|
1461
1494
|
* plugin always populates it in practice — the option exists for clarity
|
|
1462
1495
|
* and to leave room for future "no-persistence" experiments.
|
|
1463
1496
|
*
|
|
1464
|
-
* `
|
|
1465
|
-
*
|
|
1466
|
-
*
|
|
1467
|
-
*
|
|
1497
|
+
* `detail` is pre-resolved by the plugin (via `DetailResolver`) but
|
|
1498
|
+
* reporters that want to override can ignore it. `noColor` is the resolved
|
|
1499
|
+
* value of the `NO_COLOR` env var; reporters use it to gate ANSI escapes and
|
|
1500
|
+
* OSC-8 hyperlinks.
|
|
1468
1501
|
* @public
|
|
1469
1502
|
*/
|
|
1470
1503
|
export interface ResolvedReporterConfig {
|
|
@@ -1503,7 +1536,6 @@ export interface ResolvedReporterConfig {
|
|
|
1503
1536
|
* @internal
|
|
1504
1537
|
*/
|
|
1505
1538
|
readonly coverageMode: "full" | "ui-only";
|
|
1506
|
-
readonly format: OutputFormat;
|
|
1507
1539
|
readonly detail: DetailLevel;
|
|
1508
1540
|
readonly noColor: boolean;
|
|
1509
1541
|
readonly runCommand?: string;
|
|
@@ -1625,6 +1657,24 @@ export interface VitestAgentReporter {
|
|
|
1625
1657
|
* awaits it and treats a rejection as a logged warning.
|
|
1626
1658
|
*/
|
|
1627
1659
|
readonly close?: () => Promise<void>;
|
|
1660
|
+
/**
|
|
1661
|
+
* Print one whole line of stray output: bytes a test process wrote
|
|
1662
|
+
* straight to the terminal, past Vitest's console capture (a child
|
|
1663
|
+
* process spawned with inherited stdio is the usual source). A reporter
|
|
1664
|
+
* that draws in place (a live view) implements this to print the line
|
|
1665
|
+
* above its drawing instead of under it, where the next redraw would
|
|
1666
|
+
* strand it. Return `true` when the line was taken; `false` sends it to
|
|
1667
|
+
* the terminal unchanged.
|
|
1668
|
+
*
|
|
1669
|
+
* The plugin calls it synchronously, from the factory's return until just
|
|
1670
|
+
* before {@link VitestAgentReporter.close | close}, only for lines bound
|
|
1671
|
+
* for the process's own stdout or stderr (never when Vitest was given
|
|
1672
|
+
* other streams, as MCP's `run_tests` does), and only while it owns the
|
|
1673
|
+
* console (any mode but `passthrough`). The line carries no trailing
|
|
1674
|
+
* newline. Optional; without it every line passes through unchanged. The
|
|
1675
|
+
* run's stray output is reported on `AgentReport.strayOutput` either way.
|
|
1676
|
+
*/
|
|
1677
|
+
readonly printStrayLine?: (stream: "stdout" | "stderr", line: string) => boolean;
|
|
1628
1678
|
}
|
|
1629
1679
|
/**
|
|
1630
1680
|
* Factory that the plugin's `reporter` option accepts. The plugin calls
|
|
@@ -2932,11 +2982,62 @@ export declare const RunReportFile: Schema.Struct<{
|
|
|
2932
2982
|
readonly files: Schema.Number;
|
|
2933
2983
|
}>>;
|
|
2934
2984
|
}>>;
|
|
2985
|
+
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
2986
|
+
readonly total: Schema.Number;
|
|
2987
|
+
readonly stdout: Schema.Number;
|
|
2988
|
+
readonly stderr: Schema.Number;
|
|
2989
|
+
readonly bytes: Schema.Number;
|
|
2990
|
+
readonly samples: Schema.$Array<Schema.Struct<{
|
|
2991
|
+
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
2992
|
+
readonly text: Schema.String;
|
|
2993
|
+
}>>;
|
|
2994
|
+
}>>;
|
|
2935
2995
|
}>>;
|
|
2936
2996
|
}>;
|
|
2937
2997
|
/** @public */
|
|
2938
2998
|
export type RunReportFile = typeof RunReportFile.Type;
|
|
2939
2999
|
//#endregion
|
|
3000
|
+
//#region src/schemas/StrayOutput.d.ts
|
|
3001
|
+
/**
|
|
3002
|
+
* One line of stray output, kept as a sample: the stream it was written to,
|
|
3003
|
+
* and its text with escapes stripped, cut to a sane length.
|
|
3004
|
+
* @public
|
|
3005
|
+
*/
|
|
3006
|
+
export declare const StrayOutputSample: Schema.Struct<{
|
|
3007
|
+
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
3008
|
+
readonly text: Schema.String;
|
|
3009
|
+
}>;
|
|
3010
|
+
/** @public */
|
|
3011
|
+
export type StrayOutputSample = typeof StrayOutputSample.Type;
|
|
3012
|
+
/**
|
|
3013
|
+
* Output a test run wrote straight to the terminal, past Vitest's console
|
|
3014
|
+
* capture: bytes a worker process (or a child process it spawned with
|
|
3015
|
+
* inherited stdio, such as `execFileSync("git", ...)`) wrote to its own
|
|
3016
|
+
* stdout or stderr, which Vitest pipes through to the main process
|
|
3017
|
+
* untouched. Distinct from `consoleLeaks`, which counts `console.*` calls
|
|
3018
|
+
* Vitest did capture.
|
|
3019
|
+
*
|
|
3020
|
+
* A run-level signal: the bytes arrive on one stream shared by every project,
|
|
3021
|
+
* so nothing attributes them to a project, file or test. Every project report
|
|
3022
|
+
* of a run carries the same value. Counts are of non-blank lines; `bytes`
|
|
3023
|
+
* counts everything recorded. `samples` holds the first few lines. Omitted
|
|
3024
|
+
* when the run wrote nothing stray (and whenever the plugin does not own the
|
|
3025
|
+
* console: `passthrough` mode records nothing).
|
|
3026
|
+
* @public
|
|
3027
|
+
*/
|
|
3028
|
+
export declare const StrayOutput: Schema.Struct<{
|
|
3029
|
+
readonly total: Schema.Number;
|
|
3030
|
+
readonly stdout: Schema.Number;
|
|
3031
|
+
readonly stderr: Schema.Number;
|
|
3032
|
+
readonly bytes: Schema.Number;
|
|
3033
|
+
readonly samples: Schema.$Array<Schema.Struct<{
|
|
3034
|
+
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
3035
|
+
readonly text: Schema.String;
|
|
3036
|
+
}>>;
|
|
3037
|
+
}>;
|
|
3038
|
+
/** @public */
|
|
3039
|
+
export type StrayOutput = typeof StrayOutput.Type;
|
|
3040
|
+
//#endregion
|
|
2940
3041
|
//#region src/schemas/Tdd.d.ts
|
|
2941
3042
|
/** @public */
|
|
2942
3043
|
export declare const GoalStatus: Schema.Literals<readonly ["pending", "in_progress", "done", "abandoned"]>;
|
|
@@ -4144,6 +4245,79 @@ export declare const probeHostMetadataFromEnv: (env: Record<string, string | und
|
|
|
4144
4245
|
*/
|
|
4145
4246
|
export declare function safeFilename(name: string): string;
|
|
4146
4247
|
//#endregion
|
|
4248
|
+
//#region src/utils/stray-output.d.ts
|
|
4249
|
+
/**
|
|
4250
|
+
* A stream stray output was written to.
|
|
4251
|
+
* @public
|
|
4252
|
+
*/
|
|
4253
|
+
export type StrayOutputStream = "stdout" | "stderr";
|
|
4254
|
+
/**
|
|
4255
|
+
* Records stray output for one run at a time: counts its non-blank lines per
|
|
4256
|
+
* stream and its bytes, and keeps the first few lines as samples. Memory is
|
|
4257
|
+
* bounded whatever is written: only the samples and one partial line per
|
|
4258
|
+
* stream (itself cut to the sample length) are held.
|
|
4259
|
+
* @public
|
|
4260
|
+
*/
|
|
4261
|
+
export interface StrayOutputRecorder {
|
|
4262
|
+
/** Record text written to `stream`. A write made only of escape sequences is ignored. */
|
|
4263
|
+
readonly write: (stream: StrayOutputStream, text: string) => void;
|
|
4264
|
+
/** What the run has written so far, a trailing partial line included; `undefined` when nothing. */
|
|
4265
|
+
readonly snapshot: () => StrayOutput | undefined;
|
|
4266
|
+
/** Forget everything: a new run starts. */
|
|
4267
|
+
readonly reset: () => void;
|
|
4268
|
+
}
|
|
4269
|
+
/**
|
|
4270
|
+
* Options for {@link makeStrayOutputRecorder}.
|
|
4271
|
+
* @public
|
|
4272
|
+
*/
|
|
4273
|
+
export interface StrayOutputRecorderOptions {
|
|
4274
|
+
/** How many lines to keep as samples. @defaultValue 5 */
|
|
4275
|
+
readonly maxSamples?: number;
|
|
4276
|
+
/** How many characters of a sample to keep before cutting it with `…`. @defaultValue 160 */
|
|
4277
|
+
readonly maxSampleChars?: number;
|
|
4278
|
+
}
|
|
4279
|
+
/**
|
|
4280
|
+
* True when `text` is nothing but terminal escape sequences: the cursor
|
|
4281
|
+
* hide/show, clear-screen and erase-scrollback writes Vitest's own `Logger`
|
|
4282
|
+
* makes straight to its output stream. Empty text is not escape-only.
|
|
4283
|
+
* @public
|
|
4284
|
+
*/
|
|
4285
|
+
export declare const isEscapeOnly: (text: string) => boolean;
|
|
4286
|
+
/**
|
|
4287
|
+
* Make a {@link StrayOutputRecorder}.
|
|
4288
|
+
* @public
|
|
4289
|
+
*/
|
|
4290
|
+
export declare const makeStrayOutputRecorder: (options?: StrayOutputRecorderOptions) => StrayOutputRecorder;
|
|
4291
|
+
/**
|
|
4292
|
+
* The key a stray-output capture publishes its {@link StrayOutputSource}
|
|
4293
|
+
* under, on the Vitest `Logger` whose streams it wraps. `Symbol.for`, so a
|
|
4294
|
+
* plugin and an MCP server that load different copies of this package still
|
|
4295
|
+
* meet on it.
|
|
4296
|
+
* @public
|
|
4297
|
+
*/
|
|
4298
|
+
export declare const STRAY_OUTPUT_SOURCE: unique symbol;
|
|
4299
|
+
/**
|
|
4300
|
+
* What a stray-output capture publishes for a reader that did not install it.
|
|
4301
|
+
* @public
|
|
4302
|
+
*/
|
|
4303
|
+
export interface StrayOutputSource {
|
|
4304
|
+
/** The current run's stray output, or `undefined` when it wrote none. */
|
|
4305
|
+
readonly snapshot: () => StrayOutput | undefined;
|
|
4306
|
+
}
|
|
4307
|
+
/**
|
|
4308
|
+
* Read the current run's stray output from a Vitest `Logger` a capture was
|
|
4309
|
+
* installed on. `undefined` when none was installed, or the run wrote nothing.
|
|
4310
|
+
* @public
|
|
4311
|
+
*/
|
|
4312
|
+
export declare const readStrayOutput: (logger: unknown) => StrayOutput | undefined;
|
|
4313
|
+
/**
|
|
4314
|
+
* The note a renderer prints for a run's stray output: one line naming the
|
|
4315
|
+
* count, the streams, the usual cause and the fix, then up to three sample
|
|
4316
|
+
* lines and how many it left out.
|
|
4317
|
+
* @public
|
|
4318
|
+
*/
|
|
4319
|
+
export declare const formatStrayOutputNote: (stray: StrayOutput) => ReadonlyArray<string>;
|
|
4320
|
+
//#endregion
|
|
4147
4321
|
//#region src/utils/test-location.d.ts
|
|
4148
4322
|
/** Directory holding source files whose tests may be co-located. @public */
|
|
4149
4323
|
export declare const SRC_DIR = "src";
|
package/index.js
CHANGED
|
@@ -7,10 +7,11 @@ import { GitCommandError, ProbeMissError } from "./errors/RunContextError.js";
|
|
|
7
7
|
import { BehaviorNotFoundError, GoalNotFoundError, IllegalStatusTransitionError, TddTaskAlreadyEndedError, TddTaskNotFoundError } from "./errors/TddErrors.js";
|
|
8
8
|
import { ActorType, AgentId, ChatId, ConversationId, HostKind, ProjectKey, TddTaskId } from "./schemas/Identity.js";
|
|
9
9
|
import { Agent, IdempotencyHit } from "./schemas/Agent.js";
|
|
10
|
-
import { AgentConsoleMode, CiConsoleMode, ConsoleMode, ConsoleOutputMode, DetailLevel, Environment, Executor, HumanConsoleMode,
|
|
10
|
+
import { AgentConsoleMode, CiConsoleMode, ConsoleMode, ConsoleOutputMode, DetailLevel, Environment, Executor, HumanConsoleMode, PackageManager, ReportError, TestClassification, TestRunReason, TestState } from "./schemas/Common.js";
|
|
11
11
|
import { ConsoleLeakFile, ConsoleLeaks, ConsoleLeaksFromFailingTests } from "./schemas/ConsoleLeaks.js";
|
|
12
12
|
import { MetricThresholds, PatternMetricThresholds, PatternThresholds, PerFileThresholds, ResolvedThresholds } from "./schemas/Thresholds.js";
|
|
13
13
|
import { CoverageReport, CoverageTotals, FileCoverageReport, GlobShortfall } from "./schemas/Coverage.js";
|
|
14
|
+
import { StrayOutput, StrayOutputSample } from "./schemas/StrayOutput.js";
|
|
14
15
|
import { AgentReport, ModuleReport, ReportSummary, TagCountEntry, TestReport } from "./schemas/AgentReport.js";
|
|
15
16
|
import { CoverageBaselines } from "./schemas/Baselines.js";
|
|
16
17
|
import { CacheManifest, CacheManifestEntry } from "./schemas/CacheManifest.js";
|
|
@@ -57,9 +58,10 @@ import { normalizeWorkspaceKey } from "./utils/normalize-workspace-key.js";
|
|
|
57
58
|
import { basenamePosix, joinPosix, relativePosix, toPosix } from "./utils/posix-path.js";
|
|
58
59
|
import { probeHostMetadataFromEnv } from "./utils/probe-host-metadata.js";
|
|
59
60
|
import { safeFilename } from "./utils/safe-filename.js";
|
|
61
|
+
import { STRAY_OUTPUT_SOURCE, formatStrayOutputNote, isEscapeOnly, makeStrayOutputRecorder, readStrayOutput } from "./utils/stray-output.js";
|
|
60
62
|
import { BATS_FILE_GLOB_SUFFIX, NON_DISCOVERABLE_DIRS, SRC_DIR, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS, classifyTestPath, findOwningWorkspace, isTestFileName } from "./utils/test-location.js";
|
|
61
63
|
import { validateCoverageTargetsShape } from "./utils/validate-coverage-targets-shape.js";
|
|
62
64
|
import { requiredArtifactForTransition, transitionEnforcesBehaviorMatch, validatePhaseTransition } from "./utils/validate-phase-transition.js";
|
|
63
65
|
import { CURRENT_SDK_VERSION } from "./version.js";
|
|
64
66
|
|
|
65
|
-
export { ActionSeverity, ActorType, Agent, AgentConsoleMode, AgentId, AgentNotFoundError, AgentPluginOptions, AgentReport, AgentReporterOptions, BATS_FILE_GLOB_SUFFIX, BehaviorAbandonedEvent, BehaviorAddedEvent, BehaviorCompletedEvent, BehaviorDetail, BehaviorNotFoundError, BehaviorRow, BehaviorScopedEventTypes, BehaviorStartedEvent, BehaviorStatus, BehaviorsReadyEvent, BlockedEvent, CURRENT_SDK_VERSION, CacheManifest, CacheManifestEntry, ChannelEvent, ChatId, CiConsoleMode, ConsoleLeakFile, ConsoleLeaks, ConsoleLeaksFromFailingTests, ConsoleLogLevel, ConsoleMode, ConsoleOutputMode, ConsoleOutputs, ConversationId, ConversationNotFoundError, CoverageBaselines, FileCoverageReport as CoverageFile, CoverageGap, CoverageLevel, CoverageMetric, CoverageRenderState, CoverageReport, CoverageTargets, CoverageTargetsMetrics, CoverageTotals, DataStoreError, DetailLevel, DiscoveryError, Environment, Executor, FailureRecord, FileCoverageReport, FileEditPayload, GitCommandError, GlobShortfall, GoalAbandonedEvent, GoalAddedEvent, GoalCompletedEvent, GoalDetail, GoalNotFoundError, GoalRow, GoalStartedEvent, GoalStatus, GoalsReadyEvent, HistoryRecord, HookFirePayload, HookStatus, HookType, HostKind, HostMetadataParseError, HumanConsoleMode, HypothesisPayload, IdempotencyHit, IllegalStatusTransitionError, MetricThresholds, ModuleRecord, ModuleReport, NON_DISCOVERABLE_DIRS, NotePayload,
|
|
67
|
+
export { ActionSeverity, ActorType, Agent, AgentConsoleMode, AgentId, AgentNotFoundError, AgentPluginOptions, AgentReport, AgentReporterOptions, BATS_FILE_GLOB_SUFFIX, BehaviorAbandonedEvent, BehaviorAddedEvent, BehaviorCompletedEvent, BehaviorDetail, BehaviorNotFoundError, BehaviorRow, BehaviorScopedEventTypes, BehaviorStartedEvent, BehaviorStatus, BehaviorsReadyEvent, BlockedEvent, CURRENT_SDK_VERSION, CacheManifest, CacheManifestEntry, ChannelEvent, ChatId, CiConsoleMode, ConsoleLeakFile, ConsoleLeaks, ConsoleLeaksFromFailingTests, ConsoleLogLevel, ConsoleMode, ConsoleOutputMode, ConsoleOutputs, ConversationId, ConversationNotFoundError, CoverageBaselines, FileCoverageReport as CoverageFile, CoverageGap, CoverageLevel, CoverageMetric, CoverageRenderState, CoverageReport, CoverageTargets, CoverageTargetsMetrics, CoverageTotals, DataStoreError, DetailLevel, DiscoveryError, Environment, Executor, FailureRecord, FileCoverageReport, FileEditPayload, GitCommandError, GlobShortfall, GoalAbandonedEvent, GoalAddedEvent, GoalCompletedEvent, GoalDetail, GoalNotFoundError, GoalRow, GoalStartedEvent, GoalStatus, GoalsReadyEvent, HistoryRecord, HookFirePayload, HookStatus, HookType, HostKind, HostMetadataParseError, HumanConsoleMode, HypothesisPayload, IdempotencyHit, IllegalStatusTransitionError, MetricThresholds, ModuleRecord, ModuleReport, NON_DISCOVERABLE_DIRS, NotePayload, PackageManager, PathResolutionError, PatternMetricThresholds, PatternThresholds, PerFileThresholds, PhaseTransitionEvent, ProbeMissError, ProjectIdentityNotResolvableError, ProjectKey, RUN_REPORT_FILE_SCHEMA_URL, RegistrationConflictError, RenderState, RenderTotals, ReportError, ReportOption, ReportSettings, ReportSummary, ResolvedThresholds, RunEvent, RunReportFile, SRC_DIR, STRAY_OUTPUT_SOURCE, SessionCompleteEvent, SessionNotFoundError, SessionOutcome, SidecarTimeoutError, StrayOutput, StrayOutputSample, SuggestedActionRecord, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS, TagCountEntry, TddTaskAlreadyEndedError, TddTaskId, TddTaskNotFoundError, TestAnnotation, TestArtifact, TestArtifactLocation, TestAttachment, TestClassification, TestHistory, TestRecord, TestReport, TestRun, TestRunReason, TestState, ToolCallPayload, ToolResultPayload, Transport, TrendEntry, TrendRecord, TurnPayload, UserPromptPayload, VitestAgentConfig, ansi, basenamePosix, buildAgentReport, buildConsoleLeaks, buildEnvPrefix, canonicalizeGitUrl, classifyTest, classifyTestPath, coerceErrorField, coerceErrorText, collectConsoleLeakEntries, compressLines, computeTrend, countSuiteFailures, detectNonDefaultDiscoverStrategy, detectPackageManager, detectVitestScripts, determineTier, extractSqlReason, findFunctionBoundary, findOwningWorkspace, formatConsoleMarkdown, formatFatalError, formatGfm, formatScopedCoverageNote, formatStrayOutputNote, getRecentDirection, getRunCommand, getWorstMetric, gitUrlToProjectKey, hashTargets, initialRenderState, isEscapeOnly, isTestFileName, isTimeoutError, isVitestInvocation, joinPosix, makeStrayOutputRecorder, normalizeWorkspaceKey, osc8, probeHostMetadataFromEnv, readStrayOutput, relativePath, relativePosix, requiredArtifactForTransition, resolveCoverageInput, rewriteBashCommand, safeFilename, stripAnsi, toPosix, transitionEnforcesBehaviorMatch, validateCoverageConfig, validateCoverageTargetsShape, validatePhaseTransition };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Platform-free core for the vitest-agent package family: Effect Schema definitions, reporter and dispatcher contracts, errors, and pure utilities.",
|
|
6
6
|
"keywords": [
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"./package.json": "./package.json"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"acorn": "^8.
|
|
44
|
+
"acorn": "^8.19.0",
|
|
45
45
|
"acorn-typescript": "^1.4.13",
|
|
46
|
-
"effect": "
|
|
46
|
+
"effect": "4.0.2"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
49
|
"node": ">=24.11.0"
|
package/schemas/5.0/run.json
CHANGED
|
@@ -887,6 +887,77 @@
|
|
|
887
887
|
"required": ["total", "byFile"],
|
|
888
888
|
"additionalProperties": true
|
|
889
889
|
},
|
|
890
|
+
"StrayOutputSample": {
|
|
891
|
+
"type": "object",
|
|
892
|
+
"properties": {
|
|
893
|
+
"stream": {
|
|
894
|
+
"type": "string",
|
|
895
|
+
"enum": ["stdout", "stderr"]
|
|
896
|
+
},
|
|
897
|
+
"text": {
|
|
898
|
+
"type": "string"
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
"required": ["stream", "text"],
|
|
902
|
+
"additionalProperties": true
|
|
903
|
+
},
|
|
904
|
+
"StrayOutput": {
|
|
905
|
+
"type": "object",
|
|
906
|
+
"properties": {
|
|
907
|
+
"total": {
|
|
908
|
+
"anyOf": [
|
|
909
|
+
{
|
|
910
|
+
"type": "number"
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
"type": "string",
|
|
914
|
+
"enum": ["Infinity", "-Infinity", "NaN"]
|
|
915
|
+
}
|
|
916
|
+
]
|
|
917
|
+
},
|
|
918
|
+
"stdout": {
|
|
919
|
+
"anyOf": [
|
|
920
|
+
{
|
|
921
|
+
"type": "number"
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
"type": "string",
|
|
925
|
+
"enum": ["Infinity", "-Infinity", "NaN"]
|
|
926
|
+
}
|
|
927
|
+
]
|
|
928
|
+
},
|
|
929
|
+
"stderr": {
|
|
930
|
+
"anyOf": [
|
|
931
|
+
{
|
|
932
|
+
"type": "number"
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
"type": "string",
|
|
936
|
+
"enum": ["Infinity", "-Infinity", "NaN"]
|
|
937
|
+
}
|
|
938
|
+
]
|
|
939
|
+
},
|
|
940
|
+
"bytes": {
|
|
941
|
+
"anyOf": [
|
|
942
|
+
{
|
|
943
|
+
"type": "number"
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
"type": "string",
|
|
947
|
+
"enum": ["Infinity", "-Infinity", "NaN"]
|
|
948
|
+
}
|
|
949
|
+
]
|
|
950
|
+
},
|
|
951
|
+
"samples": {
|
|
952
|
+
"type": "array",
|
|
953
|
+
"items": {
|
|
954
|
+
"$ref": "#/$defs/StrayOutputSample"
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
"required": ["total", "stdout", "stderr", "bytes", "samples"],
|
|
959
|
+
"additionalProperties": true
|
|
960
|
+
},
|
|
890
961
|
"AgentReport": {
|
|
891
962
|
"type": "object",
|
|
892
963
|
"properties": {
|
|
@@ -959,6 +1030,16 @@
|
|
|
959
1030
|
"type": "null"
|
|
960
1031
|
}
|
|
961
1032
|
]
|
|
1033
|
+
},
|
|
1034
|
+
"strayOutput": {
|
|
1035
|
+
"anyOf": [
|
|
1036
|
+
{
|
|
1037
|
+
"$ref": "#/$defs/StrayOutput"
|
|
1038
|
+
},
|
|
1039
|
+
{
|
|
1040
|
+
"type": "null"
|
|
1041
|
+
}
|
|
1042
|
+
]
|
|
962
1043
|
}
|
|
963
1044
|
},
|
|
964
1045
|
"required": ["timestamp", "reason", "summary", "failed", "unhandledErrors", "failedFiles"],
|
package/schemas/AgentReport.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReportError, TestClassification, TestRunReason, TestState } from "./Common.js";
|
|
2
2
|
import { ConsoleLeaks } from "./ConsoleLeaks.js";
|
|
3
3
|
import { CoverageReport } from "./Coverage.js";
|
|
4
|
+
import { StrayOutput } from "./StrayOutput.js";
|
|
4
5
|
import { Schema } from "effect";
|
|
5
6
|
|
|
6
7
|
//#region src/schemas/AgentReport.ts
|
|
@@ -71,7 +72,8 @@ const AgentReport = Schema.Struct({
|
|
|
71
72
|
failedFiles: Schema.Array(Schema.String),
|
|
72
73
|
coverage: Schema.optional(CoverageReport),
|
|
73
74
|
tagCounts: Schema.optional(Schema.Record(Schema.String, TagCountEntry)),
|
|
74
|
-
consoleLeaks: Schema.optional(ConsoleLeaks)
|
|
75
|
+
consoleLeaks: Schema.optional(ConsoleLeaks),
|
|
76
|
+
strayOutput: Schema.optional(StrayOutput)
|
|
75
77
|
}).annotate({ identifier: "AgentReport" });
|
|
76
78
|
|
|
77
79
|
//#endregion
|
package/schemas/Common.js
CHANGED
|
@@ -116,18 +116,6 @@ const Executor = Schema.Literals([
|
|
|
116
116
|
"ci"
|
|
117
117
|
]).annotate({ identifier: "Executor" });
|
|
118
118
|
/**
|
|
119
|
-
* Output format for the reporter pipeline.
|
|
120
|
-
* @public
|
|
121
|
-
*/
|
|
122
|
-
const OutputFormat = Schema.Literals([
|
|
123
|
-
"terminal",
|
|
124
|
-
"markdown",
|
|
125
|
-
"json",
|
|
126
|
-
"vitest-bypass",
|
|
127
|
-
"silent",
|
|
128
|
-
"ci-annotations"
|
|
129
|
-
]).annotate({ identifier: "OutputFormat" });
|
|
130
|
-
/**
|
|
131
119
|
* Level of detail in reporter output.
|
|
132
120
|
* @public
|
|
133
121
|
*/
|
|
@@ -158,4 +146,4 @@ const ReportError = Schema.Struct({
|
|
|
158
146
|
}).annotate({ identifier: "ReportError" });
|
|
159
147
|
|
|
160
148
|
//#endregion
|
|
161
|
-
export { AgentConsoleMode, CiConsoleMode, ConsoleMode, ConsoleOutputMode, DetailLevel, Environment, Executor, HumanConsoleMode,
|
|
149
|
+
export { AgentConsoleMode, CiConsoleMode, ConsoleMode, ConsoleOutputMode, DetailLevel, Environment, Executor, HumanConsoleMode, PackageManager, ReportError, TestClassification, TestRunReason, TestState };
|
package/schemas/RenderState.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReportError, TestClassification, TestState } from "./Common.js";
|
|
2
2
|
import { MetricThresholds } from "./Thresholds.js";
|
|
3
3
|
import { CoverageTotals, GlobShortfall } from "./Coverage.js";
|
|
4
|
+
import { StrayOutput } from "./StrayOutput.js";
|
|
4
5
|
import { ActionSeverity, CoverageGap, CoverageMetric } from "./RunEvent.js";
|
|
5
6
|
import { Schema } from "effect";
|
|
6
7
|
|
|
@@ -156,7 +157,13 @@ const RenderState = Schema.Struct({
|
|
|
156
157
|
* (older replay data, hand-built fixtures) defaults to `[]`. See
|
|
157
158
|
* issue #240.
|
|
158
159
|
*/
|
|
159
|
-
unhandledErrors: Schema.Array(ReportError)
|
|
160
|
+
unhandledErrors: Schema.Array(ReportError),
|
|
161
|
+
/**
|
|
162
|
+
* What the run wrote straight to the terminal, past Vitest's console
|
|
163
|
+
* capture, folded from `RunFinished.strayOutput`. Absent when the run
|
|
164
|
+
* wrote nothing stray.
|
|
165
|
+
*/
|
|
166
|
+
strayOutput: Schema.optional(StrayOutput)
|
|
160
167
|
}).annotate({ identifier: "RenderState" });
|
|
161
168
|
/**
|
|
162
169
|
* Initial state — what the reducer returns when no events have been
|
package/schemas/RunEvent.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReportError, TestClassification, TestState } from "./Common.js";
|
|
2
2
|
import { MetricThresholds } from "./Thresholds.js";
|
|
3
3
|
import { CoverageTotals, FileCoverageReport, GlobShortfall } from "./Coverage.js";
|
|
4
|
+
import { StrayOutput } from "./StrayOutput.js";
|
|
4
5
|
import { TestArtifactLocation, TestAttachment } from "./TestArtifacts.js";
|
|
5
6
|
import { Schema } from "effect";
|
|
6
7
|
|
|
@@ -219,7 +220,13 @@ const RunEvent = Schema.Union([
|
|
|
219
220
|
* argument to `onTestRunEnd`. Optional so hand-built fixtures
|
|
220
221
|
* and older replay data keep decoding. See issue #240.
|
|
221
222
|
*/
|
|
222
|
-
unhandledErrors: Schema.optional(Schema.Array(ReportError))
|
|
223
|
+
unhandledErrors: Schema.optional(Schema.Array(ReportError)),
|
|
224
|
+
/**
|
|
225
|
+
* What the run wrote straight to the terminal, past Vitest's console
|
|
226
|
+
* capture (`AgentReport.strayOutput`). Optional: absent when the run
|
|
227
|
+
* wrote nothing stray, and in older replay data.
|
|
228
|
+
*/
|
|
229
|
+
strayOutput: Schema.optional(StrayOutput)
|
|
223
230
|
})
|
|
224
231
|
]).annotate({ identifier: "RunEvent" });
|
|
225
232
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/schemas/StrayOutput.ts
|
|
4
|
+
/**
|
|
5
|
+
* One line of stray output, kept as a sample: the stream it was written to,
|
|
6
|
+
* and its text with escapes stripped, cut to a sane length.
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
const StrayOutputSample = Schema.Struct({
|
|
10
|
+
stream: Schema.Literals(["stdout", "stderr"]),
|
|
11
|
+
text: Schema.String
|
|
12
|
+
}).annotate({ identifier: "StrayOutputSample" });
|
|
13
|
+
/**
|
|
14
|
+
* Output a test run wrote straight to the terminal, past Vitest's console
|
|
15
|
+
* capture: bytes a worker process (or a child process it spawned with
|
|
16
|
+
* inherited stdio, such as `execFileSync("git", ...)`) wrote to its own
|
|
17
|
+
* stdout or stderr, which Vitest pipes through to the main process
|
|
18
|
+
* untouched. Distinct from `consoleLeaks`, which counts `console.*` calls
|
|
19
|
+
* Vitest did capture.
|
|
20
|
+
*
|
|
21
|
+
* A run-level signal: the bytes arrive on one stream shared by every project,
|
|
22
|
+
* so nothing attributes them to a project, file or test. Every project report
|
|
23
|
+
* of a run carries the same value. Counts are of non-blank lines; `bytes`
|
|
24
|
+
* counts everything recorded. `samples` holds the first few lines. Omitted
|
|
25
|
+
* when the run wrote nothing stray (and whenever the plugin does not own the
|
|
26
|
+
* console: `passthrough` mode records nothing).
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
const StrayOutput = Schema.Struct({
|
|
30
|
+
total: Schema.Number,
|
|
31
|
+
stdout: Schema.Number,
|
|
32
|
+
stderr: Schema.Number,
|
|
33
|
+
bytes: Schema.Number,
|
|
34
|
+
samples: Schema.Array(StrayOutputSample)
|
|
35
|
+
}).annotate({ identifier: "StrayOutput" });
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { StrayOutput, StrayOutputSample };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
//#region src/utils/stray-output.ts
|
|
2
|
+
const ESCAPE = /\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-Z\\-_]|c)/g;
|
|
3
|
+
const CONTROL = /[\x00-\x08\x0b-\x1f\x7f]/g;
|
|
4
|
+
/**
|
|
5
|
+
* True when `text` is nothing but terminal escape sequences: the cursor
|
|
6
|
+
* hide/show, clear-screen and erase-scrollback writes Vitest's own `Logger`
|
|
7
|
+
* makes straight to its output stream. Empty text is not escape-only.
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
const isEscapeOnly = (text) => text.length > 0 && text.replace(ESCAPE, "").length === 0;
|
|
11
|
+
const encoder = new TextEncoder();
|
|
12
|
+
/**
|
|
13
|
+
* Make a {@link StrayOutputRecorder}.
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
const makeStrayOutputRecorder = (options = {}) => {
|
|
17
|
+
const maxSamples = options.maxSamples ?? 5;
|
|
18
|
+
const maxChars = options.maxSampleChars ?? 160;
|
|
19
|
+
let counts = {
|
|
20
|
+
stdout: 0,
|
|
21
|
+
stderr: 0
|
|
22
|
+
};
|
|
23
|
+
let bytes = 0;
|
|
24
|
+
let samples = [];
|
|
25
|
+
let pending = {
|
|
26
|
+
stdout: {
|
|
27
|
+
text: "",
|
|
28
|
+
cut: false
|
|
29
|
+
},
|
|
30
|
+
stderr: {
|
|
31
|
+
text: "",
|
|
32
|
+
cut: false
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const clean = (line) => line.replace(ESCAPE, "").replace(CONTROL, "").trim();
|
|
36
|
+
const sampleOf = (line) => {
|
|
37
|
+
const text = clean(line.text);
|
|
38
|
+
return line.cut || text.length > maxChars ? `${text.slice(0, maxChars)}…` : text;
|
|
39
|
+
};
|
|
40
|
+
const finish = (stream, line, into, out) => {
|
|
41
|
+
if (clean(line.text) === "") return;
|
|
42
|
+
into[stream]++;
|
|
43
|
+
if (out.length < maxSamples) out.push({
|
|
44
|
+
stream,
|
|
45
|
+
text: sampleOf(line)
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const append = (line, text) => {
|
|
49
|
+
const room = maxChars * 4 - line.text.length;
|
|
50
|
+
if (text.length <= room) line.text += text;
|
|
51
|
+
else {
|
|
52
|
+
line.text += text.slice(0, Math.max(0, room));
|
|
53
|
+
line.cut = true;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
write: (stream, text) => {
|
|
58
|
+
if (text.length === 0 || isEscapeOnly(text)) return;
|
|
59
|
+
bytes += encoder.encode(text).length;
|
|
60
|
+
const parts = text.split("\n");
|
|
61
|
+
const line = pending[stream];
|
|
62
|
+
append(line, parts[0] ?? "");
|
|
63
|
+
for (let i = 1; i < parts.length; i++) {
|
|
64
|
+
finish(stream, line, counts, samples);
|
|
65
|
+
line.text = "";
|
|
66
|
+
line.cut = false;
|
|
67
|
+
append(line, parts[i] ?? "");
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
snapshot: () => {
|
|
71
|
+
const totals = { ...counts };
|
|
72
|
+
const out = [...samples];
|
|
73
|
+
finish("stdout", pending.stdout, totals, out);
|
|
74
|
+
finish("stderr", pending.stderr, totals, out);
|
|
75
|
+
const total = totals.stdout + totals.stderr;
|
|
76
|
+
if (total === 0) return void 0;
|
|
77
|
+
return {
|
|
78
|
+
total,
|
|
79
|
+
stdout: totals.stdout,
|
|
80
|
+
stderr: totals.stderr,
|
|
81
|
+
bytes,
|
|
82
|
+
samples: out
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
reset: () => {
|
|
86
|
+
counts = {
|
|
87
|
+
stdout: 0,
|
|
88
|
+
stderr: 0
|
|
89
|
+
};
|
|
90
|
+
bytes = 0;
|
|
91
|
+
samples = [];
|
|
92
|
+
pending = {
|
|
93
|
+
stdout: {
|
|
94
|
+
text: "",
|
|
95
|
+
cut: false
|
|
96
|
+
},
|
|
97
|
+
stderr: {
|
|
98
|
+
text: "",
|
|
99
|
+
cut: false
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* The key a stray-output capture publishes its {@link StrayOutputSource}
|
|
107
|
+
* under, on the Vitest `Logger` whose streams it wraps. `Symbol.for`, so a
|
|
108
|
+
* plugin and an MCP server that load different copies of this package still
|
|
109
|
+
* meet on it.
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
112
|
+
const STRAY_OUTPUT_SOURCE = Symbol.for("vitest-agent/stray-output");
|
|
113
|
+
/**
|
|
114
|
+
* Read the current run's stray output from a Vitest `Logger` a capture was
|
|
115
|
+
* installed on. `undefined` when none was installed, or the run wrote nothing.
|
|
116
|
+
* @public
|
|
117
|
+
*/
|
|
118
|
+
const readStrayOutput = (logger) => {
|
|
119
|
+
if (logger === null || typeof logger !== "object") return void 0;
|
|
120
|
+
const source = logger[STRAY_OUTPUT_SOURCE];
|
|
121
|
+
if (source === null || typeof source !== "object") return void 0;
|
|
122
|
+
const snapshot = source.snapshot;
|
|
123
|
+
return typeof snapshot === "function" ? snapshot() : void 0;
|
|
124
|
+
};
|
|
125
|
+
const MAX_NOTE_SAMPLES = 3;
|
|
126
|
+
/**
|
|
127
|
+
* The note a renderer prints for a run's stray output: one line naming the
|
|
128
|
+
* count, the streams, the usual cause and the fix, then up to three sample
|
|
129
|
+
* lines and how many it left out.
|
|
130
|
+
* @public
|
|
131
|
+
*/
|
|
132
|
+
const formatStrayOutputNote = (stray) => {
|
|
133
|
+
const streams = stray.stdout > 0 && stray.stderr > 0 ? "stdout and stderr" : stray.stdout > 0 ? "stdout" : "stderr";
|
|
134
|
+
const lines = [`Stray output: tests wrote ${stray.total} ${stray.total === 1 ? "line" : "lines"} directly to ${streams}, bypassing Vitest's console capture. A child process with inherited stdio is the usual cause; pass \`stdio: "pipe"\`.`];
|
|
135
|
+
const shown = stray.samples.slice(0, MAX_NOTE_SAMPLES);
|
|
136
|
+
for (const sample of shown) lines.push(` ${sample.stream}: ${sample.text}`);
|
|
137
|
+
const more = stray.total - shown.length;
|
|
138
|
+
if (more > 0 && shown.length > 0) lines.push(` (+${more} more)`);
|
|
139
|
+
return lines;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
export { STRAY_OUTPUT_SOURCE, formatStrayOutputNote, isEscapeOnly, makeStrayOutputRecorder, readStrayOutput };
|