improvement(agent): allow variable references in reasoning effort, verbosity, and thinking level - #6233
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Visibility uses Execution path: Safety: Reviewed by Cursor Bugbot for commit 9aa8fe0. Configure here. |
Greptile SummaryThis PR makes agent tuning fields editable and resolves their reference-bound values at execution time. It also normalizes resolved values and changes capability filtering so unknown static models retain tuning settings, while dynamic-provider models continue using protective capability drops.
Confidence Score: 4/5The PR should not merge until the outstanding silent removal of provider-supported tuning levels from dynamic-provider model requests is addressed. Dynamic-provider IDs are classified as known while their discovered entries expose no tuning capabilities, causing requested reasoning, verbosity, or thinking settings to be cleared before provider dispatch even when the underlying provider model supports them. Files Needing Attention: apps/sim/providers/index.ts, apps/sim/providers/models.ts, apps/sim/providers/utils.ts
|
| Filename | Overview |
|---|---|
| apps/sim/providers/index.ts | Normalizes tuning fields and preserves them for unknown static models, but the previously reported silent drop for capable dynamic-provider models remains. |
| apps/sim/providers/models.ts | Adds known-model classification that treats dynamic-provider prefixes as catalogued even though dynamically discovered entries carry empty capability metadata. |
| apps/sim/providers/utils.ts | Adds model-reference visibility support and safe descriptions for model-level diagnostic values. |
| apps/sim/blocks/blocks/agent.ts | Converts three agent tuning fields to editable comboboxes and keeps them visible for dynamically bound model IDs. |
| apps/sim/lib/workflows/sanitization/references.ts | Adds detection for block, workflow-variable, and environment-variable references used by dynamic field visibility. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Agent tuning field] --> B[Resolve block, variable, or environment reference]
B --> C[Trim and lowercase value]
C --> D{Model classification}
D -->|Unknown static model| E[Forward tuning value]
D -->|Catalogued or dynamic-provider model| F{Capability declared}
F -->|Yes| E
F -->|No| G[Clear tuning value]
E --> H[Provider request]
G --> H
Reviews (7): Last reviewed commit: "refactor(providers): drop the sanitizer'..." | Re-trigger Greptile
… verbosity Reasoning Effort and Verbosity were select-only dropdowns, so a workflow could not sweep them from a variable or an upstream block the way it already can with the model. Both become editable comboboxes, matching the model field directly above them and the managed-agent selectors. - switch both subblocks to `combobox`, keeping their fetched per-model option lists intact - keep them visible when `model` itself holds a reference, since the concrete model id is only known at execution time and cannot be matched against the static capability list - normalize the resolved level in the provider chokepoint so a reference that resolves to `"High"` or to nothing behaves sanely instead of hitting a provider 400
Extends the same treatment to Thinking Level so all three model-tuning fields behave consistently, and logs a level a model does not declare. - switch `thinkingLevel` to `combobox` with the reference-aware condition - normalize it alongside the other two; an empty resolve now takes the deliberate "send nothing" path rather than the incoherent half-state it hit before, and stays distinct from an explicit `none` - warn when a level is not one the model declares, still forwarding it: Sim's per-model lists drive the pickers and can lag a provider, and a sweep needs the provider's own error rather than a silent fallback to the default
e67372d to
034aec7
Compare
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 034aec7. Configure here.
A model bound to a variable or block reference only resolves at execution time, so a run whose reference landed on a model outside Sim's catalogue had its requested level cleared with no signal and quietly fell back to that model's default. Dropping stays the safe default — a provider with no such parameter rejects the whole request — but it is now reported. - log the field, model, and value whenever an unsupported-field level is cleared - cover both diagnostics, including that they stay quiet for a declared level and for the `auto` / `none` sentinels
|
Addressing the two points in the last summary. 1. "Sanitization clears levels when the resolved model is absent from Sim's capability lists, without warning" — valid, fixed in This was a real gap and my own reasoning was inconsistent: I argued against silently dropping undeclared values, while the pre-existing Dropping stays the behavior, because it is genuinely protective — a provider with no such parameter rejects the whole request, and we can't distinguish "model we haven't catalogued yet" from "model that truly has no such field". But it is no longer silent: 2. "Undeclared values for capable models continue to be forwarded" — still deliberate. Reasoning unchanged from the resolved thread on Net: every path that discards or forwards a non-obvious level is observable. Full suite green at 18,393. |
|
@cursor review |
The model-level fields accept environment and block references, so an unrecognized level is not necessarily a mistyped level — it is whatever the reference resolved to, which can be secret content. The diagnostics added for dropped and undeclared levels echoed it straight into server logs. - log a level only when the catalogue declares it somewhere, or it is an `auto` / `none` sentinel; anything else is reported by length alone - stop discarding levels for a model the catalogue has never seen. Absent is unknown, not known-incapable, and a reference is exactly how a newly released model arrives before Sim catalogues it — the provider decides instead. Models the catalogue knows, and every dynamic-provider id, keep the protective drop
|
Both points addressed in Security — resolved level content in logs. Valid, and self-inflicted. The diagnostics I added last round logged the raw Fixed by gating what gets echoed: a level is logged only when the catalogue declares it somewhere ( Dynamic models losing levels. You were right, and I had the distinction wrong. I was treating "absent from the capability list" as "known to be incapable", but those are different: a model the catalogue has never seen is unknown, and a reference is exactly how a newly released model arrives before we catalogue it. So the drop is now conditioned on
That makes the behavior consistent with the forwarding argument from the earlier thread rather than contradicting it. All three cases have tests. Suite green at 18,397; typecheck and boundary gates clean. |
|
@cursor review |
|
On the remaining point — dynamic-provider ids still taking the drop path. Confirmed the underlying fact: zero Leaving it as-is in this PR, deliberately:
Happy to do it as a follow-up if we want dynamic-provider models to accept tuning levels — it is roughly a one-line predicate change plus provider-by-provider testing. Cursor Bugbot: no bugs on this commit. Open review threads: 0. |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
1 issue from previous review remains unresolved.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit fe52248. Configure here.
|
Rejecting the round-5 security finding. I implemented it, then checked whether it actually holds — it doesn't. A model id is not secret, and this codebase already treats it as non-secret everywhere. In this same function, 12 lines below the warning in question: logger.info('API key resolved', {
provider: providerId,
model: request.model, // verbatim, every single request
workspaceId: request.workspaceId,
isBYOK,
})and in the adjacent catch: logger.error('Failed to resolve API key:', { provider: providerId, model: request.model, ... })Plus So redacting the model in two warning lines, while the very next log statement prints it verbatim on every request, protects nothing. It is theater that costs real diagnostic value — for a self-hosted Ollama or vLLM setup, which model is the single most useful field when debugging why a level was dropped, and this change would have replaced it with The premise is also weak on its own terms: the model id is an identifier that must by definition be transmitted to a third-party API. It is not a credential. Reverted, along with the now-unused The level redaction from Current state: 4 commits, all CI green, 0 open threads, Bugbot clean, 18,397 tests passing. |
…warning
Forwarding an undeclared level is deliberate, but it means the Anthropic adapter
receives it and interpolates it straight into its "not supported, ignoring"
warning. Since the field is reference-bound, that value can be whatever a
mistyped `{{ENV_VAR}}` or block reference resolved to — so the redaction added
for the sanitizer's own diagnostics was leaking one layer downstream.
- promote the level renderer to `providers/utils` as `describeModelLevel`, the
single gate every site echoing a caller-supplied level goes through
- use it in Anthropic's warning and in both sanitizer diagnostics
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a4c7abb. Configure here.
|
Round 6: 0 open threads, Cursor Bugbot passing with no findings, all CI green. Two points remain in the summary; declining both, with reasons. 1. "Redaction allowlists common level strings globally, leaving reference-resolved secrets with those exact values visible." The allowlist is not open-ended. Enumerated from the catalogue, it is 9 fixed lowercase words, longest 8 characters: For this to leak, a secret would have to be exactly one of those nine words. At that point echoing it reveals nothing — an attacker already has the full vocabulary, and there is no entropy to protect. Redaction exists to keep high-entropy resolved content out of logs, which it does: anything outside those nine tokens is reduced to a length. Widening it further would only degrade diagnostics, since these are precisely the values an operator needs to read. 2. Dynamic-provider levels still discarded. Third time raised; declining for the reasons already on the record — pre-existing on Stopping the review loop here. Six rounds produced three real defects, all fixed:
and two findings I could not substantiate (model ids as secrets; this allowlist point), both answered with evidence rather than code changes. Remaining before merge: this has not been exercised in a browser — no one has typed a reference into these fields or run a workflow with a reference-bound level. |
The two warnings logged server-side, where the workflow author who set the level never sees them, and the surprising case they described — a level discarded for a model newer than the catalogue — is now fixed at the source rather than narrated. They also carried the redaction that leaked resolved content before it was caught, so removing them removes that surface entirely. Levels still normalize, and still drop for a catalogued model that does not take the field. `describeModelLevel` stays for Anthropic's unsupported-thinking warning, which is a pre-existing log this feature newly exposes to resolved reference content.
|
Trimmed scope in Removed the two sanitizer diagnostics ( Kept, after checking each on merit rather than by scope:
Total diff is down from 645 to 441 insertions. Suite green at 18,397; typecheck clean. |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 9aa8fe0. Configure here.
|
Final status after the scope cut. All 11 checks passing · 0 open threads · Cursor Bugbot clean · mergeable. Greptile holds at 4/5 on a single point: dynamic-provider ids ( Declining it for the reasons already on the record: it is pre-existing on Worth noting the review did not re-raise the diagnostics removed in Seven rounds produced three real defects, all fixed:
and two findings that did not survive verification (model ids as secrets; the 9-word redaction allowlist), both answered with evidence. Outstanding before merge: not exercised in a browser. No one has typed a reference into these fields or run a workflow with a reference-bound level. |

Summary
<block.output>/<variable.x>/{{ENV_VAR}}references — same as the Model field directly above them. Each keeps its per-model option list.modelitself holds a reference, since the concrete model id is only known at execution time and can't be matched against the static capability list.sanitizeRequest, and a reference that resolves to nothing reads as unset rather than sending an empty string the provider rejects (and stays distinct from an explicitnone).One deliberate behavior change
Levels are no longer discarded for a model absent from
models.ts.The Model field is already an editable combobox on
staging, so a model newer than our catalogue is typeable today and routes by pattern (gpt-5.2 → openai,claude-opus-6 → anthropic) and executes normally — butsupportsReasoningEffort('gpt-5.2')isfalse, so its tuning level was silently dropped. Every model release re-created that papercut until someone updated the catalogue.Catalogued models that genuinely lack the field (
claude-opus-4-6+reasoningEffort) and every dynamic-provider id (ollama/…,openrouter/…) keep the protective drop, so the "stale level after switching models" path is unchanged.Everything else is behavior-neutral.
Testing
Full
apps/simsuite green (18,397). Coverage added for: reference resolution into all three fields (block output, workflow variable, env var); level normalization including thenone-vs-unset distinction; catalogued/uncatalogued/dynamic-provider drop behavior; combobox conversion and reference-model visibility; and level redaction.Verified rather than assumed:
containsReference— 0 false positives across all 204 model ids and all level valuesmode: 'advanced', so with advanced collapsed the condition isn't consulted at all — that path is byte-identicaldropdownsubblock typethinkingLevelconsumersNot done
Not exercised in a browser — no one has typed a reference into these fields or run a workflow with a reference-bound level.
Checklist