@dypai-ai/mcp 1.4.5 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +268 -426
- package/src/tools/introspect.js +311 -0
- package/src/tools/manage-database.js +546 -0
- package/src/tools/run-migration.js +269 -0
- package/src/tools/search-logs-offload.js +151 -0
- package/src/tools/sql-guard.js +164 -0
- package/src/tools/sync/codec.js +2 -1
- package/src/tools/sync/pull.js +19 -3
- package/src/tools/sync/transforms.js +10 -2
package/src/index.js
CHANGED
|
@@ -54,7 +54,13 @@ import { dypaiPullTool, dypaiDiffTool, dypaiPushTool, dypaiValidateTool, dypaiTe
|
|
|
54
54
|
import { proxyToolCall } from "./tools/proxy.js"
|
|
55
55
|
import { enrichSuccess, enrichError } from "./tools/enrich.js"
|
|
56
56
|
import { maybeRefreshSchemaAfterExecuteSql } from "./tools/sql-side-effects.js"
|
|
57
|
+
import { maybeOffloadSearchLogs } from "./tools/search-logs-offload.js"
|
|
57
58
|
import { withProjectContext, invalidateProjectContext } from "./tools/project-context.js"
|
|
59
|
+
import { validateSql, formatValidationError } from "./tools/sql-guard.js"
|
|
60
|
+
import { manageDatabaseTool } from "./tools/manage-database.js"
|
|
61
|
+
// run_migration and introspect were collapsed into manage_database (discriminated
|
|
62
|
+
// union by `operation`). Their standalone files still live on disk in case we
|
|
63
|
+
// want to re-expose them, but the catalog only advertises manage_database.
|
|
58
64
|
// summarizeDypaiTraceResponse (from ./tools/trace-summarize.js) is kept on
|
|
59
65
|
// disk for when dypai_trace is re-enabled, but not imported here.
|
|
60
66
|
|
|
@@ -83,6 +89,12 @@ const LOCAL_TOOLS = [
|
|
|
83
89
|
dypaiDiffTool,
|
|
84
90
|
dypaiPushTool,
|
|
85
91
|
dypaiTestEndpointTool,
|
|
92
|
+
// ── Schema / DB management ────────────────────────────────────────────────
|
|
93
|
+
// One discriminated-union tool for migrations, introspection, and multi-
|
|
94
|
+
// statement scripts. Uses the same `manage_*(operation, ...)` pattern as
|
|
95
|
+
// manage_users / manage_roles / manage_drafts. See tools/manage-database.js
|
|
96
|
+
// for the operation list + semantics.
|
|
97
|
+
manageDatabaseTool,
|
|
86
98
|
// dypaiTestTool (YAML test-suite runner) — hidden until v2. See import comment above.
|
|
87
99
|
// dypaiCodegenTool removed from v1 — see codegen import comment above.
|
|
88
100
|
]
|
|
@@ -110,7 +122,7 @@ const REMOTE_TOOLS = [
|
|
|
110
122
|
// Note: `get_app_tables` is intentionally NOT exposed — dypai/schema.sql already
|
|
111
123
|
// caches table info locally (auto-refreshed on DDL). For ad-hoc introspection,
|
|
112
124
|
// use execute_sql against information_schema.
|
|
113
|
-
{ name: "execute_sql", description: "BACKEND ONLY —
|
|
125
|
+
{ name: "execute_sql", description: "BACKEND ONLY — execute a SINGLE SQL statement on the project database. Supports SELECT, INSERT, UPDATE, DELETE, and single-statement DDL on `public.*`.\n\nSchema access: `public.*` writable. `auth`, `storage`, `system` are READ-ONLY — SELECT works, any write is rejected with a guided error.\n\nRejected (use other tools): multi-statement scripts, DO $$, EXECUTE, CALL, COPY, LOAD, SET ROLE/search_path. For multi-statement migrations, use `manage_database(operation:\"apply_migration\")`. For inspection, use `manage_database(operation:\"introspect_*\")`. Timeout: 30 s. DDL on public.* auto-refreshes dypai/schema.sql.", inputSchema: { type: "object", properties: { project_id: { type: "string" }, sql: { type: "string", description: "A single SQL statement." } }, required: ["sql"] } },
|
|
114
126
|
|
|
115
127
|
// ── API Endpoints ─────────────────────────────────────────────────────────
|
|
116
128
|
// Full CRUD + exploration goes through the git-first flow:
|
|
@@ -447,6 +459,26 @@ endpoint YAML and \`dypai_push\`. This tool does NOT modify the definition.`,
|
|
|
447
459
|
},
|
|
448
460
|
},
|
|
449
461
|
|
|
462
|
+
// ── Observability ─────────────────────────────────────────────────────────
|
|
463
|
+
{
|
|
464
|
+
name: "search_logs",
|
|
465
|
+
description: "Search recent errors and warnings for the current project. ALWAYS call this FIRST when the user reports any error, bug, or 'this isn't working' — don't guess from the code; check what actually broke. Returns a unified, time-ordered list mixing failed workflow executions and warn/error log lines from the engine. Defaults to the last 24h. Data retention: 7 days.\n\nWorkflow:\n 1) Call with no args (or just `since:'1h'`) → see recent failures.\n 2) Pick the relevant entry → call again with `endpoint` + tighter `query` to narrow down.\n 3) For the full step-by-step debug trace of a specific failure, set `include_trace:true` (response is much larger; you'll likely get a `file_path` to read the full JSON from disk).\n\nUse `environment:'live'` when investigating a production user complaint (excludes draft test runs). Use `environment:'draft'` when the user says 'I just tested X locally and it failed' (their local UI hits the draft overlay).",
|
|
466
|
+
inputSchema: {
|
|
467
|
+
type: "object",
|
|
468
|
+
properties: {
|
|
469
|
+
project_id: { type: "string", description: "Project UUID. Auto-detected for project tokens." },
|
|
470
|
+
query: { type: "string", description: "Optional substring to match (case-insensitive) in error messages and log lines. e.g. 'timeout', 'OpenAI', 'permission denied'." },
|
|
471
|
+
endpoint: { type: "string", description: "Optional endpoint name filter (e.g. 'create-order')." },
|
|
472
|
+
since: { type: "string", default: "24h", description: "Time window: relative ('15m', '1h', '24h', '7d') or ISO 8601 timestamp. Default 24h. Hard cap: 7d (retention)." },
|
|
473
|
+
level: { type: "string", enum: ["error", "warn", "all"], default: "all", description: "Filter by severity. 'error' includes failed/timeout executions + error logs. 'warn' is warning logs. 'all' (default) returns both." },
|
|
474
|
+
environment: { type: "string", enum: ["live", "draft", "all"], default: "all", description: "live = production traffic only (excludes draft overlay test runs). draft = only requests through dev-<project_id>.dypai.dev. all = both. Use 'live' for real user bug reports." },
|
|
475
|
+
limit: { type: "integer", default: 50, minimum: 1, maximum: 200, description: "Max items to return. Default 50, max 200." },
|
|
476
|
+
include_trace: { type: "boolean", default: false, description: "Attach the full step-by-step debug trace per failed execution. Verbose — combine with `query`/`endpoint` filters and a low `limit`. If the response gets large, the local proxy writes it to disk and returns a file_path you can Read." }
|
|
477
|
+
},
|
|
478
|
+
required: []
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
|
|
450
482
|
// ── Knowledge ─────────────────────────────────────────────────────────────
|
|
451
483
|
{ name: "search_docs", description: "Search DYPAI documentation. Use this when unsure about SDK usage, auth patterns, workflow nodes, or platform features. Returns relevant documentation chunks.", inputSchema: { type: "object", properties: { query: { type: "string", description: "What you want to learn about" } }, required: ["query"] } },
|
|
452
484
|
{ name: "search_workflow_templates", description: "Search workflow templates by description. Returns ready-to-use workflow code for common patterns: CRUD operations, payment gateways, email sending, AI chatbots, data pipelines, etc.", inputSchema: { type: "object", properties: { query: { type: "string", description: "What the workflow should do (e.g. 'send email', 'stripe payment')" }, category: { type: "string", description: "Optional: AI, Database, Payments, Communication, Logic, Storage" } }, required: ["query"] } },
|
|
@@ -457,6 +489,127 @@ endpoint YAML and \`dypai_push\`. This tool does NOT modify the definition.`,
|
|
|
457
489
|
|
|
458
490
|
const SERVER_INSTRUCTIONS = `You are building full-stack applications on the DYPAI platform. You handle BACKEND (workflow endpoints, database, auth, realtime) and FRONTEND (SDK integration, React/Vite/Next code).
|
|
459
491
|
|
|
492
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
493
|
+
# TALKING TO THE USER — plain language, not tool names
|
|
494
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
495
|
+
|
|
496
|
+
The user is **not a developer watching your tool calls**. They read only your prose. Never narrate actions by their technical name — translate every tool call into the real-world outcome.
|
|
497
|
+
|
|
498
|
+
## Translation rule of thumb
|
|
499
|
+
|
|
500
|
+
Replace the VERB with what the user perceives:
|
|
501
|
+
|
|
502
|
+
| Instead of (tool-speak) | Say (human) |
|
|
503
|
+
|---|---|
|
|
504
|
+
| "Voy a hacer dypai_push / I'll call dypai_push" | "Voy a subir los cambios al borrador para que los pruebes" |
|
|
505
|
+
| "Voy a ejecutar manage_drafts(publish)" | "Voy a publicar los cambios en producción" |
|
|
506
|
+
| "Ejecutando dypai_pull" | "Un momento, sincronizo tu proyecto" |
|
|
507
|
+
| "Calling manage_frontend(deploy)" | "Voy a desplegar la nueva versión de tu web" |
|
|
508
|
+
| "Running execute_sql to add a column" | "Voy a añadir un campo nuevo a la tabla X" |
|
|
509
|
+
| "manage_database(operation:'apply_migration')" | "Voy a aplicar los cambios de estructura a la base de datos" |
|
|
510
|
+
| "search_logs returned a 500" | "He mirado los registros: el error viene de..." |
|
|
511
|
+
| "manage_drafts(list) shows 3 pending" | "Tienes 3 cambios pendientes de publicar" |
|
|
512
|
+
| "On the draft overlay" / "en la overlay de draft" | "En tu entorno de previsualización" o "en el borrador" |
|
|
513
|
+
| "dypai_validate rejected the workflow" | "Hay un problema con la configuración:" |
|
|
514
|
+
|
|
515
|
+
## Principles
|
|
516
|
+
|
|
517
|
+
1. **State outcomes, not function calls.** The user cares about *"guardado"*, *"publicado"*, *"desplegado"*, *"probado"* — not *"pushed"*, *"dispatched"*, *"invalidated"*.
|
|
518
|
+
2. **Draft vs live → borrador vs producción / previsualización vs en vivo.** Never say "overlay", "engine", "endpoint hit" in user-facing prose.
|
|
519
|
+
3. **Errors: summarize, don't paste.** *"Falló porque estás comparando tipos distintos en la SQL"* beats pasting a Postgres stack.
|
|
520
|
+
4. **Confirmations use real-world verbs.** *"¿Lo publico a producción?"* not *"¿Ejecuto manage_drafts(publish, confirm:true)?"*.
|
|
521
|
+
5. **Progress updates in sentences, not tool names.** *"Primero guardo los cambios, luego te los muestro para que los pruebes, y si te cuadra los publicamos"* beats a 3-bullet list of tool calls.
|
|
522
|
+
|
|
523
|
+
## When you CAN mention a tool name
|
|
524
|
+
|
|
525
|
+
- The user explicitly asks *"qué herramienta estás usando"* / *"how does this work under the hood"*.
|
|
526
|
+
- You're writing documentation or a runbook (ONLY if they asked).
|
|
527
|
+
- Error messages where the user will see an exact tool-level failure they'd have to react to.
|
|
528
|
+
|
|
529
|
+
Default is **no tool names in user-facing text**.
|
|
530
|
+
|
|
531
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
532
|
+
# SEARCH BEFORE YOU GUESS — \`search_docs\` is your reference manual
|
|
533
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
534
|
+
|
|
535
|
+
This prompt is the MAP of the DYPAI platform. The detailed docs live in
|
|
536
|
+
\`search_docs\` (vectorized semantic search over the full manual). **If
|
|
537
|
+
you're about to write something on a topic that feels fuzzy, search first
|
|
538
|
+
— one \`search_docs\` call is always cheaper than a wrong push you then have
|
|
539
|
+
to undo.**
|
|
540
|
+
|
|
541
|
+
## When to call \`search_docs\` (triggers)
|
|
542
|
+
|
|
543
|
+
- **Before writing an unfamiliar node type**: agent / stripe / telegram / google_sheets / webhook / schedule — query its name.
|
|
544
|
+
- **Before touching a platform feature**: auth, realtime, storage, credentials, SDK, frontend framework specifics.
|
|
545
|
+
- **When the user mentions a concept you don't have in cache**: "signup flow", "magic link", "custom domains", "file upload", "realtime channels", "draft flow", "migrations", "tools for agents".
|
|
546
|
+
- **When a tool's response contains \`search_docs("X")\` hint**: call it — the hint is written in by the tool author for exactly that reason.
|
|
547
|
+
- **When the user asks "how does X work"**: search BEFORE answering. Better an accurate answer after one search than a confident wrong one.
|
|
548
|
+
|
|
549
|
+
Don't search for: generic programming (JS/Python/SQL syntax), third-party
|
|
550
|
+
APIs outside DYPAI, anything you already have in this prompt.
|
|
551
|
+
|
|
552
|
+
## Topic map — what's searchable
|
|
553
|
+
|
|
554
|
+
Query with the phrases in parentheses. Semantic search is fuzzy; these are
|
|
555
|
+
anchors, not exact strings. If the first result is thin, rephrase with a
|
|
556
|
+
synonym.
|
|
557
|
+
|
|
558
|
+
### Orientation
|
|
559
|
+
- \`"platform guide"\` — what AI can do vs what lives in the user manual
|
|
560
|
+
- \`"project setup"\` — phased build flow from empty project to live app
|
|
561
|
+
|
|
562
|
+
### Backend core
|
|
563
|
+
- \`"git-first workflow"\` — edit → validate → push → test → publish loop
|
|
564
|
+
- \`"trigger model"\` — http_api / webhook / schedule / telegram options
|
|
565
|
+
- \`"workflow patterns"\` — canonical shapes: CRUD, auth-gated, branching, multi-return, error handling
|
|
566
|
+
- \`"drafts and publish"\` — push vs publish, draft overlay at \`dev-<id>.dypai.dev\`
|
|
567
|
+
- \`"manage database"\` — migrations (\`apply_migration\`) + introspection + multi-statement scripts
|
|
568
|
+
|
|
569
|
+
### Nodes
|
|
570
|
+
- \`"nodes reference"\` — all nodes with input/output schemas (also in \`dypai/node-catalog.json\`)
|
|
571
|
+
- \`"node types"\` — high-level grouping of what each node family does
|
|
572
|
+
- \`"agent ai"\` — agent node: providers, tools, memory, streaming, tool endpoints
|
|
573
|
+
- \`"javascript code node"\` — custom code escape hatch when native nodes don't fit
|
|
574
|
+
|
|
575
|
+
### Auth
|
|
576
|
+
- \`"auth flows"\` — signup / login / reset / magic link / role upgrade canonical flows
|
|
577
|
+
- \`"auth defaults"\` — what auth_mode to pick when the user doesn't specify
|
|
578
|
+
|
|
579
|
+
### Integrations
|
|
580
|
+
- \`"integrations guide"\` — step-by-step integration recipes (Stripe has full coverage; Telegram, Slack, WhatsApp, Resend, Google Sheets are referenced inside this doc)
|
|
581
|
+
- \`"credentials reference"\` — what fields each provider needs
|
|
582
|
+
- To find a specific provider: \`search_docs("integrations guide stripe")\` (combine the topic + the provider name) — semantic search will pick up the provider's section
|
|
583
|
+
|
|
584
|
+
### Realtime
|
|
585
|
+
- \`"realtime channels"\` — client API for WebSocket subscriptions
|
|
586
|
+
- \`"realtime policies"\` — backend \`dypai/realtime.yaml\` format + access control
|
|
587
|
+
|
|
588
|
+
### Storage
|
|
589
|
+
- \`"file storage"\` — buckets, upload flow, signed URLs, \`manage_storage\` ops
|
|
590
|
+
|
|
591
|
+
### Frontend
|
|
592
|
+
- \`"sdk reference"\` — raw \`@dypai-ai/client-sdk\` methods
|
|
593
|
+
- \`"react hooks"\` — \`useAuth\`, \`useEndpoint\`, \`useAction\`, \`useUpload\`, \`ProtectedRoute\`
|
|
594
|
+
- \`"frontend frameworks"\` — Next SSR gotchas, Vite config, Astro
|
|
595
|
+
- \`"manage frontend"\` — sync / deploy / status / logs ops deep dive
|
|
596
|
+
|
|
597
|
+
### Ops / debugging
|
|
598
|
+
- \`"testing endpoints"\` — \`dypai_test_endpoint\` patterns + assertions
|
|
599
|
+
- \`"troubleshooting"\` — common failures + fixes (catch-all for gotchas)
|
|
600
|
+
- \`"manage domain"\` — custom domains + DNS / SSL
|
|
601
|
+
- \`"manage database"\` — introspection ops (\`introspect_schema\` / \`introspect_table\` / \`introspect_function\`)
|
|
602
|
+
|
|
603
|
+
## How to phrase a query
|
|
604
|
+
|
|
605
|
+
- **Concept, not code.** \`search_docs("magic link auth flow")\` beats \`search_docs("dypai.auth.signInWithMagicLink")\`.
|
|
606
|
+
- **Two or three words max.** Long queries dilute the vector. *"how do I send email"* → just \`"email integration"\` or \`"resend"\`.
|
|
607
|
+
- **If first hit is unrelated, try a synonym.** "realtime" vs "websockets" vs "subscriptions" may all match different chunks.
|
|
608
|
+
- **One search is cheap.** Two or three narrow searches beat one giant one if the topic is broad.
|
|
609
|
+
|
|
610
|
+
When docs say the opposite of this prompt → **trust the docs**. This prompt
|
|
611
|
+
is the quickstart; docs are authoritative and current.
|
|
612
|
+
|
|
460
613
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
461
614
|
# QUICK START — read this section even if you skip everything else
|
|
462
615
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
@@ -511,6 +664,7 @@ Use this BEFORE picking a tool. If unsure which row matches, ask the user.
|
|
|
511
664
|
| "Publish the new UI" / "ship the frontend" | \`manage_frontend(deploy, confirm:true)\` | (deploy is the publish — there is no separate step) |
|
|
512
665
|
| "Roll back" | Backend: \`get_endpoint_versions\` then write old code back. Frontend: re-deploy older source. | — |
|
|
513
666
|
| "Upload a file / a CSV / seed data" | \`bulk_upsert\` (data) or \`manage_storage(upload_file)\` (binary) | — |
|
|
667
|
+
| "X is broken" / "I'm getting an error" / "this doesn't work" / "users are reporting Y" | \`search_logs\` FIRST (don't guess from the code) | If a specific failure is found → \`search_logs(include_trace:true, query:'...')\` for the full step-by-step trace |
|
|
514
668
|
|
|
515
669
|
## Confirm rules — the ONLY operations that need \`confirm:true\`
|
|
516
670
|
|
|
@@ -547,465 +701,129 @@ User: "Add a /api/list-tasks endpoint that returns the current user's tasks, and
|
|
|
547
701
|
|
|
548
702
|
**Order matters**: publish backend BEFORE deploying frontend. Otherwise the new UI calls an endpoint that doesn't exist on live yet → 404s for users. The \`manage_frontend(deploy)\` confirmation hint will warn you if backend drafts are still pending.
|
|
549
703
|
|
|
550
|
-
##
|
|
551
|
-
|
|
552
|
-
- "Development vs production environment" — the user never sees this. Backend changes always go through draft-and-publish. Frontend changes always go through deploy. That's the whole model.
|
|
553
|
-
- "Create auth endpoints" — auth is built into the SDK. \`dypai.auth.signInWithPassword()\` works out of the box. NEVER write a login/signup workflow.
|
|
554
|
-
- "RLS / row-level security" — there is none. You filter by \`\${current_user_id}\` in your SQL WHERE clauses. Forgetting this is the #1 multi-tenancy bug.
|
|
555
|
-
- "Rate limiting / CORS / JWT verification" — handled by the engine.
|
|
556
|
-
- "Promoting projects to production" — every new project already has the draft-publish flow enabled. \`manage_project(promote_to_production)\` is legacy and you should never need it.
|
|
557
|
-
|
|
558
|
-
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
559
|
-
# DEEP REFERENCE — the rest of this document
|
|
560
|
-
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
561
|
-
|
|
562
|
-
## Getting Started
|
|
563
|
-
1. list_projects() → pick project_id.
|
|
564
|
-
2. **dypai_pull FIRST** on any project you haven't worked on in this session — materializes ./dypai/ (endpoints, SQL, prompts, code, schema.sql, node-catalog.json, realtime.yaml) and returns an \`overview\` with endpoint groups, credentials, and tool-enabled endpoints. Backend only.
|
|
565
|
-
3. For frontend code not yet on disk: \`manage_frontend(operation: "sync", targetDirectory: <path>)\`. Independent of dypai_pull — run both for full-stack work.
|
|
566
|
-
4. BEFORE an unfamiliar feature: \`search_docs\` ("auth", "upload files", "realtime", "stripe", etc.).
|
|
567
|
-
5. Build backend first, then frontend.
|
|
568
|
-
|
|
569
|
-
## Creating a new project — template-first
|
|
570
|
-
When the user asks to CREATE a new project, ALWAYS \`search_project_templates(query: "...")\` first with keywords describing the app (e.g. "clinic", "gym", "waitlist", "saas dashboard"). If a template fits, confirm briefly with the user and call \`create_project(name, template_slug: "<slug>")\`. Only use \`template_slug: "blank"\` when nothing matches — blank costs the user hours of boilerplate. If uncertain, ASK before creating.
|
|
571
|
-
|
|
572
|
-
## Mental model — everything server-side is a workflow
|
|
573
|
-
DYPAI has NO standalone "edge functions" / "serverless functions". Every piece of server-side logic (API endpoints, cron jobs, webhooks, AI agents, background tasks) is a workflow endpoint under \`dypai/endpoints/\`. A workflow can be:
|
|
574
|
-
- **Single \`javascript_code\` / \`python_code\` node** → equivalent to a Vercel/Deno edge function. Put raw code in \`dypai/code/<name>.js\`, reference via \`code_file\`.
|
|
575
|
-
- **Chain of native nodes** → visual, validated, traceable per step (\`dypai_database\`, \`http_request\`, \`agent\`, \`stripe\`, etc.).
|
|
576
|
-
- **Mix** → auth/validation with native nodes, custom logic in a code node.
|
|
577
|
-
|
|
578
|
-
Mental translations:
|
|
579
|
-
- "I need an edge function" → workflow with one \`javascript_code\` node (+ free auth, rate limiting, per-step tracing)
|
|
580
|
-
- "I need a cron" → add \`trigger.schedule\` to the YAML
|
|
581
|
-
- "I need a webhook handler" → add \`trigger.webhook\`
|
|
582
|
-
|
|
583
|
-
## Build Backend (git-first workflow)
|
|
584
|
-
Endpoints live in ./dypai/ — there is NO create_endpoint / update_endpoint / add_node tool.
|
|
585
|
-
|
|
586
|
-
1. Tables: \`execute_sql\` for DDL. \`schema.sql\` auto-refreshes. **DDL is the only backend mutation that bypasses drafts — it hits live immediately.**
|
|
587
|
-
2. Endpoints / realtime / webhooks / crons:
|
|
588
|
-
- Edit/Write YAMLs in \`dypai/endpoints/<group>/<name>.yaml\`.
|
|
589
|
-
- Long SQL / prompts / code go in \`dypai/sql/\`, \`dypai/prompts/\`, \`dypai/code/\` (referenced via \`query_file\`, \`system_prompt_file\`, \`code_file\`).
|
|
590
|
-
- \`dypai_validate\` → catches placeholder / schema / credential / node-param errors. Run before push (push also runs it as pre-flight).
|
|
591
|
-
- \`dypai_diff\` → preview changes (read-only).
|
|
592
|
-
- \`dypai_push\` → **stages your edits as drafts on the platform**. This is the "save" step. NOT a publish. Run after every meaningful change set, not just at the end of a session — until you push, neither the engine nor the local frontend (which talks to the draft overlay) can see your edits.
|
|
593
|
-
- \`manage_drafts(operation:'publish', confirm:true)\` → ONLY when the user signs off. Promotes ALL pending drafts atomically to live.
|
|
594
|
-
3. \`dypai_test_endpoint\` to execute an endpoint against the engine. Three sources via \`mode\`: \`local\` (default — your YAML on disk, BEFORE push, fastest while iterating), \`draft\` (the version staged by \`dypai_push\` but not yet published — what live will look like after \`manage_drafts(publish)\`), \`live\` (currently deployed). Canonical loop: edit → \`dypai_validate\` → \`dypai_test_endpoint(mode:'local')\` → \`dypai_push\` → \`dypai_test_endpoint(mode:'draft')\` (or user tests local UI on the draft overlay) → \`manage_drafts(publish, confirm:true)\`.
|
|
595
|
-
|
|
596
|
-
## Picking nodes — catalog-first
|
|
597
|
-
|
|
598
|
-
\`dypai/node-catalog.json\` (cached by dypai_pull) is the SINGLE source of truth for every node_type and its input/output schema. Read it directly — don't guess params, don't invent node_types. If a type isn't in the catalog, it doesn't exist.
|
|
599
|
-
|
|
600
|
-
**Decision table** — check before reaching for \`javascript_code\`:
|
|
601
|
-
|
|
602
|
-
| Step is... | Use | Only JS if... |
|
|
603
|
-
|---|---|---|
|
|
604
|
-
| SELECT/INSERT/UPDATE/DELETE | \`dypai_database\` | Tight branching across multiple queries in one block |
|
|
605
|
-
| Reshape / rename / merge fields | \`set_fields\` | Arbitrary business logic (scoring, pricing rules) |
|
|
606
|
-
| if / else / early return | \`logic\` | Branching mixed with other custom logic |
|
|
607
|
-
| Iterate an array | \`foreach\` (or \`filter\`, \`merge\`) | Stateful accumulation / complex reduce |
|
|
608
|
-
| External HTTP call | \`http_request\` | Orchestrating multiple calls with interdependencies |
|
|
609
|
-
| LLM prompt / tool use | \`agent\` | Always agent — handles memory, tools, streaming |
|
|
610
|
-
| Stripe/Telegram/Slack/Resend/etc | Dedicated integration node | Provider not covered → \`http_request\` |
|
|
611
|
-
| Upload / download / delete files | \`dypai_storage\` | Never |
|
|
612
|
-
| Dates / crypto / delays | \`datetime\`, \`crypto\`, \`wait_delay\` | Never |
|
|
613
|
-
|
|
614
|
-
**JS red flags** (replace with native):
|
|
615
|
-
- \`await db.query(...)\` → \`dypai_database\`
|
|
616
|
-
- \`return { a: data.x, b: nodes.prev.y }\` → \`set_fields\`
|
|
617
|
-
- \`await http.post(...)\` → \`http_request\`
|
|
618
|
-
- \`if (x > 100) return {...} else return {...}\` → \`logic\`
|
|
619
|
-
|
|
620
|
-
JS wins when logic is genuinely custom and bundling multiple concerns is clearer than chaining 4+ native nodes. Native nodes are better otherwise: they're validated, traceable, and readable in diffs.
|
|
621
|
-
|
|
622
|
-
## Realtime (built-in, WebSocket)
|
|
623
|
-
|
|
624
|
-
Four capabilities: DB change notifications, broadcast (ephemeral messaging), presence (who's online), persistent channels (chat with history + DMs + unread counts — zero backend endpoints needed).
|
|
625
|
-
|
|
626
|
-
**Backend**: \`dypai/realtime.yaml\` declares table policies. Tables not there → deny-by-default. Default policy auto-created on CREATE TABLE (filters by \`user_id = \${current_user_id}\` if column exists).
|
|
627
|
-
|
|
628
|
-
**Frontend hooks**: \`useRealtime(table, opts)\` for DB changes, \`useChannel(name, opts)\` for broadcast+presence, \`useChannelMessages(id)\` + \`useChannels()\` for chat.
|
|
629
|
-
|
|
630
|
-
→ Deep details: \`search_docs("realtime channels")\` (client API), \`search_docs("realtime policies")\` (backend YAML format).
|
|
631
|
-
|
|
632
|
-
## Agent tools (endpoints as tools for \`agent\` nodes)
|
|
633
|
-
|
|
634
|
-
Any endpoint can be flagged \`tool: true\` to be callable by \`agent\` nodes. How you give an LLM ability to query DB / send emails / call APIs.
|
|
635
|
-
|
|
636
|
-
**Essentials**:
|
|
637
|
-
- Mark the endpoint with \`tool: true\` + \`tool_description\` (what the LLM sees when deciding to call it — be specific) + \`input:\` JSON Schema (tight schema = tight calls).
|
|
638
|
-
- \`auth_mode: api_key\` for tools (server-to-server). NEVER \`public\` for writes.
|
|
639
|
-
- In the \`agent\` node: \`tools: [endpoint-names]\` — by NAME, the codec resolves to UUIDs.
|
|
640
|
-
|
|
641
|
-
⚠️ **TRAP**: raw engine param is \`tool_ids\` (UUIDs). In YAML ALWAYS write \`tools: [names]\`. Using \`tool_ids: ["my-endpoint"]\` bypasses codec → engine gets literal string → fails silently in production.
|
|
642
|
-
|
|
643
|
-
**Discovery**: \`dypai_pull\` → \`overview.endpoints.tool_endpoints\` lists existing tools. Check before writing duplicates.
|
|
644
|
-
**Validation**: \`dypai_validate\` catches typos (rule \`agent_tool_not_found\`). Engine enforces depth limit 3 on agent→tool→agent chains.
|
|
645
|
-
|
|
646
|
-
→ Full example + patterns: \`search_docs("agent tools")\` or copy from \`dypai/endpoints/_example.yaml.disabled\`.
|
|
704
|
+
## Debugging user-reported errors — \`search_logs\` is your starting point
|
|
647
705
|
|
|
648
|
-
|
|
706
|
+
**Rule**: whenever the user says any of these — "X is broken", "this isn't working", "I'm getting an error", "users are reporting Y", "the page is white", "nothing happens when I click" — **call \`search_logs\` BEFORE reading any code**. The engine's logs are the ground truth; the code is your hypothesis. Trying to debug from the source first is how you waste 20 minutes solving the wrong problem.
|
|
649
707
|
|
|
650
|
-
|
|
708
|
+
### The standard flow
|
|
651
709
|
|
|
652
|
-
1. **\`dypai_test_endpoint(mode:'local')\`** — runs YOUR YAML on disk against the engine BEFORE \`dypai_push\`. Fastest feedback loop while iterating on a single endpoint. Pass \`as_user\` UUID for jwt endpoints.
|
|
653
|
-
2. **\`dypai_test_endpoint(mode:'draft')\`** — runs the version staged by \`dypai_push\` (i.e. exactly what \`manage_drafts(publish)\` will promote). Use as the final isolated check before publishing.
|
|
654
|
-
3. **End-to-end from the local frontend** (Layer 2.5 draft overlay) — after \`dypai_push\`, the user's local frontend already calls \`https://dev-<project_id>.dypai.dev\` (set by \`manage_frontend(sync)\`), which serves drafts on top of live. So real UI flows hit the draft transparently. No setup, no env-flip, no headers. Read-only impact on prod data — drafts share the SAME database as live.
|
|
655
|
-
4. **\`dypai_test_endpoint(mode:'live')\`** — repro a bug that's already in production.
|
|
656
|
-
|
|
657
|
-
Other tools:
|
|
658
|
-
- **\`dypai_test\`** — YAML regression suites at \`dypai/tests/<name>.test.yaml\` with assertions (equals, matches, contains, type, exists, gte, lte) + setup_sql / teardown_sql.
|
|
659
|
-
- **\`dypai_validate\`** — static linting (placeholders, tables, columns, node params, credentials). Run before EVERY push.
|
|
660
|
-
- **Prod debugging**: \`get_recent_workflow_activity(only_errors=true)\` surfaces recent failures.
|
|
661
|
-
|
|
662
|
-
→ Deep patterns: \`search_docs("testing endpoints")\` (test setup + assertions), \`search_docs("troubleshooting")\` (common failures + fixes).
|
|
663
|
-
|
|
664
|
-
## The \`dypai/\` folder (BACKEND source of truth)
|
|
665
|
-
|
|
666
|
-
\`dypai_pull\` materializes this at the PROJECT ROOT (sibling of \`src/\`, \`package.json\`, etc. — NOT inside \`src/\`). Everything backend-related lives here. Edit files directly with Edit/Write, then \`dypai_validate\` → \`dypai_diff\` → \`dypai_push\`.
|
|
667
|
-
|
|
668
|
-
\`\`\`
|
|
669
|
-
<project-root>/
|
|
670
|
-
├── package.json
|
|
671
|
-
├── src/ ← frontend code (React/Vite/Next)
|
|
672
|
-
│ └── lib/dypai.ts ← SDK client, do NOT hand-edit
|
|
673
|
-
├── public/ ← static assets (frontend bundle)
|
|
674
|
-
└── dypai/ ← BACKEND (created by dypai_pull)
|
|
675
|
-
├── dypai.config.yaml ← project id, engine URL. Don't hand-edit.
|
|
676
|
-
├── endpoints/ ← workflow definitions (YOU EDIT these)
|
|
677
|
-
│ ├── list-tasks.yaml
|
|
678
|
-
│ ├── create-order.yaml
|
|
679
|
-
│ └── Admin/ ← subfolder = endpoint group "Admin"
|
|
680
|
-
│ └── ban-user.yaml
|
|
681
|
-
├── sql/ ← extracted SQL (referenced via query_file)
|
|
682
|
-
│ └── complex-report.sql
|
|
683
|
-
├── prompts/ ← agent system prompts (referenced via system_prompt_file)
|
|
684
|
-
│ └── shop-assistant.md
|
|
685
|
-
├── code/ ← raw JS/Python (referenced via code_file)
|
|
686
|
-
│ └── scoring.js
|
|
687
|
-
├── schema.sql ← READ-ONLY. DDL of public.* tables (auto-refreshed on DDL)
|
|
688
|
-
├── node-catalog.json ← READ-ONLY. Every node_type + its I/O schema
|
|
689
|
-
├── realtime.yaml ← realtime subscription policies (edit + push to customize)
|
|
690
|
-
├── tests/ ← YAML test suites (optional)
|
|
691
|
-
│ └── create-order.test.yaml
|
|
692
|
-
└── .dypai/ ← local cache, gitignored — DO NOT touch
|
|
693
|
-
├── deploy-manifest.json ← SHA hashes from last deploy (delta engine)
|
|
694
|
-
└── media-manifest.json ← media files uploaded to the storage bucket
|
|
695
|
-
\`\`\`
|
|
696
|
-
|
|
697
|
-
### Where to put what
|
|
698
|
-
|
|
699
|
-
| If you need to add... | Put it here | How it's referenced |
|
|
700
|
-
|---|---|---|
|
|
701
|
-
| A new API endpoint | \`dypai/endpoints/<name>.yaml\` | URL: \`/api/v0/<name>\` |
|
|
702
|
-
| Endpoint organized in a group | \`dypai/endpoints/<Group>/<name>.yaml\` | URL: same — group is organizational only |
|
|
703
|
-
| A cron job | \`dypai/endpoints/<name>.yaml\` with \`trigger.schedule\` | Runs automatically |
|
|
704
|
-
| A webhook handler | \`dypai/endpoints/<name>.yaml\` with \`trigger.webhook\` | URL: \`/api/v0/webhooks/<name>\` |
|
|
705
|
-
| A telegram bot | \`dypai/endpoints/<name>.yaml\` with \`trigger.telegram\` | Webhook auto-registered |
|
|
706
|
-
| SQL too long for a YAML field | \`dypai/sql/<name>.sql\` | \`query_file: sql/<name>.sql\` |
|
|
707
|
-
| Long agent system prompt | \`dypai/prompts/<name>.md\` | \`system_prompt_file: prompts/<name>.md\` |
|
|
708
|
-
| Raw JS/Python function body | \`dypai/code/<name>.js\` or \`.py\` | \`code_file: code/<name>.js\` |
|
|
709
|
-
| A regression test | \`dypai/tests/<name>.test.yaml\` | Run via \`dypai_test\` |
|
|
710
|
-
| Realtime policy | Add entry to \`dypai/realtime.yaml\` | Auto-applied on push |
|
|
711
|
-
| A new database table | Use \`execute_sql\` (CREATE TABLE …) | Auto-reflected in \`schema.sql\` |
|
|
712
|
-
| A credential (OpenAI key, etc.) | Dashboard (not via MCP yet) | Reference by name: \`credential: openai-prod\` |
|
|
713
|
-
|
|
714
|
-
### What NOT to edit
|
|
715
|
-
|
|
716
|
-
- **\`schema.sql\`** — auto-generated snapshot of DB DDL. To change tables: \`execute_sql\` with CREATE/ALTER/DROP TABLE, schema.sql refreshes automatically.
|
|
717
|
-
- **\`node-catalog.json\`** — auto-generated from the engine. Read it to learn node schemas; never hand-edit.
|
|
718
|
-
- **\`dypai.config.yaml\`** — project identity (UUID, slug). Regenerated on pull.
|
|
719
|
-
- **\`.dypai/\`** — local cache. The delta deploy + media manifest live here.
|
|
720
|
-
- **\`src/lib/dypai.ts\`** — SDK client config. Regenerated by \`manage_frontend(sync)\`.
|
|
721
|
-
|
|
722
|
-
### Path resolution inside YAML
|
|
723
|
-
|
|
724
|
-
All \`*_file\` paths are relative to \`dypai/\` root:
|
|
725
|
-
\`\`\`yaml
|
|
726
|
-
# ✅ CORRECT (dypai/sql/get-orders.sql exists)
|
|
727
|
-
query_file: sql/get-orders.sql
|
|
728
|
-
|
|
729
|
-
# ❌ WRONG
|
|
730
|
-
query_file: ./sql/get-orders.sql
|
|
731
|
-
query_file: dypai/sql/get-orders.sql
|
|
732
|
-
query_file: /absolute/path/sql/get-orders.sql
|
|
733
|
-
\`\`\`
|
|
734
|
-
|
|
735
|
-
### Paths on the engine side
|
|
736
|
-
|
|
737
|
-
- \`/api/v0/<endpoint_name>\` — HTTP endpoints
|
|
738
|
-
- \`/api/v0/webhooks/<endpoint_name>\` — webhook endpoints (different path prefix)
|
|
739
|
-
- \`/public/<path>\` — media served from the storage bucket (auto-populated on deploy; see "Frontend deploy")
|
|
740
|
-
- \`https://<project_id>.dypai.dev\` — engine base URL serving LIVE traffic (what the deployed frontend's SDK points to)
|
|
741
|
-
- \`https://dev-<project_id>.dypai.dev\` — engine base URL serving the **draft overlay** (Layer 2.5): drafts staged via \`dypai_push\` are served here, falling back to live for anything not drafted. This is what the SDK in \`.env.local\` points to during local frontend development, so a local UI can validate backend drafts end-to-end BEFORE \`manage_drafts(publish)\`.
|
|
742
|
-
|
|
743
|
-
## Endpoint YAML skeleton (top-level fields)
|
|
744
|
-
|
|
745
|
-
\`\`\`yaml
|
|
746
|
-
name: create-order # required. URL = /api/v0/create-order
|
|
747
|
-
description: "..." # optional, shown in dashboard
|
|
748
|
-
method: POST # optional (default POST)
|
|
749
|
-
|
|
750
|
-
trigger: # required, exactly ONE of:
|
|
751
|
-
http_api: { auth_mode: jwt } # HTTP (jwt | api_key | public)
|
|
752
|
-
# webhook: { path, methods, stripe_webhook, hmac_secret_env, auth_mode: public }
|
|
753
|
-
# schedule: { cron: "0 9 * * *", timezone: "Europe/Madrid", payload }
|
|
754
|
-
# telegram: { credential: my-bot }
|
|
755
|
-
|
|
756
|
-
input: # optional JSON Schema — engine validates before workflow runs
|
|
757
|
-
type: object
|
|
758
|
-
required: [product_id]
|
|
759
|
-
properties:
|
|
760
|
-
product_id: { type: string }
|
|
761
|
-
|
|
762
|
-
allowed_roles: [admin] # optional — restrict to these roles
|
|
763
|
-
|
|
764
|
-
tool: true # optional — exposes this endpoint to agent nodes
|
|
765
|
-
tool_description: "..." # required when tool:true
|
|
766
|
-
|
|
767
|
-
workflow:
|
|
768
|
-
nodes:
|
|
769
|
-
- { id: x, type: ..., return: true, ...params }
|
|
770
|
-
edges:
|
|
771
|
-
- { from: a, to: b } # optional — default is declaration order
|
|
772
710
|
\`\`\`
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
**Legacy ops** (\`select\`/\`insert\`/\`update\`/\`delete\`/\`upsert\`/\`aggregate\`/\`custom_query\`) still work but don't write new ones — validator warns.
|
|
797
|
-
|
|
798
|
-
→ Full shapes + examples: \`search_docs("nodes reference")\` or read \`dypai/node-catalog.json\` entry for \`dypai_database\`.
|
|
799
|
-
|
|
800
|
-
## SQL placeholders — no manual casts
|
|
801
|
-
|
|
802
|
-
The engine binds placeholders as Postgres params (\$1, \$2…), injection-safe. Auto-casts by value shape: UUID-shaped strings → \`\$1::uuid\`, objects/arrays → \`\$1::jsonb\`, Date → \`\$1::timestamptz\`, primitives → inferred.
|
|
803
|
-
|
|
804
|
-
Write SQL naturally:
|
|
805
|
-
\`\`\`
|
|
806
|
-
✅ WHERE user_id = \${current_user_id}
|
|
807
|
-
❌ WHERE user_id = '\${current_user_id}'::uuid (redundant — validator warns)
|
|
808
|
-
\`\`\`
|
|
809
|
-
|
|
810
|
-
## Canonical YAML slip-ups (typical mistakes the codec catches but don't ship)
|
|
811
|
-
|
|
812
|
-
- **Trigger is TOP-LEVEL**, never a node. \`trigger: { http_api | webhook | schedule | telegram }\`. Don't write \`start_trigger\` as a node.
|
|
813
|
-
- **Nodes use \`type\` (not \`node_type\`) and \`return: true\` (not \`is_return\`)**. Codec accepts aliases — write canonical.
|
|
814
|
-
- **Edges use \`from/to\`** (not \`source/target\`). Branching: \`{ from: cond, to: y, source_handle: "true" }\`.
|
|
815
|
-
- **Params are FLAT on the node**. Only nest under \`parameters:\` when a param name collides with reserved keys (id/type/return/variable).
|
|
816
|
-
- **Multiple \`return: true\` nodes are fine** — execution reaches one per run.
|
|
817
|
-
|
|
818
|
-
When in doubt → \`dypai/endpoints/_example.yaml.disabled\` (shipped on empty projects) or \`search_docs("trigger model")\` for a complete tour.
|
|
819
|
-
|
|
820
|
-
## Workflow execution model (what happens under the hood)
|
|
821
|
-
|
|
822
|
-
- **Nodes form a DAG** from \`edges\`. With no explicit edges, nodes run sequentially in declaration order.
|
|
823
|
-
- **Data flow**: each node reads \`\${input.<field>}\` (endpoint input) and \`\${nodes.<id>.<field>}\` (prior node output). A node's output is whatever its last expression/return produces.
|
|
824
|
-
- **Return**: the HTTP response is the output of the FIRST node reached with \`return: true\`. Multiple \`return: true\` nodes are fine — only one fires per run.
|
|
825
|
-
- **Errors**: an unhandled throw in any node fails the whole workflow → HTTP 500 with the error message. Use the \`logic\` node or try/catch in \`javascript_code\` for controlled error responses.
|
|
826
|
-
- **Timeouts**: default workflow timeout is 30s (raised to 90s on Pro+). Individual nodes don't have sub-timeouts — design around the total.
|
|
827
|
-
- **Concurrency**: per-project, not per-endpoint. Pool-mode projects share a slot budget based on plan.
|
|
828
|
-
|
|
829
|
-
## Auth (better-auth backed)
|
|
830
|
-
|
|
831
|
-
**No DIY auth.** Never write login/signup/session endpoints — the SDK handles everything:
|
|
832
|
-
\`\`\`ts
|
|
833
|
-
await dypai.auth.signUp({ email, password, name })
|
|
834
|
-
await dypai.auth.signInWithPassword({ email, password })
|
|
835
|
-
await dypai.auth.signOut()
|
|
836
|
-
const session = await dypai.auth.getSession() // null if not logged in
|
|
711
|
+
1. search_logs({ since: "1h", level: "error" })
|
|
712
|
+
→ Quick scan of recent failures. If empty, widen to "24h".
|
|
713
|
+
|
|
714
|
+
2. # Did the user say "I just tested this in my local UI"?
|
|
715
|
+
# → add environment: "draft" (their UI hits the draft overlay)
|
|
716
|
+
# Did they say "production users are reporting..."?
|
|
717
|
+
# → add environment: "live" (excludes their own draft test runs)
|
|
718
|
+
|
|
719
|
+
3. # Found the relevant entry? Narrow down:
|
|
720
|
+
search_logs({ endpoint: "create-order", query: "stripe", since: "1h" })
|
|
721
|
+
|
|
722
|
+
4. # For the full step-by-step trace of one specific failure:
|
|
723
|
+
search_logs({
|
|
724
|
+
endpoint: "create-order",
|
|
725
|
+
query: "<a unique substring from the error message>",
|
|
726
|
+
include_trace: true,
|
|
727
|
+
limit: 5
|
|
728
|
+
})
|
|
729
|
+
→ If the response is large the local proxy writes it to a temp file
|
|
730
|
+
and returns a \`file_path\`. Read that file with the Read tool ONLY
|
|
731
|
+
when you need fields beyond the inline summary.
|
|
732
|
+
|
|
733
|
+
5. # Now you know exactly which node failed and why → fix the code.
|
|
837
734
|
\`\`\`
|
|
838
735
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
**Role-based access**: add \`allowed_roles: [admin, editor]\` at the endpoint top-level to restrict. Roles live in \`system.roles\` (manage via \`manage_roles\`). Built-in roles: \`authenticated\`, \`admin\`. User role lives in \`auth.users.role\` (manage via \`manage_users\`).
|
|
842
|
-
|
|
843
|
-
**Protected frontend routes**: wrap with \`<ProtectedRoute>\` from \`@dypai-ai/client-sdk/react\` — redirects to login if no session.
|
|
844
|
-
|
|
845
|
-
**No RLS magic**: the engine does NOT auto-filter queries by user. You MUST write \`WHERE user_id = \${current_user_id}\` in your SQL. Missing this is the #1 multi-tenancy bug.
|
|
846
|
-
|
|
847
|
-
→ Canonical flows (signup/reset/role upgrade/magic link): \`search_docs("auth flows")\`. What defaults to pick when user doesn't specify: \`search_docs("auth defaults")\`.
|
|
848
|
-
|
|
849
|
-
## Agent node (\`type: agent\`)
|
|
850
|
-
|
|
851
|
-
Vercel AI SDK under the hood. Supports OpenAI, Anthropic, Google. Key params: \`memory_key\` (persist conversation), \`tools: [names]\` (see Agent tools), \`max_iterations\` (default 5), \`return: true\` for streaming via SSE.
|
|
852
|
-
|
|
853
|
-
**Cost awareness**: agents with tools + high max_iterations fan out (5-10 LLM calls per chat). For simple completions without tools, \`http_request\` to the provider is cheaper.
|
|
854
|
-
|
|
855
|
-
→ Deep details: \`search_docs("agent ai")\` — memory, streaming, structured output, provider specifics.
|
|
856
|
-
|
|
857
|
-
## Core DYPAI nodes (the primitives)
|
|
858
|
-
|
|
859
|
-
These are the engine-level building blocks you'll use in almost every workflow. Learn these well — integrations are just on top.
|
|
860
|
-
|
|
861
|
-
| Node | What it does |
|
|
862
|
-
|---|---|
|
|
863
|
-
| \`dypai_database\` | SQL (query) or declarative CRUD (mutation) against the project's Postgres. THE most-used node. |
|
|
864
|
-
| \`dypai_storage\` | Upload / download / delete files in storage buckets. |
|
|
865
|
-
| \`agent\` | LLM call with tools, memory, streaming. OpenAI / Anthropic / Google. |
|
|
866
|
-
| \`http_request\` | Generic HTTP call for anything without a dedicated integration node. |
|
|
867
|
-
| \`set_fields\` | Reshape / rename / merge fields into a new object. Use instead of JS for trivial transforms. |
|
|
868
|
-
| \`logic\` | if / else / switch branching with multiple outcomes. |
|
|
869
|
-
| \`foreach\` / \`filter\` / \`merge\` | Array iteration, filtering, combining collections. |
|
|
870
|
-
| \`wait_delay\` | Pause the workflow for N seconds (debouncing, rate limiting). |
|
|
871
|
-
| \`datetime\` | Parse, format, arithmetic on dates/times. |
|
|
872
|
-
| \`crypto\` | Hash, HMAC, encrypt/decrypt, generate tokens. |
|
|
873
|
-
| \`javascript_code\` / \`python_code\` | Escape hatch for custom logic that doesn't fit the native nodes. |
|
|
874
|
-
|
|
875
|
-
Triggers (separate category — they START a workflow):
|
|
876
|
-
- \`webhook_trigger\` / \`schedule_trigger\` / \`telegram_trigger\` → declared via \`trigger:\` at the endpoint top-level, not as workflow nodes.
|
|
877
|
-
|
|
878
|
-
## Finding 3rd-party integrations (native-first rule)
|
|
736
|
+
### What \`search_logs\` returns
|
|
879
737
|
|
|
880
|
-
|
|
738
|
+
Each item has \`type\` (\`execution_failed\` | \`log\`), \`level\` (\`error\` | \`warn\`), \`time\`, \`endpoint\`, \`message\`, and \`environment\` (\`live\` | \`draft\` | null for legacy rows). Failed executions also include \`status\` (\`error\` | \`timeout\`) and \`duration_ms\`. With \`include_trace:true\` they also include \`trace\` — a per-node log of inputs, outputs, errors, and stacks.
|
|
881
739
|
|
|
882
|
-
|
|
883
|
-
2. Search by provider name or concept: "stripe", "email", "telegram", "sheets".
|
|
884
|
-
3. If a dedicated node exists → use it. Better error messages, credential auto-wiring, validator coverage.
|
|
885
|
-
4. If none exists → fall back to \`http_request\` + a credential.
|
|
740
|
+
### Common pitfalls
|
|
886
741
|
|
|
887
|
-
|
|
742
|
+
- **Don't skip this and read code first.** The bug is almost never where you'd guess. Logs tell you exactly which node blew up and the exact error string.
|
|
743
|
+
- **Don't dump every error you see at the user.** Filter, summarize, then propose ONE fix.
|
|
744
|
+
- **\`environment\` matters.** A draft test failure is the user testing pending changes — fixing the draft is fine. A live failure is real users hitting production — fix urgently and follow up with backend publish.
|
|
745
|
+
- **Retention is 7 days.** If the user reports a bug from "last week", the data is likely gone. Tell them.
|
|
888
746
|
|
|
889
|
-
|
|
890
|
-
- **Referencing**: in YAML, point at a credential by name: \`credential: openai-prod\`. The engine injects the secret at runtime — the secret value NEVER appears in the YAML.
|
|
891
|
-
- **Discovery**: \`get_app_credentials(project_id)\` lists what's already configured. Check this BEFORE writing a node that references a credential — if it doesn't exist, tell the user to create it.
|
|
892
|
-
- **Common trap**: writing \`credential: openai\` when the user named it \`openai-prod\` → workflow fails with "credential not found". Always match the exact name from \`get_app_credentials\`.
|
|
893
|
-
|
|
894
|
-
→ Full credential types + which provider needs what: \`search_docs("credentials reference")\`. Step-by-step integration flows: \`search_docs("integrations guide")\`.
|
|
895
|
-
|
|
896
|
-
## Environment variables
|
|
747
|
+
## What you do NOT have to think about
|
|
897
748
|
|
|
898
|
-
-
|
|
899
|
-
-
|
|
900
|
-
-
|
|
901
|
-
-
|
|
749
|
+
- "Development vs production environment" — the user never sees this. Backend changes always go through draft-and-publish. Frontend changes always go through deploy. That's the whole model.
|
|
750
|
+
- "Create auth endpoints" — auth is built into the SDK. \`dypai.auth.signInWithPassword()\` works out of the box. NEVER write a login/signup workflow.
|
|
751
|
+
- "RLS / row-level security" — there is none. You filter by \`\${current_user_id}\` in your SQL WHERE clauses. Forgetting this is the #1 multi-tenancy bug.
|
|
752
|
+
- "Rate limiting / CORS / JWT verification" — handled by the engine.
|
|
753
|
+
- "Promoting projects to production" — every new project already has the draft-publish flow enabled. \`manage_project(promote_to_production)\` is legacy and you should never need it.
|
|
902
754
|
|
|
903
|
-
|
|
755
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
756
|
+
# ESSENTIALS — things you reuse every session
|
|
757
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
904
758
|
|
|
905
|
-
|
|
759
|
+
## Mental model — everything server-side is a workflow
|
|
906
760
|
|
|
907
|
-
|
|
908
|
-
- \`dypai.api\` → \`get\` / \`post\` / \`put\` / \`delete\` / \`upload\` / \`stream\` (SSE async iterator)
|
|
909
|
-
- \`dypai.auth\` → \`signUp\` / \`signInWithPassword\` / \`signOut\` / \`getSession\` / \`resetPassword\` / \`updateUser\` — NEVER create auth endpoints, the SDK does everything.
|
|
910
|
-
- \`dypai.realtime\` → \`subscribe(table, opts, callback)\` with Postgres-style filters (\`user_id=eq.\${uid}\`)
|
|
761
|
+
DYPAI has NO standalone "edge functions". Every piece of server-side logic (API endpoints, crons, webhooks, AI agents) is a workflow endpoint under \`dypai/endpoints/<name>.yaml\`. A workflow is either a chain of native nodes (\`dypai_database\`, \`http_request\`, \`agent\`, \`stripe\`, etc.) OR a single \`javascript_code\` / \`python_code\` node for custom logic. Mix freely.
|
|
911
762
|
|
|
912
|
-
|
|
763
|
+
Mental translations: "edge function" → workflow with one code node; "cron" → \`trigger.schedule\` in the YAML; "webhook receiver" → \`trigger.webhook\`; "internal API" → \`trigger.http_api auth_mode:jwt\`.
|
|
913
764
|
|
|
914
|
-
→ Full
|
|
765
|
+
→ Full workflow patterns + YAML shape: \`search_docs("workflow patterns")\` and \`search_docs("trigger model")\`. Node catalog (full input/output schemas): read \`dypai/node-catalog.json\`.
|
|
915
766
|
|
|
916
767
|
## What the engine handles for you (don't reinvent)
|
|
917
768
|
|
|
918
769
|
- **JWT verification** — jwt auth_mode validates the session token automatically. \`\${current_user_id}\` is trusted.
|
|
919
770
|
- **Rate limiting** — per-plan. Returns 429 automatically.
|
|
920
771
|
- **CORS** — allowed origins per project (configured in dashboard).
|
|
921
|
-
- **Request logging** — every execution
|
|
922
|
-
- **Input validation** —
|
|
772
|
+
- **Request logging** — every execution recorded with duration, status, environment, and (on failure) per-node trace. Query with \`search_logs\` (7-day retention).
|
|
773
|
+
- **Input validation** — \`input:\` schema in YAML → invalid payloads rejected with 400 before the workflow runs.
|
|
923
774
|
- **SQL injection** — placeholders bind as Postgres params. Safe by construction.
|
|
924
|
-
- **Secrets
|
|
925
|
-
- **Scheduled jobs** — schedule_trigger endpoints
|
|
926
|
-
- **
|
|
927
|
-
|
|
928
|
-
##
|
|
929
|
-
|
|
930
|
-
- **Auth-gated CRUD**: CREATE TABLE with \`user_id UUID\`. Endpoints jwt-auth. SQL always \`WHERE user_id = \${current_user_id}\`. Frontend wraps routes in \`ProtectedRoute\`.
|
|
931
|
-
- **Admin panel**: endpoints with \`allowed_roles: [admin]\`. Same SQL but no user_id filter (admin sees all). Promote users via \`manage_users(operation: update_role)\`.
|
|
932
|
-
- **AI chat**: single endpoint with \`agent\` node + \`memory_key: "chat:\${current_user_id}"\`. Frontend uses \`dypai.api.stream()\` for SSE.
|
|
933
|
-
- **Payments (Stripe)**: \`stripe\` node for operations (checkout, invoices). Webhook endpoint with \`trigger.webhook\` + \`stripe_webhook: true\` receives events — the node auto-verifies the signature.
|
|
934
|
-
- **Cron job**: endpoint with \`trigger.schedule: { cron: "0 9 * * *", timezone: "Europe/Madrid" }\`. Workflow body runs in engine — same syntax as HTTP endpoints.
|
|
935
|
-
- **File uploads**: frontend uses \`dypai.api.upload(endpoint, file)\`. The endpoint is a workflow with one \`dypai_storage\` node (\`operation: upload\`, \`bucket: <name>\`, \`confirm: false\`). SDK handles the signed URL PUT flow.
|
|
936
|
-
- **Live dashboard**: normal endpoint returning aggregated data + frontend \`useRealtime(table, { onInsert: refetch })\`. Auto-updates when rows change.
|
|
937
|
-
- **Multi-tenant**: every row has \`org_id\` or \`user_id\`. Every endpoint filters by \`\${current_user_id}\`. Shared resources → endpoints with \`allowed_roles\`.
|
|
938
|
-
|
|
939
|
-
## Gotchas that will cost time if you don't know them
|
|
940
|
-
|
|
941
|
-
- **\`.env\` missing after sync** → frontend SDK can't reach the engine → every API call 404s. The fix is always creating .env, not debugging code.
|
|
942
|
-
- **\`tool_ids\` vs \`tools\`** → write \`tools: [names]\` in YAML. Using \`tool_ids\` makes the engine receive the name as a literal UUID. Fails silently in prod.
|
|
943
|
-
- **Missing \`return: true\`** → endpoint returns \`null\` or empty. Every path that should produce an HTTP response needs it.
|
|
944
|
-
- **\`public\` auth_mode with placeholders** → \`\${current_user_id}\` is empty → SQL fails or returns wrong data. Use jwt if you need the user.
|
|
945
|
-
- **Forgetting \`WHERE user_id = \${current_user_id}\`** → users see each other's data. The engine does NOT auto-filter.
|
|
946
|
-
- **Credentials not created** → workflow fails with "credential not found". Check \`get_app_credentials\` before referencing one in a node. Create in dashboard (not via MCP yet).
|
|
947
|
-
- **Binary files in \`dypai/code/\`** → only text code files here. Binary assets go to the frontend \`public/\` or to a bucket.
|
|
948
|
-
- **\`dypai_push\` without \`dypai_validate\`** → pushing a broken workflow. Always validate first.
|
|
949
|
-
- **Editing a YAML and forgetting \`dypai_push\`** → the user reloads their local frontend (which points at the draft overlay \`dev-<project_id>.dypai.dev\`) and sees the OLD behavior because your edit only exists on YOUR DISK. Symptom: "I tested it locally and nothing changed." First check: did you push? Push after every meaningful change set, not at the end.
|
|
950
|
-
- **Treating \`dypai_push\` as a deploy** → It's a "save as draft", not a publish. Live traffic is unaffected until \`manage_drafts(publish, confirm:true)\`. Don't ask the user "ready to ship?" before push — push freely, only ask before publish.
|
|
951
|
-
- **Frontend dev server + remote media** → media files are auto-uploaded to the storage bucket on deploy but \`vite dev\` doesn't proxy to it. Run \`manage_frontend(sync)\` first to pull media to disk.
|
|
952
|
-
|
|
953
|
-
## Frontend
|
|
954
|
-
|
|
955
|
-
SDK is pre-configured at \`src/lib/dypai.ts\`. Import \`dypai\` from there. Every method returns \`{ data, error }\` — never throws.
|
|
956
|
-
|
|
957
|
-
- **API calls**: \`dypai.api.get(name)\`, \`.post(name, body)\`, \`.put()\`, \`.delete()\`, \`.upload(name, file)\`
|
|
958
|
-
- **Auth**: \`dypai.auth.signInWithPassword()\`, \`.signUp()\`, \`.signOut()\`, \`.getSession()\` — DO NOT create auth endpoints
|
|
959
|
-
- **Types**: \`import { api } from '@/dypai'\` for typed endpoint calls
|
|
960
|
-
- **Realtime hooks**: \`useRealtime\`, \`useChannel\`, \`useChannelMessages\` (see Realtime section)
|
|
961
|
-
- **Rule**: NEVER \`fetch()\` directly — always through the SDK
|
|
962
|
-
|
|
963
|
-
**\`.env.local\` is auto-managed by \`manage_frontend(sync)\`** — when missing, sync writes it for you pointing at the **draft overlay** (\`https://dev-<project_id>.dypai.dev\`) so your local frontend transparently consumes backend drafts. The variable name follows your framework: \`VITE_DYPAI_URL\` for Vite, \`NEXT_PUBLIC_DYPAI_URL\` for Next.js. **Do not overwrite a user-authored \`.env.local\`** — sync respects an existing file. Only create it manually if \`env_file_missing: true\` in the sync response AND you have a reason to deviate.
|
|
964
|
-
|
|
965
|
-
\`\`\`bash
|
|
966
|
-
# What sync writes (Vite)
|
|
967
|
-
VITE_DYPAI_URL=https://dev-<project_id>.dypai.dev
|
|
968
|
-
|
|
969
|
-
# What sync writes (Next.js)
|
|
970
|
-
NEXT_PUBLIC_DYPAI_URL=https://dev-<project_id>.dypai.dev
|
|
971
|
-
\`\`\`
|
|
775
|
+
- **Secrets** — credentials never appear in YAML or logs.
|
|
776
|
+
- **Scheduled jobs** — \`schedule_trigger\` endpoints enqueued on startup/deploy. No cron daemon.
|
|
777
|
+
- **NOT automatic**: webhook retries (add explicit \`wait_delay\` + retry in the workflow if needed).
|
|
778
|
+
|
|
779
|
+
## Top gotchas (the expensive ones)
|
|
972
780
|
|
|
973
|
-
|
|
781
|
+
1. **Forgetting \`WHERE user_id = \${current_user_id}\`** — users see each other's data. #1 multi-tenancy bug. The engine does NOT auto-filter. RLS doesn't exist.
|
|
782
|
+
2. **Editing YAML without \`dypai_push\`** — your change is on YOUR DISK only. Local frontend (which points at the draft overlay) keeps serving the old version. Symptom: *"I tested it locally and nothing changed"*. Always push after each meaningful change set.
|
|
783
|
+
3. **Treating \`dypai_push\` as a deploy** — it's "save as draft", not publish. Live traffic is untouched until \`manage_drafts(publish, confirm:true)\`. Push freely, only ask the user before publish.
|
|
784
|
+
4. **\`public\` auth_mode with \`\${current_user_id}\`** — no JWT → placeholder empty → SQL fails or returns wrong data. Use \`jwt\` if you need the user.
|
|
785
|
+
5. **Missing \`return: true\`** — endpoint returns \`null\`. Every path that should produce an HTTP response needs one node with \`return: true\`.
|
|
786
|
+
6. **\`tool_ids\` in YAML instead of \`tools\`** — write \`tools: [name1, name2]\`. \`tool_ids\` bypasses the codec and fails silently in prod.
|
|
974
787
|
|
|
975
|
-
|
|
788
|
+
→ Longer list of common pitfalls + fixes: \`search_docs("troubleshooting")\`.
|
|
976
789
|
|
|
977
|
-
|
|
790
|
+
## Common app recipes (one-liners)
|
|
978
791
|
|
|
979
|
-
|
|
792
|
+
- **Auth-gated CRUD**: table with \`user_id UUID\`, jwt endpoints, SQL always filters by \`\${current_user_id}\`, frontend \`<ProtectedRoute>\`.
|
|
793
|
+
- **Admin panel**: endpoints with \`allowed_roles: [admin]\`. Same SQL, no user filter (admin sees all). Promote via \`manage_users(update_role)\`.
|
|
794
|
+
- **AI chat**: single endpoint with \`agent\` node + \`memory_key: "chat:\${current_user_id}"\`. Frontend \`dypai.api.stream()\`.
|
|
795
|
+
- **Payments (Stripe)**: \`stripe\` node for ops. Webhook endpoint with \`trigger.webhook\` + \`stripe_webhook: true\` (auto-verifies signature).
|
|
796
|
+
- **Cron**: \`trigger.schedule: { cron: "0 9 * * *", timezone: "..." }\`. Same workflow syntax as HTTP endpoints.
|
|
797
|
+
- **File upload**: frontend \`dypai.api.upload(endpoint, file)\`. Endpoint = one \`dypai_storage\` node. SDK handles signed-URL PUT.
|
|
798
|
+
- **Live dashboard**: normal endpoint + frontend \`useRealtime(table, { onInsert: refetch })\`.
|
|
799
|
+
- **Multi-tenant**: every row has \`org_id\` or \`user_id\`. Every endpoint filters by \`\${current_user_id}\`.
|
|
980
800
|
|
|
981
|
-
|
|
801
|
+
→ Full canonical patterns + pitfalls: \`search_docs("workflow patterns")\`.
|
|
982
802
|
|
|
983
|
-
|
|
803
|
+
## Frontend essentials
|
|
984
804
|
|
|
985
|
-
|
|
805
|
+
SDK is pre-configured at \`src/lib/dypai.ts\` (or \`src/dypai.ts\`). Import \`dypai\` from there. Every method returns \`{ data, error }\` — never throws.
|
|
986
806
|
|
|
987
|
-
- \`
|
|
988
|
-
- \`
|
|
989
|
-
- \`
|
|
990
|
-
-
|
|
991
|
-
- \`manage_storage\` — buckets + objects. \`upload_file\` reads local path, signs URL, PUTs direct to the storage bucket, registers. Max 100MB/file. → \`search_docs("file storage")\`.
|
|
992
|
-
- \`manage_drafts\` — universal draft-and-publish workflow. \`dypai_push\` always saves changes as drafts; use \`list\` to show the user what's pending, \`publish\` (confirm:true) to apply them atomically to live, \`discard\` to throw them away. Test pending drafts with \`dypai_test_endpoint(mode:'draft')\` before publishing.
|
|
807
|
+
- **API**: \`dypai.api.get(name)\`, \`.post(name, body)\`, \`.put()\`, \`.delete()\`, \`.upload(name, file)\`, \`.stream(name, body)\`.
|
|
808
|
+
- **Auth**: \`dypai.auth.signInWithPassword()\`, \`.signUp()\`, \`.signOut()\`, \`.getSession()\`. **Never** create login/signup workflows — auth is built-in.
|
|
809
|
+
- **Hooks**: \`useAuth\`, \`useEndpoint\`, \`useAction\`, \`useUpload\`, \`useRealtime\`, \`<ProtectedRoute>\`.
|
|
810
|
+
- **Rule**: NEVER \`fetch()\` directly — always through the SDK.
|
|
993
811
|
|
|
994
|
-
|
|
812
|
+
**\`.env.local\` is auto-managed by \`manage_frontend(sync)\`** — when missing, sync writes it pointing at the **draft overlay** (\`https://dev-<project_id>.dypai.dev\`) so local dev transparently consumes backend drafts. Var name: \`VITE_DYPAI_URL\` (Vite) or \`NEXT_PUBLIC_DYPAI_URL\` (Next.js). **Do not overwrite a user-authored \`.env.local\`** — sync respects an existing file. For production, \`manage_frontend(deploy)\` injects the live URL (without \`dev-\`) at build time automatically.
|
|
995
813
|
|
|
996
|
-
|
|
814
|
+
→ Deep: \`search_docs("sdk reference")\`, \`search_docs("react hooks")\`, \`search_docs("frontend frameworks")\` (Next SSR, Vite, Astro gotchas), \`search_docs("manage frontend")\` (deploy operations).
|
|
997
815
|
|
|
998
|
-
|
|
999
|
-
- **Backend**: "git-first workflow", "trigger model", "workflow patterns", "nodes reference", "node types", "javascript code node"
|
|
1000
|
-
- **AI**: "agent ai" (agent node deep dive)
|
|
1001
|
-
- **Auth**: "auth flows" (canonical patterns), "auth defaults" (what to default to)
|
|
1002
|
-
- **Integrations**: "integrations guide", "credentials reference"
|
|
1003
|
-
- **Realtime**: "realtime channels" (client API), "realtime policies" (backend YAML)
|
|
1004
|
-
- **Storage**: "file storage"
|
|
1005
|
-
- **Frontend**: "sdk reference", "react hooks", "frontend frameworks", "manage frontend"
|
|
1006
|
-
- **Ops**: "manage domain", "testing endpoints", "troubleshooting"
|
|
816
|
+
## Other tools (short hits)
|
|
1007
817
|
|
|
1008
|
-
|
|
818
|
+
- \`bulk_upsert\` — CSV/JSON → table. Seed data.
|
|
819
|
+
- \`manage_domain\` — custom domains. \`add\` returns CNAME to configure. \`verify\` rechecks DNS/SSL. → \`search_docs("manage domain")\`.
|
|
820
|
+
- \`manage_users\` / \`manage_roles\` — app-level RBAC (better-auth). Create/ban/assign roles.
|
|
821
|
+
- \`manage_schedules\` / \`manage_webhooks\` — pause/resume/history. To change the DEFINITION, edit the YAML and push.
|
|
822
|
+
- \`manage_storage\` — buckets + objects. \`upload_file\` reads local path, signs URL, PUTs, registers. Max 100MB/file. → \`search_docs("file storage")\`.
|
|
823
|
+
- \`manage_drafts\` — universal draft/publish. \`list\` before \`publish\` so the user sees what's shipping. \`discard\` to throw away. Always pair with \`dypai_test_endpoint(mode:'draft')\` before publish.
|
|
824
|
+
- \`manage_database\` — migrations + introspection + multi-statement scripts. \`apply_migration\` for versioned DDL in \`dypai/migrations/\`. \`introspect_*\` for tables/functions/schemas.
|
|
825
|
+
|
|
826
|
+
→ For any unfamiliar territory: the topic map at the top covers where to dig. When in doubt — search first, code after.
|
|
1009
827
|
`
|
|
1010
828
|
|
|
1011
829
|
// ── MCP Protocol (JSON-RPC over stdio) ──────────────────────────────────────
|
|
@@ -1077,6 +895,20 @@ async function handleRequest(msg) {
|
|
|
1077
895
|
})
|
|
1078
896
|
}
|
|
1079
897
|
|
|
898
|
+
// Pre-flight: block DDL/DML on protected schemas (auth, storage,
|
|
899
|
+
// system, pg_*, _timescaledb_*). SELECT is always allowed. Failing
|
|
900
|
+
// here with a guided message is strictly UX — the cloud tool also
|
|
901
|
+
// enforces the same rules, but a clear message from the local MCP
|
|
902
|
+
// saves the agent from parsing raw Postgres permission errors.
|
|
903
|
+
// manage_database runs its own guard internally, so it's NOT
|
|
904
|
+
// re-validated here.
|
|
905
|
+
if (name === "execute_sql" && typeof finalArgs?.sql === "string") {
|
|
906
|
+
const v = validateSql(finalArgs.sql)
|
|
907
|
+
if (!v.ok) {
|
|
908
|
+
throw new Error(formatValidationError(v))
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
1080
912
|
// Resolve endpoint_name → endpoint_id for tools that accept only the UUID.
|
|
1081
913
|
// Keeps the agent-facing API name-based while the remote keeps its
|
|
1082
914
|
// UUID-based contract. Best-effort: if the lookup fails we still pass
|
|
@@ -1099,7 +931,9 @@ async function handleRequest(msg) {
|
|
|
1099
931
|
result = enrichSuccess(name, raw)
|
|
1100
932
|
|
|
1101
933
|
// Side effect: if the user ran schema-changing DDL via execute_sql,
|
|
1102
|
-
// re-dump dypai/schema.sql so the validator stays in sync.
|
|
934
|
+
// re-dump dypai/schema.sql so the validator stays in sync. The
|
|
935
|
+
// helper is a no-op for non-DDL. manage_database handles its own
|
|
936
|
+
// schema refresh for apply_migration / execute_script operations.
|
|
1103
937
|
if (name === "execute_sql") {
|
|
1104
938
|
const refresh = await maybeRefreshSchemaAfterExecuteSql(args || {}, result)
|
|
1105
939
|
if (refresh.refreshed) {
|
|
@@ -1109,6 +943,14 @@ async function handleRequest(msg) {
|
|
|
1109
943
|
}
|
|
1110
944
|
}
|
|
1111
945
|
|
|
946
|
+
// search_logs can return huge payloads when include_trace=true.
|
|
947
|
+
// Offload to a temp file when the serialized response > 60 KB so
|
|
948
|
+
// the agent's context stays clean — it gets a summary + file path
|
|
949
|
+
// and only Reads the file when it actually needs the detail.
|
|
950
|
+
if (name === "search_logs") {
|
|
951
|
+
result = maybeOffloadSearchLogs(result)
|
|
952
|
+
}
|
|
953
|
+
|
|
1112
954
|
// Note: test_workflow is no longer agent-facing (wrapped by
|
|
1113
955
|
// dypai_test_endpoint). dypai_trace is temporarily hidden until
|
|
1114
956
|
// the engine captures debug traces for real production executions.
|