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')
export async function agentChatTurn(input: AgentTurnInput = { messages: [] }) {
return await runAgentTurn(input)
}
function hasGenerationInput(input: Record<string, unknown>) {
if (typeof input.prompt === 'string' && input.prompt.trim()) return true
return Array.isArray(input.messages) && input.messages.length > 0
}
/**
* Package overview, or generate text/JSON when `messages` / `schema` are passed.
*
* @example
* import ai from 'kody:@kody/ai'
* const info = await ai()
*/
export default async function ai(input: Record<string, unknown> = {}) {
const parsed = inputRecord(input ?? {})
if (hasGenerationInput(parsed)) {
if (parsed.schema && typeof parsed.schema === 'object') {
return await generateObject(
parsed as unknown as Parameters<typeof generateObject>[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,
},
}
}