@oxy-hq/cli 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +131 -8
  2. package/dist/main.mjs +4909 -3551
  3. package/dist/main.mjs.map +1 -1
  4. package/dist/{mcp-xxWL3_dz.mjs → mcp-Bdp0rX0l.mjs} +2 -2
  5. package/dist/{mcp-xxWL3_dz.mjs.map → mcp-Bdp0rX0l.mjs.map} +1 -1
  6. package/package.json +5 -5
  7. package/skills/oxy-agents/SKILL.md +318 -0
  8. package/skills/oxy-agents/references/quick-reference.md +222 -0
  9. package/skills/oxy-agents/templates/agentic-template.yml +141 -0
  10. package/skills/oxy-airway-pipelines/SKILL.md +223 -316
  11. package/skills/oxy-automations/SKILL.md +61 -15
  12. package/skills/oxy-cli/SKILL.md +37 -4
  13. package/skills/oxy-custom-apps/SKILL.md +193 -291
  14. package/skills/oxy-custom-apps/references/minimal-starter.md +63 -0
  15. package/skills/oxy-custom-apps/references/performance-and-usage.md +105 -0
  16. package/skills/oxy-custom-apps/references/reading-data.md +106 -0
  17. package/skills/oxy-custom-apps/references/sandbox-agent-loop.md +267 -0
  18. package/skills/oxy-custom-apps/references/verifying-in-the-browser.md +65 -0
  19. package/skills/oxy-data-apps/SKILL.md +362 -0
  20. package/skills/oxy-data-apps/references/examples.md +263 -0
  21. package/skills/oxy-data-apps/references/quick-reference.md +171 -0
  22. package/skills/oxy-data-apps/templates/dashboard-with-controls.app.yml +128 -0
  23. package/skills/oxy-data-apps/templates/simple-dashboard.app.yml +102 -0
  24. package/skills/oxy-data-architecture/SKILL.md +137 -152
  25. package/skills/oxy-evals/SKILL.md +362 -0
  26. package/skills/oxy-evals/references/diagnosis.md +243 -0
  27. package/skills/oxy-evals/references/results-json.md +145 -0
  28. package/skills/oxy-evals/references/writing-expected.md +112 -0
  29. package/skills/oxy-semantic-modeling/SKILL.md +239 -59
  30. package/skills/oxy-semantic-modeling/references/warehouse-casts.md +57 -0
  31. package/skills/oxy-semantic-modeling/templates/topic-template.yml +38 -0
  32. package/skills/oxy-semantic-modeling/templates/view-template.yml +119 -0
  33. package/template/.github/workflows/publish.yaml +15 -13
  34. package/template/.nvmrc +1 -1
  35. package/template/.oxyc-managed +5 -5
  36. package/template/README.md +21 -10
  37. package/template/package.json +3 -2
  38. package/template/pnpm-workspace.yaml +28 -17
