@vitest-agent/sdk 3.1.1 → 4.0.0

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 (66) hide show
  1. package/README.md +23 -19
  2. package/dispatch.d.ts +25 -6
  3. package/formatters/markdown.js +1 -0
  4. package/formatters/terminal.js +4 -3
  5. package/index.d.ts +448 -2341
  6. package/index.js +10 -68
  7. package/internal-inject-env.js +5 -7
  8. package/package.json +3 -19
  9. package/schemas/run-report-file-1.0.0.json +18 -18
  10. package/sidecar-dispatch.js +7 -5
  11. package/utils/format-console.js +9 -10
  12. package/utils/format-terminal.js +7 -7
  13. package/utils/posix-path.js +52 -0
  14. package/utils/test-location.js +11 -5
  15. package/version.js +17 -0
  16. package/layers/ConfigLive.js +0 -46
  17. package/layers/DataReaderLive.js +0 -1923
  18. package/layers/DataStoreLive.js +0 -1287
  19. package/layers/DetailResolverLive.js +0 -9
  20. package/layers/DiscoveryRegistryLive.js +0 -70
  21. package/layers/EnvironmentDetectorLive.js +0 -21
  22. package/layers/EnvironmentDetectorTest.js +0 -13
  23. package/layers/ExecutorResolverLive.js +0 -9
  24. package/layers/FormatSelectorLive.js +0 -20
  25. package/layers/HistoryTrackerLive.js +0 -49
  26. package/layers/HistoryTrackerTest.js +0 -25
  27. package/layers/LoggerLive.js +0 -64
  28. package/layers/OutputPipelineLive.js +0 -13
  29. package/layers/OutputRendererLive.js +0 -28
  30. package/layers/PathResolutionLive.js +0 -40
  31. package/layers/PerClientSessionMapLive.js +0 -145
  32. package/layers/ProjectDiscoveryLive.js +0 -68
  33. package/layers/ProjectDiscoveryTest.js +0 -12
  34. package/layers/ProjectIdentityLive.js +0 -96
  35. package/layers/RunContextLive.js +0 -81
  36. package/lib/format-triage.js +0 -103
  37. package/lib/format-wrapup.js +0 -67
  38. package/migrations/0001_initial.js +0 -824
  39. package/migrations/0002_test_artifacts.js +0 -30
  40. package/migrations/index.js +0 -26
  41. package/migrations/registry_0001_initial.js +0 -39
  42. package/migrations/session_map_0001_initial.js +0 -38
  43. package/services/Config.js +0 -15
  44. package/services/DataReader.js +0 -8
  45. package/services/DataStore.js +0 -15
  46. package/services/DetailResolver.js +0 -8
  47. package/services/DiscoveryRegistry.js +0 -8
  48. package/services/EnvironmentDetector.js +0 -8
  49. package/services/ExecutorResolver.js +0 -8
  50. package/services/FormatSelector.js +0 -8
  51. package/services/HistoryTracker.js +0 -20
  52. package/services/OutputRenderer.js +0 -8
  53. package/services/PerClientSessionMap.js +0 -22
  54. package/services/ProjectDiscovery.js +0 -8
  55. package/services/ProjectIdentity.js +0 -63
  56. package/services/RunContext.js +0 -53
  57. package/services/idempotency.js +0 -58
  58. package/sql/assemblers.js +0 -69
  59. package/testing/layers.js +0 -21
  60. package/testing.d.ts +0 -1862
  61. package/testing.js +0 -254
  62. package/utils/ensure-migrated.js +0 -52
  63. package/utils/failure-signature.js +0 -34
  64. package/utils/resolve-data-path.js +0 -65
  65. package/utils/resolve-project-key-from-cwd.js +0 -53
  66. package/utils/resolve-workspace-key.js +0 -41
package/index.js CHANGED
@@ -16,57 +16,10 @@ import { MarkdownFormatter } from "./formatters/markdown.js";
16
16
  import { SilentFormatter } from "./formatters/silent.js";
17
17
  import { compressLines } from "./utils/compress-lines.js";
18
18
  import { osc8 } from "./utils/hyperlink.js";
19
+ import { basenamePosix, joinPosix, relativePosix, toPosix } from "./utils/posix-path.js";
19
20
  import { TerminalFormatter } from "./formatters/terminal.js";
20
- import { VitestAgentConfig } from "./schemas/Config.js";
21
- import { VitestAgentConfigFile } from "./services/Config.js";
22
- import { ConfigLive } from "./layers/ConfigLive.js";
23
- import { DataReader } from "./services/DataReader.js";
24
- import { HistoryTracker, historyKey } from "./services/HistoryTracker.js";
25
- import { DataReaderLive } from "./layers/DataReaderLive.js";
26
21
  import { ActorType, AgentId, ChatId, ConversationId, HostKind, ProjectKey, TddTaskId } from "./schemas/Identity.js";
27
22
  import { Agent, IdempotencyHit } from "./schemas/Agent.js";
