/** * CursorDriver — `ProviderDriver` for the Cursor Agent SDK runtime. * * Provider status, model discovery, orchestration, and text generation use the * official Cursor SDK and require CURSOR_API_KEY in the provider instance * environment. * * @module provider/Drivers/CursorDriver */import { CursorSettings, ProviderDriverKind } from "@t3tools/contracts";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Path from "effect/Path";import * as Schema from "effect/Schema"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts";import { ServerConfig } from "../../config.ts";import { ServerSettingsService } from "../../serverSettings.ts";import { makeCursorTextGeneration } from "../../textGeneration/CursorTextGeneration.ts";import { CursorAdapterV2Driver, type CursorAdapterV2DriverEnv,} from "../../orchestration-v2/Adapters/CursorAdapterV2.ts";import { ProviderDriverError } from "../Errors.ts";import { buildInitialCursorProviderSnapshot, checkCursorProviderStatus,} from "../Layers/CursorProvider.ts";import { CursorSdkCatalogLive } from "../Layers/CursorSdkCatalog.ts";import { makeManagedServerProvider } from "../makeManagedServerProvider.ts";import { defaultProviderContinuationIdentity, type ProviderDriver, type ProviderInstance,} from "../ProviderDriver.ts";import { withInstanceIdentity } from "./instanceIdentity.ts";import { mergeProviderInstanceEnvironment } from "../ProviderInstanceEnvironment.ts";import { makeManualOnlyProviderMaintenanceCapabilities } from "../providerMaintenance.ts";import { haveProviderSnapshotSettingsChanged, makeProviderSnapshotSettingsSource, type ProviderSnapshotSettings,} from "../providerUpdateSettings.ts";import { probeCursorSkills } from "./CursorSkills.ts";const decodeCursorSettings = Schema.decodeSync(CursorSettings); const DRIVER_KIND = ProviderDriverKind.make("cursor");const MAINTENANCE_CAPABILITIES = makeManualOnlyProviderMaintenanceCapabilities({ provider: DRIVER_KIND, packageName: null,}); export type CursorDriverEnv = | CursorAdapterV2DriverEnv | FileSystem.FileSystem | Path.Path | BackgroundPolicy.BackgroundPolicy | ServerConfig | ServerSettingsService; export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = { driverKind: DRIVER_KIND, metadata: { displayName: "Cursor", supportsMultipleInstances: true, }, configSchema: CursorSettings, defaultConfig: (): CursorSettings => decodeCursorSettings({}), create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => Effect.gen(function* () { const serverSettings = yield* ServerSettingsService; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const processEnv = mergeProviderInstanceEnvironment(environment); const continuationIdentity = defaultProviderContinuationIdentity({ driverKind: DRIVER_KIND, instanceId, }); const stampIdentity = withInstanceIdentity({ instanceId, driverKind: DRIVER_KIND, displayName, accentColor, continuationGroupKey: continuationIdentity.continuationKey, }); const effectiveConfig = { ...config, enabled } satisfies CursorSettings; const orchestrationAdapter = yield* CursorAdapterV2Driver.create({ instanceId, displayName, accentColor, environment, enabled, config, }).pipe( Effect.mapError( (cause) => new ProviderDriverError({ driver: DRIVER_KIND, instanceId, detail: "Failed to build Cursor orchestration adapter.", cause, }), ), ); const textGeneration = yield* makeCursorTextGeneration(effectiveConfig, processEnv); const checkProvider = checkCursorProviderStatus(effectiveConfig, processEnv).pipe( Effect.map(stampIdentity), Effect.provide(CursorSdkCatalogLive), ); const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings); const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CursorSettings>>({ resolveMaintenance: () => Effect.succeed(MAINTENANCE_CAPABILITIES), getSettings: snapshotSettings.getSettings, streamSettings: snapshotSettings.streamSettings, haveSettingsChanged: haveProviderSnapshotSettingsChanged, initialSnapshot: (settings) => buildInitialCursorProviderSnapshot(settings.provider).pipe(Effect.map(stampIdentity)), checkProvider, }).pipe( Effect.mapError( (cause) => new ProviderDriverError({ driver: DRIVER_KIND, instanceId, detail: `Failed to build Cursor snapshot: ${cause.message ?? String(cause)}`, cause, }), ), ); return { instanceId, driverKind: DRIVER_KIND, continuationIdentity, displayName, accentColor, enabled, snapshot, snapshotForCwd: (cwd) => !effectiveConfig.enabled ? snapshot.getSnapshot : Effect.all([ snapshot.getSnapshot, probeCursorSkills(cwd, processEnv).pipe( Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path), Effect.mapError( (cause) => new ProviderDriverError({ driver: DRIVER_KIND, instanceId, detail: `Failed to discover Cursor skills for '${cwd}'`, cause, }), ), ), ]).pipe(Effect.map(([machineSnapshot, skills]) => ({ ...machineSnapshot, skills }))), orchestrationAdapter, textGeneration, } satisfies ProviderInstance; }),};
/**
* CursorDriver — `ProviderDriver` for the Cursor Agent SDK runtime.
*
* Provider status, model discovery, orchestration, and text generation use the
* official Cursor SDK and require CURSOR_API_KEY in the provider instance
* environment.
*
* @module provider/Drivers/CursorDriver
*/
import { CursorSettings, ProviderDriverKind } from "@t3tools/contracts";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Path from "effect/Path";
import * as Schema from "effect/Schema";
import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts";
import { ServerConfig } from "../../config.ts";
import { ServerSettingsService } from "../../serverSettings.ts";
import { makeCursorTextGeneration } from "../../textGeneration/CursorTextGeneration.ts";
import {
CursorAdapterV2Driver,
type CursorAdapterV2DriverEnv,
} from "../../orchestration-v2/Adapters/CursorAdapterV2.ts";
import { ProviderDriverError } from "../Errors.ts";
import {
buildInitialCursorProviderSnapshot,
checkCursorProviderStatus,
} from "../Layers/CursorProvider.ts";
import { CursorSdkCatalogLive } from "../Layers/CursorSdkCatalog.ts";
import { makeManagedServerProvider } from "../makeManagedServerProvider.ts";
import {
defaultProviderContinuationIdentity,
type ProviderDriver,
type ProviderInstance,
} from "../ProviderDriver.ts";
import { withInstanceIdentity } from "./instanceIdentity.ts";
import { mergeProviderInstanceEnvironment } from "../ProviderInstanceEnvironment.ts";
import { makeManualOnlyProviderMaintenanceCapabilities } from "../providerMaintenance.ts";
import {
haveProviderSnapshotSettingsChanged,
makeProviderSnapshotSettingsSource,
type ProviderSnapshotSettings,
} from "../providerUpdateSettings.ts";
import { probeCursorSkills } from "./CursorSkills.ts";
const decodeCursorSettings = Schema.decodeSync(CursorSettings);
const DRIVER_KIND = ProviderDriverKind.make("cursor");
const MAINTENANCE_CAPABILITIES = makeManualOnlyProviderMaintenanceCapabilities({
provider: DRIVER_KIND,
packageName: null,
});
export type CursorDriverEnv =
| CursorAdapterV2DriverEnv
| FileSystem.FileSystem
| Path.Path
| BackgroundPolicy.BackgroundPolicy
| ServerConfig
| ServerSettingsService;
export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = {
driverKind: DRIVER_KIND,
metadata: {
displayName: "Cursor",
supportsMultipleInstances: true,
},
configSchema: CursorSettings,
defaultConfig: (): CursorSettings => decodeCursorSettings({}),
create: ({ instanceId, displayName, accentColor, environment, enabled, config }) =>
Effect.gen(function* () {
const serverSettings = yield* ServerSettingsService;
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const processEnv = mergeProviderInstanceEnvironment(environment);
const continuationIdentity = defaultProviderContinuationIdentity({
driverKind: DRIVER_KIND,
instanceId,
});
const stampIdentity = withInstanceIdentity({
instanceId,
driverKind: DRIVER_KIND,
displayName,
accentColor,
continuationGroupKey: continuationIdentity.continuationKey,
});
const effectiveConfig = { ...config, enabled } satisfies CursorSettings;
const orchestrationAdapter = yield* CursorAdapterV2Driver.create({
instanceId,
displayName,
accentColor,
environment,
enabled,
config,
}).pipe(
Effect.mapError(
(cause) =>
new ProviderDriverError({
driver: DRIVER_KIND,
instanceId,
detail: "Failed to build Cursor orchestration adapter.",
cause,
}),
),
);
const textGeneration = yield* makeCursorTextGeneration(effectiveConfig, processEnv);
const checkProvider = checkCursorProviderStatus(effectiveConfig, processEnv).pipe(
Effect.map(stampIdentity),
Effect.provide(CursorSdkCatalogLive),
);
const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CursorSettings>>({
resolveMaintenance: () => Effect.succeed(MAINTENANCE_CAPABILITIES),
getSettings: snapshotSettings.getSettings,
streamSettings: snapshotSettings.streamSettings,
haveSettingsChanged: haveProviderSnapshotSettingsChanged,
initialSnapshot: (settings) =>
buildInitialCursorProviderSnapshot(settings.provider).pipe(Effect.map(stampIdentity)),
checkProvider,
}).pipe(
Effect.mapError(
(cause) =>
new ProviderDriverError({
driver: DRIVER_KIND,
instanceId,
detail: `Failed to build Cursor snapshot: ${cause.message ?? String(cause)}`,
cause,
}),
),
);
return {
instanceId,
driverKind: DRIVER_KIND,
continuationIdentity,
displayName,
accentColor,
enabled,
snapshot,
snapshotForCwd: (cwd) =>
!effectiveConfig.enabled
? snapshot.getSnapshot
: Effect.all([
snapshot.getSnapshot,
probeCursorSkills(cwd, processEnv).pipe(
Effect.provideService(FileSystem.FileSystem, fileSystem),
Effect.provideService(Path.Path, path),
Effect.mapError(
(cause) =>
new ProviderDriverError({
driver: DRIVER_KIND,
instanceId,
detail: `Failed to discover Cursor skills for '${cwd}'`,
cause,
}),
),
),
]).pipe(Effect.map(([machineSnapshot, skills]) => ({ ...machineSnapshot, skills }))),
orchestrationAdapter,
textGeneration,
} satisfies ProviderInstance;
}),
};