import { Effect, Schema } from "effect";import { ElicitationId, ToolAddress } from "./ids";/* A tool that needs user input mid-call suspends and the host's `onElicitation` * handler (executor-level, overridable per `execute`) answers. Tools that never * elicit never trigger it. Schema-tagged so requests/responses cross the wire. *//** Implementation-defined context an upstream attached to the request. * * Opaque and never interpreted here — it is carried so a host can SHOW the * terms of an approval it would otherwise hide. The case that forced it: a * Codex plugin's per-site browser approval sends an empty `requestedSchema` * and puts `persist: "always"` and the `origin` in its metadata, so * accepting grants a permanent allow for that site. Without this the user is * asked to consent to strictly more than the prompt tells them. */export const ElicitationMeta = Schema.Record(Schema.String, Schema.Unknown);export type ElicitationMeta = typeof ElicitationMeta.Type;/** Tool needs structured input from the user (render a form). */export const FormElicitation = Schema.TaggedStruct("FormElicitation", { message: Schema.String, /** JSON Schema describing the fields to collect. */ requestedSchema: Schema.Record(Schema.String, Schema.Unknown), meta: Schema.optional(ElicitationMeta),});export type FormElicitation = typeof FormElicitation.Type;/** Tool needs the user to visit a URL (OAuth, approval page, etc.). */export const UrlElicitation = Schema.TaggedStruct("UrlElicitation", { message: Schema.String, url: Schema.String, /** Unique id so the host can correlate the callback. */ elicitationId: ElicitationId, meta: Schema.optional(ElicitationMeta),});export type UrlElicitation = typeof UrlElicitation.Type;export type ElicitationRequest = FormElicitation | UrlElicitation;export const ElicitationAction = Schema.Literals(["accept", "decline", "cancel"]);export type ElicitationAction = typeof ElicitationAction.Type;export const ElicitationResponse = Schema.Struct({ action: ElicitationAction, /** Present when `action` is "accept" — the data the user provided. */ content: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),});export type ElicitationResponse = typeof ElicitationResponse.Type;/** Handler input — the tool address being invoked, its args, and the request. */export interface ElicitationContext { readonly address: ToolAddress; readonly args: unknown; readonly request: ElicitationRequest;}/** Host-provided handler the SDK calls when a tool suspends for input. */export type ElicitationHandler = (ctx: ElicitationContext) => Effect.Effect<ElicitationResponse>;/** Executor-level elicitation policy: a handler, or `"accept-all"` to * auto-accept every request (tests / non-interactive hosts). */export type OnElicitation = ElicitationHandler | "accept-all";/** Per-call options for `execute`. */export interface InvokeOptions { /** Override the executor-level handler for this single call. */ readonly onElicitation?: OnElicitation;}/** A tool was declined or cancelled during elicitation. */export class ElicitationDeclinedError extends Schema.TaggedErrorClass<ElicitationDeclinedError>()( "ElicitationDeclinedError", { address: ToolAddress, action: Schema.Literals(["decline", "cancel"]), },) { // Derived message so telemetry (span status, logs) labels the failure // instead of rendering an Error with an empty message. override get message(): string { return `Tool approval ${this.action === "cancel" ? "cancelled" : "declined"}: ${this.address}`; }}
import { Effect, Schema } from "effect";
import { ElicitationId, ToolAddress } from "./ids";
/* A tool that needs user input mid-call suspends and the host's `onElicitation`
* handler (executor-level, overridable per `execute`) answers. Tools that never
* elicit never trigger it. Schema-tagged so requests/responses cross the wire. */
/** Implementation-defined context an upstream attached to the request.
*
* Opaque and never interpreted here — it is carried so a host can SHOW the
* terms of an approval it would otherwise hide. The case that forced it: a
* Codex plugin's per-site browser approval sends an empty `requestedSchema`
* and puts `persist: "always"` and the `origin` in its metadata, so
* accepting grants a permanent allow for that site. Without this the user is
* asked to consent to strictly more than the prompt tells them. */
export const ElicitationMeta = Schema.Record(Schema.String, Schema.Unknown);
export type ElicitationMeta = typeof ElicitationMeta.Type;
/** Tool needs structured input from the user (render a form). */
export const FormElicitation = Schema.TaggedStruct("FormElicitation", {
message: Schema.String,
/** JSON Schema describing the fields to collect. */
requestedSchema: Schema.Record(Schema.String, Schema.Unknown),
meta: Schema.optional(ElicitationMeta),
});
export type FormElicitation = typeof FormElicitation.Type;
/** Tool needs the user to visit a URL (OAuth, approval page, etc.). */
export const UrlElicitation = Schema.TaggedStruct("UrlElicitation", {
message: Schema.String,
url: Schema.String,
/** Unique id so the host can correlate the callback. */
elicitationId: ElicitationId,
meta: Schema.optional(ElicitationMeta),
});
export type UrlElicitation = typeof UrlElicitation.Type;
export type ElicitationRequest = FormElicitation | UrlElicitation;
export const ElicitationAction = Schema.Literals(["accept", "decline", "cancel"]);
export type ElicitationAction = typeof ElicitationAction.Type;
export const ElicitationResponse = Schema.Struct({
action: ElicitationAction,
/** Present when `action` is "accept" — the data the user provided. */
content: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
});
export type ElicitationResponse = typeof ElicitationResponse.Type;
/** Handler input — the tool address being invoked, its args, and the request. */
export interface ElicitationContext {
readonly address: ToolAddress;
readonly args: unknown;
readonly request: ElicitationRequest;
}
/** Host-provided handler the SDK calls when a tool suspends for input. */
export type ElicitationHandler = (ctx: ElicitationContext) => Effect.Effect<ElicitationResponse>;
/** Executor-level elicitation policy: a handler, or `"accept-all"` to
* auto-accept every request (tests / non-interactive hosts). */
export type OnElicitation = ElicitationHandler | "accept-all";
/** Per-call options for `execute`. */
export interface InvokeOptions {
/** Override the executor-level handler for this single call. */
readonly onElicitation?: OnElicitation;
}
/** A tool was declined or cancelled during elicitation. */
export class ElicitationDeclinedError extends Schema.TaggedErrorClass<ElicitationDeclinedError>()(
"ElicitationDeclinedError",
{
address: ToolAddress,
action: Schema.Literals(["decline", "cancel"]),
},
) {
// Derived message so telemetry (span status, logs) labels the failure
// instead of rendering an Error with an empty message.
override get message(): string {
return `Tool approval ${this.action === "cancel" ? "cancelled" : "declined"}: ${this.address}`;
}
}