28
- import { DataStore, INLINE_ATTACHMENT_BODY_CAP_BYTES } from "./services/DataStore.js";
29
- import { coerceErrorField, coerceErrorText } from "./utils/coerce-error-text.js";
30
- import { DataStoreLive } from "./layers/DataStoreLive.js";
31
- import { DetailResolver } from "./services/DetailResolver.js";
32
- import { DetailResolverLive } from "./layers/DetailResolverLive.js";
33
- import { DiscoveryRegistry } from "./services/DiscoveryRegistry.js";
34
- import { DiscoveryRegistryLive } from "./layers/DiscoveryRegistryLive.js";
35
- import { EnvironmentDetector } from "./services/EnvironmentDetector.js";
36
- import { EnvironmentDetectorLive } from "./layers/EnvironmentDetectorLive.js";
37
- import { EnvironmentDetectorTest } from "./layers/EnvironmentDetectorTest.js";
38
- import { ExecutorResolver } from "./services/ExecutorResolver.js";
39
- import { ExecutorResolverLive } from "./layers/ExecutorResolverLive.js";
40
- import { FormatSelector } from "./services/FormatSelector.js";
41
- import { FormatSelectorLive } from "./layers/FormatSelectorLive.js";
42
- import { classifyTest } from "./utils/classify-test.js";
43
- import { HistoryTrackerLive } from "./layers/HistoryTrackerLive.js";
44
- import { HistoryTrackerTest } from "./layers/HistoryTrackerTest.js";
45
- import { LoggerLive, resolveLogFile, resolveLogLevel } from "./layers/LoggerLive.js";
46
- import { OutputRenderer } from "./services/OutputRenderer.js";
47
- import { OutputRendererLive } from "./layers/OutputRendererLive.js";
48
- import { OutputPipelineLive } from "./layers/OutputPipelineLive.js";
49
- import { PathResolutionLive } from "./layers/PathResolutionLive.js";
50
- import { PerClientSessionMapReader, PerClientSessionMapWriter } from "./services/PerClientSessionMap.js";
51
- import { PerClientSessionMapReaderLive, PerClientSessionMapWriterLive } from "./layers/PerClientSessionMapLive.js";
52
- import { ProjectDiscovery } from "./services/ProjectDiscovery.js";
53
- 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";
54
- import { ProjectDiscoveryLive } from "./layers/ProjectDiscoveryLive.js";
55
- import { ProjectDiscoveryTest } from "./layers/ProjectDiscoveryTest.js";
56
- import { canonicalizeGitUrl, gitUrlToProjectKey } from "./utils/canonicalize-git-url.js";
57
- import { normalizeWorkspaceKey } from "./utils/normalize-workspace-key.js";
58
- import { ProjectIdentity, resolveProjectIdentityFromCandidates } from "./services/ProjectIdentity.js";
59
- import { ProjectIdentityLive, ProjectIdentityTest } from "./layers/ProjectIdentityLive.js";
60
- import { AgentContext, RunContext, RunContext$, RunContextService } from "./services/RunContext.js";
61
- import { probeHostMetadataFromEnv } from "./utils/probe-host-metadata.js";
62
- import { RunContextLive, RunContextTest } from "./layers/RunContextLive.js";
63
- import { formatTriageEffect } from "./lib/format-triage.js";
64
- import { formatWrapupEffect } from "./lib/format-wrapup.js";
65
- import migration from "./migrations/0001_initial.js";
66
- import migration$1 from "./migrations/0002_test_artifacts.js";
67
- import { PROJECT_MIGRATIONS } from "./migrations/index.js";
68
- import migration$2 from "./migrations/registry_0001_initial.js";
69
- import migration$3 from "./migrations/session_map_0001_initial.js";
70
23
  import { AgentConsoleMode, CiConsoleMode, ConsoleMode, ConsoleOutputMode, DetailLevel, Environment, Executor, HumanConsoleMode, OutputFormat, PackageManager, ReportError, TestClassification, TestRunReason, TestState } from "./schemas/Common.js";
71
24
  import { ConsoleLeakFile, ConsoleLeaks, ConsoleLeaksFromFailingTests } from "./schemas/ConsoleLeaks.js";
72
25
  import { MetricThresholds, PatternMetricThresholds, PatternThresholds, PerFileThresholds, ResolvedThresholds } from "./schemas/Thresholds.js";
@@ -75,6 +28,7 @@ import { AgentReport, ModuleReport, ReportSummary, TagCountEntry, TestReport } f
75
28
  import { CoverageBaselines } from "./schemas/Baselines.js";
76
29
  import { CacheManifest, CacheManifestEntry } from "./schemas/CacheManifest.js";
77
30
  import { BehaviorAbandonedEvent, BehaviorAddedEvent, BehaviorCompletedEvent, BehaviorScopedEventTypes, BehaviorStartedEvent, BehaviorsReadyEvent, BlockedEvent, ChannelEvent, GoalAbandonedEvent, GoalAddedEvent, GoalCompletedEvent, GoalStartedEvent, GoalsReadyEvent, PhaseTransitionEvent, SessionCompleteEvent, SessionOutcome } from "./schemas/ChannelEvent.js";
31
+ import { VitestAgentConfig } from "./schemas/Config.js";
78
32
  import { CoverageLevel, resolveCoverageInput, validateCoverageConfig } from "./schemas/CoverageLevel.js";
79
33
  import { CoverageTargets, CoverageTargetsMetrics } from "./schemas/CoverageTargets.js";
80
34
  import { HistoryRecord, TestHistory, TestRun } from "./schemas/History.js";
@@ -94,36 +48,24 @@ import { ToolCallPayload } from "./schemas/turns/ToolCallPayload.js";
94
48
  import { ToolResultPayload } from "./schemas/turns/ToolResultPayload.js";
95
49
  import { UserPromptPayload } from "./schemas/turns/UserPromptPayload.js";
96
50
  import { TurnPayload } from "./schemas/turns/index.js";
97
- import { IDEMPOTENCY_ROOT_SENTINEL, deriveIdempotencyKey } from "./services/idempotency.js";
98
- import { assembleHistoryRecord, assembleManifest } from "./sql/assemblers.js";
51
+ import { coerceErrorField, coerceErrorText } from "./utils/coerce-error-text.js";
99
52
  import { buildAgentReport, countSuiteFailures } from "./utils/build-report.js";
53
+ import { canonicalizeGitUrl, gitUrlToProjectKey } from "./utils/canonicalize-git-url.js";
54
+ import { classifyTest } from "./utils/classify-test.js";
100
55
  import { computeTrend, getRecentDirection, hashTargets } from "./utils/compute-trend.js";
