@agent-native/core 0.49.25 → 0.49.26
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/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +8 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +43 -11
- package/dist/cli/recap.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +2 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +7 -7
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +3 -3
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +4 -3
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +16 -9
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/blocks/types.d.ts +2 -2
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/coding-tools/run-code.d.ts.map +1 -1
- package/dist/coding-tools/run-code.js +198 -15
- package/dist/coding-tools/run-code.js.map +1 -1
- package/dist/extensions/fetch-tool.js +1 -1
- package/dist/extensions/fetch-tool.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +1 -0
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/builtin-tools.d.ts +8 -4
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +247 -13
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -1
- package/dist/provider-api/actions/query-staged-dataset.js +1 -0
- package/dist/provider-api/actions/query-staged-dataset.js.map +1 -1
- package/dist/provider-api/index.d.ts +9 -4
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +164 -33
- package/dist/provider-api/index.js.map +1 -1
- package/dist/provider-api/staged-datasets-store.d.ts.map +1 -1
- package/dist/provider-api/staged-datasets-store.js +29 -6
- package/dist/provider-api/staged-datasets-store.js.map +1 -1
- package/dist/provider-api/staging.d.ts +6 -1
- package/dist/provider-api/staging.d.ts.map +1 -1
- package/dist/provider-api/staging.js +35 -6
- package/dist/provider-api/staging.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +157 -80
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/prompts/shared-rules.d.ts +1 -1
- package/dist/server/prompts/shared-rules.d.ts.map +1 -1
- package/dist/server/prompts/shared-rules.js +5 -7
- package/dist/server/prompts/shared-rules.js.map +1 -1
- package/dist/server/schema-prompt.js +1 -1
- package/dist/server/schema-prompt.js.map +1 -1
- package/dist/templates/default/.agents/skills/actions/SKILL.md +16 -4
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +16 -4
- package/package.json +1 -1
- package/src/templates/default/.agents/skills/actions/SKILL.md +16 -4
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +16 -4
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* have named providers pass their own list via AgentChatPluginOptions.promptExamples.
|
|
12
12
|
*/
|
|
13
13
|
export interface PromptExamples {
|
|
14
|
-
/** Named external provider actions accessible from the agent (e.g. ["
|
|
14
|
+
/** Named external provider actions accessible from the agent (e.g. ["provider-search", "warehouse-query"]). */
|
|
15
15
|
providerActions?: string[];
|
|
16
16
|
/** Named template-specific actions to cite as examples (e.g. ["log-meal", "update-form"]). */
|
|
17
17
|
appActions?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-rules.d.ts","sourceRoot":"","sources":["../../../src/server/prompts/shared-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B
|
|
1
|
+
{"version":3,"file":"shared-rules.d.ts","sourceRoot":"","sources":["../../../src/server/prompts/shared-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,+GAA+G;IAC/G,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,8FAA8F;IAC9F,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAQD,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAsB7D;AAED,wDAAwD;AACxD,eAAO,MAAM,aAAa,4gCAAogC,CAAC;AAE/hC,mEAAmE;AACnE,eAAO,MAAM,cAAc,8cAAqc,CAAC;AAEje,qFAAqF;AACrF,eAAO,MAAM,cAAc,i4CAQgH,CAAC;AAE5I,4EAA4E;AAC5E,eAAO,MAAM,cAAc,qyBAAwxB,CAAC"}
|
|
@@ -7,12 +7,10 @@
|
|
|
7
7
|
* verbatim in both prompts — keep them here.
|
|
8
8
|
*/
|
|
9
9
|
const DEFAULT_PROVIDER_ACTIONS = [
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"jira-search",
|
|
15
|
-
"pylon-issues",
|
|
10
|
+
"provider-search",
|
|
11
|
+
"provider-records",
|
|
12
|
+
"warehouse-query",
|
|
13
|
+
"provider-api-request",
|
|
16
14
|
];
|
|
17
15
|
/** Rule 8 — db-* tools are internal only (shared between full and compact). */
|
|
18
16
|
export function sharedRule8(examples) {
|
|
@@ -33,7 +31,7 @@ export function sharedRule8(examples) {
|
|
|
33
31
|
.slice(0, 3)
|
|
34
32
|
.map((s) => s.trim())
|
|
35
33
|
.join(", ")
|
|
36
|
-
: "external data sources"}, or any external data source. If the user asks about a table that is NOT in the app schema (e.g. \`dbt_analytics.*\`, \`dbt_mart.*\`, or any fully-qualified \`project.dataset.table\`), use the appropriate template action instead — ${warehouseExample}${providerExamples ? `${providerExamples} for their respective providers, ` : ""}etc. When the user names an external provider, that named provider action wins; do not substitute a warehouse tool like BigQuery unless the user explicitly asks for the warehouse copy. **Never use \`db-query\` for external data — it will fail.** For extensions, use \`get-extension\` when you already have an id from \`<current-screen>\` or \`<current-url>\`; otherwise use \`list-extensions\`, \`update-extension\`, \`hide-extension\`, and \`delete-extension\`. Do not query the legacy \`tools\` table directly.`;
|
|
34
|
+
: "external data sources"}, or any external data source. If the user asks about a table that is NOT in the app schema (e.g. \`dbt_analytics.*\`, \`dbt_mart.*\`, or any fully-qualified \`project.dataset.table\`), use the appropriate template action instead — ${warehouseExample}${providerExamples ? `${providerExamples} for their respective providers, ` : ""}etc. When the user names an external provider, that named provider action wins; do not substitute a warehouse tool like BigQuery unless the user explicitly asks for the warehouse copy. **Never use \`db-query\` for external data — it will fail.** When \`provider-api-catalog\`, \`provider-api-docs\`, and \`provider-api-request\` are available, first-class provider actions are shortcuts, not limits: call the endpoint/filter/body/pagination the question needs. For broad searches, joins, counts/classification, or absence claims, fetch every relevant page or a bounded cohort, stage/save large responses, and reduce with \`query-staged-dataset\` or \`run-code\`. Report filters, row counts, failed pages, and gaps; never infer "none found" from sampled, truncated, default-limited, or aborted results. For extensions, use \`get-extension\` when you already have an id from \`<current-screen>\` or \`<current-url>\`; otherwise use \`list-extensions\`, \`update-extension\`, \`hide-extension\`, and \`delete-extension\`. Do not query the legacy \`tools\` table directly.`;
|
|
37
35
|
}
|
|
38
36
|
/** Rule 9 — Never fabricate factual claims (shared). */
|
|
39
37
|
export const SHARED_RULE_9 = `9. **Never fabricate factual claims or records** — Do NOT invent numbers, metrics, records, query results, URLs, citations, source attributions, customer names, dates, or success rates. This applies inside generated artifacts too: decks, documents, reports, dashboards, Slack/email replies, and charts must not contain unsupported factual specifics. Only state factual numbers/claims when the user provided them or you retrieved them with an action/tool. If a data source is unavailable, returns no rows, is missing credentials, or has a connection error, say so clearly; do not create placeholder rows or fetch unrelated external providers to make the answer look complete unless the user explicitly asked you to import/sync/backfill. If a specific metric would be useful but is not known, use qualitative wording, placeholders like \`[metric TBD]\`, or clearly labeled draft assumptions instead of plausible-looking facts. Presenting made-up data as real is a critical failure — it is worse than admitting the limitation.`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-rules.js","sourceRoot":"","sources":["../../../src/server/prompts/shared-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,MAAM,wBAAwB,GAAG;IAC/B,
|
|
1
|
+
{"version":3,"file":"shared-rules.js","sourceRoot":"","sources":["../../../src/server/prompts/shared-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,MAAM,wBAAwB,GAAG;IAC/B,iBAAiB;IACjB,kBAAkB;IAClB,iBAAiB;IACjB,sBAAsB;CACvB,CAAC;AACF,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,QAAyB;IACnD,MAAM,SAAS,GAAG,QAAQ,EAAE,eAAe,IAAI,wBAAwB,CAAC;IACxE,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,2DAA2D;IAC3D,MAAM,gBAAgB,GAAG,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC;QACrD,CAAC,CAAC,mCAAmC;QACrC,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,gBAAgB,GAAG,SAAS;SAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC;SAC/B,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SACtB,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO,8LACL,YAAY,CAAC,MAAM,GAAG,CAAC;QACrB,CAAC,CAAC,YAAY;aACT,KAAK,CAAC,GAAG,CAAC;aACV,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,IAAI,CAAC,IAAI,CAAC;QACf,CAAC,CAAC,uBACN,2OAA2O,gBAAgB,GAAG,gBAAgB,CAAC,CAAC,CAAC,GAAG,gBAAgB,mCAAmC,CAAC,CAAC,CAAC,EAAE,8iCAA8iC,CAAC;AAC73C,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,MAAM,aAAa,GAAG,igCAAigC,CAAC;AAE/hC,mEAAmE;AACnE,MAAM,CAAC,MAAM,cAAc,GAAG,kcAAkc,CAAC;AAEje,qFAAqF;AACrF,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;2IAQ6G,CAAC;AAE5I,4EAA4E;AAC5E,MAAM,CAAC,MAAM,cAAc,GAAG,qxBAAqxB,CAAC","sourcesContent":["/**\n * Shared rule text used in both FRAMEWORK_CORE (full) and FRAMEWORK_CORE_COMPACT.\n * Single source of truth so the two variants can't drift on rules that are\n * identical between them.\n *\n * Rules 8–10 (db-* tools, no fabrication, no false success) are reproduced\n * verbatim in both prompts — keep them here.\n */\n\n/**\n * Injectable provider/action examples. Defaults are generic; templates that\n * have named providers pass their own list via AgentChatPluginOptions.promptExamples.\n */\nexport interface PromptExamples {\n /** Named external provider actions accessible from the agent (e.g. [\"provider-search\", \"warehouse-query\"]). */\n providerActions?: string[];\n /** Named template-specific actions to cite as examples (e.g. [\"log-meal\", \"update-form\"]). */\n appActions?: string[];\n}\n\nconst DEFAULT_PROVIDER_ACTIONS = [\n \"provider-search\",\n \"provider-records\",\n \"warehouse-query\",\n \"provider-api-request\",\n];\n/** Rule 8 — db-* tools are internal only (shared between full and compact). */\nexport function sharedRule8(examples?: PromptExamples): string {\n const providers = examples?.providerActions ?? DEFAULT_PROVIDER_ACTIONS;\n const providerList = providers.join(\", \");\n // Build the \"e.g.\" clause for warehouse vs. named provider\n const warehouseExample = providers.includes(\"bigquery\")\n ? \"`bigquery` for warehouse tables, \"\n : \"\";\n const providerExamples = providers\n .filter((p) => p !== \"bigquery\")\n .slice(0, 4)\n .map((p) => `\\`${p}\\``)\n .join(\", \");\n\n return `8. **\\`db-*\\` tools are internal only** — \\`db-query\\`, \\`db-exec\\`, \\`db-patch\\` ONLY access the app's own SQL database (settings, application_state, template tables). They CANNOT reach ${\n providerList.length > 0\n ? providerList\n .split(\",\")\n .slice(0, 3)\n .map((s) => s.trim())\n .join(\", \")\n : \"external data sources\"\n }, or any external data source. If the user asks about a table that is NOT in the app schema (e.g. \\`dbt_analytics.*\\`, \\`dbt_mart.*\\`, or any fully-qualified \\`project.dataset.table\\`), use the appropriate template action instead — ${warehouseExample}${providerExamples ? `${providerExamples} for their respective providers, ` : \"\"}etc. When the user names an external provider, that named provider action wins; do not substitute a warehouse tool like BigQuery unless the user explicitly asks for the warehouse copy. **Never use \\`db-query\\` for external data — it will fail.** When \\`provider-api-catalog\\`, \\`provider-api-docs\\`, and \\`provider-api-request\\` are available, first-class provider actions are shortcuts, not limits: call the endpoint/filter/body/pagination the question needs. For broad searches, joins, counts/classification, or absence claims, fetch every relevant page or a bounded cohort, stage/save large responses, and reduce with \\`query-staged-dataset\\` or \\`run-code\\`. Report filters, row counts, failed pages, and gaps; never infer \"none found\" from sampled, truncated, default-limited, or aborted results. For extensions, use \\`get-extension\\` when you already have an id from \\`<current-screen>\\` or \\`<current-url>\\`; otherwise use \\`list-extensions\\`, \\`update-extension\\`, \\`hide-extension\\`, and \\`delete-extension\\`. Do not query the legacy \\`tools\\` table directly.`;\n}\n\n/** Rule 9 — Never fabricate factual claims (shared). */\nexport const SHARED_RULE_9 = `9. **Never fabricate factual claims or records** — Do NOT invent numbers, metrics, records, query results, URLs, citations, source attributions, customer names, dates, or success rates. This applies inside generated artifacts too: decks, documents, reports, dashboards, Slack/email replies, and charts must not contain unsupported factual specifics. Only state factual numbers/claims when the user provided them or you retrieved them with an action/tool. If a data source is unavailable, returns no rows, is missing credentials, or has a connection error, say so clearly; do not create placeholder rows or fetch unrelated external providers to make the answer look complete unless the user explicitly asked you to import/sync/backfill. If a specific metric would be useful but is not known, use qualitative wording, placeholders like \\`[metric TBD]\\`, or clearly labeled draft assumptions instead of plausible-looking facts. Presenting made-up data as real is a critical failure — it is worse than admitting the limitation.`;\n\n/** Rule 10 — Never fabricate success from tool errors (shared). */\nexport const SHARED_RULE_10 = `10. **Never fabricate success from tool errors** — When any tool call returns an error (marked \\`isError: true\\`, contains \"Command failed\", \"Error:\", or non-zero exit output), the operation FAILED. Do NOT synthesize a success narrative or describe what the action \"would have\" produced. Report the failure verbatim from the tool output. This applies especially to \\`bash(command=\"pnpm action ...\")\\` calls: if the action threw, it did NOT succeed.`;\n\n/** Rule 14 — Planning and progress (adapted from Codex's update_plan discipline). */\nexport const SHARED_RULE_14 = `14. **Plan and track multi-step work** — For non-trivial tasks that span several actions or phases, use \\`manage-progress\\` to make work visible and keep it on track.\n\n - Call \\`manage-progress\\` with \\`action: \"start\"\\` at the beginning of multi-step work; include a descriptive \\`title\\` and the first \\`step\\`.\n - Update with \\`action: \"update\"\\` after each meaningful milestone — include \\`step\\` (what you just did or are doing now) and \\`percent\\` when there is a known upper bound. Do not batch-complete multiple steps after the fact; update as you go.\n - Exactly one logical task should be \\`in_progress\\` at a time within a turn. Finish (or explicitly complete/cancel) a run before starting an unrelated one.\n - Mark done with \\`action: \"complete\"\\` and \\`status: \"succeeded\"\\` (or \"failed\"/\"cancelled\") as the last step. Never leave a run open indefinitely.\n - **Skip for trivial work**: single-action lookups, simple reads, one-line answers, and any task that finishes in one tool call do not need a progress run. Plans add value only when there are multiple real steps the user would want to watch.\n - Never create single-step plans — if everything fits in one \\`start\\`+\\`complete\\`, just call the action and report the outcome directly.\n - If the task pivots mid-run (unexpected blocker, scope change), update the current step to reflect the new direction before continuing.`;\n\n/** Rule 15 — Collaborate through uncertainty (better-specified version). */\nexport const SHARED_RULE_15 = `15. **Collaborate through uncertainty** — If a task stalls, errors, or depends on setup the user may not know about, shift into builder-coach mode instead of repeating the same attempt. State what you verified, name the most likely next checks, and proactively try common unblockers you can inspect (for example prompt size, missing environment variables, unavailable connections, current screen state, or tool choice). When you finish a meaningful step, offer one or two concrete next steps or improvements so non-technical users can keep iterating. When you are genuinely blocked on a decision you cannot resolve from context — and a wrong guess would be costly — use \\`ask-question\\` to present the choice instead of guessing; otherwise prefer a reasonable assumption and keep moving.`;\n"]}
|
|
@@ -257,7 +257,7 @@ export async function loadSchemaPromptBlock(opts) {
|
|
|
257
257
|
lines.push("");
|
|
258
258
|
lines.push("### External data sources vs the app database");
|
|
259
259
|
lines.push("The `db-*` tools ONLY query the app's own SQL database (the tables listed above). They do NOT reach external data warehouses, analytics platforms, or third-party services.");
|
|
260
|
-
lines.push("If the user asks about tables that are NOT in the schema above, use the
|
|
260
|
+
lines.push("If the user asks about tables that are NOT in the schema above, use the relevant provider, warehouse, MCP, or template action listed in your available tools. When provider-api-catalog/provider-api-docs/provider-api-request are available, use them for provider endpoints or filters that no first-class shortcut models.");
|
|
261
261
|
lines.push("**Never use `db-query` for external data.** It will fail because those tables don't exist in the app database.");
|
|
262
262
|
lines.push("");
|
|
263
263
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-prompt.js","sourceRoot":"","sources":["../../src/server/schema-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Auto-introspected SQL schema context block for the agent's system prompt.\n *\n * On every chat turn, the framework appends a compact, always-fresh summary\n * of the app's SQL database — every table, every column, every foreign key —\n * so the agent knows exactly what data model it's working with. The schema\n * is pulled live from `information_schema` (Postgres) or `PRAGMA table_info`\n * (SQLite), cached briefly to keep latency down but never hard-coded.\n *\n * The block also:\n * - points at the db-query / db-exec / db-patch / db-schema tools for runtime access\n * - lists Postgres column descriptions (`COMMENT ON COLUMN ...`) if present\n * - explains the current user/org data scoping so the agent doesn't re-filter\n * by hand (which would be redundant and easy to get wrong)\n */\nimport {\n getDbExec,\n getDatabaseUrl,\n isPostgres,\n type DbExec,\n} from \"../db/client.js\";\n\ninterface ColumnSchema {\n name: string;\n type: string;\n notnull: boolean;\n pk: boolean;\n comment: string | null;\n}\n\ninterface ForeignKey {\n from: string;\n table: string;\n to: string;\n}\n\ninterface TableSchema {\n name: string;\n columns: ColumnSchema[];\n foreignKeys: ForeignKey[];\n comment: string | null;\n}\n\n// Short-lived in-memory cache — schema rarely changes between messages, but\n// we want new tables to show up within a few seconds during active dev.\nconst CACHE_TTL_MS = 15_000;\nlet _cache: {\n key: string;\n expires: number;\n tables: TableSchema[];\n dialect: \"postgres\" | \"sqlite\";\n} | null = null;\n\nfunction cacheKey(): string {\n return (isPostgres() ? \"pg:\" : \"lite:\") + (getDatabaseUrl() || \"\");\n}\n\n// ─── Postgres introspection ─────────────────────────────────────────────────\n\nasync function introspectPostgres(db: DbExec): Promise<TableSchema[]> {\n const tablesRes = await db.execute({\n sql: `SELECT table_name AS name,\n obj_description((quote_ident(table_schema) || '.' || quote_ident(table_name))::regclass, 'pg_class') AS comment\n FROM information_schema.tables\n WHERE table_schema = 'public' AND table_type = 'BASE TABLE'\n ORDER BY table_name`,\n args: [],\n });\n\n const tables: TableSchema[] = [];\n\n for (const t of tablesRes.rows as any[]) {\n const name = t.name as string;\n\n const colsRes = await db.execute({\n sql: `SELECT c.column_name AS name,\n c.data_type AS type,\n CASE WHEN c.is_nullable = 'NO' THEN 1 ELSE 0 END AS notnull,\n col_description((quote_ident(c.table_schema) || '.' || quote_ident(c.table_name))::regclass, c.ordinal_position) AS comment\n FROM information_schema.columns c\n WHERE c.table_name = ? AND c.table_schema = 'public'\n ORDER BY c.ordinal_position`,\n args: [name],\n });\n\n const pksRes = await db.execute({\n sql: `SELECT kcu.column_name AS name\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n WHERE tc.table_name = ? AND tc.constraint_type = 'PRIMARY KEY'`,\n args: [name],\n });\n const pkSet = new Set((pksRes.rows as any[]).map((r) => r.name as string));\n\n const fksRes = await db.execute({\n sql: `SELECT kcu.column_name AS col_from,\n ccu.table_name AS ref_table,\n ccu.column_name AS ref_col\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n JOIN information_schema.constraint_column_usage ccu\n ON tc.constraint_name = ccu.constraint_name\n WHERE tc.table_name = ? AND tc.constraint_type = 'FOREIGN KEY'`,\n args: [name],\n });\n\n tables.push({\n name,\n comment: (t.comment as string | null) ?? null,\n columns: (colsRes.rows as any[]).map((c) => ({\n name: c.name as string,\n type: (c.type as string) || \"any\",\n notnull: Number(c.notnull) === 1,\n pk: pkSet.has(c.name as string),\n comment: (c.comment as string | null) ?? null,\n })),\n foreignKeys: (fksRes.rows as any[]).map((f) => ({\n from: f.col_from as string,\n table: f.ref_table as string,\n to: f.ref_col as string,\n })),\n });\n }\n\n return tables;\n}\n\n// ─── SQLite / libSQL / D1 introspection ────────────────────────────────────\n\nasync function introspectSqlite(db: DbExec): Promise<TableSchema[]> {\n const tablesRes = await db.execute(\n `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`,\n );\n\n const tables: TableSchema[] = [];\n\n for (const row of tablesRes.rows as any[]) {\n const name = row.name as string;\n if (!name) continue;\n // Quote the identifier for PRAGMA calls; SQLite requires doubling embedded quotes.\n const escaped = name.replace(/\"/g, '\"\"');\n\n const colsRes = await db.execute(`PRAGMA table_info(\"${escaped}\")`);\n const fksRes = await db.execute(`PRAGMA foreign_key_list(\"${escaped}\")`);\n\n tables.push({\n name,\n comment: null, // SQLite has no column/table comments\n columns: (colsRes.rows as any[]).map((c) => ({\n name: c.name as string,\n type: ((c.type as string) || \"\").toLowerCase() || \"any\",\n notnull: Number(c.notnull) === 1,\n pk: Number(c.pk) === 1,\n comment: null,\n })),\n foreignKeys: (fksRes.rows as any[]).map((f) => ({\n from: f.from as string,\n table: f.table as string,\n to: f.to as string,\n })),\n });\n }\n\n return tables;\n}\n\n// ─── Cached entry point ─────────────────────────────────────────────────────\n\nasync function getSchema(): Promise<{\n tables: TableSchema[];\n dialect: \"postgres\" | \"sqlite\";\n}> {\n const key = cacheKey();\n const now = Date.now();\n if (_cache && _cache.key === key && _cache.expires > now) {\n return { tables: _cache.tables, dialect: _cache.dialect };\n }\n\n const db = getDbExec();\n const dialect: \"postgres\" | \"sqlite\" = isPostgres() ? \"postgres\" : \"sqlite\";\n const tables =\n dialect === \"postgres\"\n ? await introspectPostgres(db)\n : await introspectSqlite(db);\n\n _cache = { key, expires: now + CACHE_TTL_MS, tables, dialect };\n return { tables, dialect };\n}\n\n/** Manually drop the cache — useful from tests or after running a migration. */\nexport function invalidateSchemaPromptCache(): void {\n _cache = null;\n}\n\n// ─── Formatting ─────────────────────────────────────────────────────────────\n\nfunction shortType(type: string): string {\n // Trim verbose Postgres type names for compactness in the prompt.\n const t = type.toLowerCase();\n if (t === \"character varying\") return \"varchar\";\n if (t === \"timestamp without time zone\") return \"timestamp\";\n if (t === \"timestamp with time zone\") return \"timestamptz\";\n if (t === \"double precision\") return \"double\";\n return t;\n}\n\nfunction formatTable(table: TableSchema): string {\n const fkByCol = new Map<string, string>();\n for (const fk of table.foreignKeys) {\n fkByCol.set(fk.from, `${fk.table}.${fk.to}`);\n }\n\n const cols = table.columns.map((c) => {\n const flags: string[] = [];\n if (c.pk) flags.push(\"pk\");\n if (!c.notnull && !c.pk) flags.push(\"null\");\n const fk = fkByCol.get(c.name);\n if (fk) flags.push(`→${fk}`);\n\n // Flag scoping columns so the agent understands per-user/per-org filtering.\n if (c.name === \"owner_email\") flags.push(\"user-scope\");\n if (c.name === \"org_id\") flags.push(\"org-scope\");\n\n const flagStr = flags.length ? ` [${flags.join(\", \")}]` : \"\";\n const commentStr = c.comment ? ` -- ${c.comment.replace(/\\s+/g, \" \")}` : \"\";\n return ` ${c.name} ${shortType(c.type)}${flagStr}${commentStr}`;\n });\n\n const header = table.comment\n ? ` ${table.name} -- ${table.comment.replace(/\\s+/g, \" \")}`\n : ` ${table.name}`;\n\n return [header, ...cols].join(\"\\n\");\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Build the `<sql-database>` block appended to the system prompt on every turn.\n *\n * `owner` and `orgId` come from the per-request context (AGENT_USER_EMAIL /\n * AGENT_ORG_ID) and are surfaced so the agent knows who it is acting on behalf\n * of — and understands that rows are already filtered for that identity.\n */\nexport async function loadSchemaPromptBlock(opts: {\n owner?: string | null;\n orgId?: string | null;\n /** If true, mention db-query/db-exec/db-patch/db-schema as available tools. */\n hasRawDbTools?: boolean;\n}): Promise<string> {\n let tables: TableSchema[];\n let dialect: \"postgres\" | \"sqlite\";\n try {\n const res = await getSchema();\n tables = res.tables;\n dialect = res.dialect;\n } catch {\n // DB not ready, or introspection blew up — don't take the chat down.\n return \"\";\n }\n\n if (tables.length === 0) return \"\";\n\n // Partition framework-internal tables from template tables so the agent\n // focuses on the data model it's most likely to touch.\n const CORE_TABLES = new Set([\n \"application_state\",\n \"settings\",\n \"oauth_tokens\",\n \"sessions\",\n \"resources\",\n \"chat_threads\",\n \"_collab_docs\",\n \"usage_events\",\n \"usage_totals\",\n \"user\",\n \"account\",\n \"verification\",\n \"organization\",\n \"member\",\n \"invitation\",\n ]);\n\n const templateTables = tables.filter((t) => !CORE_TABLES.has(t.name));\n const coreTables = tables.filter((t) => CORE_TABLES.has(t.name));\n\n const lines: string[] = [];\n lines.push(\"<sql-database>\");\n lines.push(\n `The app's state lives in a SQL database (${dialect}). The schema below is auto-introspected fresh each turn — treat it as authoritative.`,\n );\n lines.push(\"\");\n\n if (templateTables.length > 0) {\n lines.push(\"## Template tables\");\n lines.push(\"\");\n for (const t of templateTables) {\n lines.push(formatTable(t));\n lines.push(\"\");\n }\n }\n\n if (coreTables.length > 0) {\n lines.push(\n \"## Framework tables (auth, resources, chat threads, app-state, etc.) — usually read/written via dedicated tools, not raw SQL\",\n );\n lines.push(\"\");\n for (const t of coreTables) {\n lines.push(formatTable(t));\n lines.push(\"\");\n }\n }\n\n // Tooling references.\n if (opts.hasRawDbTools) {\n lines.push(\"## SQL tools\");\n lines.push(\n \"- `db-schema` — refresh the full schema with indexes and foreign keys\",\n );\n lines.push(\n \"- `db-query` — run a SELECT (read-only; results already filtered to the current user/org)\",\n );\n lines.push(\n \"- `db-exec` — run INSERT / UPDATE / DELETE / REPLACE (writes already scoped; owner_email and org_id are auto-injected on INSERT). For multiple related writes, pass `statements` so they run in one transaction instead of separate tool calls. Schema changes are blocked.\",\n );\n lines.push(\n \"- `db-patch` — surgical search-and-replace on a large text column. Send `{find, replace}` pairs instead of the full new value. Use this for edits to large fields (documents, slide HTML, dashboard/form JSON) — it avoids re-sending multi-kilobyte strings and saves tokens. Targets exactly one row (narrow `--where` by primary key). Uses the same per-user/per-org scoping as db-exec.\",\n );\n lines.push(\"\");\n lines.push(\"### When to pick which SQL tool\");\n lines.push(\n \"- Set a short column outright, update multiple columns, or do computed updates (`calories = calories + 50`) → `db-exec UPDATE`.\",\n );\n lines.push(\n '- Insert/update several rows as one logical operation → `db-exec` with `statements: \\'[{\"sql\":\"...\",\"args\":[...]}]\\'` so the batch commits or rolls back together.',\n );\n lines.push(\n \"- Change a small slice of a large text/JSON column → `db-patch`. Much cheaper token-wise than re-sending the whole column.\",\n );\n lines.push(\n \"- A template-specific action exists for the table (`edit-document`, `update-slide`, etc.) → use that action. It encodes business rules and pushes live Yjs updates to any open collaborative editor; raw SQL does neither.\",\n );\n lines.push(\n \"- Read data → `db-query`. Never re-add `WHERE owner_email = ...` — scoping already applies it.\",\n );\n lines.push(\"\");\n lines.push(\"### External data sources vs the app database\");\n lines.push(\n \"The `db-*` tools ONLY query the app's own SQL database (the tables listed above). They do NOT reach external data warehouses, analytics platforms, or third-party services.\",\n );\n lines.push(\n \"If the user asks about tables that are NOT in the schema above, use the appropriate template action instead — for example `bigquery` for BigQuery warehouse tables, `ga4-report` for Google Analytics, `hubspot-deals` for HubSpot, etc. Check your available actions for the right data-source-specific tool.\",\n );\n lines.push(\n \"**Never use `db-query` for external data.** It will fail because those tables don't exist in the app database.\",\n );\n lines.push(\"\");\n } else {\n lines.push(\n \"SQL is accessed through the template actions listed above. The schema is shown for context — so you understand the data model those actions operate on.\",\n );\n lines.push(\"\");\n }\n\n // Data scoping context.\n const ownerLine = opts.owner ? opts.owner : \"(unresolved)\";\n const orgLine = opts.orgId ? opts.orgId : \"(none)\";\n lines.push(\"## Data scoping (enforced at the SQL layer)\");\n lines.push(`- Current user: \\`${ownerLine}\\``);\n lines.push(`- Current org: \\`${orgLine}\\``);\n lines.push(\n \"- Tables with an `owner_email` column are automatically filtered to the current user via temporary views before every query.\",\n );\n lines.push(\n \"- Tables with an `org_id` column are automatically filtered to the current org as well.\",\n );\n lines.push(\n \"- On INSERT, `owner_email` and `org_id` are auto-injected — do NOT set them manually.\",\n );\n lines.push(\n \"- Do NOT add `WHERE owner_email = ...` or `WHERE org_id = ...` to your queries — the filter is already applied, and re-adding it will confuse the scoped view.\",\n );\n lines.push(\"</sql-database>\");\n\n return \"\\n\\n\" + lines.join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"schema-prompt.js","sourceRoot":"","sources":["../../src/server/schema-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Auto-introspected SQL schema context block for the agent's system prompt.\n *\n * On every chat turn, the framework appends a compact, always-fresh summary\n * of the app's SQL database — every table, every column, every foreign key —\n * so the agent knows exactly what data model it's working with. The schema\n * is pulled live from `information_schema` (Postgres) or `PRAGMA table_info`\n * (SQLite), cached briefly to keep latency down but never hard-coded.\n *\n * The block also:\n * - points at the db-query / db-exec / db-patch / db-schema tools for runtime access\n * - lists Postgres column descriptions (`COMMENT ON COLUMN ...`) if present\n * - explains the current user/org data scoping so the agent doesn't re-filter\n * by hand (which would be redundant and easy to get wrong)\n */\nimport {\n getDbExec,\n getDatabaseUrl,\n isPostgres,\n type DbExec,\n} from \"../db/client.js\";\n\ninterface ColumnSchema {\n name: string;\n type: string;\n notnull: boolean;\n pk: boolean;\n comment: string | null;\n}\n\ninterface ForeignKey {\n from: string;\n table: string;\n to: string;\n}\n\ninterface TableSchema {\n name: string;\n columns: ColumnSchema[];\n foreignKeys: ForeignKey[];\n comment: string | null;\n}\n\n// Short-lived in-memory cache — schema rarely changes between messages, but\n// we want new tables to show up within a few seconds during active dev.\nconst CACHE_TTL_MS = 15_000;\nlet _cache: {\n key: string;\n expires: number;\n tables: TableSchema[];\n dialect: \"postgres\" | \"sqlite\";\n} | null = null;\n\nfunction cacheKey(): string {\n return (isPostgres() ? \"pg:\" : \"lite:\") + (getDatabaseUrl() || \"\");\n}\n\n// ─── Postgres introspection ─────────────────────────────────────────────────\n\nasync function introspectPostgres(db: DbExec): Promise<TableSchema[]> {\n const tablesRes = await db.execute({\n sql: `SELECT table_name AS name,\n obj_description((quote_ident(table_schema) || '.' || quote_ident(table_name))::regclass, 'pg_class') AS comment\n FROM information_schema.tables\n WHERE table_schema = 'public' AND table_type = 'BASE TABLE'\n ORDER BY table_name`,\n args: [],\n });\n\n const tables: TableSchema[] = [];\n\n for (const t of tablesRes.rows as any[]) {\n const name = t.name as string;\n\n const colsRes = await db.execute({\n sql: `SELECT c.column_name AS name,\n c.data_type AS type,\n CASE WHEN c.is_nullable = 'NO' THEN 1 ELSE 0 END AS notnull,\n col_description((quote_ident(c.table_schema) || '.' || quote_ident(c.table_name))::regclass, c.ordinal_position) AS comment\n FROM information_schema.columns c\n WHERE c.table_name = ? AND c.table_schema = 'public'\n ORDER BY c.ordinal_position`,\n args: [name],\n });\n\n const pksRes = await db.execute({\n sql: `SELECT kcu.column_name AS name\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n WHERE tc.table_name = ? AND tc.constraint_type = 'PRIMARY KEY'`,\n args: [name],\n });\n const pkSet = new Set((pksRes.rows as any[]).map((r) => r.name as string));\n\n const fksRes = await db.execute({\n sql: `SELECT kcu.column_name AS col_from,\n ccu.table_name AS ref_table,\n ccu.column_name AS ref_col\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n JOIN information_schema.constraint_column_usage ccu\n ON tc.constraint_name = ccu.constraint_name\n WHERE tc.table_name = ? AND tc.constraint_type = 'FOREIGN KEY'`,\n args: [name],\n });\n\n tables.push({\n name,\n comment: (t.comment as string | null) ?? null,\n columns: (colsRes.rows as any[]).map((c) => ({\n name: c.name as string,\n type: (c.type as string) || \"any\",\n notnull: Number(c.notnull) === 1,\n pk: pkSet.has(c.name as string),\n comment: (c.comment as string | null) ?? null,\n })),\n foreignKeys: (fksRes.rows as any[]).map((f) => ({\n from: f.col_from as string,\n table: f.ref_table as string,\n to: f.ref_col as string,\n })),\n });\n }\n\n return tables;\n}\n\n// ─── SQLite / libSQL / D1 introspection ────────────────────────────────────\n\nasync function introspectSqlite(db: DbExec): Promise<TableSchema[]> {\n const tablesRes = await db.execute(\n `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`,\n );\n\n const tables: TableSchema[] = [];\n\n for (const row of tablesRes.rows as any[]) {\n const name = row.name as string;\n if (!name) continue;\n // Quote the identifier for PRAGMA calls; SQLite requires doubling embedded quotes.\n const escaped = name.replace(/\"/g, '\"\"');\n\n const colsRes = await db.execute(`PRAGMA table_info(\"${escaped}\")`);\n const fksRes = await db.execute(`PRAGMA foreign_key_list(\"${escaped}\")`);\n\n tables.push({\n name,\n comment: null, // SQLite has no column/table comments\n columns: (colsRes.rows as any[]).map((c) => ({\n name: c.name as string,\n type: ((c.type as string) || \"\").toLowerCase() || \"any\",\n notnull: Number(c.notnull) === 1,\n pk: Number(c.pk) === 1,\n comment: null,\n })),\n foreignKeys: (fksRes.rows as any[]).map((f) => ({\n from: f.from as string,\n table: f.table as string,\n to: f.to as string,\n })),\n });\n }\n\n return tables;\n}\n\n// ─── Cached entry point ─────────────────────────────────────────────────────\n\nasync function getSchema(): Promise<{\n tables: TableSchema[];\n dialect: \"postgres\" | \"sqlite\";\n}> {\n const key = cacheKey();\n const now = Date.now();\n if (_cache && _cache.key === key && _cache.expires > now) {\n return { tables: _cache.tables, dialect: _cache.dialect };\n }\n\n const db = getDbExec();\n const dialect: \"postgres\" | \"sqlite\" = isPostgres() ? \"postgres\" : \"sqlite\";\n const tables =\n dialect === \"postgres\"\n ? await introspectPostgres(db)\n : await introspectSqlite(db);\n\n _cache = { key, expires: now + CACHE_TTL_MS, tables, dialect };\n return { tables, dialect };\n}\n\n/** Manually drop the cache — useful from tests or after running a migration. */\nexport function invalidateSchemaPromptCache(): void {\n _cache = null;\n}\n\n// ─── Formatting ─────────────────────────────────────────────────────────────\n\nfunction shortType(type: string): string {\n // Trim verbose Postgres type names for compactness in the prompt.\n const t = type.toLowerCase();\n if (t === \"character varying\") return \"varchar\";\n if (t === \"timestamp without time zone\") return \"timestamp\";\n if (t === \"timestamp with time zone\") return \"timestamptz\";\n if (t === \"double precision\") return \"double\";\n return t;\n}\n\nfunction formatTable(table: TableSchema): string {\n const fkByCol = new Map<string, string>();\n for (const fk of table.foreignKeys) {\n fkByCol.set(fk.from, `${fk.table}.${fk.to}`);\n }\n\n const cols = table.columns.map((c) => {\n const flags: string[] = [];\n if (c.pk) flags.push(\"pk\");\n if (!c.notnull && !c.pk) flags.push(\"null\");\n const fk = fkByCol.get(c.name);\n if (fk) flags.push(`→${fk}`);\n\n // Flag scoping columns so the agent understands per-user/per-org filtering.\n if (c.name === \"owner_email\") flags.push(\"user-scope\");\n if (c.name === \"org_id\") flags.push(\"org-scope\");\n\n const flagStr = flags.length ? ` [${flags.join(\", \")}]` : \"\";\n const commentStr = c.comment ? ` -- ${c.comment.replace(/\\s+/g, \" \")}` : \"\";\n return ` ${c.name} ${shortType(c.type)}${flagStr}${commentStr}`;\n });\n\n const header = table.comment\n ? ` ${table.name} -- ${table.comment.replace(/\\s+/g, \" \")}`\n : ` ${table.name}`;\n\n return [header, ...cols].join(\"\\n\");\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Build the `<sql-database>` block appended to the system prompt on every turn.\n *\n * `owner` and `orgId` come from the per-request context (AGENT_USER_EMAIL /\n * AGENT_ORG_ID) and are surfaced so the agent knows who it is acting on behalf\n * of — and understands that rows are already filtered for that identity.\n */\nexport async function loadSchemaPromptBlock(opts: {\n owner?: string | null;\n orgId?: string | null;\n /** If true, mention db-query/db-exec/db-patch/db-schema as available tools. */\n hasRawDbTools?: boolean;\n}): Promise<string> {\n let tables: TableSchema[];\n let dialect: \"postgres\" | \"sqlite\";\n try {\n const res = await getSchema();\n tables = res.tables;\n dialect = res.dialect;\n } catch {\n // DB not ready, or introspection blew up — don't take the chat down.\n return \"\";\n }\n\n if (tables.length === 0) return \"\";\n\n // Partition framework-internal tables from template tables so the agent\n // focuses on the data model it's most likely to touch.\n const CORE_TABLES = new Set([\n \"application_state\",\n \"settings\",\n \"oauth_tokens\",\n \"sessions\",\n \"resources\",\n \"chat_threads\",\n \"_collab_docs\",\n \"usage_events\",\n \"usage_totals\",\n \"user\",\n \"account\",\n \"verification\",\n \"organization\",\n \"member\",\n \"invitation\",\n ]);\n\n const templateTables = tables.filter((t) => !CORE_TABLES.has(t.name));\n const coreTables = tables.filter((t) => CORE_TABLES.has(t.name));\n\n const lines: string[] = [];\n lines.push(\"<sql-database>\");\n lines.push(\n `The app's state lives in a SQL database (${dialect}). The schema below is auto-introspected fresh each turn — treat it as authoritative.`,\n );\n lines.push(\"\");\n\n if (templateTables.length > 0) {\n lines.push(\"## Template tables\");\n lines.push(\"\");\n for (const t of templateTables) {\n lines.push(formatTable(t));\n lines.push(\"\");\n }\n }\n\n if (coreTables.length > 0) {\n lines.push(\n \"## Framework tables (auth, resources, chat threads, app-state, etc.) — usually read/written via dedicated tools, not raw SQL\",\n );\n lines.push(\"\");\n for (const t of coreTables) {\n lines.push(formatTable(t));\n lines.push(\"\");\n }\n }\n\n // Tooling references.\n if (opts.hasRawDbTools) {\n lines.push(\"## SQL tools\");\n lines.push(\n \"- `db-schema` — refresh the full schema with indexes and foreign keys\",\n );\n lines.push(\n \"- `db-query` — run a SELECT (read-only; results already filtered to the current user/org)\",\n );\n lines.push(\n \"- `db-exec` — run INSERT / UPDATE / DELETE / REPLACE (writes already scoped; owner_email and org_id are auto-injected on INSERT). For multiple related writes, pass `statements` so they run in one transaction instead of separate tool calls. Schema changes are blocked.\",\n );\n lines.push(\n \"- `db-patch` — surgical search-and-replace on a large text column. Send `{find, replace}` pairs instead of the full new value. Use this for edits to large fields (documents, slide HTML, dashboard/form JSON) — it avoids re-sending multi-kilobyte strings and saves tokens. Targets exactly one row (narrow `--where` by primary key). Uses the same per-user/per-org scoping as db-exec.\",\n );\n lines.push(\"\");\n lines.push(\"### When to pick which SQL tool\");\n lines.push(\n \"- Set a short column outright, update multiple columns, or do computed updates (`calories = calories + 50`) → `db-exec UPDATE`.\",\n );\n lines.push(\n '- Insert/update several rows as one logical operation → `db-exec` with `statements: \\'[{\"sql\":\"...\",\"args\":[...]}]\\'` so the batch commits or rolls back together.',\n );\n lines.push(\n \"- Change a small slice of a large text/JSON column → `db-patch`. Much cheaper token-wise than re-sending the whole column.\",\n );\n lines.push(\n \"- A template-specific action exists for the table (`edit-document`, `update-slide`, etc.) → use that action. It encodes business rules and pushes live Yjs updates to any open collaborative editor; raw SQL does neither.\",\n );\n lines.push(\n \"- Read data → `db-query`. Never re-add `WHERE owner_email = ...` — scoping already applies it.\",\n );\n lines.push(\"\");\n lines.push(\"### External data sources vs the app database\");\n lines.push(\n \"The `db-*` tools ONLY query the app's own SQL database (the tables listed above). They do NOT reach external data warehouses, analytics platforms, or third-party services.\",\n );\n lines.push(\n \"If the user asks about tables that are NOT in the schema above, use the relevant provider, warehouse, MCP, or template action listed in your available tools. When provider-api-catalog/provider-api-docs/provider-api-request are available, use them for provider endpoints or filters that no first-class shortcut models.\",\n );\n lines.push(\n \"**Never use `db-query` for external data.** It will fail because those tables don't exist in the app database.\",\n );\n lines.push(\"\");\n } else {\n lines.push(\n \"SQL is accessed through the template actions listed above. The schema is shown for context — so you understand the data model those actions operate on.\",\n );\n lines.push(\"\");\n }\n\n // Data scoping context.\n const ownerLine = opts.owner ? opts.owner : \"(unresolved)\";\n const orgLine = opts.orgId ? opts.orgId : \"(none)\";\n lines.push(\"## Data scoping (enforced at the SQL layer)\");\n lines.push(`- Current user: \\`${ownerLine}\\``);\n lines.push(`- Current org: \\`${orgLine}\\``);\n lines.push(\n \"- Tables with an `owner_email` column are automatically filtered to the current user via temporary views before every query.\",\n );\n lines.push(\n \"- Tables with an `org_id` column are automatically filtered to the current org as well.\",\n );\n lines.push(\n \"- On INSERT, `owner_email` and `org_id` are auto-injected — do NOT set them manually.\",\n );\n lines.push(\n \"- Do NOT add `WHERE owner_email = ...` or `WHERE org_id = ...` to your queries — the filter is already applied, and re-adding it will confuse the scoped view.\",\n );\n lines.push(\"</sql-database>\");\n\n return \"\\n\\n\" + lines.join(\"\\n\");\n}\n"]}
|
|
@@ -110,9 +110,9 @@ action trio instead:
|
|
|
110
110
|
|
|
111
111
|
- `provider-api-catalog`: lists provider base URLs, auth style, credential keys,
|
|
112
112
|
docs/spec URLs, placeholders, and examples without exposing secrets.
|
|
113
|
-
- `provider-api-docs`: fetches
|
|
114
|
-
exact endpoint, filter operator, payload shape, or pagination contract is
|
|
115
|
-
uncertain.
|
|
113
|
+
- `provider-api-docs`: fetches public provider docs/spec/changelog URLs when
|
|
114
|
+
the exact endpoint, filter operator, payload shape, or pagination contract is
|
|
115
|
+
uncertain. Registered docs URLs are curated starting points.
|
|
116
116
|
- `provider-api-request`: makes a constrained authenticated HTTP request to the
|
|
117
117
|
provider host, injects configured credentials, blocks private/internal URLs,
|
|
118
118
|
and redacts secrets.
|
|
@@ -125,7 +125,7 @@ duplicating the provider config. If credentials are stored on shareable/resource
|
|
|
125
125
|
rows rather than in the shared credential or OAuth-token stores, build a resolver
|
|
126
126
|
that enforces those access checks before exposing raw provider requests. Keep
|
|
127
127
|
`provider-api-request` `http: false` unless you have a separate UI permission
|
|
128
|
-
model for arbitrary provider writes. Specific actions such as `
|
|
128
|
+
model for arbitrary provider writes. Specific actions such as `search-records`,
|
|
129
129
|
`search-emails`, or `sync-source` are convenience shortcuts, not capability
|
|
130
130
|
limits; agents should fall back to the provider API trio when a question
|
|
131
131
|
requires an endpoint or filter that the shortcut does not model.
|
|
@@ -139,6 +139,18 @@ with the user's configured credentials. For large responses, expose staging
|
|
|
139
139
|
(`stageAs`, `itemsPath`, pagination, and `query-staged-dataset`) or sandboxed
|
|
140
140
|
code execution so the agent can reduce data without flooding context.
|
|
141
141
|
|
|
142
|
+
For broad provider questions, cross-source joins, corpus-wide mention/search
|
|
143
|
+
work, classification, or any answer where absence matters, design the action
|
|
144
|
+
surface for full coverage instead of convenience-only samples. The agent should
|
|
145
|
+
be able to fetch every relevant page or an explicitly bounded cohort, stage or
|
|
146
|
+
save the raw provider response outside chat, and then use
|
|
147
|
+
`query-staged-dataset`, `run-code`, or provider-side search to count, join,
|
|
148
|
+
grep, classify, and aggregate. Tool descriptions and AGENTS.md guidance should
|
|
149
|
+
teach agents to report source, filters, time window, row/record counts,
|
|
150
|
+
pagination status, truncation, failed pages, and uncovered gaps. They must not
|
|
151
|
+
turn default limits, sampled rows, truncated excerpts, or aborted calls into a
|
|
152
|
+
confident "none found", "all records", or exhaustive conclusion.
|
|
153
|
+
|
|
142
154
|
### The `http` Option
|
|
143
155
|
|
|
144
156
|
Controls how the action is exposed as an HTTP endpoint:
|
|
@@ -110,9 +110,9 @@ action trio instead:
|
|
|
110
110
|
|
|
111
111
|
- `provider-api-catalog`: lists provider base URLs, auth style, credential keys,
|
|
112
112
|
docs/spec URLs, placeholders, and examples without exposing secrets.
|
|
113
|
-
- `provider-api-docs`: fetches
|
|
114
|
-
exact endpoint, filter operator, payload shape, or pagination contract is
|
|
115
|
-
uncertain.
|
|
113
|
+
- `provider-api-docs`: fetches public provider docs/spec/changelog URLs when
|
|
114
|
+
the exact endpoint, filter operator, payload shape, or pagination contract is
|
|
115
|
+
uncertain. Registered docs URLs are curated starting points.
|
|
116
116
|
- `provider-api-request`: makes a constrained authenticated HTTP request to the
|
|
117
117
|
provider host, injects configured credentials, blocks private/internal URLs,
|
|
118
118
|
and redacts secrets.
|
|
@@ -125,7 +125,7 @@ duplicating the provider config. If credentials are stored on shareable/resource
|
|
|
125
125
|
rows rather than in the shared credential or OAuth-token stores, build a resolver
|
|
126
126
|
that enforces those access checks before exposing raw provider requests. Keep
|
|
127
127
|
`provider-api-request` `http: false` unless you have a separate UI permission
|
|
128
|
-
model for arbitrary provider writes. Specific actions such as `
|
|
128
|
+
model for arbitrary provider writes. Specific actions such as `search-records`,
|
|
129
129
|
`search-emails`, or `sync-source` are convenience shortcuts, not capability
|
|
130
130
|
limits; agents should fall back to the provider API trio when a question
|
|
131
131
|
requires an endpoint or filter that the shortcut does not model.
|
|
@@ -139,6 +139,18 @@ with the user's configured credentials. For large responses, expose staging
|
|
|
139
139
|
(`stageAs`, `itemsPath`, pagination, and `query-staged-dataset`) or sandboxed
|
|
140
140
|
code execution so the agent can reduce data without flooding context.
|
|
141
141
|
|
|
142
|
+
For broad provider questions, cross-source joins, corpus-wide mention/search
|
|
143
|
+
work, classification, or any answer where absence matters, design the action
|
|
144
|
+
surface for full coverage instead of convenience-only samples. The agent should
|
|
145
|
+
be able to fetch every relevant page or an explicitly bounded cohort, stage or
|
|
146
|
+
save the raw provider response outside chat, and then use
|
|
147
|
+
`query-staged-dataset`, `run-code`, or provider-side search to count, join,
|
|
148
|
+
grep, classify, and aggregate. Tool descriptions and AGENTS.md guidance should
|
|
149
|
+
teach agents to report source, filters, time window, row/record counts,
|
|
150
|
+
pagination status, truncation, failed pages, and uncovered gaps. They must not
|
|
151
|
+
turn default limits, sampled rows, truncated excerpts, or aborted calls into a
|
|
152
|
+
confident "none found", "all records", or exhaustive conclusion.
|
|
153
|
+
|
|
142
154
|
### The `http` Option
|
|
143
155
|
|
|
144
156
|
Controls how the action is exposed as an HTTP endpoint:
|
package/package.json
CHANGED
|
@@ -110,9 +110,9 @@ action trio instead:
|
|
|
110
110
|
|
|
111
111
|
- `provider-api-catalog`: lists provider base URLs, auth style, credential keys,
|
|
112
112
|
docs/spec URLs, placeholders, and examples without exposing secrets.
|
|
113
|
-
- `provider-api-docs`: fetches
|
|
114
|
-
exact endpoint, filter operator, payload shape, or pagination contract is
|
|
115
|
-
uncertain.
|
|
113
|
+
- `provider-api-docs`: fetches public provider docs/spec/changelog URLs when
|
|
114
|
+
the exact endpoint, filter operator, payload shape, or pagination contract is
|
|
115
|
+
uncertain. Registered docs URLs are curated starting points.
|
|
116
116
|
- `provider-api-request`: makes a constrained authenticated HTTP request to the
|
|
117
117
|
provider host, injects configured credentials, blocks private/internal URLs,
|
|
118
118
|
and redacts secrets.
|
|
@@ -125,7 +125,7 @@ duplicating the provider config. If credentials are stored on shareable/resource
|
|
|
125
125
|
rows rather than in the shared credential or OAuth-token stores, build a resolver
|
|
126
126
|
that enforces those access checks before exposing raw provider requests. Keep
|
|
127
127
|
`provider-api-request` `http: false` unless you have a separate UI permission
|
|
128
|
-
model for arbitrary provider writes. Specific actions such as `
|
|
128
|
+
model for arbitrary provider writes. Specific actions such as `search-records`,
|
|
129
129
|
`search-emails`, or `sync-source` are convenience shortcuts, not capability
|
|
130
130
|
limits; agents should fall back to the provider API trio when a question
|
|
131
131
|
requires an endpoint or filter that the shortcut does not model.
|
|
@@ -139,6 +139,18 @@ with the user's configured credentials. For large responses, expose staging
|
|
|
139
139
|
(`stageAs`, `itemsPath`, pagination, and `query-staged-dataset`) or sandboxed
|
|
140
140
|
code execution so the agent can reduce data without flooding context.
|
|
141
141
|
|
|
142
|
+
For broad provider questions, cross-source joins, corpus-wide mention/search
|
|
143
|
+
work, classification, or any answer where absence matters, design the action
|
|
144
|
+
surface for full coverage instead of convenience-only samples. The agent should
|
|
145
|
+
be able to fetch every relevant page or an explicitly bounded cohort, stage or
|
|
146
|
+
save the raw provider response outside chat, and then use
|
|
147
|
+
`query-staged-dataset`, `run-code`, or provider-side search to count, join,
|
|
148
|
+
grep, classify, and aggregate. Tool descriptions and AGENTS.md guidance should
|
|
149
|
+
teach agents to report source, filters, time window, row/record counts,
|
|
150
|
+
pagination status, truncation, failed pages, and uncovered gaps. They must not
|
|
151
|
+
turn default limits, sampled rows, truncated excerpts, or aborted calls into a
|
|
152
|
+
confident "none found", "all records", or exhaustive conclusion.
|
|
153
|
+
|
|
142
154
|
### The `http` Option
|
|
143
155
|
|
|
144
156
|
Controls how the action is exposed as an HTTP endpoint:
|
|
@@ -110,9 +110,9 @@ action trio instead:
|
|
|
110
110
|
|
|
111
111
|
- `provider-api-catalog`: lists provider base URLs, auth style, credential keys,
|
|
112
112
|
docs/spec URLs, placeholders, and examples without exposing secrets.
|
|
113
|
-
- `provider-api-docs`: fetches
|
|
114
|
-
exact endpoint, filter operator, payload shape, or pagination contract is
|
|
115
|
-
uncertain.
|
|
113
|
+
- `provider-api-docs`: fetches public provider docs/spec/changelog URLs when
|
|
114
|
+
the exact endpoint, filter operator, payload shape, or pagination contract is
|
|
115
|
+
uncertain. Registered docs URLs are curated starting points.
|
|
116
116
|
- `provider-api-request`: makes a constrained authenticated HTTP request to the
|
|
117
117
|
provider host, injects configured credentials, blocks private/internal URLs,
|
|
118
118
|
and redacts secrets.
|
|
@@ -125,7 +125,7 @@ duplicating the provider config. If credentials are stored on shareable/resource
|
|
|
125
125
|
rows rather than in the shared credential or OAuth-token stores, build a resolver
|
|
126
126
|
that enforces those access checks before exposing raw provider requests. Keep
|
|
127
127
|
`provider-api-request` `http: false` unless you have a separate UI permission
|
|
128
|
-
model for arbitrary provider writes. Specific actions such as `
|
|
128
|
+
model for arbitrary provider writes. Specific actions such as `search-records`,
|
|
129
129
|
`search-emails`, or `sync-source` are convenience shortcuts, not capability
|
|
130
130
|
limits; agents should fall back to the provider API trio when a question
|
|
131
131
|
requires an endpoint or filter that the shortcut does not model.
|
|
@@ -139,6 +139,18 @@ with the user's configured credentials. For large responses, expose staging
|
|
|
139
139
|
(`stageAs`, `itemsPath`, pagination, and `query-staged-dataset`) or sandboxed
|
|
140
140
|
code execution so the agent can reduce data without flooding context.
|
|
141
141
|
|
|
142
|
+
For broad provider questions, cross-source joins, corpus-wide mention/search
|
|
143
|
+
work, classification, or any answer where absence matters, design the action
|
|
144
|
+
surface for full coverage instead of convenience-only samples. The agent should
|
|
145
|
+
be able to fetch every relevant page or an explicitly bounded cohort, stage or
|
|
146
|
+
save the raw provider response outside chat, and then use
|
|
147
|
+
`query-staged-dataset`, `run-code`, or provider-side search to count, join,
|
|
148
|
+
grep, classify, and aggregate. Tool descriptions and AGENTS.md guidance should
|
|
149
|
+
teach agents to report source, filters, time window, row/record counts,
|
|
150
|
+
pagination status, truncation, failed pages, and uncovered gaps. They must not
|
|
151
|
+
turn default limits, sampled rows, truncated excerpts, or aborted calls into a
|
|
152
|
+
confident "none found", "all records", or exhaustive conclusion.
|
|
153
|
+
|
|
142
154
|
### The `http` Option
|
|
143
155
|
|
|
144
156
|
Controls how the action is exposed as an HTTP endpoint:
|