// `support.summarize` — DESCRIPTOR (browser-safe). // // A `generateObject` example: take a free-text support thread / document // and produce STRUCTURED output (a typed summary object) instead of prose. // AI inference is external I/O → an action, not a mutation. // // The `output` Schema is exported separately so the server executor can // reuse the SAME schema to constrain `generateObject` — one source of // truth for the shape the model must satisfy AND the wire output. Schema // values are browser-safe, so re-importing it server-side is fine. import { defineAction } from '@voltro/protocol' import { Schema } from 'effect' // The structured shape the model must emit. Top-level `Schema.Struct` // (generateObject requires a JSON `object` at the root — an array/scalar // must be wrapped in a field, as `keyPoints` is here). export const SummaryResult = Schema.Struct({ title: Schema.String, summary: Schema.String, keyPoints: Schema.Array(Schema.String), sentiment: Schema.Literal('positive', 'neutral', 'negative'), }) export type SummaryResult = Schema.Schema.Type export const summarize = defineAction({ name: 'support.summarize', // Summarises the `text` the caller passed in this same call — it reads no // table, no other caller's data, and never returns the provider key. No // identity ships with this template (no auth strategy, no rbac), so a scope // guard here would deny every caller. // // What this does NOT say is "harmless": it spends provider tokens per call, // so before it faces the internet put `@voltro/plugin-ratelimit` in front of // it and swap this line for a `guards:` once your app has a caller to name. openAccess: 'summarises caller-supplied text; reads no table and no other caller\'s data. ' + 'Metered by the AI provider, so rate-limit and guard it before it is public.', input: Schema.Struct({ // The raw text to summarise (a support thread, a doc, a transcript). text: Schema.String, }), output: SummaryResult, })