feat: read and change workspace settings from ai sessions and mcp (#11741)

* feat: read and change workspace settings from ai sessions and mcp

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: cover wmill workspace settings commands in the settings guidance

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: say unknown settings keys are warned and ee-only ones refused

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: cover datatable_permissions and protection_rules in the settings guidance

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-10-14 13:06:26 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent a2ee052e03
commit 468cf1d994
16 changed files with 634 additions and 6 deletions
+9
View File
@@ -5191,6 +5191,8 @@ paths:
The workspace's settings in the layout of the settings.yaml that `wmill sync` writes.
Admin only.
operationId: getWorkspaceSettingsDocument
x-mcp-tool: true
x-mcp-tool-include-query-params: []
tags:
- workspace
parameters:
@@ -5217,6 +5219,13 @@ paths:
description: >-
Applies a settings document, each setting through the endpoint that owns it. Admin only.
operationId: applyWorkspaceSettingsDocument
x-mcp-tool: true
x-mcp-instructions: "Send only the top-level keys to change, each whole as getWorkspaceSettingsDocument returns it: a key the document omits is left as it is, and a key set to null clears it. Keep `__SENSITIVE_AND_UNCHANGED__` for a secret you were not given. Call with dry_run true first, show the user the changes and apply only once they agree."
x-mcp-tool-include-fields:
- document
- dry_run
x-mcp-tool-opaque-fields:
- document
tags:
- workspace
parameters:
@@ -56,6 +56,46 @@ pub fn all_tools() -> Vec<EndpointTool> {
query_field_renames: None,
body_field_renames: None,
},
EndpointTool {
name: Cow::Borrowed("getWorkspaceSettingsDocument"),
description: Cow::Borrowed("get the workspace settings document: The workspace's settings in the layout of the settings.yaml that `wmill sync` writes. Admin only"),
instructions: Cow::Borrowed(""),
path: Cow::Borrowed("/w/{workspace}/workspaces/settings_document"),
method: Cow::Borrowed("GET"),
path_params_schema: None,
query_params_schema: None,
body_schema: None,
query_field_renames: None,
body_field_renames: None,
},
EndpointTool {
name: Cow::Borrowed("applyWorkspaceSettingsDocument"),
description: Cow::Borrowed("apply a workspace settings document: Applies a settings document, each setting through the endpoint that owns it. Admin only"),
instructions: Cow::Borrowed("Send only the top-level keys to change, each whole as getWorkspaceSettingsDocument returns it: a key the document omits is left as it is, and a key set to null clears it. Keep `__SENSITIVE_AND_UNCHANGED__` for a secret you were not given. Call with dry_run true first, show the user the changes and apply only once they agree."),
path: Cow::Borrowed("/w/{workspace}/workspaces/settings_document"),
method: Cow::Borrowed("POST"),
path_params_schema: None,
query_params_schema: None,
body_schema: Some(serde_json::json!({
"type": "object",
"properties": {
"document": {
"type": "object",
"description": "The settings to apply, in the layout of the document. A secret given as `__SENSITIVE_AND_UNCHANGED__` keeps its stored value; one prefixed with `$encrypted:` is decrypted with the workspace key."
},
"dry_run": {
"description": "Only compute the changes, without applying them.",
"type": "boolean"
}
},
"required": [
"document"
],
"minProperties": 1
})),
query_field_renames: None,
body_field_renames: None,
},
EndpointTool {
name: Cow::Borrowed("listDataMetrics"),
description: Cow::Borrowed("list declared measures and dimensions on DuckLake tables: Call this before writing any aggregate query over a DuckLake table. A declared measure is the canonical definition of that number, and reproducing it yourself will silently disagree with it (a `revenue` measure typically excludes refunds or test rows). Filter by `table` for one table's declarations, or by `path_prefix` (e.g. `f/analytics`) for everything declared under a folder; omit both to browse the whole catalog. Results are keyset-paged: a full page may mean more remain, so continue with the `cursor_*` params rather than assuming a measure does not exist. Use each returned `expr` verbatim, and when a measure has a `filter` write it as `expr FILTER (WHERE filter)` so measures with different predicates can share one GROUP BY. If a number you need has no declared measure, write your own aggregate as usual. Results are limited to declarations whose producing script the caller can read"),
+86
View File
@@ -35,6 +35,7 @@ export const SKILLS: SkillMetadata[] = [
{ name: "write-workflow-as-code", description: "MUST use when writing or modifying Windmill Workflow-as-Code scripts using workflow, task, step, sleep, approvals, taskScript, taskFlow, task_script, or task_flow." },
{ name: "write-pipeline", description: "MUST use when creating or modifying a data pipeline, a set of scripts marked `pipeline` and wired together by `on` / `materialize` annotations." },
{ name: "datatables", description: "MUST use when changing a data table's schema, writing or running its migrations, or working with its roles and permissions." },
{ name: "workspace-settings", description: "MUST use when reading or changing workspace settings, or the settings.yaml file `wmill sync` keeps." },
{ name: "cli-commands", description: "MUST use when using the CLI, including debugging job failures and inspecting run history via `wmill job`." },
{ name: "preview", description: "MUST use when opening the Windmill dev page / visual preview of a flow, script, or app. Triggers on words like preview, open, navigate to, visualize, see the flow/app/script, and after writing a flow/script/app for visual verification." },
];
@@ -8263,6 +8264,91 @@ A data table under roles is reached as a Postgres role, and Postgres enforces wh
- a migration runs as the role its SQL declares on its first line, \`-- role <name>\`, the same one in its \`down\`. One that declares none runs as \`admin\`, which only those who may use \`admin\` can do.
Read the Permissions section of https://www.windmill.dev/docs/core_concepts/persistent_storage/data_tables before designing who reaches what: it covers tenants, grants, the default role and the roles a connection that names none ends up holding, rows that differ per viewer through row-level security, and how forks and raw apps behave under roles.
`,
"workspace-settings": `---
name: workspace-settings
description: MUST use when reading or changing workspace settings, or the settings.yaml file \`wmill sync\` keeps.
---
# Workspace settings
Everything an admin sets on the Workspace settings page is one document: a mapping of top-level
keys, the same layout as the \`settings.yaml\` file \`wmill sync\` keeps. Only a workspace admin can
read or change it.
## Applying a document
- A partial document changes only the keys it states; every key it omits is left as it is.
- Each key is replaced whole, so read its current value and send it back edited: a nested object
given with one field replaces the stored object with that one field.
- A key set to \`null\` clears that setting; an on/off switch cleared this way reads as \`false\`.
- Each setting is written through the same path the settings page uses, with the same validation
and audit log. The writes are not one transaction: when one fails the apply stops, and the
error says what was already written.
- A dry run reports the changes, as \`before\` / \`after\` per key, without writing anything.
- \`name\` is never applied: rename a workspace on the settings page. \`color\` is applied only when
set to a value, so \`null\` does not clear it.
- A key the server does not know is ignored and named in the warnings: check them, since a typo
otherwise reads as "nothing to change".
- A setting this edition does not offer (Enterprise only) is refused, by the dry run too.
## Secrets
\`slack_oauth_client_secret\` is the one secret. A read shows \`__SENSITIVE_AND_UNCHANGED__\` in place
of a set secret; sending that placeholder back keeps the stored value. A value starting with
\`$encrypted:\` is decrypted with the workspace key, and one encrypted with another workspace's key
is left unchanged with a warning; any other value is taken as plaintext. Never
write a secret you were not given: keep the placeholder.
## Keys
| Key | Value |
|---|---|
| \`auto_invite\` | \`{ enabled, mode: invite \\| add, operator, instance_groups?, instance_groups_roles? }\`. The instance groups are managed only when \`instance_groups\` is stated; clearing a group removes the members it added. |
| \`error_handler\` / \`success_handler\` | \`{ path, extra_args?, muted_on_cancel?, muted_on_user_path? }\`, \`path\` being \`script/<path>\` or \`flow/<path>\`. |
| \`error_handler_fallback_to_instance_alerts\` | Report failures to the instance critical alert channels when no error handler is set. |
| \`webhook\` | URL POSTed a JSON event on every change to the workspace's items. |
| \`ai_config\` | Providers, default and completion models, custom prompts, \`copilot_disabled\`. |
| \`large_file_storage\` | Workspace object storage: \`{ type, s3_resource_path \\| azure_blob_resource_path \\| gcs_resource_path, public_resource?, secondary_storage?, advanced_permissions? }\`. |
| \`git_sync\` | \`{ repositories: [...] }\`, each with \`git_repo_resource_path\`, \`script_path\` and \`settings\` (include/exclude paths and types). Server-owned fields (webhook secrets, sync state, credentials) are never read nor written. |
| \`datatable\` | \`{ datatables: { <name>: { database: { resource_type, resource_path? } } } }\`. Role permissions are \`datatable_permissions\`. |
| \`datatable_permissions\` | \`{ <data table>: { default_role, roles: { <role>: [tenants] } } }\`: who may connect to each data table as which role. Tenants are \`u/<user>\`, \`g/<group>\`, \`f/<folder>\` or \`*\`; \`admin\` is always a role, and \`default_role: null\` means no default. A data table the value omits is no longer under roles. Only a data table on a cluster Windmill manages can be. |
| \`protection_rules\` | \`{ <rule name>: { rules: [...], bypass_groups: [...], bypass_users: [...] } }\`, rules among \`DisableDirectDeployment\`, \`DisableWorkspaceForking\`, \`RestrictDeployToDeployers\`, \`RestrictAnonymousAppDeployment\`, \`RestrictPublicRunSharing\`, \`RestrictGuestAppDeployment\`. A rule the value omits is deleted. The dev workspace lock is not listed and cannot be set here. |
| \`ducklake\` | \`{ ducklakes: { <name>: { catalog, storage, ... } } }\`. |
| \`dbt_warehouses\` | dbt warehouse connections. |
| \`default_app\` | Path of the app shown as the workspace home. |
| \`default_scripts\` | \`{ order?, hidden?, default_script_content? }\` for the script language picker. |
| \`deploy_ui\` | \`{ include_path?, include_type? }\`: what the deploy-to-workspace UI offers. |
| \`operator_settings\` | What operators see and do: page visibility flags (\`runs\`, \`schedules\`, \`resources\`, \`variables\`, \`assets\`, \`audit_logs\`, \`groups\`, \`folders\`, \`workers\`, \`triggers\`) and write rights (\`manage_schedules\`, \`manage_triggers\`, \`builder_flows\`, \`builder_apps\`). A write right the value omits keeps its stored value. |
| \`mute_critical_alerts\` | Mute the workspace's critical alerts. |
| \`slack_team_id\`, \`slack_name\` | Read only: set by connecting Slack on the settings page. |
| \`slack_command_script\` / \`teams_command_script\` | Script or flow handling the Slack / Teams command. |
| \`slack_oauth_client_id\` / \`slack_oauth_client_secret\` | The workspace's own Slack OAuth app. Clearing either removes both. |
| \`public_app_execution_limit_per_minute\` | Rate limit for public apps, \`null\` for none. |
| \`guest_access_enabled\`, \`guest_jwt_public_key\`, \`guest_jwt_jwks_url\` | Guest access to public apps and the key that signs guest JWTs. |
| \`add_admins_and_developers_to_forks\` | Add the workspace's admins and developers to each fork. |
| \`color\` | Workspace color, \`#rrggbb\`. |
## From the command line
\`wmill workspace get-settings\` prints the document (\`--encrypted\` for secrets encrypted with the
workspace key, \`--json\` for JSON). \`wmill workspace apply-settings <file>\` applies a partial
document from a YAML or JSON file: it shows the changes and asks before writing (\`--dry-run\` to
only show them, \`-y\` to skip the question, \`--replace\` to clear what the file omits).
## In a synced repository
With \`includeSettings: true\` in \`wmill.yaml\`, \`wmill sync pull\` writes the document to
\`settings.yaml\`, secrets encrypted with the workspace key (plaintext only with \`plainSecrets\`), and
\`wmill sync push\` applies it. Unlike a partial apply, a push treats the file as the whole
document: a setting the file omits is cleared, except \`operator_settings\`, \`color\`, the auto-invite
instance groups, and the keys \`ducklake\`, \`dbt_warehouses\`, \`deploy_ui\`, \`teams_command_script\`,
\`public_app_execution_limit_per_minute\`, \`guest_access_enabled\`, \`guest_jwt_public_key\`,
\`guest_jwt_jwks_url\`, \`error_handler_fallback_to_instance_alerts\` and
\`add_admins_and_developers_to_forks\`, which are managed only when the file states them.
\`datatable_permissions\` is pushed only with \`includeDatatablePermissions: true\` (or
\`--include-datatable-permissions\`). A push ignores \`protection_rules\`: \`protection-rules.yaml\` and
\`wmill protection-rules push\` sync them.
`,
"cli-commands": `---
name: cli-commands
@@ -3,6 +3,8 @@
import ChatCollapsibleCard from './ChatCollapsibleCard.svelte'
import ToolCodeDiffView from './ToolCodeDiffView.svelte'
import ToolPreviewCard from './ToolPreviewCard.svelte'
import ToolConfirmationFooter from './ToolConfirmationFooter.svelte'
import { Check, XCircle } from 'lucide-svelte'
import {
diffIsWholeFile,
diffLineCounts,
@@ -21,8 +23,9 @@
const diff = $derived(toolCodeDiff(message))
const isStreaming = $derived(Boolean(message.isStreamingArguments))
const hasDiff = $derived(Boolean(diff && (diff.before !== '' || diff.after !== '')))
const awaitingConfirmation = $derived(Boolean(message.isLoading && message.needsConfirmation))
// The card body is a bordered box: opened with nothing in it, it draws an empty line.
const hasBody = $derived(hasDiff || Boolean(message.error))
const hasBody = $derived(hasDiff || Boolean(message.error) || awaitingConfirmation)
// Keyed by call id: a bare flag would carry the expansion onto the next message that
// reuses this instance. An active or failed call opens by itself, and remains open
@@ -45,7 +48,11 @@
toggled = { id: toggleKey, open: true }
}
})
const expanded = $derived(hasBody && (toggled?.id === toggleKey ? toggled.open : opensByDefault))
// The Apply/Reject footer is inside the body, so a pending confirmation keeps it open.
const expanded = $derived(
awaitingConfirmation ||
(hasBody && (toggled?.id === toggleKey ? toggled.open : opensByDefault))
)
const lines = $derived(diff && expanded ? toolDiffLines(diff, isStreaming) : undefined)
const counts = $derived(
diff && expanded
@@ -79,7 +86,7 @@
label={message.content}
{expanded}
onToggle={() => (toggled = { id: toggleKey, open: !expanded })}
toggleable={hasBody}
toggleable={hasBody && !awaitingConfirmation}
shimmer={isRunning}
class={message.isQueued && !message.error
? 'opacity-60 hover:opacity-100 transition-opacity'
@@ -91,6 +98,16 @@
{#if diff && hasDiff && expanded}
<ToolCodeDiffView {diff} diffLines={lines} streaming={isStreaming} />
{/if}
{#if awaitingConfirmation}
<ToolConfirmationFooter
class="px-3 py-2 border-t border-border-light"
toolCallId={message.tool_call_id}
rejectIcon={XCircle}
rejectDestructive
confirmLabel="Apply"
confirmIcon={Check}
/>
{/if}
{#if message.error}
<div
class="px-3 py-2 text-2xs text-red-600 dark:text-red-400 whitespace-pre-wrap break-words {hasDiff
@@ -128,6 +128,7 @@ import {
import { MAX_SKILL_DESCRIPTION_LENGTH, MAX_SKILL_INSTRUCTIONS_LENGTH } from '../skills/skillMd'
import {
getDatatablePrompt,
getWorkspaceSettingsPrompt,
getDatatableSdkReference,
getFlowPrompt,
getPipelinePrompt,
@@ -218,6 +219,7 @@ import {
getDraftDiffValues
} from '$lib/utils_draft_deploy'
import { changedLineIndices, draftDeployedPatch, windowPatch } from './draftDiff'
import { workspaceSettingsTools } from './workspaceSettingsTools'
import { getRun, summarizeRun } from './flowRunTree'
import { UserDraftDbSyncer } from '$lib/userDraftDbSyncer.svelte'
import { invalidateWorkspaceComparison } from '$lib/workspaceComparison'
@@ -318,7 +320,8 @@ const INSTRUCTION_SUBJECTS = [
// datatable SDK reference (the wmill.datatable() runnable API) the same way.
// `pipeline` likewise isn't an item type — a data pipeline is a set of
// annotated scripts in a folder, so it gets authoring guidance, not a CRUD type.
const INSTRUCTION_SUBJECTS_EXTRA = ['datatable', 'pipeline'] as const
// `workspace_settings` is the settings document an admin reads and changes.
const INSTRUCTION_SUBJECTS_EXTRA = ['datatable', 'pipeline', 'workspace_settings'] as const
const ALL_INSTRUCTION_SUBJECTS = [...INSTRUCTION_SUBJECTS, ...INSTRUCTION_SUBJECTS_EXTRA] as const
const MAX_LIST_LIMIT = 100
type ActiveGlobalEditorType = Extract<WorkspaceItemType, 'script' | 'flow' | 'app'>
@@ -378,7 +381,7 @@ const scriptLangSchema = z.enum($ScriptLang.enum)
const getInstructionsSchema = z.object({
subject: instructionSubjectSchema.describe(
'What to get authoring instructions for: a workspace item type (script, flow, resource, app), "pipeline" for building a data pipeline (a DAG of annotated scripts wired by storage assets — NOT a flow), or "datatable" for how data tables work (schema changes and migrations, roles and permissions) plus the wmill.datatable() SQL SDK used inside runnables. Schedules, triggers, and variables don\'t need instructions — their tool schemas describe everything.'
'What to get authoring instructions for: a workspace item type (script, flow, resource, app), "pipeline" for building a data pipeline (a DAG of annotated scripts wired by storage assets — NOT a flow), or "datatable" for how data tables work (schema changes and migrations, roles and permissions) plus the wmill.datatable() SQL SDK used inside runnables, or "workspace_settings" for the keys and rules of the workspace settings (admins only). Schedules, triggers, and variables don\'t need instructions — their tool schemas describe everything.'
),
language: scriptLangSchema
.optional()
@@ -2670,6 +2673,8 @@ function getInstructions(
return getDatatableInstructions(language)
case 'pipeline':
return getPipelineInstructions()
case 'workspace_settings':
return getWorkspaceSettingsPrompt()
}
}
@@ -3614,6 +3619,10 @@ export const globalTools: SessionTool<{}>[] = [
// Every subject is authoring guidance written around the draft tools by name, and a
// tool result is the one place the toolset filter cannot reach.
const access = (ctx.helpers as GlobalToolHelpers | undefined)?.access
if (parsed.subject === 'workspace_settings' && access && !access.has('admin')) {
toolCallbacks.setToolStatus(toolId, { content: 'No workspace settings guidance' })
return 'Only a workspace admin can read or change the workspace settings. Tell the user plainly that you cannot, and suggest asking a workspace admin.'
}
const canAuthor =
!access ||
access.has('write_draft') ||
@@ -4958,7 +4967,8 @@ export const globalTools: SessionTool<{}>[] = [
// Workspace DuckLake: pipeline storage prerequisite, and declared measures
...getDucklakeTools(),
// Read-only tools over files the user attached to the conversation
...fileTools
...fileTools,
...workspaceSettingsTools
]
// Tools that only make sense inside an AI session (they drive the session's
@@ -197,6 +197,15 @@ describe('session tool policies', () => {
expect((original.def.function.parameters as any).properties.type.enum).toContain('script')
})
it('offers the workspace settings tools to admins only', () => {
const settingsTools = ['get_workspace_settings', 'set_workspace_settings']
for (const [, access] of REACHABLE_PROFILES) {
const names = shippedSessionTools(access).map((t) => t.def.function.name)
const offered = settingsTools.filter((n) => names.includes(n))
expect(offered).toEqual(access.has('admin') ? settingsTools : [])
}
})
// Naming a tool the model was not given produces invented calls. Asserted over the
// ASSEMBLED message: the session-state, pipeline and plan-mode sections are each
// appended by a different caller, and each can name a withheld tool.
@@ -0,0 +1,169 @@
import { z } from 'zod'
import YAML from 'yaml'
import { ApiError, WorkspaceService, type ApplyWorkspaceSettingsDocumentResponse } from '$lib/gen'
import { createToolDef, type ToolCodeDiff } from '../shared'
import type { SessionTool, SessionToolPolicy } from '../sessionCapabilities'
import { toStableYaml } from './draftDiff'
// Both endpoints call `require_admin`.
const ADMIN: SessionToolPolicy = ['admin']
const MAX_READ_CHARS = 20_000
type SettingsChange = ApplyWorkspaceSettingsDocumentResponse['changes'][number]
const getWorkspaceSettingsSchema = z.object({
keys: z
.array(z.string())
.optional()
.describe('Only these top-level keys. Omit for the whole document.')
})
const setWorkspaceSettingsSchema = z.object({
yaml: z
.string()
.describe(
'A YAML mapping of only the top-level keys to change, each given whole (a nested object replaces the stored one). A key set to null clears it.'
)
})
function apiErrorText(e: unknown): string {
if (e instanceof ApiError) {
return typeof e.body === 'string' ? e.body : JSON.stringify(e.body)
}
return e instanceof Error ? e.message : String(e)
}
function parseSettingsYaml(text: string): Record<string, unknown> {
const doc = YAML.parse(text)
if (doc === null || typeof doc !== 'object' || Array.isArray(doc)) {
throw new Error('The yaml must be a mapping of setting keys to values.')
}
return doc
}
// An unset value is left out rather than written as null, so a side with nothing set is empty.
function changesSide(changes: SettingsChange[], side: 'before' | 'after'): string {
const set = changes.filter((c) => c[side] != null)
return set.length ? toStableYaml(Object.fromEntries(set.map((c) => [c.key, c[side]]))) : ''
}
function changesDiff(changes: SettingsChange[]): ToolCodeDiff {
return {
before: changesSide(changes, 'before'),
after: changesSide(changes, 'after'),
lang: 'yaml'
}
}
// The dry run answers before the confirmation card, and the card shows its changes. Keyed by
// the call's parsed arguments, the one object both hooks are handed.
const dryRuns = new WeakMap<object, SettingsChange[]>()
export const workspaceSettingsTools: SessionTool<{}>[] = [
{
requires: ADMIN,
def: createToolDef(
getWorkspaceSettingsSchema,
'get_workspace_settings',
'Read the workspace settings as YAML, in the layout of the settings.yaml file the CLI syncs. Secrets read as `__SENSITIVE_AND_UNCHANGED__`.'
),
planModeSafe: true,
showDetails: true,
fn: async ({ args, workspace, toolId, toolCallbacks }) => {
const parsed = getWorkspaceSettingsSchema.parse(args)
const doc = (await WorkspaceService.getWorkspaceSettingsDocument({
workspace,
secrets: 'redacted'
})) as Record<string, unknown>
let picked = doc
let note = ''
if (parsed.keys?.length) {
picked = Object.fromEntries(parsed.keys.filter((k) => k in doc).map((k) => [k, doc[k]]))
const unset = parsed.keys.filter((k) => !(k in doc))
if (unset.length) {
note = `\n# Not in the document (unset, or not a setting): ${unset.join(', ')}`
}
}
let text = YAML.stringify(picked, { aliasDuplicateObjects: false, lineWidth: 0 }) + note
if (text.length > MAX_READ_CHARS) {
text = `${text.slice(0, MAX_READ_CHARS)}\n… truncated at ${MAX_READ_CHARS} chars; read the keys you need with \`keys\`.`
}
toolCallbacks.setToolStatus(toolId, {
content: parsed.keys?.length
? `Read workspace settings: ${parsed.keys.join(', ')}`
: 'Read workspace settings'
})
return text
}
},
{
requires: ADMIN,
def: createToolDef(
setWorkspaceSettingsSchema,
'set_workspace_settings',
'Change workspace settings. Keys the yaml omits are left as they are. The user reviews the changes before they apply. Read get_instructions subject "workspace_settings" before your first change.'
),
requiresConfirmation: true,
confirmationMessage: 'Change workspace settings',
cancellationMessage: 'The user declined: no setting was changed.',
showDetails: true,
validateBeforeConfirmation: async ({ args, workspace }) => {
let changes: SettingsChange[]
let warnings: string[] | undefined
try {
const document = parseSettingsYaml(setWorkspaceSettingsSchema.parse(args).yaml)
;({ changes, warnings } = await WorkspaceService.applyWorkspaceSettingsDocument({
workspace,
requestBody: { document, replace: false, dry_run: true }
}))
} catch (e) {
const error = apiErrorText(e)
return { label: `Invalid settings: ${error}`, result: `Nothing was changed: ${error}` }
}
if (changes.length === 0) {
return {
label: 'No settings change',
result: `The settings already hold these values: nothing to change.${
warnings?.length ? ` Warnings: ${warnings.join(' ')}` : ''
}`
}
}
dryRuns.set(args, changes)
return undefined
},
onConfirmationRequested: ({ args, toolCallbacks, toolId }) => {
const changes = dryRuns.get(args)
if (changes) {
toolCallbacks.setToolStatus(toolId, { codeDiff: changesDiff(changes) })
}
},
fn: async ({ args, workspace, toolId, toolCallbacks }) => {
const document = parseSettingsYaml(setWorkspaceSettingsSchema.parse(args).yaml)
toolCallbacks.setToolStatus(toolId, { content: 'Changing workspace settings...' })
try {
const { changes, warnings } = await WorkspaceService.applyWorkspaceSettingsDocument({
workspace,
requestBody: { document, replace: false }
})
const keys = changes.map((c) => c.key)
toolCallbacks.setToolStatus(toolId, {
content: `Changed workspace settings: ${keys.join(', ')}`,
codeDiff: changesDiff(changes)
})
return JSON.stringify({
success: true,
message: `The user approved and these settings are now applied: ${keys.join(', ')}.`,
...(warnings?.length ? { warnings } : {})
})
} catch (e) {
const error = apiErrorText(e)
toolCallbacks.setToolStatus(toolId, {
content: 'Failed to change workspace settings',
error
})
return JSON.stringify({ success: false, error })
}
}
}
]
+40
View File
@@ -65,6 +65,46 @@ export const mcpEndpointTools: EndpointTool[] = [
queryFieldRenames: undefined,
bodyFieldRenames: undefined
},
{
name: "getWorkspaceSettingsDocument",
description: "get the workspace settings document: The workspace's settings in the layout of the settings.yaml that `wmill sync` writes. Admin only",
instructions: "",
path: "/w/{workspace}/workspaces/settings_document",
method: "GET",
pathParamsSchema: undefined,
queryParamsSchema: undefined,
bodySchema: undefined,
queryFieldRenames: undefined,
bodyFieldRenames: undefined
},
{
name: "applyWorkspaceSettingsDocument",
description: "apply a workspace settings document: Applies a settings document, each setting through the endpoint that owns it. Admin only",
instructions: "Send only the top-level keys to change, each whole as getWorkspaceSettingsDocument returns it: a key the document omits is left as it is, and a key set to null clears it. Keep `__SENSITIVE_AND_UNCHANGED__` for a secret you were not given. Call with dry_run true first, show the user the changes and apply only once they agree.",
path: "/w/{workspace}/workspaces/settings_document",
method: "POST",
pathParamsSchema: undefined,
queryParamsSchema: undefined,
bodySchema: {
"type": "object",
"properties": {
"document": {
"type": "object",
"description": "The settings to apply, in the layout of the document. A secret given as `__SENSITIVE_AND_UNCHANGED__` keeps its stored value; one prefixed with `$encrypted:` is decrypted with the workspace key."
},
"dry_run": {
"description": "Only compute the changes, without applying them.",
"type": "boolean"
}
},
"required": [
"document"
],
"minProperties": 1
},
queryFieldRenames: undefined,
bodyFieldRenames: undefined
},
{
name: "listDataMetrics",
description: "list declared measures and dimensions on DuckLake tables: Call this before writing any aggregate query over a DuckLake table. A declared measure is the canonical definition of that number, and reproducing it yourself will silently disagree with it (a `revenue` measure typically excludes refunds or test rows). Filter by `table` for one table's declarations, or by `path_prefix` (e.g. `f/analytics`) for everything declared under a folder; omit both to browse the whole catalog. Results are keyset-paged: a full page may mean more remain, so continue with the `cursor_*` params rather than assuming a measure does not exist. Use each returned `expr` verbatim, and when a measure has a `filter` write it as `expr FILTER (WHERE filter)` so measures with different predicates can share one GROUP BY. If a number you need has no declared measure, write your own aggregate as usual. Results are limited to declarations whose producing script the caller can read",
+1
View File
@@ -47,6 +47,7 @@ a CLI-only workflow file (`flow-cli.md`, `script-cli.md`, `raw-app-cli.md`).
| workflow-as-code | `getWorkflowAsCodePrompt(lang)` | `write-workflow-as-code` | `workflow-as-code.md`, the WAC SDK |
| pipeline | `getPipelinePrompt()` | `write-pipeline` | `pipeline-base.md` |
| data tables | `getDatatablePrompt()` | `datatables` | `datatable.md` |
| workspace settings | `getWorkspaceSettingsPrompt()` | `workspace-settings` | `workspace-settings.md` |
| triggers, schedules, preview, CLI commands | — | same names | CLI only |
The app chat embeds `RAW_APP_BASE` directly (`chat/app/core.ts`).
+1
View File
@@ -5,5 +5,6 @@ export declare function getResourcePrompt(): string;
export declare function getRawAppPrompt(language?: string): string;
export declare function getPipelinePrompt(): string;
export declare function getDatatablePrompt(): string;
export declare function getWorkspaceSettingsPrompt(): string;
export declare function getDatatableSdkReference(language?: string): string;
export declare function getWorkflowAsCodePrompt(language?: string): string;
+7
View File
@@ -80,6 +80,13 @@ export function getDatatablePrompt(): string {
].filter(Boolean).join('\n\n');
}
// Helper for the workspace settings document (chat consumers)
export function getWorkspaceSettingsPrompt(): string {
return [
prompts.WORKSPACE_SETTINGS_BASE
].filter(Boolean).join('\n\n');
}
// Helper to get the datatable SQL SDK reference (wmill.datatable()).
// Pass a language to get only that SDK; omit it to get both.
export function getDatatableSdkReference(language?: string): string {
+1
View File
@@ -6,6 +6,7 @@ export declare const RESOURCES_BASE: string;
export declare const RAW_APP_BASE: string;
export declare const PIPELINE_BASE: string;
export declare const DATATABLE_BASE: string;
export declare const WORKSPACE_SETTINGS_BASE: string;
export declare const WORKFLOW_AS_CODE_BASE: string;
export declare const FLOW_CHAT_SPECIAL_MODULES: string;
export declare const SDK_TYPESCRIPT: string;
+60
View File
@@ -1411,6 +1411,66 @@ A data table under roles is reached as a Postgres role, and Postgres enforces wh
Read the Permissions section of https://www.windmill.dev/docs/core_concepts/persistent_storage/data_tables before designing who reaches what: it covers tenants, grants, the default role and the roles a connection that names none ends up holding, rows that differ per viewer through row-level security, and how forks and raw apps behave under roles.
`;
export const WORKSPACE_SETTINGS_BASE = `# Workspace settings
Everything an admin sets on the Workspace settings page is one document: a mapping of top-level
keys, the same layout as the \`settings.yaml\` file \`wmill sync\` keeps. Only a workspace admin can
read or change it.
## Applying a document
- A partial document changes only the keys it states; every key it omits is left as it is.
- Each key is replaced whole, so read its current value and send it back edited: a nested object
given with one field replaces the stored object with that one field.
- A key set to \`null\` clears that setting; an on/off switch cleared this way reads as \`false\`.
- Each setting is written through the same path the settings page uses, with the same validation
and audit log. The writes are not one transaction: when one fails the apply stops, and the
error says what was already written.
- A dry run reports the changes, as \`before\` / \`after\` per key, without writing anything.
- \`name\` is never applied: rename a workspace on the settings page. \`color\` is applied only when
set to a value, so \`null\` does not clear it.
- A key the server does not know is ignored and named in the warnings: check them, since a typo
otherwise reads as "nothing to change".
- A setting this edition does not offer (Enterprise only) is refused, by the dry run too.
## Secrets
\`slack_oauth_client_secret\` is the one secret. A read shows \`__SENSITIVE_AND_UNCHANGED__\` in place
of a set secret; sending that placeholder back keeps the stored value. A value starting with
\`$encrypted:\` is decrypted with the workspace key, and one encrypted with another workspace's key
is left unchanged with a warning; any other value is taken as plaintext. Never
write a secret you were not given: keep the placeholder.
## Keys
| Key | Value |
|---|---|
| \`auto_invite\` | \`{ enabled, mode: invite \\| add, operator, instance_groups?, instance_groups_roles? }\`. The instance groups are managed only when \`instance_groups\` is stated; clearing a group removes the members it added. |
| \`error_handler\` / \`success_handler\` | \`{ path, extra_args?, muted_on_cancel?, muted_on_user_path? }\`, \`path\` being \`script/<path>\` or \`flow/<path>\`. |
| \`error_handler_fallback_to_instance_alerts\` | Report failures to the instance critical alert channels when no error handler is set. |
| \`webhook\` | URL POSTed a JSON event on every change to the workspace's items. |
| \`ai_config\` | Providers, default and completion models, custom prompts, \`copilot_disabled\`. |
| \`large_file_storage\` | Workspace object storage: \`{ type, s3_resource_path \\| azure_blob_resource_path \\| gcs_resource_path, public_resource?, secondary_storage?, advanced_permissions? }\`. |
| \`git_sync\` | \`{ repositories: [...] }\`, each with \`git_repo_resource_path\`, \`script_path\` and \`settings\` (include/exclude paths and types). Server-owned fields (webhook secrets, sync state, credentials) are never read nor written. |
| \`datatable\` | \`{ datatables: { <name>: { database: { resource_type, resource_path? } } } }\`. Role permissions are \`datatable_permissions\`. |
| \`datatable_permissions\` | \`{ <data table>: { default_role, roles: { <role>: [tenants] } } }\`: who may connect to each data table as which role. Tenants are \`u/<user>\`, \`g/<group>\`, \`f/<folder>\` or \`*\`; \`admin\` is always a role, and \`default_role: null\` means no default. A data table the value omits is no longer under roles. Only a data table on a cluster Windmill manages can be. |
| \`protection_rules\` | \`{ <rule name>: { rules: [...], bypass_groups: [...], bypass_users: [...] } }\`, rules among \`DisableDirectDeployment\`, \`DisableWorkspaceForking\`, \`RestrictDeployToDeployers\`, \`RestrictAnonymousAppDeployment\`, \`RestrictPublicRunSharing\`, \`RestrictGuestAppDeployment\`. A rule the value omits is deleted. The dev workspace lock is not listed and cannot be set here. |
| \`ducklake\` | \`{ ducklakes: { <name>: { catalog, storage, ... } } }\`. |
| \`dbt_warehouses\` | dbt warehouse connections. |
| \`default_app\` | Path of the app shown as the workspace home. |
| \`default_scripts\` | \`{ order?, hidden?, default_script_content? }\` for the script language picker. |
| \`deploy_ui\` | \`{ include_path?, include_type? }\`: what the deploy-to-workspace UI offers. |
| \`operator_settings\` | What operators see and do: page visibility flags (\`runs\`, \`schedules\`, \`resources\`, \`variables\`, \`assets\`, \`audit_logs\`, \`groups\`, \`folders\`, \`workers\`, \`triggers\`) and write rights (\`manage_schedules\`, \`manage_triggers\`, \`builder_flows\`, \`builder_apps\`). A write right the value omits keeps its stored value. |
| \`mute_critical_alerts\` | Mute the workspace's critical alerts. |
| \`slack_team_id\`, \`slack_name\` | Read only: set by connecting Slack on the settings page. |
| \`slack_command_script\` / \`teams_command_script\` | Script or flow handling the Slack / Teams command. |
| \`slack_oauth_client_id\` / \`slack_oauth_client_secret\` | The workspace's own Slack OAuth app. Clearing either removes both. |
| \`public_app_execution_limit_per_minute\` | Rate limit for public apps, \`null\` for none. |
| \`guest_access_enabled\`, \`guest_jwt_public_key\`, \`guest_jwt_jwks_url\` | Guest access to public apps and the key that signs guest JWTs. |
| \`add_admins_and_developers_to_forks\` | Add the workspace's admins and developers to each fork. |
| \`color\` | Workspace color, \`#rrggbb\`. |
`;
export const WORKFLOW_AS_CODE_BASE = `# Windmill Workflow-as-Code Writing Guide
## Scope
@@ -0,0 +1,84 @@
---
name: workspace-settings
description: MUST use when reading or changing workspace settings, or the settings.yaml file `wmill sync` keeps.
---
# Workspace settings
Everything an admin sets on the Workspace settings page is one document: a mapping of top-level
keys, the same layout as the `settings.yaml` file `wmill sync` keeps. Only a workspace admin can
read or change it.
## Applying a document
- A partial document changes only the keys it states; every key it omits is left as it is.
- Each key is replaced whole, so read its current value and send it back edited: a nested object
given with one field replaces the stored object with that one field.
- A key set to `null` clears that setting; an on/off switch cleared this way reads as `false`.
- Each setting is written through the same path the settings page uses, with the same validation
and audit log. The writes are not one transaction: when one fails the apply stops, and the
error says what was already written.
- A dry run reports the changes, as `before` / `after` per key, without writing anything.
- `name` is never applied: rename a workspace on the settings page. `color` is applied only when
set to a value, so `null` does not clear it.
- A key the server does not know is ignored and named in the warnings: check them, since a typo
otherwise reads as "nothing to change".
- A setting this edition does not offer (Enterprise only) is refused, by the dry run too.
## Secrets
`slack_oauth_client_secret` is the one secret. A read shows `__SENSITIVE_AND_UNCHANGED__` in place
of a set secret; sending that placeholder back keeps the stored value. A value starting with
`$encrypted:` is decrypted with the workspace key, and one encrypted with another workspace's key
is left unchanged with a warning; any other value is taken as plaintext. Never
write a secret you were not given: keep the placeholder.
## Keys
| Key | Value |
|---|---|
| `auto_invite` | `{ enabled, mode: invite \| add, operator, instance_groups?, instance_groups_roles? }`. The instance groups are managed only when `instance_groups` is stated; clearing a group removes the members it added. |
| `error_handler` / `success_handler` | `{ path, extra_args?, muted_on_cancel?, muted_on_user_path? }`, `path` being `script/<path>` or `flow/<path>`. |
| `error_handler_fallback_to_instance_alerts` | Report failures to the instance critical alert channels when no error handler is set. |
| `webhook` | URL POSTed a JSON event on every change to the workspace's items. |
| `ai_config` | Providers, default and completion models, custom prompts, `copilot_disabled`. |
| `large_file_storage` | Workspace object storage: `{ type, s3_resource_path \| azure_blob_resource_path \| gcs_resource_path, public_resource?, secondary_storage?, advanced_permissions? }`. |
| `git_sync` | `{ repositories: [...] }`, each with `git_repo_resource_path`, `script_path` and `settings` (include/exclude paths and types). Server-owned fields (webhook secrets, sync state, credentials) are never read nor written. |
| `datatable` | `{ datatables: { <name>: { database: { resource_type, resource_path? } } } }`. Role permissions are `datatable_permissions`. |
| `datatable_permissions` | `{ <data table>: { default_role, roles: { <role>: [tenants] } } }`: who may connect to each data table as which role. Tenants are `u/<user>`, `g/<group>`, `f/<folder>` or `*`; `admin` is always a role, and `default_role: null` means no default. A data table the value omits is no longer under roles. Only a data table on a cluster Windmill manages can be. |
| `protection_rules` | `{ <rule name>: { rules: [...], bypass_groups: [...], bypass_users: [...] } }`, rules among `DisableDirectDeployment`, `DisableWorkspaceForking`, `RestrictDeployToDeployers`, `RestrictAnonymousAppDeployment`, `RestrictPublicRunSharing`, `RestrictGuestAppDeployment`. A rule the value omits is deleted. The dev workspace lock is not listed and cannot be set here. |
| `ducklake` | `{ ducklakes: { <name>: { catalog, storage, ... } } }`. |
| `dbt_warehouses` | dbt warehouse connections. |
| `default_app` | Path of the app shown as the workspace home. |
| `default_scripts` | `{ order?, hidden?, default_script_content? }` for the script language picker. |
| `deploy_ui` | `{ include_path?, include_type? }`: what the deploy-to-workspace UI offers. |
| `operator_settings` | What operators see and do: page visibility flags (`runs`, `schedules`, `resources`, `variables`, `assets`, `audit_logs`, `groups`, `folders`, `workers`, `triggers`) and write rights (`manage_schedules`, `manage_triggers`, `builder_flows`, `builder_apps`). A write right the value omits keeps its stored value. |
| `mute_critical_alerts` | Mute the workspace's critical alerts. |
| `slack_team_id`, `slack_name` | Read only: set by connecting Slack on the settings page. |
| `slack_command_script` / `teams_command_script` | Script or flow handling the Slack / Teams command. |
| `slack_oauth_client_id` / `slack_oauth_client_secret` | The workspace's own Slack OAuth app. Clearing either removes both. |
| `public_app_execution_limit_per_minute` | Rate limit for public apps, `null` for none. |
| `guest_access_enabled`, `guest_jwt_public_key`, `guest_jwt_jwks_url` | Guest access to public apps and the key that signs guest JWTs. |
| `add_admins_and_developers_to_forks` | Add the workspace's admins and developers to each fork. |
| `color` | Workspace color, `#rrggbb`. |
## From the command line
`wmill workspace get-settings` prints the document (`--encrypted` for secrets encrypted with the
workspace key, `--json` for JSON). `wmill workspace apply-settings <file>` applies a partial
document from a YAML or JSON file: it shows the changes and asks before writing (`--dry-run` to
only show them, `-y` to skip the question, `--replace` to clear what the file omits).
## In a synced repository
With `includeSettings: true` in `wmill.yaml`, `wmill sync pull` writes the document to
`settings.yaml`, secrets encrypted with the workspace key (plaintext only with `plainSecrets`), and
`wmill sync push` applies it. Unlike a partial apply, a push treats the file as the whole
document: a setting the file omits is cleared, except `operator_settings`, `color`, the auto-invite
instance groups, and the keys `ducklake`, `dbt_warehouses`, `deploy_ui`, `teams_command_script`,
`public_app_execution_limit_per_minute`, `guest_access_enabled`, `guest_jwt_public_key`,
`guest_jwt_jwks_url`, `error_handler_fallback_to_instance_alerts` and
`add_admins_and_developers_to_forks`, which are managed only when the file states them.
`datatable_permissions` is pushed only with `includeDatatablePermissions: true` (or
`--include-datatable-permissions`). A push ignores `protection_rules`: `protection-rules.yaml` and
`wmill protection-rules push` sync them.
+81
View File
@@ -0,0 +1,81 @@
# Workspace settings
Everything an admin sets on the Workspace settings page is one document: a mapping of top-level
keys, the same layout as the `settings.yaml` file `wmill sync` keeps. Only a workspace admin can
read or change it.
## Applying a document
- A partial document changes only the keys it states; every key it omits is left as it is.
- Each key is replaced whole, so read its current value and send it back edited: a nested object
given with one field replaces the stored object with that one field.
- A key set to `null` clears that setting; an on/off switch cleared this way reads as `false`.
- Each setting is written through the same path the settings page uses, with the same validation
and audit log. The writes are not one transaction: when one fails the apply stops, and the
error says what was already written.
- A dry run reports the changes, as `before` / `after` per key, without writing anything.
- `name` is never applied: rename a workspace on the settings page. `color` is applied only when
set to a value, so `null` does not clear it.
- A key the server does not know is ignored and named in the warnings: check them, since a typo
otherwise reads as "nothing to change".
- A setting this edition does not offer (Enterprise only) is refused, by the dry run too.
## Secrets
`slack_oauth_client_secret` is the one secret. A read shows `__SENSITIVE_AND_UNCHANGED__` in place
of a set secret; sending that placeholder back keeps the stored value. A value starting with
`$encrypted:` is decrypted with the workspace key, and one encrypted with another workspace's key
is left unchanged with a warning; any other value is taken as plaintext. Never
write a secret you were not given: keep the placeholder.
## Keys
| Key | Value |
|---|---|
| `auto_invite` | `{ enabled, mode: invite \| add, operator, instance_groups?, instance_groups_roles? }`. The instance groups are managed only when `instance_groups` is stated; clearing a group removes the members it added. |
| `error_handler` / `success_handler` | `{ path, extra_args?, muted_on_cancel?, muted_on_user_path? }`, `path` being `script/<path>` or `flow/<path>`. |
| `error_handler_fallback_to_instance_alerts` | Report failures to the instance critical alert channels when no error handler is set. |
| `webhook` | URL POSTed a JSON event on every change to the workspace's items. |
| `ai_config` | Providers, default and completion models, custom prompts, `copilot_disabled`. |
| `large_file_storage` | Workspace object storage: `{ type, s3_resource_path \| azure_blob_resource_path \| gcs_resource_path, public_resource?, secondary_storage?, advanced_permissions? }`. |
| `git_sync` | `{ repositories: [...] }`, each with `git_repo_resource_path`, `script_path` and `settings` (include/exclude paths and types). Server-owned fields (webhook secrets, sync state, credentials) are never read nor written. |
| `datatable` | `{ datatables: { <name>: { database: { resource_type, resource_path? } } } }`. Role permissions are `datatable_permissions`. |
| `datatable_permissions` | `{ <data table>: { default_role, roles: { <role>: [tenants] } } }`: who may connect to each data table as which role. Tenants are `u/<user>`, `g/<group>`, `f/<folder>` or `*`; `admin` is always a role, and `default_role: null` means no default. A data table the value omits is no longer under roles. Only a data table on a cluster Windmill manages can be. |
| `protection_rules` | `{ <rule name>: { rules: [...], bypass_groups: [...], bypass_users: [...] } }`, rules among `DisableDirectDeployment`, `DisableWorkspaceForking`, `RestrictDeployToDeployers`, `RestrictAnonymousAppDeployment`, `RestrictPublicRunSharing`, `RestrictGuestAppDeployment`. A rule the value omits is deleted. The dev workspace lock is not listed and cannot be set here. |
| `ducklake` | `{ ducklakes: { <name>: { catalog, storage, ... } } }`. |
| `dbt_warehouses` | dbt warehouse connections. |
| `default_app` | Path of the app shown as the workspace home. |
| `default_scripts` | `{ order?, hidden?, default_script_content? }` for the script language picker. |
| `deploy_ui` | `{ include_path?, include_type? }`: what the deploy-to-workspace UI offers. |
| `operator_settings` | What operators see and do: page visibility flags (`runs`, `schedules`, `resources`, `variables`, `assets`, `audit_logs`, `groups`, `folders`, `workers`, `triggers`) and write rights (`manage_schedules`, `manage_triggers`, `builder_flows`, `builder_apps`). A write right the value omits keeps its stored value. |
| `mute_critical_alerts` | Mute the workspace's critical alerts. |
| `slack_team_id`, `slack_name` | Read only: set by connecting Slack on the settings page. |
| `slack_command_script` / `teams_command_script` | Script or flow handling the Slack / Teams command. |
| `slack_oauth_client_id` / `slack_oauth_client_secret` | The workspace's own Slack OAuth app. Clearing either removes both. |
| `public_app_execution_limit_per_minute` | Rate limit for public apps, `null` for none. |
| `guest_access_enabled`, `guest_jwt_public_key`, `guest_jwt_jwks_url` | Guest access to public apps and the key that signs guest JWTs. |
| `add_admins_and_developers_to_forks` | Add the workspace's admins and developers to each fork. |
| `color` | Workspace color, `#rrggbb`. |
<!-- cli-only -->
## From the command line
`wmill workspace get-settings` prints the document (`--encrypted` for secrets encrypted with the
workspace key, `--json` for JSON). `wmill workspace apply-settings <file>` applies a partial
document from a YAML or JSON file: it shows the changes and asks before writing (`--dry-run` to
only show them, `-y` to skip the question, `--replace` to clear what the file omits).
## In a synced repository
With `includeSettings: true` in `wmill.yaml`, `wmill sync pull` writes the document to
`settings.yaml`, secrets encrypted with the workspace key (plaintext only with `plainSecrets`), and
`wmill sync push` applies it. Unlike a partial apply, a push treats the file as the whole
document: a setting the file omits is cleared, except `operator_settings`, `color`, the auto-invite
instance groups, and the keys `ducklake`, `dbt_warehouses`, `deploy_ui`, `teams_command_script`,
`public_app_execution_limit_per_minute`, `guest_access_enabled`, `guest_jwt_public_key`,
`guest_jwt_jwks_url`, `error_handler_fallback_to_instance_alerts` and
`add_admins_and_developers_to_forks`, which are managed only when the file states them.
`datatable_permissions` is pushed only with `includeDatatablePermissions: true` (or
`--include-datatable-permissions`). A push ignores `protection_rules`: `protection-rules.yaml` and
`wmill protection-rules push` sync them.
<!-- /cli-only -->
+13
View File
@@ -1702,6 +1702,12 @@ TOPICS: dict[str, dict] = {
'chat_helper': 'getDatatablePrompt',
'parts': ['datatable.md'],
},
'workspace_settings': {
'skill': 'workspace-settings',
'description': 'MUST use when reading or changing workspace settings, or the settings.yaml file `wmill sync` keeps.',
'chat_helper': 'getWorkspaceSettingsPrompt',
'parts': ['workspace-settings.md'],
},
'cli_commands': {
'skill': 'cli-commands',
'description': 'MUST use when using the CLI, including debugging job failures and inspecting run history via `wmill job`.',
@@ -1722,6 +1728,7 @@ CHAT_CONSTANTS = {
'raw-app.md': 'RAW_APP_BASE',
'pipeline-base.md': 'PIPELINE_BASE',
'datatable.md': 'DATATABLE_BASE',
'workspace-settings.md': 'WORKSPACE_SETTINGS_BASE',
'workflow-as-code.md': 'WORKFLOW_AS_CODE_BASE',
'flow-chat-special-modules.md': 'FLOW_CHAT_SPECIAL_MODULES',
}
@@ -2662,6 +2669,11 @@ export function getDatatablePrompt(): string {
__RETURN_datatable__
}
// Helper for the workspace settings document (chat consumers)
export function getWorkspaceSettingsPrompt(): string {
__RETURN_workspace_settings__
}
// Helper to get the datatable SQL SDK reference (wmill.datatable()).
// Pass a language to get only that SDK; omit it to get both.
export function getDatatableSdkReference(language?: string): string {
@@ -2724,6 +2736,7 @@ export declare function getResourcePrompt(): string;
export declare function getRawAppPrompt(language?: string): string;
export declare function getPipelinePrompt(): string;
export declare function getDatatablePrompt(): string;
export declare function getWorkspaceSettingsPrompt(): string;
export declare function getDatatableSdkReference(language?: string): string;
export declare function getWorkflowAsCodePrompt(language?: string): string;
"""