101
56
  import { buildConsoleLeaks, collectConsoleLeakEntries } from "./utils/console-leaks.js";
102
57
  import { detectNonDefaultDiscoverStrategy } from "./utils/detect-non-default-discover-strategy.js";
103
58
  import { detectPackageManager, getRunCommand } from "./utils/detect-pm.js";
104
59
  import { isTimeoutError } from "./utils/detect-timeout.js";
105
- import { _resetMigrationCacheForTesting, ensureMigrated } from "./utils/ensure-migrated.js";
106
- import { computeFailureSignature, normalizeAssertionShape } from "./utils/failure-signature.js";
107
60
  import { formatFatalError } from "./utils/format-fatal-error.js";
108
61
  import { findFunctionBoundary } from "./utils/function-boundary.js";
109
62
  import { buildEnvPrefix, detectVitestScripts, isVitestInvocation, rewriteBashCommand } from "./utils/match-vitest-command.js";
110
- import { resolveProjectKeyFromCwd } from "./utils/resolve-project-key-from-cwd.js";
111
- import { DATABASE_FILENAME, resolveDataPath } from "./utils/resolve-data-path.js";
112
- import { resolveWorkspaceKey } from "./utils/resolve-workspace-key.js";
63
+ import { normalizeWorkspaceKey } from "./utils/normalize-workspace-key.js";
64
+ import { probeHostMetadataFromEnv } from "./utils/probe-host-metadata.js";
113
65
  import { safeFilename } from "./utils/safe-filename.js";
66
+ 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";
114
67
  import { validateCoverageTargetsShape } from "./utils/validate-coverage-targets-shape.js";
115
68
  import { requiredArtifactForTransition, transitionEnforcesBehaviorMatch, validatePhaseTransition } from "./utils/validate-phase-transition.js";
69
+ import { CURRENT_SDK_VERSION } from "./version.js";
116
70
 
