From bdb5d43425305e0d60f24047bd3914ebbbb40297 Mon Sep 17 00:00:00 2001 From: hugocasa Date: Thu, 8 Oct 2026 10:29:52 +0200 Subject: [PATCH] feat: guide the AI when no resource type fits a service Co-Authored-By: Claude Opus 5.5 --- ai_evals/adapters/frontend/mockBackend.ts | 68 +++++++++++++++++++ .../adapters/frontend/vitestAdapter.test.ts | 17 ++++- ai_evals/cases/global.yaml | 52 ++++++++++++++ .../initial/resource_types_not_synced.json | 5 ++ .../commands/resource-type/resource-type.ts | 19 ++++-- cli/src/guidance/skills.gen.ts | 28 ++++++++ cli/test/standalone_commands.test.ts | 5 +- .../components/copilot/chat/global/core.ts | 17 ++--- .../copilot/chat/script/core.test.ts | 35 +++++++++- .../components/copilot/chat/script/core.ts | 68 +++++++++++++++---- .../components/copilot/chat/shared.test.ts | 26 ++++++- .../src/lib/components/copilot/chat/shared.ts | 27 +++++++- system_prompts/auto-generated/prompts.ts | 10 +++ system_prompts/auto-generated/script.md | 1 + .../auto-generated/skills/resources/SKILL.md | 8 +++ .../skills/write-script-ansible/SKILL.md | 1 + .../skills/write-script-bash/SKILL.md | 1 + .../skills/write-script-bigquery/SKILL.md | 1 + .../skills/write-script-bun/SKILL.md | 1 + .../skills/write-script-bunnative/SKILL.md | 1 + .../skills/write-script-csharp/SKILL.md | 1 + .../skills/write-script-deno/SKILL.md | 1 + .../skills/write-script-duckdb/SKILL.md | 1 + .../skills/write-script-go/SKILL.md | 1 + .../skills/write-script-graphql/SKILL.md | 1 + .../skills/write-script-java/SKILL.md | 1 + .../skills/write-script-mssql/SKILL.md | 1 + .../skills/write-script-mysql/SKILL.md | 1 + .../skills/write-script-php/SKILL.md | 1 + .../skills/write-script-postgresql/SKILL.md | 1 + .../skills/write-script-powershell/SKILL.md | 1 + .../skills/write-script-python3/SKILL.md | 1 + .../skills/write-script-rlang/SKILL.md | 1 + .../skills/write-script-rust/SKILL.md | 1 + .../skills/write-script-snowflake/SKILL.md | 1 + system_prompts/base/resources.md | 11 +++ system_prompts/base/script-base.md | 1 + 37 files changed, 382 insertions(+), 36 deletions(-) create mode 100644 ai_evals/fixtures/frontend/global/initial/resource_types_not_synced.json diff --git a/ai_evals/adapters/frontend/mockBackend.ts b/ai_evals/adapters/frontend/mockBackend.ts index 2f47e85be6..2e651e4631 100644 --- a/ai_evals/adapters/frontend/mockBackend.ts +++ b/ai_evals/adapters/frontend/mockBackend.ts @@ -11,6 +11,7 @@ import type { ListableResource, ListableVariable, Resource, + ResourceType, Script } from '../../../frontend/src/lib/gen' import type { @@ -98,6 +99,51 @@ export interface BenchmarkWorkspaceResource { description?: string } +/** A resource type the instance holds. Only a seeded one has a schema to look up by name. */ +export interface BenchmarkWorkspaceResourceType { + name: string + schema?: unknown + description?: string +} + +/** Names a hub-synced instance holds, for cases that seed no resource types. Their schemas are + * not modelled, so a lookup by name misses them, as the similarity search does. */ +const SYNCED_RESOURCE_TYPE_NAMES = [ + 'ai_instruction', + 'ai_skill', + 'airtable', + 'asana', + 'aws', + 'azure_blob', + 'clickup', + 'discord_webhook', + 'gcal', + 'gcp_service_account', + 'github', + 'gitlab', + 'gmail', + 'gsheets', + 'hubspot', + 'jira', + 'linear', + 'mailchimp', + 'mongodb', + 'mysql', + 'notion', + 'openai', + 'postgresql', + 's3', + 'salesforce', + 'sendgrid', + 'shopify', + 'slack', + 'smtp', + 'stripe', + 'supabase', + 'twilio', + 'zendesk' +] + export interface BenchmarkWorkspaceJob { /** Stable id so a case prompt can reference a specific run (e.g. for get_run). */ id?: string @@ -118,6 +164,8 @@ export interface BenchmarkWorkspaceRunnables { variables?: BenchmarkWorkspaceVariable[] aiProviders?: BenchmarkWorkspaceAiProvider[] resources?: BenchmarkWorkspaceResource[] + /** Unset means an instance synced with the hub. */ + resourceTypes?: BenchmarkWorkspaceResourceType[] datatables?: BenchmarkDatatableSeed[] /** DuckLake catalog names, as `list_ducklakes` reports them. */ ducklakes?: string[] @@ -328,6 +376,26 @@ export function listBenchmarkPlainResources(workspace: string): ListableResource })) } +/** Resource type names of a benchmark workspace, as `ResourceService.listResourceTypeNames` + * returns them. Null when the workspace is not a benchmark one. */ +export function listBenchmarkResourceTypeNames(workspace: string): string[] | null { + if (!benchmarkWorkspaces.has(workspace)) { + return null + } + const seeded = benchmarkWorkspaceRunnables.get(workspace)?.resourceTypes + return seeded ? seeded.map((rt) => rt.name) : SYNCED_RESOURCE_TYPE_NAMES +} + +/** A seeded resource type, as `ResourceService.getResourceType` returns it. */ +export function getBenchmarkResourceType(workspace: string, name: string): ResourceType | null { + const seed = benchmarkWorkspaceRunnables + .get(workspace) + ?.resourceTypes?.find((rt) => rt.name === name && rt.schema !== undefined) + return seed + ? { workspace_id: 'admins', name: seed.name, schema: seed.schema, description: seed.description } + : null +} + /** A seeded resource with its value, as `ResourceService.getResource` returns it. Covers both * seed kinds, so it agrees with `existsResource` and `listResource` — both of those report AI * providers too, and a case that lists resources and then reads one by path would otherwise get diff --git a/ai_evals/adapters/frontend/vitestAdapter.test.ts b/ai_evals/adapters/frontend/vitestAdapter.test.ts index 288c67e3eb..79fb4cb3a9 100644 --- a/ai_evals/adapters/frontend/vitestAdapter.test.ts +++ b/ai_evals/adapters/frontend/vitestAdapter.test.ts @@ -104,8 +104,10 @@ vi.mock('$lib/gen', async () => { getBenchmarkVariableByPath, hasBenchmarkWorkspace, getBenchmarkResource, + getBenchmarkResourceType, listBenchmarkAiProviderResources, listBenchmarkPlainResources, + listBenchmarkResourceTypeNames, listBenchmarkApps, listBenchmarkDatatables, listBenchmarkDataMetrics, @@ -463,7 +465,20 @@ vi.mock('$lib/gen', async () => { return value }, queryResourceTypes: async (data: { workspace: string }) => - hasBenchmarkWorkspace(data.workspace) ? [] : actual.ResourceService.queryResourceTypes(data) + hasBenchmarkWorkspace(data.workspace) ? [] : actual.ResourceService.queryResourceTypes(data), + listResourceTypeNames: async (data: { workspace: string }) => + listBenchmarkResourceTypeNames(data.workspace) ?? + actual.ResourceService.listResourceTypeNames(data), + getResourceType: async (data: { workspace: string; path: string }) => { + if (!hasBenchmarkWorkspace(data.workspace)) { + return actual.ResourceService.getResourceType(data) + } + const resourceType = getBenchmarkResourceType(data.workspace, data.path) + if (!resourceType) { + throw new Error(`Resource type "${data.path}" not found in benchmark workspace`) + } + return resourceType + } }), VariableService: wrapService(actual.VariableService, { existsVariable: async (data: { workspace: string; path: string }) => diff --git a/ai_evals/cases/global.yaml b/ai_evals/cases/global.yaml index b66af3e8dd..59ea364a77 100644 --- a/ai_evals/cases/global.yaml +++ b/ai_evals/cases/global.yaml @@ -759,6 +759,58 @@ - keeps both drafts under a sensible eval/global Slack-related path - does not create schedules, triggers, or deployed workspace changes +# The instance never synced the hub's resource types, so the one Confluence takes is missing: +# the script's connection picker stays empty until a superadmin syncs, which only the +# assistant's reply can tell the user. +- id: global-resource-type-hub-not-synced + prompt: |- + Write a script that creates a page in our Confluence space from a space key, a title and the page body. + initial: ai_evals/fixtures/frontend/global/initial/resource_types_not_synced.json + runtime: + maxTurns: 10 + validate: + requiredDrafts: + - type: script + valueExcludes: + - getVariable + - get_variable + toolExpect: + forbiddenToolsUsed: + - write_variable + - deploy_workspace_item + assistantExpect: + requiredMentionsAnyOf: + - - sync + judgeChecklist: + - the script takes the Confluence connection as one resource-typed parameter rather than separate string credentials or a variable read inside the code + - the script creates a Confluence page from a space key, a title and a body + +# An in-house API has no hub resource type, so its connection needs a type of the workspace's +# own: a token parameter or a variable read inside the code is the fallback the guidance rules out. +- id: global-resource-type-custom-api + prompt: |- + Write a script that fetches an invoice by id from our internal billing API at https://billing.acme.internal/api/v1. It authenticates with a bearer token. + runtime: + maxTurns: 10 + userAnswers: + - Yes, use a resource type + validate: + requiredDrafts: + - type: script + valueExcludes: + - getVariable + - get_variable + toolExpect: + forbiddenToolsUsed: + - write_variable + - deploy_workspace_item + assistantExpect: + requiredMentionsAnyOf: + - - resource type + judgeChecklist: + - the script takes the billing API's base URL and token together as one resource-typed parameter, not a plain string token or a variable read inside the code + - the script fetches an invoice by id with a bearer token + - id: global-test19-datatable-not-configured-asks-to-set-up prompt: |- Here are two newsletter signups: alice@example.com and bob@example.com. diff --git a/ai_evals/fixtures/frontend/global/initial/resource_types_not_synced.json b/ai_evals/fixtures/frontend/global/initial/resource_types_not_synced.json new file mode 100644 index 0000000000..8e0cb5a54d --- /dev/null +++ b/ai_evals/fixtures/frontend/global/initial/resource_types_not_synced.json @@ -0,0 +1,5 @@ +{ + "workspace": { + "resourceTypes": [{ "name": "ai_skill" }, { "name": "ai_instruction" }] + } +} diff --git a/cli/src/commands/resource-type/resource-type.ts b/cli/src/commands/resource-type/resource-type.ts index 505200f07a..2661b0143d 100644 --- a/cli/src/commands/resource-type/resource-type.ts +++ b/cli/src/commands/resource-type/resource-type.ts @@ -91,6 +91,19 @@ async function push(opts: PushOptions, filePath: string, name: string) { log.info(colors.bold.underline.green("Resource pushed")); } +// The hub publishes hundreds of resource types and an instance seeds only a few of its own, +// so fewer than this means the instance never synced with the hub. +const UNSYNCED_RESOURCE_TYPE_COUNT = 20; + +function warnIfNotSyncedWithHub(resourceTypes: ResourceType[]) { + const count = new Set(resourceTypes.map((rt) => rt.name)).size; + if (count < UNSYNCED_RESOURCE_TYPE_COUNT) { + log.warnAlways( + `Only ${count} resource types exist on this instance, so it has most likely never synced with the Windmill Hub, which has types for most services. A superadmin can sync resource types from the hub with "Sync resource types with Hub" in the Add resource dialog of the Resources page, or by running the u/admin/hub_sync script in the admins workspace.` + ); + } +} + async function list(opts: GlobalOptions & { schema?: boolean; json?: boolean }) { if (opts.json) log.setSilent(true); const workspace = await resolveWorkspace(opts); @@ -99,12 +112,9 @@ async function list(opts: GlobalOptions & { schema?: boolean; json?: boolean }) workspace: workspace.workspaceId, }); + warnIfNotSyncedWithHub(res); if (opts.json) { console.log(JSON.stringify(res)); - } else if (res.length === 0) { - log.info("No custom resource types found in this workspace."); - log.info("Built-in types like 'postgresql', 'slack', 'mysql', etc. are available from the Windmill Hub."); - return; } else if (opts.schema) { new Table() .header(["Workspace", "Name", "Schema"]) @@ -172,6 +182,7 @@ export async function generateRTNamespace(opts: GlobalOptions) { const rts = await wmill.listResourceType({ workspace: workspace.workspaceId, }); + warnIfNotSyncedWithHub(rts); let namespaceContent = "declare namespace RT {\n"; namespaceContent += rts diff --git a/cli/src/guidance/skills.gen.ts b/cli/src/guidance/skills.gen.ts index dc9541b4c5..da076d6689 100644 --- a/cli/src/guidance/skills.gen.ts +++ b/cli/src/guidance/skills.gen.ts @@ -95,6 +95,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -259,6 +260,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -383,6 +385,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -500,6 +503,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -1323,6 +1327,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -2158,6 +2163,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -2274,6 +2280,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -3099,6 +3106,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -3249,6 +3257,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -3382,6 +3391,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -3502,6 +3512,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -3615,6 +3626,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -3731,6 +3743,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -3848,6 +3861,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -3980,6 +3994,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -4095,6 +4110,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -4225,6 +4241,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -5234,6 +5251,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -5394,6 +5412,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -5544,6 +5563,7 @@ Use \`wmill resource-type list --schema\` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -7178,6 +7198,14 @@ The prefix must be on the string itself. } \`\`\` +## When No Resource Type Fits + +The Windmill Hub publishes resource types for most services, and an instance gets them by syncing with the hub. When none fits the service: + +- If looking up resource types reports that the instance is missing the hub's types, tell the user so that a superadmin syncs, and don't create a type for the service meanwhile: the hub most likely has one, and after the sync the workspace would hold two. +- Otherwise propose a resource type holding the service's connection fields (base URL, account or region identifiers, credentials), as in "Custom Resource Types" below, and ask the user before creating it: every resource of that type and every script taking one depends on its fields. +- Use a plain variable instead of a resource only when the user prefers it. + ## Custom Resource Types Create custom resource types with JSON Schema: diff --git a/cli/test/standalone_commands.test.ts b/cli/test/standalone_commands.test.ts index 271b13867b..561ccf9d42 100644 --- a/cli/test/standalone_commands.test.ts +++ b/cli/test/standalone_commands.test.ts @@ -139,10 +139,7 @@ describe("resource-type commands", () => { ); expect(result.code).toEqual(0); - // When empty, shows helpful message; when populated, shows table with Name header - const hasTable = result.stdout.includes("Name"); - const hasEmptyMessage = result.stdout.includes("No custom resource types"); - expect(hasTable || hasEmptyMessage).toBe(true); + expect(result.stdout).toContain("Name"); }); }); diff --git a/frontend/src/lib/components/copilot/chat/global/core.ts b/frontend/src/lib/components/copilot/chat/global/core.ts index 3e3baa36d7..1fd6feda16 100644 --- a/frontend/src/lib/components/copilot/chat/global/core.ts +++ b/frontend/src/lib/components/copilot/chat/global/core.ts @@ -104,7 +104,7 @@ import { createInlineScriptSession, findUnresolvedInlineScriptRefs } from '../flow/inlineScriptsUtils' -import { searchNpmPackagesTool } from '../script/core' +import { searchNpmPackagesTool, searchResourceTypes } from '../script/core' import type { McpServer } from './mcpTools' import { logFeatureUsage } from '$lib/utils/featureUsage' import { isSkillEnabled } from '../skills/enabledSkills' @@ -4283,19 +4283,16 @@ export const globalTools: SessionTool<{}>[] = [ toolCallbacks.setToolStatus(toolId, { content: `Searching resource types for "${parsed.query}"...` }) - const results = await ResourceService.queryResourceTypes({ + const { resourceTypes, hubSyncNote } = await searchResourceTypes( + parsed.query, workspace, - text: parsed.query, - limit: parsed.limit ?? 5 - }) + parsed.limit ?? 5 + ) toolCallbacks.setToolStatus(toolId, { - content: `Found ${results.length} resource type(s) for "${parsed.query}"` + content: `Found ${resourceTypes.length} resource type(s) for "${parsed.query}"` }) return JSON.stringify( - results.map((rt) => ({ - name: rt.name, - schema: rt.schema - })), + hubSyncNote ? { resource_types: resourceTypes, note: hubSyncNote } : resourceTypes, null, 2 ) diff --git a/frontend/src/lib/components/copilot/chat/script/core.test.ts b/frontend/src/lib/components/copilot/chat/script/core.test.ts index f235ae3796..414c42d758 100644 --- a/frontend/src/lib/components/copilot/chat/script/core.test.ts +++ b/frontend/src/lib/components/copilot/chat/script/core.test.ts @@ -14,7 +14,40 @@ vi.mock('@codingame/monaco-vscode-standalone-typescript-language-features', () = vi.mock('@codingame/monaco-vscode-languages-service-override', () => ({ default: () => ({}) })) vi.mock('$lib/components/vscode', () => ({})) -import { editCodeToolWithDiff } from './core' +import { ResourceService } from '$lib/gen' +import { editCodeToolWithDiff, searchResourceTypes } from './core' + +describe('searchResourceTypes', () => { + const syncedNames = Array.from({ length: 30 }, (_, i) => `type_${i}`) + + // The similarity index trails the table by up to a day, and builds without embeddings + // have none, so a type the query names must still be found. + it('finds a type the query names when the similarity search misses it', async () => { + vi.spyOn(ResourceService, 'queryResourceTypes').mockResolvedValue([]) + vi.spyOn(ResourceService, 'listResourceTypeNames').mockResolvedValue([...syncedNames, 'stripe']) + vi.spyOn(ResourceService, 'getResourceType').mockResolvedValue({ + name: 'stripe', + schema: { type: 'object' } + }) + + const { resourceTypes, hubSyncNote } = await searchResourceTypes('Stripe payments', 'ws', 5) + + expect(resourceTypes.map((rt) => rt.name)).toEqual(['stripe']) + expect(hubSyncNote).toBeUndefined() + }) + + it('notes an instance that never synced with the hub', async () => { + vi.spyOn(ResourceService, 'queryResourceTypes').mockResolvedValue([]) + vi.spyOn(ResourceService, 'listResourceTypeNames').mockResolvedValue([ + 'ai_skill', + 'ai_instruction' + ]) + + const { hubSyncNote } = await searchResourceTypes('stripe', 'ws', 5) + + expect(hubSyncNote).toContain('never synced') + }) +}) describe('editCodeToolWithDiff', () => { it('records the complete script diff after applying replacements', async () => { diff --git a/frontend/src/lib/components/copilot/chat/script/core.ts b/frontend/src/lib/components/copilot/chat/script/core.ts index 92cb889b24..1437f2f227 100644 --- a/frontend/src/lib/components/copilot/chat/script/core.ts +++ b/frontend/src/lib/components/copilot/chat/script/core.ts @@ -19,7 +19,8 @@ import { type ScriptLintResult, formatScriptLintResult, createSearchWorkspaceTool, - createGetRunnableDetailsTool + createGetRunnableDetailsTool, + HUB_SYNC_INSTRUCTIONS } from '../shared' import { createWorkspaceMutationTools } from '../workspaceTools' import { setupTypeAcquisition, type DepsToGet } from '$lib/ata' @@ -41,7 +42,7 @@ const TYPES_CONTEXT_PERCENTAGE = 1 export const DIFF_BASED_EDIT_PROVIDERS: AIProvider[] = [] export function formatResourceTypes( - allResourceTypes: ResourceType[], + allResourceTypes: Pick[], lang: 'python3' | 'php' | 'bun' | 'deno' | 'nativets' | 'bunnative' ) { if (lang === 'python3') { @@ -67,14 +68,52 @@ export function formatResourceTypes( } } -async function getResourceTypes(prompt: string, workspace: string) { - const resourceTypes = await ResourceService.queryResourceTypes({ - workspace: workspace, - text: prompt, - limit: 5 - }) +// The hub publishes hundreds of resource types and an instance seeds only a few of its own, +// so fewer than this means the instance never synced with the hub. +const UNSYNCED_RESOURCE_TYPE_COUNT = 20 - return resourceTypes +/** + * Similarity matches, led by any resource type the query names exactly: the similarity index + * is rebuilt from the table once a day, and builds without embeddings have no index at all. + */ +export async function searchResourceTypes( + query: string, + workspace: string, + limit: number +): Promise<{ resourceTypes: Pick[]; hubSyncNote?: string }> { + const [similar, names] = await Promise.all([ + ResourceService.queryResourceTypes({ workspace, text: query, limit }).catch(() => []), + ResourceService.listResourceTypeNames({ workspace }) + .then((listed) => new Set(listed)) + .catch(() => undefined) + ]) + + const normalized = query.trim().toLowerCase() + const candidates = new Set([ + normalized.replace(/[\s-]+/g, '_'), + ...normalized.split(/[^a-z0-9_]+/) + ]) + const exactNames = [...candidates].filter( + (name) => names?.has(name) && !similar.some((rt) => rt.name === name) + ) + const exact = await Promise.all( + exactNames + .slice(0, limit) + .map((name) => + ResourceService.getResourceType({ workspace, path: name }).catch(() => undefined) + ) + ) + const resourceTypes = [ + ...exact.flatMap((rt) => (rt ? [{ name: rt.name, schema: rt.schema }] : [])), + ...similar + ].slice(0, limit) + + const hubSyncNote = + names && names.size < UNSYNCED_RESOURCE_TYPE_COUNT + ? `Only ${names.size} resource types exist on this instance, so it has most likely never synced with the Windmill Hub, which has types for most services. ${HUB_SYNC_INSTRUCTIONS}` + : undefined + + return { resourceTypes, hubSyncNote } } export const SUPPORTED_CHAT_SCRIPT_LANGUAGES = [ @@ -149,13 +188,14 @@ export async function getFormattedResourceTypes( case 'bunnative': case 'python3': case 'php': { - const resourceTypes = await getResourceTypes(prompt, workspace) + const { resourceTypes, hubSyncNote } = await searchResourceTypes(prompt, workspace, 5) - const intro = `RESOURCE_TYPES:\n` + const found = + resourceTypes.length > 0 + ? `RESOURCE_TYPES:\n${formatResourceTypes(resourceTypes, lang)}` + : `No resource type matches "${prompt}".` - const resourceTypesText = formatResourceTypes(resourceTypes, lang) - - return intro + resourceTypesText + return hubSyncNote ? `${found}\n\n${hubSyncNote}` : found } default: return '' diff --git a/frontend/src/lib/components/copilot/chat/shared.test.ts b/frontend/src/lib/components/copilot/chat/shared.test.ts index ff7c59d6cb..c78cd648c9 100644 --- a/frontend/src/lib/components/copilot/chat/shared.test.ts +++ b/frontend/src/lib/components/copilot/chat/shared.test.ts @@ -57,7 +57,8 @@ vi.mock('$lib/gen', () => ({ EmailTriggerService: { createEmailTrigger: vi.fn() }, SettingService: { getGlobal: vi.fn() }, ResourceService: { - getResourceValue: vi.fn(async ({ path }: { path: string }) => ({ content: `body of ${path}` })) + getResourceValue: vi.fn(async ({ path }: { path: string }) => ({ content: `body of ${path}` })), + listResourceTypeNames: vi.fn(async () => ['confluence']) } })) @@ -2270,6 +2271,29 @@ describe('getHubIntegrationTool', () => { expect(!!parsed.scripts_note).toBe(expected) }) + // Without the note the model writes against a type the instance lacks, and the + // script's resource picker stays empty until someone syncs. + it.each([ + [['confluence'], false], + [['ai_skill'], true] + ])('flags hub resource types the instance lacks (%s)', async (present, flagged) => { + const { IntegrationService, ResourceService } = await import('$lib/gen') + Object.assign(IntegrationService, { getHubIntegrationMeta: vi.fn(async () => doc) }) + vi.mocked(ResourceService.listResourceTypeNames).mockResolvedValueOnce(present) + + const { getHubIntegrationTool } = await import('./shared') + const parsed = JSON.parse( + await getHubIntegrationTool.fn({ + args: { integration: 'confluence' }, + workspace: 'ws', + toolId: 't1', + toolCallbacks: { setToolStatus: vi.fn() } + } as any) + ) + + expect(parsed.resource_types_note?.includes('confluence') ?? false).toBe(flagged) + }) + // A hub that times out has said nothing about whether the integration exists, and // reporting it as absent would stick for the rest of the conversation. it('does not report a transient hub failure as a missing integration', async () => { diff --git a/frontend/src/lib/components/copilot/chat/shared.ts b/frontend/src/lib/components/copilot/chat/shared.ts index 6897a22444..0a85e7674d 100644 --- a/frontend/src/lib/components/copilot/chat/shared.ts +++ b/frontend/src/lib/components/copilot/chat/shared.ts @@ -49,6 +49,7 @@ import { FlowService, IntegrationService, JobService, + ResourceService, type Job, type CompletedJob, type FlowValue, @@ -1710,12 +1711,31 @@ function parseResourceTypeSchema(schema: unknown): unknown { } } +export const HUB_SYNC_INSTRUCTIONS = + 'A superadmin can sync resource types from the hub with "Sync resource types with Hub" in the Add resource dialog of the Resources page, or by running the u/admin/hub_sync script in the admins workspace.' + +async function missingResourceTypesNote( + workspace: string, + names: string[] +): Promise { + if (names.length === 0) { + return undefined + } + const present = await ResourceService.listResourceTypeNames({ workspace }) + .then((listed) => new Set(listed)) + .catch(() => undefined) + const missing = present ? names.filter((name) => !present.has(name)) : [] + return missing.length > 0 + ? `This instance does not have the ${missing.join(', ')} resource type${missing.length > 1 ? 's' : ''} yet. ${HUB_SYNC_INSTRUCTIONS}` + : undefined +} + export const getHubIntegrationTool = { requires: NONE, def: getHubIntegrationToolDef, // Reads one hub document over a GET and writes nothing, so planning may use it. planModeSafe: true, - fn: async ({ args, toolId, toolCallbacks }) => { + fn: async ({ args, workspace, toolId, toolCallbacks }) => { const { integration } = getHubIntegrationSchema.parse(args) toolCallbacks.setToolStatus(toolId, { content: `Reading the ${integration} integration...` }) @@ -1745,6 +1765,10 @@ export const getHubIntegrationTool = { // every section is read as optional: a hub that sends less should return less, // not fail the call. const derived = doc.derived + const resourceTypesNote = await missingResourceTypesNote( + workspace, + (doc.resource_types ?? []).map((rt) => rt.name) + ) return JSON.stringify({ integration: doc.app, display_name: doc.display_name, @@ -1778,6 +1802,7 @@ export const getHubIntegrationTool = { ...(rt.description ? { description: rt.description } : {}), schema: parseResourceTypeSchema(rt.schema) })), + ...(resourceTypesNote ? { resource_types_note: resourceTypesNote } : {}), example_scripts: (derived?.top_scripts ?? []).slice(0, MAX_INTEGRATION_EXAMPLES).map((s) => ({ path: s.path, summary: s.summary, diff --git a/system_prompts/auto-generated/prompts.ts b/system_prompts/auto-generated/prompts.ts index 00e5655154..1902e43804 100644 --- a/system_prompts/auto-generated/prompts.ts +++ b/system_prompts/auto-generated/prompts.ts @@ -5,6 +5,7 @@ export const SCRIPT_BASE = `# Windmill Script Writing Guide ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it \`main\` (\`Main\` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no \`main\`: their language section shows how they take arguments - Where the language has a Windmill client (\`wmill\`), use it to interact with the platform @@ -827,6 +828,15 @@ The prefix must be on the string itself. } \`\`\` +## When No Resource Type Fits + +The Windmill Hub publishes resource types for most services, and an instance gets them by syncing with the hub. When none fits the service: + +- If looking up resource types reports that the instance is missing the hub's types, tell the user so that a superadmin syncs, and don't create a type for the service meanwhile: the hub most likely has one, and after the sync the workspace would hold two. +- Otherwise propose a resource type holding the service's connection fields (base URL, account or region identifiers, credentials), as in "Custom Resource Types" below, and ask the user before creating it: every resource of that type and every script taking one depends on its fields. +- You cannot create a resource type yourself: give the user the schema to add with **Add resource type** on the Resources page. +- Use a plain variable instead of a resource only when the user prefers it. + ## Custom Resource Types Create custom resource types with JSON Schema: diff --git a/system_prompts/auto-generated/script.md b/system_prompts/auto-generated/script.md index cb5ae40490..7a00d648c4 100644 --- a/system_prompts/auto-generated/script.md +++ b/system_prompts/auto-generated/script.md @@ -3,6 +3,7 @@ ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/resources/SKILL.md b/system_prompts/auto-generated/skills/resources/SKILL.md index bd690b1dec..986d423032 100644 --- a/system_prompts/auto-generated/skills/resources/SKILL.md +++ b/system_prompts/auto-generated/skills/resources/SKILL.md @@ -214,6 +214,14 @@ The prefix must be on the string itself. } ``` +## When No Resource Type Fits + +The Windmill Hub publishes resource types for most services, and an instance gets them by syncing with the hub. When none fits the service: + +- If looking up resource types reports that the instance is missing the hub's types, tell the user so that a superadmin syncs, and don't create a type for the service meanwhile: the hub most likely has one, and after the sync the workspace would hold two. +- Otherwise propose a resource type holding the service's connection fields (base URL, account or region identifiers, credentials), as in "Custom Resource Types" below, and ask the user before creating it: every resource of that type and every script taking one depends on its fields. +- Use a plain variable instead of a resource only when the user prefers it. + ## Custom Resource Types Create custom resource types with JSON Schema: diff --git a/system_prompts/auto-generated/skills/write-script-ansible/SKILL.md b/system_prompts/auto-generated/skills/write-script-ansible/SKILL.md index 9bb87cb4a1..fef66bc86c 100644 --- a/system_prompts/auto-generated/skills/write-script-ansible/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-ansible/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-bash/SKILL.md b/system_prompts/auto-generated/skills/write-script-bash/SKILL.md index 0c4634b775..b3509b5379 100644 --- a/system_prompts/auto-generated/skills/write-script-bash/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-bash/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-bigquery/SKILL.md b/system_prompts/auto-generated/skills/write-script-bigquery/SKILL.md index 497336b001..5172e8d9c0 100644 --- a/system_prompts/auto-generated/skills/write-script-bigquery/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-bigquery/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-bun/SKILL.md b/system_prompts/auto-generated/skills/write-script-bun/SKILL.md index 21b2f382c3..33cb58fac1 100644 --- a/system_prompts/auto-generated/skills/write-script-bun/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-bun/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-bunnative/SKILL.md b/system_prompts/auto-generated/skills/write-script-bunnative/SKILL.md index f9e902fe78..cd799b2a3e 100644 --- a/system_prompts/auto-generated/skills/write-script-bunnative/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-bunnative/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-csharp/SKILL.md b/system_prompts/auto-generated/skills/write-script-csharp/SKILL.md index 389c683f7c..07646ed623 100644 --- a/system_prompts/auto-generated/skills/write-script-csharp/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-csharp/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-deno/SKILL.md b/system_prompts/auto-generated/skills/write-script-deno/SKILL.md index 71608cd16e..0602e83e65 100644 --- a/system_prompts/auto-generated/skills/write-script-deno/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-deno/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-duckdb/SKILL.md b/system_prompts/auto-generated/skills/write-script-duckdb/SKILL.md index 2ffcf4607c..8492d4d109 100644 --- a/system_prompts/auto-generated/skills/write-script-duckdb/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-duckdb/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-go/SKILL.md b/system_prompts/auto-generated/skills/write-script-go/SKILL.md index 1707fcda4c..6d9590bd19 100644 --- a/system_prompts/auto-generated/skills/write-script-go/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-go/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-graphql/SKILL.md b/system_prompts/auto-generated/skills/write-script-graphql/SKILL.md index c928dea3bc..9d67f725b1 100644 --- a/system_prompts/auto-generated/skills/write-script-graphql/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-graphql/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-java/SKILL.md b/system_prompts/auto-generated/skills/write-script-java/SKILL.md index a768a5faa5..cf34a9dcbb 100644 --- a/system_prompts/auto-generated/skills/write-script-java/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-java/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-mssql/SKILL.md b/system_prompts/auto-generated/skills/write-script-mssql/SKILL.md index 9215f06906..9674ff51b6 100644 --- a/system_prompts/auto-generated/skills/write-script-mssql/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-mssql/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-mysql/SKILL.md b/system_prompts/auto-generated/skills/write-script-mysql/SKILL.md index d682cce351..253b6a65f3 100644 --- a/system_prompts/auto-generated/skills/write-script-mysql/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-mysql/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-php/SKILL.md b/system_prompts/auto-generated/skills/write-script-php/SKILL.md index 16c68834dc..97e872446b 100644 --- a/system_prompts/auto-generated/skills/write-script-php/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-php/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-postgresql/SKILL.md b/system_prompts/auto-generated/skills/write-script-postgresql/SKILL.md index b0e14a6b12..71840a0c95 100644 --- a/system_prompts/auto-generated/skills/write-script-postgresql/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-postgresql/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-powershell/SKILL.md b/system_prompts/auto-generated/skills/write-script-powershell/SKILL.md index 540776c68a..1b085aeb7c 100644 --- a/system_prompts/auto-generated/skills/write-script-powershell/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-powershell/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-python3/SKILL.md b/system_prompts/auto-generated/skills/write-script-python3/SKILL.md index decb356ba6..409b384972 100644 --- a/system_prompts/auto-generated/skills/write-script-python3/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-python3/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-rlang/SKILL.md b/system_prompts/auto-generated/skills/write-script-rlang/SKILL.md index 061e1dcc45..f34f7a2d97 100644 --- a/system_prompts/auto-generated/skills/write-script-rlang/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-rlang/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-rust/SKILL.md b/system_prompts/auto-generated/skills/write-script-rust/SKILL.md index d9b599dffa..224cc5cf74 100644 --- a/system_prompts/auto-generated/skills/write-script-rust/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-rust/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/auto-generated/skills/write-script-snowflake/SKILL.md b/system_prompts/auto-generated/skills/write-script-snowflake/SKILL.md index 8c438e7038..cbd87bddbc 100644 --- a/system_prompts/auto-generated/skills/write-script-snowflake/SKILL.md +++ b/system_prompts/auto-generated/skills/write-script-snowflake/SKILL.md @@ -53,6 +53,7 @@ Use `wmill resource-type list --schema` to discover available resource types. ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform diff --git a/system_prompts/base/resources.md b/system_prompts/base/resources.md index 2929051a03..92c99292bd 100644 --- a/system_prompts/base/resources.md +++ b/system_prompts/base/resources.md @@ -213,6 +213,17 @@ The prefix must be on the string itself. } ``` +## When No Resource Type Fits + +The Windmill Hub publishes resource types for most services, and an instance gets them by syncing with the hub. When none fits the service: + +- If looking up resource types reports that the instance is missing the hub's types, tell the user so that a superadmin syncs, and don't create a type for the service meanwhile: the hub most likely has one, and after the sync the workspace would hold two. +- Otherwise propose a resource type holding the service's connection fields (base URL, account or region identifiers, credentials), as in "Custom Resource Types" below, and ask the user before creating it: every resource of that type and every script taking one depends on its fields. + +- You cannot create a resource type yourself: give the user the schema to add with **Add resource type** on the Resources page. + +- Use a plain variable instead of a resource only when the user prefers it. + ## Custom Resource Types Create custom resource types with JSON Schema: diff --git a/system_prompts/base/script-base.md b/system_prompts/base/script-base.md index 9bd878ea9b..6e94dd4b85 100644 --- a/system_prompts/base/script-base.md +++ b/system_prompts/base/script-base.md @@ -3,6 +3,7 @@ ## General Principles - A script's inputs are its parameters. Credentials and configuration come in as resource-typed parameters, never hard-coded or read from the environment; the language section below shows how that language declares parameters +- When no resource type exists for a service, don't fall back to a plain string parameter or a variable read inside the code. If looking up resource types reports that the instance is missing the Windmill Hub's types, tell the user so that a superadmin syncs: the hub has types for most services. Otherwise propose a resource type holding the service's connection fields and ask before creating it. Use a variable only if the user prefers one: a resource keeps a connection's fields together, lets whoever runs the script pick an existing connection, and keeps the secret out of the arguments a run records - Libraries are installed automatically - do not show installation instructions - In a language with an entrypoint function (TypeScript, Python, Go, Rust, PHP, R, …), name it `main` (`Main` in C#) and do not call it; in TypeScript it must be async. SQL, GraphQL, Bash, PowerShell and Ansible scripts have no `main`: their language section shows how they take arguments - Where the language has a Windmill client (`wmill`), use it to interact with the platform