/** * @fileoverview Root @kody/ai entry: package overview, or generate text/JSON when `messages` / `schema` are passed. * Prefer `./complete` for one-shot completions and `./settings` for provider defaults. * * @example * import ai from 'kody:@kody/ai' * const info = await ai() */ import { generateText } from './complete.ts' import { generateObject } from './generate-object.ts' import { PROVIDER_IDS, defaultSecretName, providerCatalog, providerSecretUrl, } from './providers.ts' import { runAgentTurn, type AgentTurnInput } from './turn.ts' import { inputRecord, optionalString } from './validation.ts' export { generateText, listModels } from './complete.ts' export { generateObject } from './generate-object.ts' export { runModelStep, kodyAgentToolDefinitions } from './model-step.ts' export { runAgentTurn, projectSearchOutput, projectExecuteOutput } from './turn.ts' export { default as aiSettings, getSettings } from './settings.ts' export { smokeTest } from './smoke-test.ts' export { isMutationDryRun, isPreviewDryRun } from './validation.ts' /** Default system guidance for any Kody agent turn (email, chat, automation). */ export const defaultKodyAgentSystem = [ 'You are Kody AI: a concise tool-using assistant with two tools, `search` and `execute`.', 'Workflow (generic — apply to any task):', '1. Prefer `search` first when you need to discover capabilities, saved packages, values, integrations, or secret metadata. Skip search for trivial pure-JS work (dates, formatting, simple transforms) — go straight to `execute`.', '2. When a search hit looks useful but usage is unclear, call `search` again with `entity` set to that hit\'s `entityRef` before inventing APIs.', '3. When entity detail includes `executeExample`, prefer that module (or a slim projection of its return value) instead of inventing imports. Always `import { kody } from "kody:runtime"` before calling `kody.*`.', '4. When the plan is clear, call `execute` with one complete ESM module that has `export default async function main(input = {}) { ... }` and returns a slim projected result.', 'Auth: never ask the user to paste secrets. Use saved integrations/secrets via documented Kody helpers, or report the specific blocker with evidence.', 'Tool calls: use the tool-calling API only. Never write TOOL_CALLS, tool JSON, or API docs as your final answer text.', 'Answers: be brief, grounded in tool evidence, and summarize outcomes (or the blocker) — not raw dumps.', ].join('\n') /** * Root @kody/ai entry: package overview, or generate text/JSON when `messages` / `schema` are passed. * Prefer `./complete` for one-shot completions and `./settings` for provider defaults. * * @param input - Agent turn input with messages. * @returns Agent turn result. * * @example * import ai from 'kody:@kody/ai' * const info = await ai() */ export async function agentChatTurn(input: AgentTurnInput = { messages: [] }) { return await runAgentTurn(input) } function hasGenerationInput(input: Record) { if (typeof input.prompt === 'string' && input.prompt.trim()) return true return Array.isArray(input.messages) && input.messages.length > 0 } /** * Root @kody/ai entry: package overview, or generate text/JSON when `messages` / `schema` are passed. * Prefer `./complete` for one-shot completions and `./settings` for provider defaults. * * @param input - Optional generation fields (`messages`, `prompt`, `schema`, `provider`, …) or empty for overview. * @returns Provider catalog + safety notes, or a completion / object-generation result. * * @example * import ai from 'kody:@kody/ai' * const info = await ai() * // => { auth: 'secret-backed-api-key', providers: [...], exports: [...], safety: {...} } */ export default async function ai(input: Record = {}) { const parsed = inputRecord(input ?? {}) if (hasGenerationInput(parsed)) { if (parsed.schema && typeof parsed.schema === 'object') { return await generateObject( parsed as unknown as Parameters[0], ) } return await generateText(parsed) } return { auth: 'secret-backed-api-key', oauth: false, jobs: false, publicUrl: 'https://kody.codes/@kody/ai', providers: PROVIDER_IDS.map((id) => { const entry = providerCatalog(id) const secret = defaultSecretName(id, optionalString(parsed, 'account')) return { id, secret, hosts: entry.hosts, defaultModel: entry.defaultModel || null, secretUrl: providerSecretUrl(id, secret), } }), exports: [ 'complete', 'generate-object', 'model-step', 'turn', 'settings', 'guide', 'smoke-test', 'types', ], safety: { settingsWritesDefaultToDryRun: true, settingsRequireConfirm: true, completionDryRunAvailable: true, noBakedApiKeys: true, noBakedAccountIds: true, noGatewayAutoCreate: true, noJobs: true, }, } }