117
- //#region src/index.ts
118
- /**
119
- * The version of this package. Inlined at build time from
120
- * package.json#version via rslib-builder's __PACKAGE_VERSION__ substitution.
121
- * Source-level reads (workspace `exports: "./src/index.ts"` during dev)
122
- * see the `"0.0.0"` fallback — a clear signal the build pipeline has not
123
- * substituted yet. Exported for version introspection by downstream tooling.
124
- * @public
125
- */
126
- const CURRENT_SDK_VERSION = "3.1.1";
127
-
128
- //#endregion
129
- export { ActionSeverity, ActorType, Agent, AgentConsoleMode, AgentContext, 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, ConfigLive, ConsoleLeakFile, ConsoleLeaks, ConsoleLeaksFromFailingTests, ConsoleLogLevel, ConsoleMode, ConsoleOutputMode, ConsoleOutputs, ConversationId, ConversationNotFoundError, CoverageBaselines, FileCoverageReport as CoverageFile, CoverageGap, CoverageLevel, CoverageMetric, CoverageRenderState, CoverageReport, CoverageTargets, CoverageTargetsMetrics, CoverageTotals, DATABASE_FILENAME, DataReader, DataReaderLive, DataStore, DataStoreError, DataStoreLive, DetailLevel, DetailResolver, DetailResolverLive, DiscoveryError, DiscoveryRegistry, DiscoveryRegistryLive, Environment, EnvironmentDetector, EnvironmentDetectorLive, EnvironmentDetectorTest, Executor, ExecutorResolver, ExecutorResolverLive, FailureRecord, FileCoverageReport, FileEditPayload, FormatSelector, FormatSelectorLive, GfmFormatter, GitCommandError, GoalAbandonedEvent, GoalAddedEvent, GoalCompletedEvent, GoalDetail, GoalNotFoundError, GoalRow, GoalStartedEvent, GoalStatus, GoalsReadyEvent, HistoryRecord, HistoryTracker, HistoryTrackerLive, HistoryTrackerTest, HookFirePayload, HookStatus, HookType, HostKind, HostMetadataParseError, HumanConsoleMode, HypothesisPayload, IDEMPOTENCY_ROOT_SENTINEL, INLINE_ATTACHMENT_BODY_CAP_BYTES, IdempotencyHit, IllegalStatusTransitionError, JsonFormatter, LoggerLive, MarkdownFormatter, MetricThresholds, ModuleRecord, ModuleReport, NON_DISCOVERABLE_DIRS, NotePayload, OutputFormat, OutputPipelineLive, OutputRenderer, OutputRendererLive, PROJECT_MIGRATIONS, PackageManager, PathResolutionError, PathResolutionLive, PatternMetricThresholds, PatternThresholds, PerClientSessionMapReader, PerClientSessionMapReaderLive, PerClientSessionMapWriter, PerClientSessionMapWriterLive, PerFileThresholds, PhaseTransitionEvent, ProbeMissError, ProjectDiscovery, ProjectDiscoveryLive, ProjectDiscoveryTest, ProjectIdentity, ProjectIdentityLive, ProjectIdentityNotResolvableError, ProjectIdentityTest, ProjectKey, RUN_REPORT_FILE_SCHEMA_URL, RegistrationConflictError, RenderState, RenderTotals, ReportError, ReportOption, ReportSettings, ReportSummary, ResolvedThresholds, RunContext, RunContext$, RunContextLive, RunContextService, RunContextTest, RunEvent, RunReportFile, SRC_DIR, SessionCompleteEvent, SessionNotFoundError, SessionOutcome, SidecarTimeoutError, SilentFormatter, SuggestedActionRecord, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS, TagCountEntry, TddTaskAlreadyEndedError, TddTaskId, TddTaskNotFoundError, TerminalFormatter, TestAnnotation, TestArtifact, TestArtifactLocation, TestAttachment, TestClassification, TestHistory, TestRecord, TestReport, TestRun, TestRunReason, TestState, ToolCallPayload, ToolResultPayload, Transport, TrendEntry, TrendRecord, TurnPayload, UserPromptPayload, VitestAgentConfig, VitestAgentConfigFile, _resetMigrationCacheForTesting, ansi, assembleHistoryRecord, assembleManifest, buildAgentReport, buildConsoleLeaks, buildEnvPrefix, canonicalizeGitUrl, ciAnnotationsFormatter, classifyTest, classifyTestPath, coerceErrorField, coerceErrorText, collectConsoleLeakEntries, compressLines, computeFailureSignature, computeTrend, countSuiteFailures, deriveIdempotencyKey, detectNonDefaultDiscoverStrategy, detectPackageManager, detectVitestScripts, determineTier, ensureMigrated, extractSqlReason, findFunctionBoundary, findOwningWorkspace, formatConsoleMarkdown, formatFatalError, formatGfm, formatScopedCoverageNote, formatTriageEffect, formatWrapupEffect, getRecentDirection, getRunCommand, getWorstMetric, gitUrlToProjectKey, hashTargets, historyKey, initialRenderState, isTestFileName, isTimeoutError, isVitestInvocation, migration as migration0001, migration$1 as migration0002, normalizeAssertionShape, normalizeWorkspaceKey, osc8, probeHostMetadataFromEnv, migration$2 as registryMigration0001, relativePath, requiredArtifactForTransition, resolveCoverageInput, resolveDataPath, resolveLogFile, resolveLogLevel, resolveProjectIdentityFromCandidates, resolveProjectKeyFromCwd, resolveWorkspaceKey, rewriteBashCommand, safeFilename, migration$3 as sessionMapMigration0001, stripAnsi, transitionEnforcesBehaviorMatch, validateCoverageConfig, validateCoverageTargetsShape, validatePhaseTransition };
71
+ 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, GfmFormatter, GitCommandError, GoalAbandonedEvent, GoalAddedEvent, GoalCompletedEvent, GoalDetail, GoalNotFoundError, GoalRow, GoalStartedEvent, GoalStatus, GoalsReadyEvent, HistoryRecord, HookFirePayload, HookStatus, HookType, HostKind, HostMetadataParseError, HumanConsoleMode, HypothesisPayload, IdempotencyHit, IllegalStatusTransitionError, JsonFormatter, MarkdownFormatter, MetricThresholds, ModuleRecord, ModuleReport, NON_DISCOVERABLE_DIRS, NotePayload, OutputFormat, 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, SessionCompleteEvent, SessionNotFoundError, SessionOutcome, SidecarTimeoutError, SilentFormatter, SuggestedActionRecord, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS, TagCountEntry, TddTaskAlreadyEndedError, TddTaskId, TddTaskNotFoundError, TerminalFormatter, 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, ciAnnotationsFormatter, classifyTest, classifyTestPath, coerceErrorField, coerceErrorText, collectConsoleLeakEntries, compressLines, computeTrend, countSuiteFailures, detectNonDefaultDiscoverStrategy, detectPackageManager, detectVitestScripts, determineTier, extractSqlReason, findFunctionBoundary, findOwningWorkspace, formatConsoleMarkdown, formatFatalError, formatGfm, formatScopedCoverageNote, getRecentDirection, getRunCommand, getWorstMetric, gitUrlToProjectKey, hashTargets, initialRenderState, isTestFileName, isTimeoutError, isVitestInvocation, joinPosix, normalizeWorkspaceKey, osc8, probeHostMetadataFromEnv, relativePath, relativePosix, requiredArtifactForTransition, resolveCoverageInput, rewriteBashCommand, safeFilename, stripAnsi, toPosix, transitionEnforcesBehaviorMatch, validateCoverageConfig, validateCoverageTargetsShape, validatePhaseTransition };
@@ -1,11 +1,9 @@
1
1
  import { detectVitestScripts, rewriteBashCommand } from "./utils/match-vitest-command.js";
2
- import { join } from "node:path";
3
- import { readFileSync } from "node:fs";
4
2
 
5
3
  //#region src/internal-inject-env.ts