@@ -0,0 +1,318 @@
1
+ ---
2
+ name: oxy-agents
3
+ description: >-
4
+ Use when creating, editing, reviewing or debugging an Oxy agent, a `.agentic.yml` file that
5
+ answers questions from the semantic model by clarifying, specifying, solving (SQL), executing
6
+ and interpreting. Triggers include "create an analytics agent", "my agent picks the wrong
7
+ metric", "my solving instructions are ignored", "the agent never uses my verified query", "the
8
+ agent can't see a database", "which tools can a state call", "set thinking, effort,
9
+ budget_tokens or extended thinking", "per-state model or max_retries", "validation rules",
10
+ "Cube or Looker semantic_engine", "analytics vs app-builder agent", "`.agent.yml` or
11
+ `.agentic.yml`", and validating, running (`oxy agentic run`) or testing (`oxy test`) an agent.
12
+ Not for writing the semantic model itself (views, topics) or automations.
13
+ ---
14
+
15
+ # Oxy agents (`.agentic.yml`)
16
+
17
+ An Oxy agent is one YAML file, `<name>.agentic.yml`. It configures a fixed
18
+ pipeline that turns a natural-language question into a grounded query against
19
+ the semantic model, runs it, checks the result and explains it. You configure
20
+ the pipeline. You do not define its states or its tools.
21
+
22
+ ## There is one kind of agent
23
+
24
+ - **`.agentic.yml` is the agent.** The classic `.agent.yml` format (a single
25
+ LLM call with custom tools) has been removed. Do not create one, and do not
26
+ copy one from an old workspace.
27
+ - **There is no `kind:` field.** Every `.agentic.yml` runs the same analytics
28
+ pipeline. A file named `app_builder.agentic.yml` is not special. Generating
29
+ `.app.yml` data apps, views or agent files is the in-product **Builder Agent**
30
+ (chat Build mode), and no `.agentic.yml` configures it.
31
+ - **Automations are a different file.** Deterministic multi-step work is an
32
+ automation, `.automation.yml` (`.procedure.yml` still works). `.workflow.yml`
33
+ is no longer recognised at all. An agent can *run* an automation it finds in
34
+ its context. It cannot *be* one.
35
+ - **Instruction-only agents skip the pipeline.** A file with `instructions:`
36
+ and no `databases:`, `context:` or `states:` is answered with one LLM call
37
+ and queries nothing. Add any of those three and you get the full pipeline.
38
+
39
+ **Use an agent** for conversational questions over data that has a semantic
40
+ model. **Use a verified query** (a `.sql` file with an `oxy:` header, below)
41
+ when one question must always get exactly the same SQL. **Use an automation**
42
+ when the steps are fixed and need no LLM to choose between them.
43
+
44
+ ## Minimal file
45
+
46
+ ```yaml
47
+ llm:
48
+ ref: claude # a `name:` under config.yml `models:`
49
+ context:
50
+ - ./semantics/**/*.view.yml
51
+ - ./semantics/**/*.topic.yml
52
+ ```
53
+
54
+ That is enough when the views declare a `datasource`. The agent can query
55
+ every database its context references. The full annotated file is
56
+ [templates/agentic-template.yml](templates/agentic-template.yml).
57
+
58
+ Top-level keys: `instructions`, `timezone`, `databases`, `context`, `llm`,
59
+ `thinking`, `states`, `validation`, `semantic_engine`. All are optional.
60
+ **Unknown keys are ignored silently at every level**, by the runtime and by
61
+ both validators. A typo such as `stats:` or `solve:` costs you the setting
62
+ with no error.
63
+
64
+ ## How a question flows
65
+
66
+ ```
67
+ clarifying → specifying → solving → executing → interpreting → done
68
+ ↑ ↑ ↑ ↑
69
+ └──────── diagnosing (when a check fails) ────────┘
70
+ ```
71
+
72
+ 1. **clarifying** classifies the question as a trend, comparison, breakdown,
73
+ single value, distribution, general inquiry, root cause or opportunity. It
74
+ searches the catalog and automations, and may ask the user to clarify.
75
+ Three outcomes leave the normal path early:
76
+ - **General inquiry** ("what metrics do you have?"). Answered directly,
77
+ with no query.
78
+ - **Root cause / opportunity** ("why did revenue drop?", "where can we
79
+ grow?"). Handled by metric-tree and anomaly tools, skipping
80
+ specifying/solving.
81
+ - **Confident semantic query.** If the model proposes the exact measures
82
+ and dimensions with high confidence and the semantic model compiles them,
83
+ specifying and solving are both skipped. Likewise when it selects a
84
+ verified query or automation, that is run instead.
85
+ 2. **specifying** turns the question into a semantic query. If the semantic
86
+ model compiles it, **solving is skipped** and the compiled SQL runs.
87
+ 3. **solving** writes SQL itself. This happens only when the semantic model
88
+ could not answer.
89
+ 4. **executing** runs the SQL. It has no LLM. Then the result checks run.
90
+ 5. **interpreting** writes the answer and picks a chart.
91
+
92
+ **Diagnosing** routes a failure back upstream:
93
+
94
+ - An unresolved metric or join, an empty result, a compile error or bad SQL
95
+ goes back to specifying.
96
+ - An ambiguous column goes back to clarifying.
97
+ - Outliers, a stale period or a bad chart go to interpreting, which still
98
+ answers and states the caveat.
99
+
100
+ The practical consequence: **guidance every answer needs goes in the global
101
+ `instructions:`, `clarifying` or `interpreting`, never only in `solving`.** On a
102
+ well-modelled workspace, solving rarely runs.
103
+
104
+ ## Tools each state can call
105
+
106
+ The tool set is fixed by the runtime and is not configurable. There is no
107
+ `tools:` key.
108
+
109
+ | State | Tools |
110
+ | --- | --- |
111
+ | clarifying | `search_catalog`, `search_automations`, `propose_semantic_query`, `ask_user` |
112
+ | clarifying, root cause / opportunity | `explain_metric`, `find_opportunities`, `metric_sensitivity`, `predict_impact`, plus `list_anomalies`, `detect_anomalies` and `explain_anomaly` when anomaly monitoring is set up |
113
+ | specifying | `search_catalog`, `sample_columns`, `ask_user`. With a semantic model: `check_data_freshness`. Without one: `get_join_path`, `list_tables`, `describe_table` |
114
+ | solving | `execute_preview` (runs a candidate query with `LIMIT 5`) |
115
+ | executing | none |
116
+ | interpreting | `render_chart` |
117
+
118
+ `search_automations` searches only the automations and verified queries this
119
+ agent's `context:` reaches. With none, it finds nothing. The metric-tree and
120
+ anomaly tools are offered in interactive runs. They are not offered in
121
+ workspace previews or scheduled runs.
122
+
123
+ ## The fields that matter
124
+
125
+ **`instructions`** go into every LLM state's system prompt. Use them for the
126
+ persona, business conventions ("fiscal year starts in February", "customer
127
+ means the `customers` view") and which views or topics to prefer. Per-state
128
+ `instructions` are **appended** to these, not substituted, so don't repeat the
129
+ global text in them.
130
+
131
+ **`timezone`** is an IANA name used for "today", "yesterday" and "last week".
132
+ It falls back to config.yml's `timezone:`, then UTC. An invalid name fails the
133
+ run.
134
+
135
+ **`databases`** lists names from config.yml `databases:`. The agent can query
136
+ exactly these plus every database its context files reference: a view's
137
+ `datasource` (or `data_source`), a verified query's `database:`, or an
138
+ automation task's `database:`. There is **no fallback to every database in
139
+ config.yml**. With none at all, the run fails with `no databases configured`.
140
+
141
+ **`context`** takes globs relative to the agent file's directory. Each match is
142
+ bucketed by its file name:
143
+
144
+ | Matches | Becomes |
145
+ | --- | --- |
146
+ | `*.view.yml`, `*.topic.yml` | the semantic catalog |
147
+ | `*.sql` with an `oxy:` header | a **verified query**: found by `search_automations` and run verbatim |
148
+ | `*.sql` without the header | SQL style examples in the solving prompt only |
149
+ | `*.md` | domain notes in the clarifying and interpreting prompts |
150
+ | `*.automation.yml`, `*.procedure.yml` | automations the agent may pick and run |
151
+ | anything else | ignored |
152
+
153
+ A verified query opts in with a leading comment block:
154
+
155
+ ```sql
156
+ /*
157
+ oxy:
158
+ description: Monthly revenue by region
159
+ database: warehouse
160
+ */
161
+ SELECT ...
162
+ ```
163
+
164
+ Without the header, the same file is only a style hint. Context files under a
165
+ dot-directory, `build/`, `dist/`, `target/` or `node_modules/`, or with
166
+ `.test.` in the name, are not deployed, so a glob that works on your machine
167
+ can come back empty in the cloud.
168
+
169
+ **`llm`** picks the model.
170
+
171
+ - `ref` names a model in config.yml `models:`, and the vendor, API key and base
172
+ URL come from that entry. In the cloud the key is a workspace secret, so
173
+ prefer `ref`.
174
+ - With neither `ref` nor `model`, the first model in config.yml is used.
175
+ - `model` overrides only the model ID. On its own (no `ref`) it uses
176
+ `api_key` or the server's `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`.
177
+ - `vendor` (`anthropic`, `open_ai`, `open_ai_compat`) overrides the ref's wire
178
+ protocol. Use `open_ai_compat` for gateways that serve only
179
+ `/chat/completions`.
180
+ - Google/Gemini models are not supported in agents.
181
+
182
+ **`states`** honours four keys: `clarifying`, `specifying`, `solving` and
183
+ `interpreting`.
184
+
185
+ - `executing` has no LLM, so it is accepted and does nothing. Every other key
186
+ is ignored.
187
+ - Each state takes `instructions`, `thinking`, `model` and `max_retries`.
188
+ - `model` is a provider model ID. It inherits the vendor, key and base URL
189
+ from `llm:`, so it must be the same provider.
190
+ - `max_retries` is the number of **tool-call rounds** per visit. When they run
191
+ out, the run pauses and asks the user whether to continue. It is not a
192
+ failure-retry count. Defaults: specifying 5, solving 3, interpreting 5.
193
+ Clarifying has a fixed limit and ignores the setting.
194
+
195
+ **`validation`** sets the result checks. Leave it out and 15 default checks
196
+ run. If you include it, it **replaces** the defaults: only the rules you list
197
+ run, and a stage you leave empty is not checked. To switch one check off or
198
+ retune it, list every rule that should still run. Rule names and parameters
199
+ are in [references/quick-reference.md](references/quick-reference.md).
200
+
201
+ **`semantic_engine`** routes queries through Cube or Looker ahead of Oxy's own
202
+ semantic model. `${VAR}` placeholders are read from the server's environment,
203
+ not from workspace secrets. The engine is pinged when a run starts, and an
204
+ unreachable engine fails the run.
205
+
206
+ ## Thinking and models
207
+
208
+ Thinking is resolved per state, in this order:
209
+
210
+ 1. the state's own `thinking`
211
+ 2. `llm.thinking`
212
+ 3. top-level `thinking`
213
+ 4. the state's built-in default: **adaptive for solving, disabled for
214
+ everything else**
215
+
216
+ A global value replaces every built-in default. So top-level
217
+ `thinking: adaptive` makes clarifying, specifying and interpreting think too,
218
+ and `llm.thinking: disabled` turns thinking off in solving. Prefer per-state
219
+ settings.
220
+
221
+ | Form | Meaning |
222
+ | --- | --- |
223
+ | `adaptive` | the model decides when and how much to think |
224
+ | `disabled` | no thinking (bare or quoted; **never** `false`, which is a parse error) |
225
+ | `effort:high` or `{effort: high}` | depth hint: `low`, `medium`, `high`, `xhigh`, `max` |
226
+ | `{budget_tokens: 10000}` | hard token ceiling (Claude 4.6 and earlier) |
227
+
228
+ How each provider treats these forms:
229
+
230
+ - **Anthropic.** `adaptive` and `effort` are native from Claude 4.6, and
231
+ `xhigh` from 4.7. Claude 4.7 and later **reject** `budget_tokens`, so Oxy
232
+ sends it as the nearest effort level and logs a warning. On older models it
233
+ goes the other way: `adaptive` and `effort` become a token budget, and
234
+ `xhigh` becomes `high`. Write `effort` on current models.
235
+ - **OpenAI** (`open_ai`, Responses API). `effort` maps to reasoning effort,
236
+ with `xhigh` and `max` sent as `high`. `adaptive` leaves the model's default
237
+ effort. `budget_tokens` is ignored.
238
+ - **`open_ai_compat`.** Any setting other than `disabled` adds a
239
+ chain-of-thought instruction to the prompt, because there is no native
240
+ control.
241
+
242
+ A string that isn't one of the forms above (`thinking: enabled`,
243
+ `thinking: high`) silently means **disabled**. An unknown effort level
244
+ silently means `medium`.
245
+
246
+ `llm.extended_thinking: {model, thinking}` is used while the user has the
247
+ **extended thinking** toggle on (`--thinking-mode extended_thinking` from the
248
+ CLI). It replaces the model and the thinking setting in every state. Without
249
+ the block, the toggle changes nothing.
250
+
251
+ Leave `llm.max_tokens` unset unless you need a hard cap. When it is unset, Oxy
252
+ uses 4,096 without thinking and 16,384 (or the budget plus 4,096) with
253
+ thinking. A value you set is used as-is, and thinking tokens count against it.
254
+
255
+ ## Validate, run, test
256
+
257
+ ```bash
258
+ oxyc validate --file agents/sales.agentic.yml # offline schema check, no server
259
+ oxy validate --file agents/sales.agentic.yml # the runtime's own parser
260
+ oxy agentic run agents/sales.agentic.yml -q "Revenue by region last quarter?"
261
+ oxy test agents/sales.agentic.test.yml # LLM-judged evals
262
+ ```
263
+
264
+ - **Both validators are structural only.** They catch a bad shape (a
265
+ malformed `thinking`, a wrong type, a duplicate key). `oxyc validate` also
266
+ rejects the vendor spellings `openai` and `openai_compat`, which the runtime
267
+ accepts, so write `open_ai` and `open_ai_compat` to satisfy both. Neither
268
+ validator resolves `databases:` or `llm.ref`, expands globs, reads `${VAR}`
269
+ placeholders, checks rule names or timezones, or notices unknown keys.
270
+ - **The first full check is a run.** `oxy agentic run <path> -q "<question>"`
271
+ builds the agent exactly as the server does and streams each state. It runs
272
+ in a workspace directory and needs Oxy's Postgres database (`OXY_DATABASE_URL`),
273
+ like `oxy serve`. Add `--json` for JSONL events, or `--thinking-mode
274
+ extended_thinking`. A CLI run that pauses to ask the user something can't be
275
+ usefully resumed from the CLI, because `oxy agentic answer` only marks the
276
+ run as running and does not deliver the text. Ask a more specific question,
277
+ or continue in the app.
278
+ - **`oxy build` does not check agents.** It builds search embeddings and syncs
279
+ integrations.
280
+ - **`oxy test`** runs `.test.yml` evals against an agent and scores each case
281
+ with an LLM judge. The format and flags are in
282
+ [references/quick-reference.md](references/quick-reference.md).
283
+
284
+ To point an editor at the schema, start the file with:
285
+
286
+ ```yaml
287
+ # yaml-language-server: $schema=https://raw.githubusercontent.com/oxy-hq/oxygen/refs/heads/main/json-schemas/agentic.json
288
+ ```
289
+
290
+ ## Pitfalls
291
+
292
+ | Symptom | Cause | Fix |
293
+ | --- | --- | --- |
294
+ | `no databases configured` | nothing in `databases:` and no context file references a database | add a name from config.yml, or views with `datasource` |
295
+ | agent can't see a table it "should" | the agent only reaches databases it declares or references | add it to `databases:` |
296
+ | wrong model answers, or an auth error on the first question | `llm.ref` is not a `name:` in config.yml, so the agent fell back to a built-in Claude model and the server's `ANTHROPIC_API_KEY`; or the model's key secret is unset | fix the name; set the key |
297
+ | "does not have a metric tree configured" | the question was classified as root cause or opportunity, and this workspace or run has no metric tree | define a metric tree, or ask for the numbers directly |
298
+ | `solving` instructions never take effect | the semantic model compiled the question, so solving was skipped | move them to the global `instructions:` or `clarifying` |
299
+ | verified query never chosen | `.sql` has no `oxy:` header, or isn't matched by `context:` | add the header and a `description`; check the glob |
300
+ | state override does nothing | misspelled key, or `executing` / `clarifying.max_retries` | use the four honoured keys |
301
+ | thinking silently off | `thinking: enabled` or another unknown string | use one of the forms above |
302
+ | `thinking: false` rejected | boolean, not a mode | `thinking: disabled` |
303
+ | a default check stopped running | a `validation:` section replaced the defaults | list every rule you still want |
304
+ | `validation config error: unknown … rule` | rule name typo, or a rule under the wrong stage | names and stages are in the quick reference |
305
+ | `invalid timezone '…'` | not an IANA name | e.g. `America/New_York`, `UTC` |
306
+ | `environment variable '${X}' … is not set` | `semantic_engine` placeholder unset on the server | set it in the server environment |
307
+ | `semantic engine connection error` | Cube/Looker unreachable or rejected the credentials | check `base_url` and credentials |
308
+ | agent missing in the cloud, fine locally | `.agentic.yaml` extension, a dot-directory/`build/` path, or `.test.` in the name | rename or move it |
309
+
310
+ ## Read next
311
+
312
+ - [references/quick-reference.md](references/quick-reference.md): every field,
313
+ default, rule and parameter in tables, plus the `.test.yml` format.
314
+ - [templates/agentic-template.yml](templates/agentic-template.yml): an annotated
315
+ starter file that validates against the schema.
316
+ - Related skills: **oxy-semantic-modeling** for the views and topics an agent
317
+ queries, **oxy-evals** for writing and repairing `.test.yml` evals, and
318
+ **oxy-automations** for automations.
@@ -0,0 +1,222 @@
1
+ # `.agentic.yml` quick reference
2
+
3
+ Dense tables. The reasoning behind them is in [../SKILL.md](../SKILL.md); a
4
+ starter file is [../templates/agentic-template.yml](../templates/agentic-template.yml).
5
+
6
+ ## File
7
+
8
+ | Rule | Detail |
9
+ | --- | --- |
10
+ | Name | `<name>.agentic.yml`. `.agentic.yaml` is not deployed to the cloud |
11
+ | Location | anywhere in the workspace, except under a dot-directory, `build/`, `dist/`, `target/` or `node_modules/` |
12
+ | Not deployed | any file with `.test.` in its name (that is the eval-file convention) |
13
+ | Referenced as | its workspace-relative path, e.g. `agents/sales.agentic.yml`. The UI shows the stem (`sales`) |
14
+ | Unknown keys | ignored silently at every level, by the runtime and by both validators |
15
+ | Duplicate keys | a YAML parse error |
16
+ | Editor schema | `# yaml-language-server: $schema=https://raw.githubusercontent.com/oxy-hq/oxygen/refs/heads/main/json-schemas/agentic.json` |
17
+
18
+ ## Top-level fields
19
+
20
+ | Field | Type | Default | Notes |
21
+ | --- | --- | --- | --- |
22
+ | `instructions` | string | none | Added to every LLM state's system prompt |
23
+ | `timezone` | IANA name | config.yml `timezone:`, then UTC | Used for "today" and relative dates. An invalid name fails the run |
24
+ | `databases` | list of config.yml database names | `[]` | Combined with the databases the context references. There is no all-databases fallback |
25
+ | `context` | list of globs | `[]` | Relative to the agent file's directory |
26
+ | `llm` | map | see below | Model, key, thinking |
27
+ | `thinking` | thinking form | none | Global thinking. Overrides each state's built-in default; `llm.thinking` overrides it |
28
+ | `states` | map | `{}` | Per-state overrides |
29
+ | `validation` | map | 15 default rules | **Replaces** the defaults when present |
30
+ | `semantic_engine` | map | none | Cube or Looker ahead of Oxy's semantic model |
31
+
32
+ An instruction-only agent (`instructions`, and no `databases`, `context` or
33
+ `states`) is one LLM call. It runs no pipeline and no queries.
34
+
35
+ ## `llm:`
36
+
37
+ | Field | Default | Notes |
38
+ | --- | --- | --- |
39
+ | `ref` | none | A `name:` under config.yml `models:`. Supplies the vendor, key (a workspace secret in the cloud), base URL, Azure settings and headers |
40
+ | `model` | from `ref`, else the first config.yml model | A provider model ID. Setting `model` without `ref` skips config.yml entirely, so the key must come from `api_key` or the server environment |
41
+ | `vendor` | from `ref`, else `anthropic` | `anthropic`, `open_ai` (Responses API) or `open_ai_compat` (`/chat/completions`). The runtime also accepts `openai` and `openai_compat`; `oxyc validate` does not |
42
+ | `api_key` | from `ref`, else `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` on the server | Prefer `ref` |
43
+ | `base_url` | from `ref`, else the provider default | `open_ai_compat` defaults to `http://localhost:11434/v1` |
44
+ | `max_tokens` | 4,096 without thinking; 16,384 (or budget + 4,096) with thinking | A value you set is used as-is, and thinking tokens count against it |
45
+ | `thinking` | none | Beats top-level `thinking` |
46
+ | `extended_thinking` | none | `{model, thinking}`. Replaces both in every state while the user's extended-thinking toggle is on |
47
+
48
+ A `ref` that is not in config.yml is not an error at load time. The agent falls
49
+ back to a built-in Claude model and the server's `ANTHROPIC_API_KEY`.
50
+ Google/Gemini models are not supported in agents.
51
+
52
+ ## `states:`
53
+
54
+ | Key | LLM | Thinking default | `max_retries` default | Prompt extras |
55
+ | --- | --- | --- | --- | --- |
56
+ | `clarifying` | yes | disabled | fixed; the setting is ignored | `.md` context docs |
57
+ | `specifying` | yes | disabled | 5 | SQL dialect |
58
+ | `solving` | yes | adaptive | 3 | SQL dialect, `.sql` examples |
59
+ | `executing` | **no** | n/a | n/a | key accepted, does nothing |
60
+ | `interpreting` | yes | disabled | 5 | `.md` context docs |
61
+
62
+ Any other key (`solve`, `diagnosing`, …) is ignored silently.
63
+
64
+ | Per-state field | Notes |
65
+ | --- | --- |
66
+ | `instructions` | **Appended** after the global `instructions` for this state |
67
+ | `thinking` | Any thinking form. Highest precedence (except the extended-thinking toggle) |
68
+ | `model` | Provider model ID. Inherits the vendor, key and base URL from `llm:`, so it must be the same provider |
69
+ | `max_retries` | Tool-call rounds per visit. When they run out, the run pauses and asks the user whether to continue |
70
+
71
+ ## Thinking
72
+
73
+ | Form | Meaning |
74
+ | --- | --- |
75
+ | `adaptive` | the model decides |
76
+ | `disabled` / `"disabled"` | off |
77
+ | `effort:<level>` / `{effort: <level>}` | `low`, `medium`, `high`, `xhigh`, `max`. An unknown level means `medium` |
78
+ | `{budget_tokens: N}` | hard ceiling. Native on Claude 3.7 to 4.6 |
79
+ | any other string | silently **disabled** |
80
+ | `false` / `true` | parse error |
81
+
82
+ Precedence, highest first: the extended-thinking toggle, then `states.<s>.thinking`,
83
+ then `llm.thinking`, then top-level `thinking`, then the state default.
84
+
85
+ | Provider | `adaptive` | `effort` | `budget_tokens` |
86
+ | --- | --- | --- | --- |
87
+ | Anthropic, Claude 4.7+ | native | native, all 5 levels | API rejects it; Oxy sends the nearest effort level and warns |
88
+ | Anthropic, Claude 4.6 | native | native; `xhigh` → `high` | native |
89
+ | Anthropic, 3.7 to 4.5 | sent as a mid-range budget | sent as a budget; `xhigh` → `high` | native |
90
+ | OpenAI (`open_ai`) | the model's default reasoning | reasoning effort; `xhigh` and `max` → `high` | ignored |
91
+ | `open_ai_compat` | chain-of-thought prompt | chain-of-thought prompt | chain-of-thought prompt |
92
+
93
+ ## `context:` buckets
94
+
95
+ | Matches | Becomes | Also |
96
+ | --- | --- | --- |
97
+ | `*.view.yml`, `*.view.yaml` | semantic catalog | its `datasource` / `data_source` adds a database |
98
+ | `*.topic.yml`, `*.topic.yaml` | semantic catalog | |
99
+ | `*.sql` with an `oxy:` header | verified query, run verbatim when chosen | header `database:` adds a database |
100
+ | `*.sql` without the header | SQL examples in the solving prompt | |
101
+ | `*.md` | domain notes for clarifying and interpreting | |
102
+ | `*.automation.yml/.yaml`, `*.procedure.yml/.yaml` | automations `search_automations` can return | task `database:` values add databases |
103
+ | anything else | ignored | |
104
+
105
+ Verified-query header fields: `description` (used to match the question) and
106
+ `database` (the connection to run on).
107
+
108
+ ```sql
109
+ /*
110
+ oxy:
111
+ description: Weekly sales for all stores
112
+ database: warehouse
113
+ */
114
+ ```
115
+
116
+ ## Tools by state (fixed; no `tools:` key)
117
+
118
+ | State | Tools |
119
+ | --- | --- |
120
+ | clarifying | `search_catalog`, `search_automations`, `propose_semantic_query`, `ask_user` |
121
+ | clarifying → root cause / opportunity | `explain_metric`, `find_opportunities`, `metric_sensitivity`, `predict_impact`, `search_catalog`, `search_automations`, `propose_semantic_query`; with anomaly monitoring also `list_anomalies`, `detect_anomalies`, `explain_anomaly` |
122
+ | specifying | `search_catalog`, `sample_columns`, `ask_user`; with a semantic model `check_data_freshness`; without one `get_join_path`, `list_tables`, `describe_table` |
123
+ | solving | `execute_preview` |
124
+ | executing | none |
125
+ | interpreting | `render_chart` |
126
+
127
+ Root-cause and opportunity questions need a metric tree. Without one the agent
128
+ replies that the workspace "does not have a metric tree configured". Metric-tree
129
+ and anomaly tools are not offered in workspace previews or scheduled runs.
130
+
131
+ ## `validation:`
132
+
133
+ Absent: the 15 rules marked default run. Present: only the listed rules run (an
134
+ entry with `enabled: false` is skipped), and a stage with no list is not
135
+ checked. Entry shape: `- name: <rule>`, optional `enabled`, plus the rule's
136
+ parameters at the same level. Misspelled parameters are ignored silently.
137
+
138
+ | Stage (checked after) | Rule | Default | Params (default) | Fails when |
139
+ | --- | --- | --- | --- | --- |
140
+ | `specified` (specifying) | `metric_resolves` | yes | | a resolved metric's table or column is not in the catalog |
141
+ | | `join_key_exists` | yes | | a join's tables or key are not in the catalog |
142
+ | | `filter_unambiguous` | yes | | a filter column is unknown or matches several tables |
143
+ | `solvable` (solving) | `sql_syntax` | yes | `dialect` (`generic`): accepted, but parsing is generic whatever its value | the SQL does not parse |
144
+ | | `tables_exist_in_catalog` | yes | | `FROM`/`JOIN` names a table the catalog lacks |
145
+ | | `spec_tables_present` | yes | | a table the spec resolved is missing from the SQL |
146
+ | | `column_refs_valid` | yes | | a `table.column` reference does not exist |
147
+ | | `timeseries_order_by_check` | yes | | a time series has no outer `ORDER BY` |
148
+ | `solved` (executing) | `non_empty` | yes | | zero rows |
149
+ | | `truncation_warning` | yes | | a scalar or series result hit the row cap |
150
+ | | `no_nan_inf` | yes | | NaN or ±∞ in a numeric cell |
151
+ | | `outlier_detection` | yes | `threshold_sigma` (5.0), `min_rows` (4) | a value is beyond the z-score threshold |
152
+ | | `null_ratio_check` | yes | `threshold` (0.5) | a numeric column's NULL share is above the threshold |
153
+ | | `duplicate_row_check` | yes | `max_duplicate_ratio` (0.1) | the duplicate-row share is above the ratio |
154
+ | | `freshness_check` | yes | `threshold_days` (1) | the requested range runs past the latest date in the result |
155
+ | | `shape_match` | no | | the result doesn't have the expected shape |
156
+ | | `timeseries_date_check` | no | | a time series' first column is not date-like |
157
+
158
+ Where a failure goes (diagnosing):
159
+
160
+ | Failure | Back to |
161
+ | --- | --- |
162
+ | unresolved metric or join, empty result, semantic compile error | specifying |
163
+ | SQL syntax, null ratio or duplicates found after solving | specifying (fresh attempt) |
164
+ | ambiguous column | clarifying |
165
+ | outlier, freshness gap, bad chart config | interpreting (answers and states the caveat) |
166
+
167
+ ## `semantic_engine:`
168
+
169
+ | Field | Cube | Looker |
170
+ | --- | --- | --- |
171
+ | `vendor` | `cube` | `looker` |
172
+ | `base_url` | required | required |
173
+ | `api_token` | used | n/a |
174
+ | `client_id`, `client_secret` | n/a | used |
175
+
176
+ `${VAR}` is read from the **server's environment**. If the variable is unset,
177
+ or the engine is unreachable at run start, the run fails.
178
+
179
+ ## Commands
180
+
181
+ | Command | Checks |
182
+ | --- | --- |
183
+ | `oxyc validate --file <path>` | JSON-schema shape, offline. Catches wrong types, a malformed `thinking` and vendor spelling. Misses unknown keys, `ref`, databases, globs, rule names, env vars |
184
+ | `oxy validate --file <path>` | parses with the runtime's own parser. Structural only, with the same blind spots |
185
+ | `oxy agentic run <path> -q "<question>"` | the real build and run, streamed by state. `--json`, `--thinking-mode extended_thinking`. Needs Oxy's Postgres (`OXY_DATABASE_URL`) |
186
+ | `oxy test [<file>.test.yml]` | LLM-judged evals; with no file, every `*.test.yml` in the workspace |
187
+ | `oxy build` | not an agent check: it builds embeddings and syncs integrations |
188
+
189
+ ## `.test.yml` (evals)
190
+
191
+ ```yaml
192
+ target: agents/sales.agentic.yml # workspace-relative; omit to infer it
193
+ settings:
194
+ runs: 3 # runs per case (default 3), used for consistency
195
+ concurrency: 5 # default 5
196
+ judge_model: gpt-5-mini # config.yml model name; use a different family
197
+ cases:
198
+ - name: weekly-total # optional
199
+ prompt: "What were total sales last week?"
200
+ expected: "Total sales were $1.2M"
201
+ tags: [sales, critical]
202
+ ```
203
+
204
+ | Detail | Value |
205
+ | --- | --- |
206
+ | Target inference | `sales.agentic.test.yml` tests `sales.agentic.yml` in the same directory |
207
+ | Target kind | must be an `.agentic.yml` |
208
+ | `tool:` on a case | parsed, not yet checked |
209
+ | Useful flags | `--tag <t>`, `--case <index-or-name>`, `--min-accuracy 0.8` (exit 1 below), `--threshold-mode average\|all`, `--format json`, `--output-json`, `-v` |
210
+
211
+ ## Errors → fix
212
+
213
+ | Message | Fix |
214
+ | --- | --- |
215
+ | `no databases configured` | name a database in `databases:`, or give context views a `datasource` |
216
+ | `YAML parse error: …` | malformed YAML, a duplicate key, `thinking: false`, or an unknown `semantic_engine.vendor` |
217
+ | `validation config error: unknown <stage> rule: '…'` | fix the rule name or move it to its stage |
218
+ | `validation config error: invalid params for rule '…'` | fix the parameter's type |
219
+ | `invalid timezone '…'` | use an IANA name |
220
+ | `environment variable '${X}' referenced in config is not set` | set it in the server environment |
221
+ | `semantic engine connection error: …` | check `base_url` and credentials |
222
+ | `glob pattern error: …` | fix the `context:` pattern |