import { Data, Schema } from "effect"; import { ConnectionId, ToolId, SecretId } from "./ids"; // ---------------------------------------------------------------------------// Tool lifecycle// --------------------------------------------------------------------------- export class ToolNotFoundError extends Schema.TaggedErrorClass<ToolNotFoundError>()( "ToolNotFoundError", { toolId: ToolId },) {} export class ToolInvocationError extends Data.TaggedError("ToolInvocationError")<{ readonly toolId: ToolId; readonly message: string; readonly cause?: unknown;}> {} /** Tool row exists in the DB but its owning plugin isn't loaded. Means * the tool was registered by a plugin that's no longer present in the * current executor config — usually a stale row from an older session. */export class PluginNotLoadedError extends Schema.TaggedErrorClass<PluginNotLoadedError>()( "PluginNotLoadedError", { pluginId: Schema.String, toolId: ToolId, },) {} /** Tool was found but its owning plugin has no `invokeTool` handler — * the plugin only declares static tools and this one's id matched * dynamically somehow. Shouldn't happen in practice; guards against * programmer error. */export class NoHandlerError extends Schema.TaggedErrorClass<NoHandlerError>()("NoHandlerError", { toolId: ToolId, pluginId: Schema.String,}) {} /** Tool invocation was rejected because a workspace `tool_policy` rule * with `action: "block"` matched. `pattern` is the matched policy * pattern so callers / agents can render a useful "this is blocked * by your `vercel.dns.*` rule" message. */export class ToolBlockedError extends Schema.TaggedErrorClass<ToolBlockedError>()( "ToolBlockedError", { toolId: ToolId, pattern: Schema.String, },) {} // ---------------------------------------------------------------------------// Source lifecycle// --------------------------------------------------------------------------- export class SourceNotFoundError extends Schema.TaggedErrorClass<SourceNotFoundError>()( "SourceNotFoundError", { sourceId: Schema.String },) {} /** `executor.sources.remove({ id, targetScope })` was called on a * source with `canRemove: false` — typically a static source declared * by a plugin at startup. Removing static sources is a bug in the * caller. */export class SourceRemovalNotAllowedError extends Schema.TaggedErrorClass<SourceRemovalNotAllowedError>()( "SourceRemovalNotAllowedError", { sourceId: Schema.String },) {} // ---------------------------------------------------------------------------// Secrets// --------------------------------------------------------------------------- export class SecretNotFoundError extends Schema.TaggedErrorClass<SecretNotFoundError>()( "SecretNotFoundError", { secretId: SecretId },) {} export class SecretResolutionError extends Schema.TaggedErrorClass<SecretResolutionError>()( "SecretResolutionError", { secretId: SecretId, message: Schema.String, },) {} /** Raised when `secrets.remove({ id, targetScope })` is called on a secret whose row has * `owned_by_connection_id` set. The connection owns the lifecycle — * callers must go through `connections.remove(connectionId)` to * delete it along with its siblings. */export class SecretOwnedByConnectionError extends Schema.TaggedErrorClass<SecretOwnedByConnectionError>()( "SecretOwnedByConnectionError", { secretId: SecretId, connectionId: ConnectionId, },) {} /** Raised when `secrets.remove({ id, targetScope })` is called on a secret that's still * referenced by one or more sources / bindings across plugins. The UI's * "Used by" list tells the user which sources to detach first. App- * level RESTRICT — the codebase doesn't enforce DB-level FKs because * composite `(scope_id, id)` PKs make single-column references * impossible to constrain in sqlite. `usageCount` is a hint for the * caller; the full list is queryable via `secrets.usages(id)`. */export class SecretInUseError extends Schema.TaggedErrorClass<SecretInUseError>()( "SecretInUseError", { secretId: SecretId, usageCount: Schema.Number, },) {} // ---------------------------------------------------------------------------// Connections// --------------------------------------------------------------------------- export class ConnectionNotFoundError extends Schema.TaggedErrorClass<ConnectionNotFoundError>()( "ConnectionNotFoundError", { connectionId: ConnectionId },) {} export class ConnectionProviderNotRegisteredError extends Schema.TaggedErrorClass<ConnectionProviderNotRegisteredError>()( "ConnectionProviderNotRegisteredError", { provider: Schema.String, connectionId: Schema.optional(ConnectionId), },) {} export class ConnectionRefreshNotSupportedError extends Schema.TaggedErrorClass<ConnectionRefreshNotSupportedError>()( "ConnectionRefreshNotSupportedError", { connectionId: ConnectionId, provider: Schema.String, },) {} /** * Raised by `connections.accessToken(id)` when the provider's refresh * handler reported that the stored refresh token is permanently * invalid (RFC 6749 §5.2 `invalid_grant` and friends). The caller — * typically a tool invocation — surfaces this so the UI can prompt the * user to sign in again. Distinct from `ConnectionRefreshError` so * "the network flaked, retry later" and "the grant is dead, re-auth" * don't collapse into one error tag at the plugin boundary. */export class ConnectionReauthRequiredError extends Schema.TaggedErrorClass<ConnectionReauthRequiredError>()( "ConnectionReauthRequiredError", { connectionId: ConnectionId, provider: Schema.String, message: Schema.String, },) {} /** Raised when `connections.remove(id)` is called on a connection that's * still referenced by sources / bindings. Mirrors `SecretInUseError`. */export class ConnectionInUseError extends Schema.TaggedErrorClass<ConnectionInUseError>()( "ConnectionInUseError", { connectionId: ConnectionId, usageCount: Schema.Number, },) {} // ---------------------------------------------------------------------------// Union type for convenience in signatures.// --------------------------------------------------------------------------- export type ExecutorError = | ToolNotFoundError | ToolInvocationError | PluginNotLoadedError | NoHandlerError | ToolBlockedError | SourceNotFoundError | SourceRemovalNotAllowedError | SecretNotFoundError | SecretResolutionError | SecretOwnedByConnectionError | SecretInUseError | ConnectionNotFoundError | ConnectionProviderNotRegisteredError | ConnectionRefreshNotSupportedError | ConnectionReauthRequiredError | ConnectionInUseError;
import { Data, Schema } from "effect";
import { ConnectionId, ToolId, SecretId } from "./ids";
// ---------------------------------------------------------------------------
// Tool lifecycle
// ---------------------------------------------------------------------------
export class ToolNotFoundError extends Schema.TaggedErrorClass<ToolNotFoundError>()(
"ToolNotFoundError",
{ toolId: ToolId },
) {}
export class ToolInvocationError extends Data.TaggedError("ToolInvocationError")<{
readonly toolId: ToolId;
readonly message: string;
readonly cause?: unknown;
}> {}
/** Tool row exists in the DB but its owning plugin isn't loaded. Means
* the tool was registered by a plugin that's no longer present in the
* current executor config — usually a stale row from an older session. */
export class PluginNotLoadedError extends Schema.TaggedErrorClass<PluginNotLoadedError>()(
"PluginNotLoadedError",
{
pluginId: Schema.String,
toolId: ToolId,
},
) {}
/** Tool was found but its owning plugin has no `invokeTool` handler —
* the plugin only declares static tools and this one's id matched
* dynamically somehow. Shouldn't happen in practice; guards against
* programmer error. */
export class NoHandlerError extends Schema.TaggedErrorClass<NoHandlerError>()("NoHandlerError", {
toolId: ToolId,
pluginId: Schema.String,
}) {}
/** Tool invocation was rejected because a workspace `tool_policy` rule
* with `action: "block"` matched. `pattern` is the matched policy
* pattern so callers / agents can render a useful "this is blocked
* by your `vercel.dns.*` rule" message. */
export class ToolBlockedError extends Schema.TaggedErrorClass<ToolBlockedError>()(
"ToolBlockedError",
{
toolId: ToolId,
pattern: Schema.String,
},
) {}
// ---------------------------------------------------------------------------
// Source lifecycle
// ---------------------------------------------------------------------------
export class SourceNotFoundError extends Schema.TaggedErrorClass<SourceNotFoundError>()(
"SourceNotFoundError",
{ sourceId: Schema.String },
) {}
/** `executor.sources.remove({ id, targetScope })` was called on a
* source with `canRemove: false` — typically a static source declared
* by a plugin at startup. Removing static sources is a bug in the
* caller. */
export class SourceRemovalNotAllowedError extends Schema.TaggedErrorClass<SourceRemovalNotAllowedError>()(
"SourceRemovalNotAllowedError",
{ sourceId: Schema.String },
) {}
// ---------------------------------------------------------------------------
// Secrets
// ---------------------------------------------------------------------------
export class SecretNotFoundError extends Schema.TaggedErrorClass<SecretNotFoundError>()(
"SecretNotFoundError",
{ secretId: SecretId },
) {}
export class SecretResolutionError extends Schema.TaggedErrorClass<SecretResolutionError>()(
"SecretResolutionError",
{
secretId: SecretId,
message: Schema.String,
},
) {}
/** Raised when `secrets.remove({ id, targetScope })` is called on a secret whose row has
* `owned_by_connection_id` set. The connection owns the lifecycle —
* callers must go through `connections.remove(connectionId)` to
* delete it along with its siblings. */
export class SecretOwnedByConnectionError extends Schema.TaggedErrorClass<SecretOwnedByConnectionError>()(
"SecretOwnedByConnectionError",
{
secretId: SecretId,
connectionId: ConnectionId,
},
) {}
/** Raised when `secrets.remove({ id, targetScope })` is called on a secret that's still
* referenced by one or more sources / bindings across plugins. The UI's
* "Used by" list tells the user which sources to detach first. App-
* level RESTRICT — the codebase doesn't enforce DB-level FKs because
* composite `(scope_id, id)` PKs make single-column references
* impossible to constrain in sqlite. `usageCount` is a hint for the
* caller; the full list is queryable via `secrets.usages(id)`. */
export class SecretInUseError extends Schema.TaggedErrorClass<SecretInUseError>()(
"SecretInUseError",
{
secretId: SecretId,
usageCount: Schema.Number,
},
) {}
// ---------------------------------------------------------------------------
// Connections
// ---------------------------------------------------------------------------
export class ConnectionNotFoundError extends Schema.TaggedErrorClass<ConnectionNotFoundError>()(
"ConnectionNotFoundError",
{ connectionId: ConnectionId },
) {}
export class ConnectionProviderNotRegisteredError extends Schema.TaggedErrorClass<ConnectionProviderNotRegisteredError>()(
"ConnectionProviderNotRegisteredError",
{
provider: Schema.String,
connectionId: Schema.optional(ConnectionId),
},
) {}
export class ConnectionRefreshNotSupportedError extends Schema.TaggedErrorClass<ConnectionRefreshNotSupportedError>()(
"ConnectionRefreshNotSupportedError",
{
connectionId: ConnectionId,
provider: Schema.String,
},
) {}
/**
* Raised by `connections.accessToken(id)` when the provider's refresh
* handler reported that the stored refresh token is permanently
* invalid (RFC 6749 §5.2 `invalid_grant` and friends). The caller —
* typically a tool invocation — surfaces this so the UI can prompt the
* user to sign in again. Distinct from `ConnectionRefreshError` so
* "the network flaked, retry later" and "the grant is dead, re-auth"
* don't collapse into one error tag at the plugin boundary.
*/
export class ConnectionReauthRequiredError extends Schema.TaggedErrorClass<ConnectionReauthRequiredError>()(
"ConnectionReauthRequiredError",
{
connectionId: ConnectionId,
provider: Schema.String,
message: Schema.String,
},
) {}
/** Raised when `connections.remove(id)` is called on a connection that's
* still referenced by sources / bindings. Mirrors `SecretInUseError`. */
export class ConnectionInUseError extends Schema.TaggedErrorClass<ConnectionInUseError>()(
"ConnectionInUseError",
{
connectionId: ConnectionId,
usageCount: Schema.Number,
},
) {}
// ---------------------------------------------------------------------------
// Union type for convenience in signatures.
// ---------------------------------------------------------------------------
export type ExecutorError =
| ToolNotFoundError
| ToolInvocationError
| PluginNotLoadedError
| NoHandlerError
| ToolBlockedError
| SourceNotFoundError
| SourceRemovalNotAllowedError
| SecretNotFoundError
| SecretResolutionError
| SecretOwnedByConnectionError
| SecretInUseError
| ConnectionNotFoundError
| ConnectionProviderNotRegisteredError
| ConnectionRefreshNotSupportedError
| ConnectionReauthRequiredError
| ConnectionInUseError;