6
- const readPackageScripts = (cwd) => {
4
+ const readPackageScripts = (cwd, readFile) => {
7
5
  try {
8
- const raw = readFileSync(join(cwd, "package.json"), "utf-8");
6
+ const raw = readFile(`${cwd}/package.json`);
9
7
  return JSON.parse(raw).scripts ?? {};
10
8
  } catch {
11
9
  return {};
@@ -19,15 +17,15 @@ const readPackageScripts = (cwd) => {
19
17
  * - `VITEST_AGENT_CONVERSATION_ID` or `VITEST_AGENT_AGENT_ID` is
20
18
  * missing from env (no agent context to attribute to)
21
19
  *
22
- * Always synchronous — the package.json read is the only I/O and is
23
- * fast enough not to need Effect wrapping.
20
+ * Always synchronous — the package.json read is the only I/O, goes through
21
+ * the injected `readFile`, and is fast enough not to need Effect wrapping.
24
22
  * @public
25
23
  */
26
24
  const injectEnv = (input) => {
27
25
  const conversationId = input.env.VITEST_AGENT_CONVERSATION_ID;
28
26
  const agentId = input.env.VITEST_AGENT_AGENT_ID;
29
27
  if (conversationId === void 0 || agentId === void 0) return input.command;
30
- const scripts = readPackageScripts(input.cwd);
28
+ const scripts = readPackageScripts(input.cwd, input.readFile);
31
29
  const vitestScripts = detectVitestScripts(scripts);
32
30
  const parentAgentId = input.env.VITEST_AGENT_PARENT_AGENT_ID;
33
31
  return rewriteBashCommand({
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@vitest-agent/sdk",
3
- "version": "3.1.1",
3
+ "version": "4.0.0",
4
4
  "private": false,
5
- "description": "Shared Effect Schema, SQLite migrations, data layer, services, formatters, and utilities for vitest-agent packages.",
5
+ "description": "Platform-free core for the vitest-agent package family: Effect Schema definitions, reporter and dispatcher contracts, errors, formatters, and pure utilities.",
6
6
  "keywords": [
7
7
  "vitest",
8
8
  "agent",
@@ -37,29 +37,13 @@
37
37
  "import": "./dispatch.js",
38
38
  "default": "./dispatch.js"
39
39
  },
40
- "./testing": {
41
- "types": "./testing.d.ts",
42
- "import": "./testing.js",
43
- "default": "./testing.js"
44
- },
45
40
  "./schemas/*.json": "./schemas/*.json",
46
41
  "./package.json": "./package.json"
47
42
  },
48
43
  "dependencies": {
49
- "@effect/platform-node": "4.0.0-rc.112",
50
- "@effect/sql-sqlite-node": "4.0.0-rc.112",
51
- "@effected/config-file": "^0.7.0",
52
- "@effected/jsonc": "^0.9.0",
53
- "@effected/toml": "^0.6.0",
54
- "@effected/walker": "^0.7.0",
55
- "@effected/workspaces": "^0.20.3",
56
- "@effected/xdg": "^0.4.1",
57
- "@effected/yaml": "^0.14.0",
58
44
  "acorn": "^8.18.0",
59
45
  "acorn-typescript": "^1.4.13",
60
- "ajv": "^8.20.0",
61
- "effect": "4.0.0-rc.112",
62
- "std-env": "^4.2.0"
46
+ "effect": "4.0.0-rc.115"
63
47
  },
64
48
  "engines": {
65
49
  "node": ">=24.11.0"
@@ -90,7 +90,7 @@
90
90
  }
91
91
  },
92
92
  "required": ["total", "passed", "failed", "skipped", "duration"],
93
- "additionalProperties": false
93
+ "additionalProperties": true
94
94
  },
95
95
  "TestState": {
96
96
  "type": "string",
@@ -144,7 +144,7 @@
144
144
  }
145
145
  },
146
146
  "required": ["message"],
147
- "additionalProperties": false
147
+ "additionalProperties": true
148
148
  },
149
149
  "TestClassification": {
150
150
  "type": "string",
@@ -225,7 +225,7 @@
225
225
  }
226
226
  },
227
227
  "required": ["name", "fullName", "state"],
228
- "additionalProperties": false
228
+ "additionalProperties": true
229
229
  },
230
230
  "ModuleReport": {
231
231
  "type": "object",
@@ -275,7 +275,7 @@
275
275
  }
276
276
  },
277
277
  "required": ["file", "state", "tests"],
278
- "additionalProperties": false
278
+ "additionalProperties": true
279
279
  },
280
280
  "CoverageTotals": {
281
281
  "type": "object",
@@ -326,7 +326,7 @@
326
326
  }
327
327
  },
328
328
  "required": ["statements", "branches", "functions", "lines"],
329
- "additionalProperties": false
329
+ "additionalProperties": true
330
330
  },
331
331
  "MetricThresholds": {
332
332
  "type": "object",
@@ -404,7 +404,7 @@
404
404
  ]
405
405
  }
406
406
  },
407
- "additionalProperties": false
407
+ "additionalProperties": true
408
408
  },
409
409
  "PerFileThresholds": {
410
410
  "anyOf": [
@@ -495,7 +495,7 @@
495
495
  "$ref": "#/$defs/PerFileThresholds"
496
496
  }
497
497
  },
498
- "additionalProperties": false
498
+ "additionalProperties": true
499
499
  },
500
500
  "PatternThresholds": {
501
501
  "type": "array",
@@ -524,7 +524,7 @@
524
524
  }
525
525
  },
526
526
  "required": ["file", "summary", "uncoveredLines"],
527
- "additionalProperties": false
527
+ "additionalProperties": true
528
528
  },
529
529
  "CoverageReport": {
530
530
  "type": "object",
@@ -546,7 +546,7 @@
546
546
  }
547
547
  },
548
548
  "required": ["global"],
549
- "additionalProperties": false
549
+ "additionalProperties": true
550
550
  },
551
551
  "targets": {
552
552
  "anyOf": [
@@ -564,7 +564,7 @@
564
564
  }
565
565
  },
566
566
  "required": ["global"],
567
- "additionalProperties": false
567
+ "additionalProperties": true
568
568
  },
569
569
  {
570
570
  "type": "null"
@@ -587,7 +587,7 @@
587
587
  }
588
588
  },
589
589
  "required": ["global"],
590
- "additionalProperties": false
590
+ "additionalProperties": true
591
591
  },
592
592
  {
593
593
  "type": "null"
@@ -668,7 +668,7 @@
668
668
  }
669
669
  },
670
670
  "required": ["totals", "thresholds", "lowCoverage", "lowCoverageFiles"],
671
- "additionalProperties": false
671
+ "additionalProperties": true
672
672
  },
673
673
  "TagCountEntry": {
674
674
  "type": "object",
@@ -728,7 +728,7 @@
728
728
  ]
729
729
  }
730
730
  },
731
- "additionalProperties": false
731
+ "additionalProperties": true
732
732
  },
733
733
  "ConsoleLeakFile": {
734
734
  "type": "object",
@@ -783,7 +783,7 @@
783
783
  }
784
784
  },
785
785
  "required": ["file", "stdout", "stderr"],
786
- "additionalProperties": false
786
+ "additionalProperties": true
787
787
  },
