- openai provider: use .chat() (Chat Completions) instead of the default callable (Responses API), which gateways reject on multi-turn -> 400. - updateAiProviderSettings: assemble settings.ai.provider via jsonb_build_object with ::text-cast bound params + jsonb_typeof self-heal (postgres.js was double-encoding it into an array; the ::text cast avoids 'could not determine data type of parameter'). - chat agent: drop the hard maxOutputTokens cap (truncated complex tool calls); keep a tiny cap only on the test-connection ping. - testConnection + chat stream: surface the real provider error (statusCode+message) to logs and the UI instead of generic masks; never log the API key. - chat UI: typing indicator, incremental streaming render, tool 'running' status, Stop. Also bundled (prior uncommitted ai-chat work): - history 'AI agent' provenance badge; vector RAG (pgvector image + page_embeddings + AI_QUEUE indexer + space-scoped semanticSearch); external MCP servers backend (@ai-sdk/mcp client, SSRF IP-pinning, encrypted headers, admin CRUD/Test); yjs duplicate-instance fix via pnpm patch (single CJS instance server-side).
45 lines
1.7 KiB
TypeScript
45 lines
1.7 KiB
TypeScript
import { type Kysely, sql } from 'kysely';
|
|
|
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
await db.schema
|
|
.createTable('ai_mcp_servers')
|
|
.ifNotExists()
|
|
.addColumn('id', 'uuid', (col) =>
|
|
col.primaryKey().defaultTo(sql`gen_uuid_v7()`),
|
|
)
|
|
.addColumn('workspace_id', 'uuid', (col) =>
|
|
col.references('workspaces.id').onDelete('cascade').notNull(),
|
|
)
|
|
// display name, e.g. 'Tavily'.
|
|
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
// 'http' | 'sse' — the @ai-sdk/mcp transport type.
|
|
.addColumn('transport', 'varchar', (col) => col.notNull())
|
|
// remote MCP endpoint URL.
|
|
.addColumn('url', 'text', (col) => col.notNull())
|
|
// SECURITY (§8.10): AES-256-GCM blob of the JSON auth headers. Write-only;
|
|
// NEVER added to workspace baseFields and NEVER returned by any endpoint.
|
|
.addColumn('headers_enc', 'text', (col) => col)
|
|
// optional: restrict which remote tool names to expose to the agent.
|
|
.addColumn('tool_allowlist', 'jsonb', (col) => col)
|
|
.addColumn('enabled', 'boolean', (col) => col.notNull().defaultTo(true))
|
|
.addColumn('created_at', 'timestamptz', (col) =>
|
|
col.notNull().defaultTo(sql`now()`),
|
|
)
|
|
.addColumn('updated_at', 'timestamptz', (col) =>
|
|
col.notNull().defaultTo(sql`now()`),
|
|
)
|
|
.execute();
|
|
|
|
// Scoped lookups (listByWorkspace / listEnabled) hit workspace_id first.
|
|
await db.schema
|
|
.createIndex('ai_mcp_servers_workspace_id_idx')
|
|
.ifNotExists()
|
|
.on('ai_mcp_servers')
|
|
.column('workspace_id')
|
|
.execute();
|
|
}
|
|
|
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
await db.schema.dropTable('ai_mcp_servers').execute();
|
|
}
|