788
788
  "ConsoleLeaksFromFailingTests": {
789
789
  "type": "object",
@@ -812,7 +812,7 @@
812
812
  }
813
813
  },
814
814
  "required": ["total", "files"],
815
- "additionalProperties": false
815
+ "additionalProperties": true
816
816
  },
817
817
  "ConsoleLeaks": {
818
818
  "type": "object",
@@ -856,7 +856,7 @@
856
856
  }
857
857
  },
858
858
  "required": ["total", "byFile"],
859
- "additionalProperties": false
859
+ "additionalProperties": true
860
860
  },
861
861
  "AgentReport": {
862
862
  "type": "object",
@@ -933,7 +933,7 @@
933
933
  }
934
934
  },
935
935
  "required": ["timestamp", "reason", "summary", "failed", "unhandledErrors", "failedFiles"],
936
- "additionalProperties": false
936
+ "additionalProperties": true
937
937
  },
938
938
  "RunReportFile": {
939
939
  "type": "object",
@@ -956,7 +956,7 @@
956
956
  }
957
957
  },
958
958
  "required": ["schemaVersion", "generatedAt", "reports"],
959
- "additionalProperties": false,
959
+ "additionalProperties": true,
960
960
  "description": "The vitest-agent run report file (.vitest/vitest-agent/run.json): one AgentReport per Vitest project from the most recent run. Read reports[].summary for pass/fail counts and reports[].failed for the failing modules and their tests. schemaVersion is the envelope contract version; generatedAt is when the run finished."
961
961
  }
962
962
  }
@@ -37,17 +37,18 @@ const messageFromError = (err) => {
37
37
  };
38
38
  /**
39
39
  * Dispatch one argv invocation. `argv` is the post-`node post-bin`
40
- * slice — i.e. `process.argv.slice(2)`. Never throws: every failure is
41
- * folded into the returned {@link DispatchResult}.
40
+ * slice — i.e. `process.argv.slice(2)` — and `io` carries the process
41
+ * facts (cwd, env, a file reader) the runner owns. Never throws: every
42
+ * failure is folded into the returned {@link DispatchResult}.
42
43
  * @public
43
44
  */
44
- const dispatch = async (argv) => {
45
+ const dispatch = async (argv, io) => {
45
46
  const subcommand = argv[0];
46
47
  const rest = argv.slice(1);
47
48
  if (subcommand === "inject-env") {
48
49
  const flags = parseFlags(rest);
49
50
  const command = flags.command;
50
- const cwd = flags.cwd ?? process.cwd();
51
+ const cwd = flags.cwd ?? io.cwd;
51
52
  if (command === void 0) return {
52
53
  stdout: "",
53
54
  stderr: `${exitCodeForTag("Defect")} Defect: inject-env requires --command\n`,
@@ -58,7 +59,8 @@ const dispatch = async (argv) => {
58
59
  stdout: `${injectEnv({
59
60
  command,
60
61
  cwd,
61
- env: process.env
62
+ env: io.env,
63
+ readFile: io.readFile
62
64
  })}\n`,
63
65
  stderr: "",
64
66
  code: 0
@@ -40,15 +40,14 @@ function getWorstMetric(f) {
40
40
  * project-relative path for display.
41
41
  *
42
42
  * @param filePath - Absolute or relative file path
43
- * @param cwd - Working directory to strip; defaults to `process.cwd()`
43
+ * @param cwd - Working directory to strip
44
44
  * @returns Project-relative path string
45
45
  *
46
46
  * @internal
47
47
  */
48
48
  function relativePath(filePath, cwd) {
49
- const root = cwd ?? process.cwd();
50
- if (filePath.startsWith(root)) {
51
- const rel = filePath.slice(root.length);
49
+ if (filePath.startsWith(cwd)) {
50
+ const rel = filePath.slice(cwd.length);
52
51
  return rel.startsWith("/") ? rel.slice(1) : rel;
53
52
  }
54
53
  return filePath;
@@ -93,7 +92,7 @@ function determineTier(report) {
93
92
  * @internal
94
93
  */
95
94
  function formatConsoleMarkdown(report, options) {
96
- const { consoleOutput, coverageConsoleLimit, noColor } = options;
95
+ const { consoleOutput, coverageConsoleLimit, noColor, cwd } = options;
97
96
  if (consoleOutput === "silent") return "";
98
97
  const ao = { noColor };
99
98
  const lines = [];
@@ -130,7 +129,7 @@ function formatConsoleMarkdown(report, options) {
130
129
  }
131
130
  }
132
131
  if (hasFailures) for (const mod of report.failed) {
133
- const file = relativePath(mod.file);
132
+ const file = relativePath(mod.file, cwd);
134
133
  lines.push(`### ${ansi("✗", "red", ao)} \`${file}\``);
135
134
  lines.push("");
136
135
  if (mod.errors && mod.errors.length > 0) for (const err of mod.errors) {
@@ -171,7 +170,7 @@ function formatConsoleMarkdown(report, options) {
171
170
  lines.push(`### Coverage gaps`);
172
171
  lines.push("");
173
172
  for (const f of filesToShow) {
174
- const file = relativePath(f.file);
173
+ const file = relativePath(f.file, cwd);
175
174
  const worst = getWorstMetric(f);
176
175
  const thresholdValue = globalThresholds[worst.label === "Stmts" ? "statements" : worst.label === "Branch" ? "branches" : worst.label === "Funcs" ? "functions" : "lines"];
177
176
  const thresholdSuffix = thresholdValue != null ? ` (threshold: ${thresholdValue}%)` : "";
@@ -183,7 +182,7 @@ function formatConsoleMarkdown(report, options) {
183
182
  lines.push(`### Coverage improvements needed`);
184
183
  lines.push("");
185
184
  for (const f of cov.belowTarget.slice(0, coverageConsoleLimit)) {
186
- const file = relativePath(f.file);
185
+ const file = relativePath(f.file, cwd);
187
186
  const worst = getWorstMetric(f);
188
187
  const metricKey = worst.label === "Stmts" ? "statements" : worst.label === "Branch" ? "branches" : worst.label === "Funcs" ? "functions" : "lines";
189
188
  const targetValue = cov.targets.global[metricKey];
@@ -202,12 +201,12 @@ function formatConsoleMarkdown(report, options) {
202
201
  const persistent = allFailedTests.filter((t) => t.classification === "persistent");
203
202
  const flaky = allFailedTests.filter((t) => t.classification === "flaky");
204
203
  if (newFailures.length > 0) {
205
- const files = [...new Set(report.failed.filter((m) => m.tests.some((t) => t.classification === "new-failure")).map((m) => relativePath(m.file)))];
204
+ const files = [...new Set(report.failed.filter((m) => m.tests.some((t) => t.classification === "new-failure")).map((m) => relativePath(m.file, cwd)))];
206
205
  lines.push(`- Fix ${newFailures.length} new failure${newFailures.length > 1 ? "s" : ""} in ${files.map((f) => `\`${f}\``).join(", ")} (likely caused by recent changes)`);
207
206
  }
208
207
  if (persistent.length > 0) lines.push(`- ${persistent.length} persistent failure${persistent.length > 1 ? "s" : ""} (pre-existing, may not be yours)`);
209
208
  if (flaky.length > 0) lines.push(`- ${flaky.length} flaky test${flaky.length > 1 ? "s" : ""} (may pass on retry)`);
210
- for (const file of report.failedFiles) lines.push(`- Re-run: \`vitest run ${relativePath(file)}\``);
209
+ for (const file of report.failedFiles) lines.push(`- Re-run: \`vitest run ${relativePath(file, cwd)}\``);
211
210
  if (options.mcp) {
212
211
  lines.push("- Use `test_history` for failure trends");
213
212
  lines.push("- Use `test_errors` to search errors by type");
@@ -209,10 +209,10 @@ const sepRow = (widths) => widths.map((w) => "-".repeat(w + 2)).join("|");
209
209
  *
210
210
  * @internal
211
211
  */
212
- const renderBelowTargetTable = (files) => {
212
+ const renderBelowTargetTable = (files, cwd) => {
213
213
  if (files.length === 0) return [];
214
214
  const rows = files.map((f) => ({
215
- file: relativePath(f.file),
215
+ file: relativePath(f.file, cwd),
216
216
  stmts: String(Math.round(f.summary.statements)),
217
217
  branches: String(Math.round(f.summary.branches)),
218
218
  funcs: String(Math.round(f.summary.functions)),
@@ -331,7 +331,7 @@ const renderCoverageSection = (reports, options, ao) => {
331
331
  lines.push("Files below aspirational target:");
332
332
  const limit = options.coverageConsoleLimit;
333
333
  const shown = agg.belowTargetFiles.slice(0, limit);
334
- lines.push(...renderBelowTargetTable(shown));
334
+ lines.push(...renderBelowTargetTable(shown, options.cwd));
335
335
  if (agg.belowTargetFiles.length > limit) lines.push(`… ${agg.belowTargetFiles.length - limit} more (use the test_coverage MCP tool for the full list)`);
336
336
  }
337
337
  return lines;
@@ -342,7 +342,7 @@ const renderCoverageSection = (reports, options, ao) => {
342
342
  *
343
343
  * @internal
344
344
  */
345
- const renderFailuresSection = (reports, ao) => {
345
+ const renderFailuresSection = (reports, cwd, ao) => {
346
346
  const lines = [];
347
347
  const reportsWithFailures = reports.filter((r) => r.summary.failed > 0 || r.unhandledErrors.length > 0);
348
348
  if (reportsWithFailures.length === 0) return lines;
@@ -351,7 +351,7 @@ const renderFailuresSection = (reports, ao) => {
351
351
  const projectName = report.project ?? "default";
352
352
  lines.push(` ${projectName}`);
353
353
  for (const mod of report.failed) {
354
- const file = relativePath(mod.file);
354
+ const file = relativePath(mod.file, cwd);
355
355
  const failedTests = mod.tests.filter((t) => t.state === "failed");
356
356
  for (const test of failedTests) lines.push(...renderFailedTest(file, test, ao));
357
357
  if (mod.errors && mod.errors.length > 0) for (const err of mod.errors) {
@@ -403,7 +403,7 @@ const renderTotal = (reports, ao) => {
403
403
  const renderNextSteps = (reports, options) => {
404
404
  const lines = [];
405
405
  const allFailedFiles = /* @__PURE__ */ new Set();
406
- for (const r of reports) for (const f of r.failedFiles) allFailedFiles.add(relativePath(f));
406
+ for (const r of reports) for (const f of r.failedFiles) allFailedFiles.add(relativePath(f, options.cwd));
407
407
  if (allFailedFiles.size === 0) return lines;
408
408
  lines.push("Next steps:");
409
409
  for (const file of allFailedFiles) lines.push(` Re-run: pnpm vitest run ${file}`);
@@ -470,7 +470,7 @@ const formatTerminal = (reports, options) => {
470
470
  out.push("");
471
471
  }
472
472
  }
473
- const failures = renderFailuresSection(reports, ao);
473
+ const failures = renderFailuresSection(reports, options.cwd, ao);
474
474
  if (failures.length > 0) {
475
475
  out.push(...failures);
476
476
  out.push("");
@@ -0,0 +1,52 @@
1
+ //#region src/utils/posix-path.ts
2
+ /**
3
+ * Pure POSIX path helpers for the platform-free core.
4
+ *
5
+ * `@vitest-agent/sdk` must not import `node:path`, so the handful of path
6
+ * operations the core needs are implemented here over normalized
7
+ * forward-slash strings. Every helper accepts Windows-style input (a
8
+ * backslash separator) and normalizes it via {@link toPosix} at entry, so a
9
+ * caller never has to know which separator the platform used.
10
+ */
11
+ /**
12
+ * Normalizes `p` to forward slashes: every backslash becomes `/`.
13
+ * @public
14
+ */
15
+ const toPosix = (p) => p.replaceAll("\\", "/");
16
+ /**
17
+ * Last segment of `p` after normalization — `"b.ts"` for `"/a/b.ts"`.
18
+ * Trailing separators are ignored, so `"/a/b/"` yields `"b"`.
19
+ * @public
20
+ */
21
+ const basenamePosix = (p) => {
22
+ const normalized = toPosix(p).replace(/\/+$/, "");
23
+ const index = normalized.lastIndexOf("/");
24
+ return index === -1 ? normalized : normalized.slice(index + 1);
25
+ };
26
+ /**
27
+ * Joins `parts` with a single `/` between each, normalizing every part and
28
+ * collapsing duplicate separators at the seams. Empty parts are skipped.
29
+ * @public
30
+ */
31
+ const joinPosix = (...parts) => {
32
+ const normalized = parts.map(toPosix).filter((part) => part.length > 0);
33
+ if (normalized.length === 0) return "";
34
+ const [first, ...rest] = normalized;
35
+ return rest.reduce((acc, part) => `${acc.replace(/\/+$/, "")}/${part.replace(/^\/+/, "")}`, first);
36
+ };
37
+ /**
38
+ * Path of `to` relative to `from`, both normalized. Returns `""` when they
39
+ * are the same path and `to` unchanged when it is not under `from` (no
40
+ * `../` climbing — the core has no notion of a filesystem to climb through).
41
+ * @public
42
+ */
43
+ const relativePosix = (from, to) => {
44
+ const base = toPosix(from).replace(/\/+$/, "");
45
+ const target = toPosix(to);
46
+ if (target === base) return "";
47
+ const prefix = `${base}/`;
48
+ return target.startsWith(prefix) ? target.slice(prefix.length) : target;
49
+ };
50
+
51
+ //#endregion
52
+ export { basenamePosix, joinPosix, relativePosix, toPosix };
@@ -1,4 +1,4 @@
1
- import { basename, join, relative, sep } from "node:path";
1
+ import { basenamePosix, joinPosix, relativePosix, toPosix } from "./posix-path.js";
2
2
 
3
3
  //#region src/utils/test-location.ts
4
4
  /** Directory holding source files whose tests may be co-located. @public */
@@ -58,9 +58,15 @@ const TEST_FILE_SUFFIX_RE = /\.(?:test|spec)\.(?:ts|tsx|js|jsx)$/;
58
58
  function isTestFileName(filePathOrName) {
59
59
  return TEST_FILE_SUFFIX_RE.test(filePathOrName);
60
60
  }
61
- /** True when `child` is `parent` or sits beneath it on a segment boundary. */
61
+ /**
62
+ * True when `child` is `parent` or sits beneath it on a segment boundary.
63
+ * Both sides are normalized to forward slashes first, so a Windows path
64
+ * compares the same way a POSIX one does.
65
+ */
62
66
  function contains(parent, child) {
63
- return child === parent || child.startsWith(parent.endsWith(sep) ? parent : `${parent}${sep}`);
67
+ const parentPosix = toPosix(parent);
68
+ const childPosix = toPosix(child);
69
+ return childPosix === parentPosix || childPosix.startsWith(parentPosix.endsWith("/") ? parentPosix : `${parentPosix}/`);
64
70
  }
65
71
  /**
66
72
  * Returns the deepest supplied workspace containing `filePath`, or `null` when
@@ -100,7 +106,7 @@ function findOwningWorkspace(workspaces, filePath) {
100
106
  function classifyTestPath(workspaces, filePath) {
101
107
  const owner = findOwningWorkspace(workspaces, filePath);
102
108
  if (owner === null) return null;
103
- const segments = relative(owner.path, filePath).split(sep);
109
+ const segments = relativePosix(owner.path, filePath).split("/");
104
110
  if (segments.some((s) => NON_DISCOVERABLE_DIRS.has(s))) return null;
105
111
  const [head] = segments;
106
112
  if (head === "src") return {
@@ -119,7 +125,7 @@ function classifyTestPath(workspaces, filePath) {
119
125
  return {
120
126
  verdict: "invalid",
121
127
  workspace: owner.name,
122
- suggestedPath: join(owner.path, TEST_DIR, basename(filePath))
128
+ suggestedPath: joinPosix(owner.path, TEST_DIR, basenamePosix(filePath))
123
129
  };
124
130
  }
125
131
 
package/version.js ADDED
@@ -0,0 +1,17 @@
1
+ //#region src/version.ts
2
+ /**
3
+ * The version of this package. Inlined at build time from
4
+ * package.json#version via rslib-builder's __PACKAGE_VERSION__ substitution.
5
+ * Source-level reads (workspace `exports: "./src/index.ts"` during dev)
6
+ * see the `"0.0.0"` fallback — a clear signal the build pipeline has not
7
+ * substituted yet. Exported for version introspection by downstream tooling.
8
+ *
9
+ * This is the one sanctioned `process` read in the platform-free core: the
10
+ * bundler replaces the token at build time, so no runtime `process` access
11
+ * survives into the published artifact.
12
+ * @public
13
+ */
14
+ const CURRENT_SDK_VERSION = "4.0.0";
15
+
16
+ //#endregion
17
+ export { CURRENT_SDK_VERSION };