fix(automations): resolve agent presets live from host instead of snapshot#4481
Conversation
…pshot Automation dispatch was reading a `ResolvedAgentConfig` snapshot stored on the row at create time, sourced from the desktop's local v1 settings (`electronTrpc.settings.getAgentPresets`). That source is disjoint from the v2 host service's `host_agent_configs` table that `Settings → Agents` now reads and writes, so v2 preset edits silently never reached runs. Replace the snapshot with an `agent` id and delegate to the host's `agents.run` over the relay. The host resolves the current `HostAgentConfig` from its own DB (instance id first, then `presetId` fallback) and launches chat (`agent === "superset"`) or terminal with the live command/args/env. Edits in v2 settings now apply on the next run, with no client-side snapshot to drift. - DB: replace `automations.agent_config jsonb` with `agent text not null`; migration backfills from `agent_config->>'id'` before dropping. - tRPC/SDK/CLI/MCP: rename `agentConfig` -> `agent` end-to-end; drop the `AgentConfig` type. - Renderer: `AgentPicker` and `AgentCell` now source from `useV2AgentChoices(useHostUrl(hostId))`. `CreateAutomationDialog` and `AutomationDetailSidebar` pass `targetHostId` and submit `agent: <id>`. - Dispatcher: single `agents.run` call via `relayMutation`; chat session rows are now created host-side by `runChatAgent`. Existing rows backfill to preset slugs (`claude`/`codex`) which fall through the host's `resolveHostAgentConfig` presetId path; new automations created via the picker pin to the host-agent instance UUID.
📝 WalkthroughWalkthroughThe PR replaces the structured automation ChangesAgent field modernization
🎯 3 (Moderate) | ⏱️ ~25 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews. |
Greptile SummaryThis PR replaces the snapshotted
Confidence Score: 3/5The migration and automations list view have two issues that should be addressed before shipping to production. The migration's backfill has no fallback for rows where the JSON extraction returns NULL; if any such row exists the NOT NULL step aborts the migration and blocks the deploy. Separately, the automations list passes The migration SQL and
|
| Filename | Overview |
|---|---|
| packages/db/drizzle/0051_replace_automation_agent_config_with_agent_id.sql | 4-step migration (add → backfill → NOT NULL → drop) with no COALESCE fallback in the backfill; any row without a valid agent_config.id would cause the NOT NULL step to fail. |
| packages/trpc/src/router/automation/dispatch.ts | Dispatcher simplified cleanly: single relayMutation to agents.run replaces the old branching on agent kind; workspace creation and run-row bookkeeping are unchanged. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/page.tsx | AgentCell now receives targetHostId directly; automations with no pinned host pass null, causing the agent label to fall back to the raw agentId (regression vs. old snapshot label). |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx | Agent state migrated from hardcoded "claude" to live hostAgents[0] default; fallback effect works correctly but can briefly clear the selection during host-switch transitions. |
| packages/trpc/src/router/automation/schema.ts | agentSchema (string, 1–200 chars) cleanly replaces the old complex AgentConfig object schema for both create and update inputs. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/AgentCell/AgentCell.tsx | Switches from prop-passed label to live host resolution; gracefully falls back to raw agentId when host is unavailable, though UUID IDs will be unreadable in that state. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/AgentPicker/AgentPicker.tsx | Cleanly switched to useV2AgentChoices for live host-scoped choices; iconId fallback to agent.id preserves icon resolution for both presets and custom agents. |
| packages/db/src/schema/schema.ts | automations table updated to agent text().notNull(), consistent with the migration; no other schema concerns. |
| packages/sdk/src/resources/automations.ts | SDK types updated correctly: AutomationSummary.agent replaces agentConfig, create/update params align with new schema. |
| packages/cli/src/commands/automations/create/command.ts | Removes loadAgentConfigFromFile and resolveDefaultAgentConfig helpers; --agent flag now passes through directly as a string, and --agentConfigFile is dropped. |
Sequence Diagram
sequenceDiagram
participant Cron as Cron/runNow
participant Dispatch as dispatchAutomation
participant DB as Neon DB (automations)
participant Relay as Relay → Host
participant Host as Host (agents.run)
Cron->>Dispatch: "{ automation.agent, scheduledFor }"
Dispatch->>DB: "INSERT automation_run (status=dispatching)"
Dispatch->>Relay: workspaces.create (if no v2WorkspaceId)
Relay-->>Dispatch: "{ workspaceId }"
Dispatch->>Relay: "agents.run({ workspaceId, agent, prompt })"
Relay->>Host: resolveHostAgentConfig(agent)
Host-->>Relay: launch terminal/chat session
Relay-->>Dispatch: "{ kind, sessionId }"
Dispatch->>DB: "UPDATE automation_run (status=dispatched, sessionId)"
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
packages/db/drizzle/0051_replace_automation_agent_config_with_agent_id.sql:2
No COALESCE fallback in the backfill means any row whose `agent_config` is NULL (or whose JSON blob lacks an `id` key) will leave `agent` as NULL after the UPDATE, and the subsequent `ALTER COLUMN … SET NOT NULL` will fail with a constraint-violation error, aborting the migration. Even if production data is clean today, a defensive fallback costs nothing and prevents a silent deploy failure.
```suggestion
UPDATE "automations" SET "agent" = COALESCE("agent_config" ->> 'id', 'claude') WHERE "agent" IS NULL;--> statement-breakpoint
```
### Issue 2 of 3
apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/page.tsx:481-484
**Agent label missing for automations without a pinned host**
`AgentCell` receives `hostId={automation.targetHostId ?? null}`, but automations created before this PR (and CLI-created ones) typically have `targetHostId = null`. With a null `hostId`, `useHostUrl` returns no URL, `useV2AgentChoices` returns `[]`, `match` is undefined, and the cell falls back to rendering the raw `agentId`. For backfilled slug values ("claude", "codex") this is readable but unstyled; for any UUID-format agent IDs it shows the raw UUID. The `AutomationDetailSidebar` handles this correctly by falling back to `localHostId` — the same pattern should be applied here.
### Issue 3 of 3
apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx:76-80
**Agent selection cleared during transient `hostAgents` reload**
When the user switches the target host (via `DevicePicker`), `hostAgents` briefly becomes `[]` while the new host's agents are fetched. During that window the guard `hostAgents.some((option) => option.id === agent)` fails (empty list), `fallback` evaluates to `null`, and `setAgent(null)` fires — clearing the in-progress selection. The picker then shows empty and the Create button becomes disabled until the load settles. Using a stable "previous value" pattern (e.g. keeping the old agent until the new list loads and then applying the fallback only if the list is non-empty) would avoid the flash.
Reviews (1): Last reviewed commit: "fix(automations): resolve agent presets ..." | Re-trigger Greptile
| @@ -0,0 +1,4 @@ | |||
| ALTER TABLE "automations" ADD COLUMN "agent" text;--> statement-breakpoint | |||
| UPDATE "automations" SET "agent" = "agent_config" ->> 'id' WHERE "agent" IS NULL;--> statement-breakpoint | |||
There was a problem hiding this comment.
No COALESCE fallback in the backfill means any row whose
agent_config is NULL (or whose JSON blob lacks an id key) will leave agent as NULL after the UPDATE, and the subsequent ALTER COLUMN … SET NOT NULL will fail with a constraint-violation error, aborting the migration. Even if production data is clean today, a defensive fallback costs nothing and prevents a silent deploy failure.
| UPDATE "automations" SET "agent" = "agent_config" ->> 'id' WHERE "agent" IS NULL;--> statement-breakpoint | |
| UPDATE "automations" SET "agent" = COALESCE("agent_config" ->> 'id', 'claude') WHERE "agent" IS NULL;--> statement-breakpoint |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/db/drizzle/0051_replace_automation_agent_config_with_agent_id.sql
Line: 2
Comment:
No COALESCE fallback in the backfill means any row whose `agent_config` is NULL (or whose JSON blob lacks an `id` key) will leave `agent` as NULL after the UPDATE, and the subsequent `ALTER COLUMN … SET NOT NULL` will fail with a constraint-violation error, aborting the migration. Even if production data is clean today, a defensive fallback costs nothing and prevents a silent deploy failure.
```suggestion
UPDATE "automations" SET "agent" = COALESCE("agent_config" ->> 'id', 'claude') WHERE "agent" IS NULL;--> statement-breakpoint
```
How can I resolve this? If you propose a fix, please make it concise.|
|
||
| <span className="min-w-0 text-xs text-muted-foreground"> | ||
| <AgentCell | ||
| agentId={automation.agentConfig.id} | ||
| label={automation.agentConfig.label} | ||
| agentId={automation.agent} |
There was a problem hiding this comment.
Agent label missing for automations without a pinned host
AgentCell receives hostId={automation.targetHostId ?? null}, but automations created before this PR (and CLI-created ones) typically have targetHostId = null. With a null hostId, useHostUrl returns no URL, useV2AgentChoices returns [], match is undefined, and the cell falls back to rendering the raw agentId. For backfilled slug values ("claude", "codex") this is readable but unstyled; for any UUID-format agent IDs it shows the raw UUID. The AutomationDetailSidebar handles this correctly by falling back to localHostId — the same pattern should be applied here.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/page.tsx
Line: 481-484
Comment:
**Agent label missing for automations without a pinned host**
`AgentCell` receives `hostId={automation.targetHostId ?? null}`, but automations created before this PR (and CLI-created ones) typically have `targetHostId = null`. With a null `hostId`, `useHostUrl` returns no URL, `useV2AgentChoices` returns `[]`, `match` is undefined, and the cell falls back to rendering the raw `agentId`. For backfilled slug values ("claude", "codex") this is readable but unstyled; for any UUID-format agent IDs it shows the raw UUID. The `AutomationDetailSidebar` handles this correctly by falling back to `localHostId` — the same pattern should be applied here.
How can I resolve this? If you propose a fix, please make it concise.| useEffect(() => { | ||
| if (agent && hostAgents.some((option) => option.id === agent)) return; | ||
| const fallback = hostAgents[0]?.id ?? null; | ||
| if (fallback !== agent) setAgent(fallback); | ||
| }, [agent, hostAgents]); |
There was a problem hiding this comment.
Agent selection cleared during transient
hostAgents reload
When the user switches the target host (via DevicePicker), hostAgents briefly becomes [] while the new host's agents are fetched. During that window the guard hostAgents.some((option) => option.id === agent) fails (empty list), fallback evaluates to null, and setAgent(null) fires — clearing the in-progress selection. The picker then shows empty and the Create button becomes disabled until the load settles. Using a stable "previous value" pattern (e.g. keeping the old agent until the new list loads and then applying the fallback only if the list is non-empty) would avoid the flash.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx
Line: 76-80
Comment:
**Agent selection cleared during transient `hostAgents` reload**
When the user switches the target host (via `DevicePicker`), `hostAgents` briefly becomes `[]` while the new host's agents are fetched. During that window the guard `hostAgents.some((option) => option.id === agent)` fails (empty list), `fallback` evaluates to `null`, and `setAgent(null)` fires — clearing the in-progress selection. The picker then shows empty and the Create button becomes disabled until the load settles. Using a stable "previous value" pattern (e.g. keeping the old agent until the new list loads and then applying the fallback only if the list is non-empty) would avoid the flash.
How can I resolve this? If you propose a fix, please make it concise.Mirrors the API change. Drops the dead `--agent-config-file` flag from the CLI reference and the matching `agentConfig` field from the SDK example. CLI_SPEC_TARGET aligned with the shipped surface.
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx (1)
274-279: 💤 Low valueConsider making AgentPicker accept nullable value.
Using
agent ?? ""as a sentinel for "no selection" works but is implicit. The empty string won't match any agent, but it would be clearer ifAgentPickerexplicitly acceptedvalue: string | nulland handled null as "no selection" internally.Alternative approach
Update
AgentPickerprops to acceptvalue: string | null, then simplify the call site:<AgentPicker className="w-[100px]" hostId={targetHostId} - value={agent ?? ""} + value={agent} onChange={setAgent} />And in
AgentPicker.tsx:interface AgentPickerProps { hostId: string | null | undefined; - value: string; + value: string | null; onChange: (next: string) => void; className?: string; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx` around lines 274 - 279, Update AgentPicker to accept a nullable value type so callers can pass null instead of using an empty-string sentinel; change its prop signature to value: string | null and ensure the component treats null as "no selection" internally (e.g., renders placeholder and maps selections to string or null onChange). Then simplify this call site by passing value={agent} (and keep onChange={setAgent}, hostId={targetHostId}) so the agent state can be null when nothing is selected; ensure AgentPicker’s onChange still emits string | null to match setAgent.apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/AgentPicker/AgentPicker.tsx (1)
24-87: ⚡ Quick winConsider showing a disabled state when no agents are available.
When
hostIdis null oruseV2AgentChoicesreturns an empty array, the picker shows "Select agent" but the dropdown only contains "Configure agents…". This could be confusing. Consider disabling the picker or showing a helper message whenagents.length === 0.🎨 Suggested enhancement
export function AgentPicker({ hostId, value, onChange, className, }: AgentPickerProps) { const navigate = useNavigate(); const hostUrl = useHostUrl(hostId); const { agents } = useV2AgentChoices(hostUrl); const isDark = useIsDarkTheme(); const selectedAgent = agents.find((agent) => agent.id === value); const selectedIcon = selectedAgent ? getPresetIcon(selectedAgent.iconId ?? selectedAgent.id, isDark) : null; return ( <DropdownMenu> <DropdownMenuTrigger asChild> <PickerTrigger + disabled={agents.length === 0} className={className} icon={ selectedIcon ? ( <img src={selectedIcon} alt="" className="size-3.5 shrink-0 object-contain" /> ) : ( <LuCpu className="size-4 shrink-0" /> ) } - label={selectedAgent?.label ?? "Select agent"} + label={ + agents.length === 0 + ? "No agents available" + : (selectedAgent?.label ?? "Select agent") + } /> </DropdownMenuTrigger>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/AgentPicker/AgentPicker.tsx` around lines 24 - 87, AgentPicker currently shows "Select agent" but the dropdown only contains "Configure agents…" when there are no agents; update AgentPicker to detect when agents.length === 0 or hostId is null and render a disabled state: pass a disabled prop to PickerTrigger/DropdownMenuTrigger (or render a visually disabled trigger) and change the label to "No agents available" (or similar), and inside DropdownMenuContent render a non-selectable helper item like "No agents found" (use a disabled DropdownMenuItem or aria-disabled) above the "Configure agents…" entry so users understand why selection is unavailable; also ensure onSelect isn't called for the helper and keep accessibility attributes (aria-disabled/role) consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/trpc/src/router/automation/automation.ts`:
- Around line 271-272: The code is allowing targetHostId to be null when storing
HostAgentConfig.id (see the agent and targetHostId fields), which causes
dispatch to forward the UUID to the wrong host; enforce a non-null targetHostId
by validating input.targetHostId and rejecting or throwing when it's missing for
create/update paths that persist HostAgentConfig.id (adjust the handlers that
set agent: input.agent, targetHostId: input.targetHostId ?? null and the similar
block around the other occurrence), and update any request/validation schema to
require targetHostId so the stored config always contains a concrete host UUID.
In `@packages/trpc/src/router/automation/schema.ts`:
- Line 4: The agentSchema currently allows whitespace-only strings because
z.string().min(1) checks length without trimming; update agentSchema to call
.trim() before applying .min(1). Specifically, change the schema definition for
agentSchema (z.string()) to include .trim() and keep the existing bounds (e.g.,
.trim().min(1).max(200)) so leading/trailing whitespace is removed before
validation.
---
Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/AgentPicker/AgentPicker.tsx`:
- Around line 24-87: AgentPicker currently shows "Select agent" but the dropdown
only contains "Configure agents…" when there are no agents; update AgentPicker
to detect when agents.length === 0 or hostId is null and render a disabled
state: pass a disabled prop to PickerTrigger/DropdownMenuTrigger (or render a
visually disabled trigger) and change the label to "No agents available" (or
similar), and inside DropdownMenuContent render a non-selectable helper item
like "No agents found" (use a disabled DropdownMenuItem or aria-disabled) above
the "Configure agents…" entry so users understand why selection is unavailable;
also ensure onSelect isn't called for the helper and keep accessibility
attributes (aria-disabled/role) consistent.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx`:
- Around line 274-279: Update AgentPicker to accept a nullable value type so
callers can pass null instead of using an empty-string sentinel; change its prop
signature to value: string | null and ensure the component treats null as "no
selection" internally (e.g., renders placeholder and maps selections to string
or null onChange). Then simplify this call site by passing value={agent} (and
keep onChange={setAgent}, hostId={targetHostId}) so the agent state can be null
when nothing is selected; ensure AgentPicker’s onChange still emits string |
null to match setAgent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 69f6e3de-e9c4-4e77-92d2-abce35570fa6
📒 Files selected for processing (22)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/$automationId/components/AutomationDetailSidebar/AutomationDetailSidebar.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/AgentCell/AgentCell.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/AgentPicker/AgentPicker.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/HistoryDropdown.tsxpackages/cli/src/commands/automations/create/command.tspackages/cli/src/commands/automations/list/command.tspackages/cli/src/commands/automations/update/command.tspackages/db/drizzle/0051_replace_automation_agent_config_with_agent_id.sqlpackages/db/drizzle/meta/0051_snapshot.jsonpackages/db/drizzle/meta/_journal.jsonpackages/db/src/schema/schema.tspackages/mcp-v2/src/tools/automations/create.tspackages/mcp-v2/src/tools/automations/update.tspackages/sdk/src/client.tspackages/sdk/src/index.tspackages/sdk/src/resources/automations.tspackages/sdk/src/resources/index.tspackages/trpc/src/router/automation/automation.tspackages/trpc/src/router/automation/dispatch.tspackages/trpc/src/router/automation/schema.ts
💤 Files with no reviewable changes (3)
- packages/sdk/src/index.ts
- packages/sdk/src/resources/index.ts
- packages/sdk/src/client.ts
| agent: input.agent, | ||
| targetHostId: input.targetHostId ?? null, |
There was a problem hiding this comment.
Require targetHostId when storing a host-agent UUID.
New automations now store HostAgentConfig.id values, but create/update still allow targetHostId to be null. When that happens, dispatch falls back to the first accessible online host and forwards the UUID there, which makes host-side agent resolution fail as soon as the config belongs to a different host.
Suggested guard
+function isUuidLike(value: string): boolean {
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
+}
+
create: protectedProcedure
.input(createAutomationSchema)
.mutation(async ({ ctx, input }) => {
+ if (isUuidLike(input.agent) && !input.targetHostId) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "targetHostId is required when agent is a host agent id",
+ });
+ }
+
const { organizationId, subscription } =
await requireActiveOrgMembershipWithSubscription(ctx);
requirePaidSubscription(subscription);
@@
update: protectedProcedure
.input(updateAutomationSchema)
.mutation(async ({ ctx, input }) => {
const organizationId = await requireActiveOrgMembership(ctx);
const existing = await getAutomationForUser(
ctx.session.user.id,
organizationId,
input.id,
);
+ const nextAgent = input.agent ?? existing.agent;
+ const nextTargetHostId =
+ input.targetHostId === undefined
+ ? existing.targetHostId
+ : input.targetHostId;
+ if (isUuidLike(nextAgent) && !nextTargetHostId) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "targetHostId is required when agent is a host agent id",
+ });
+ }
if (input.targetHostId !== undefined && input.targetHostId !== null) {
await verifyHostAccess(Also applies to: 345-349
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/trpc/src/router/automation/automation.ts` around lines 271 - 272,
The code is allowing targetHostId to be null when storing HostAgentConfig.id
(see the agent and targetHostId fields), which causes dispatch to forward the
UUID to the wrong host; enforce a non-null targetHostId by validating
input.targetHostId and rejecting or throwing when it's missing for create/update
paths that persist HostAgentConfig.id (adjust the handlers that set agent:
input.agent, targetHostId: input.targetHostId ?? null and the similar block
around the other occurrence), and update any request/validation schema to
require targetHostId so the stored config always contains a concrete host UUID.
| kind: z.enum(["terminal", "chat"]), | ||
| }) | ||
| .passthrough() as unknown as z.ZodType<ResolvedAgentConfig>; | ||
| const agentSchema = z.string().min(1).max(200); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's check the file structure and see the schema file
cd packages/trpc/src/router/automation/ 2>/dev/null && ls -la || echo "Path not found, listing repository structure..."Repository: superset-sh/superset
Length of output: 621
🏁 Script executed:
# Find the schema file
find . -name "schema.ts" -path "*/automation/*" | head -20Repository: superset-sh/superset
Length of output: 112
🏁 Script executed:
# Read the schema file to understand the context
cat packages/trpc/src/router/automation/schema.ts | head -60Repository: superset-sh/superset
Length of output: 1724
🏁 Script executed:
# Check if there are any Biome lint issues in this file
fd "biome" 2>/dev/null || echo "Checking for biome configuration..."Repository: superset-sh/superset
Length of output: 76
🏁 Script executed:
# Check Zod version and test the validation behavior
cd . && cat package.json | grep -A 2 '"zod"'Repository: superset-sh/superset
Length of output: 46
🏁 Script executed:
# Create a quick test to validate Zod's behavior with whitespace
cat > /tmp/test_zod.mjs << 'EOF'
import { z } from 'zod';
const schema = z.string().min(1).max(200);
const schemaWithTrim = z.string().trim().min(1).max(200);
// Test whitespace-only string
const whitespaceStr = " ";
console.log("Testing: ' ' (3 spaces)");
try {
const result = schema.parse(whitespaceStr);
console.log("Original schema (.min(1)): PASSES - value:", JSON.stringify(result));
} catch (e) {
console.log("Original schema (.min(1)): FAILS");
}
try {
const result = schemaWithTrim.parse(whitespaceStr);
console.log("With .trim(): PASSES - value:", JSON.stringify(result));
} catch (e) {
console.log("With .trim(): FAILS -", e.errors[0].message);
}
// Test single character
const singleChar = "a";
console.log("\nTesting: 'a'");
try {
const result = schema.parse(singleChar);
console.log("Original schema: PASSES");
} catch (e) {
console.log("Original schema: FAILS");
}
EOF
# Check if we can run this
node /tmp/test_zod.mjs 2>&1 || echo "Node test not available in sandbox"Repository: superset-sh/superset
Length of output: 1090
🏁 Script executed:
# Check for Biome lint in the file
rg "TODO|FIXME|any" packages/trpc/src/router/automation/schema.tsRepository: superset-sh/superset
Length of output: 46
🏁 Script executed:
# Look at the biome config to understand lint rules
cat biome.jsonc | head -100Repository: superset-sh/superset
Length of output: 1581
🏁 Script executed:
# Check if there are any Biome lint overrides for packages/trpc
rg "packages/trpc" biome.jsoncRepository: superset-sh/superset
Length of output: 46
🏁 Script executed:
# Check the actual usage of agentSchema to confirm it's reused as described
rg "agentSchema" packages/trpc/src/router/automation/schema.tsRepository: superset-sh/superset
Length of output: 166
🏁 Script executed:
# Verify Zod string.min() documentation from web to confirm whitespace behavior
web_search
Zod z.string().min(1) whitespace validation behaviorRepository: superset-sh/superset
Length of output: 253
🌐 Web query:
Zod z.string().min(1) whitespace validation behavior trim
💡 Result:
Answer: z.string.trim only trims leading/trailing whitespace (it does not remove whitespace inside the string). Zod also applies.min(1) to the resulting string after transforms, so “spaces-only” inputs will fail.min(1) only if you trim first. If you want “trim + min 1” behavior: const schema = z.string.trim.min(1) This is the common pattern discussed in Zod issues (e.g., z.string.trim.min(1,...)). [1] If you want to remove ALL whitespace characters (spaces/tabs/newlines inside the string too), you need a transform (or preprocess) that deletes internal whitespace, then validate the non-empty result. One approach is transform + pipe: z.string.transform(value => value.replace(/\s+/g, '')).pipe(z.string.min(1, { message: 'This field is required' })) This is explicitly suggested as the way to remove all whitespace before additional validations. [2] Zod’s docs also list z.string.trim as a string transform that trims whitespace. [3]
Citations:
- 1: z.string() validates empty strings colinhacks/zod#2466
- 2: https://stackoverflow.com/questions/77134910/how-can-i-remove-all-whitespace-in-zod
- 3: https://v4.zod.dev/api?id=strings
🏁 Script executed:
# Verify the exact line numbers and context one more time for precision
sed -n '1,45p' packages/trpc/src/router/automation/schema.ts | cat -nRepository: superset-sh/superset
Length of output: 1563
Trim agent before the non-empty check.
z.string().min(1) accepts whitespace-only strings like " " since Zod applies .min() to string length without trimming by default. Callers can persist an unusable agent identifier that only fails later at dispatch time. Use .trim() to remove leading and trailing whitespace before validation.
Suggested fix
-const agentSchema = z.string().min(1).max(200);
+const agentSchema = z.string().trim().min(1).max(200);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const agentSchema = z.string().min(1).max(200); | |
| const agentSchema = z.string().trim().min(1).max(200); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/trpc/src/router/automation/schema.ts` at line 4, The agentSchema
currently allows whitespace-only strings because z.string().min(1) checks length
without trimming; update agentSchema to call .trim() before applying .min(1).
Specifically, change the schema definition for agentSchema (z.string()) to
include .trim() and keep the existing bounds (e.g., .trim().min(1).max(200)) so
leading/trailing whitespace is removed before validation.
Greptile flagged that the original 0051 had no fallback if any row's `agent_config->>'id'` returned NULL — that would trip the NOT NULL step and abort the deploy. Worked clean on dev (56/56 rows had id), but defends against a stray row in prod. Also wrap every statement in `IF [NOT] EXISTS` / EXISTS-guards so a re-apply against a partially-migrated branch is a no-op (the information_schema check on the UPDATE prevents it from referencing agent_config after the column has already been dropped).
There was a problem hiding this comment.
🧹 Nitpick comments (3)
apps/docs/content/docs/cli/cli-reference.mdx (2)
883-883: 💤 Low valueConsider adding context for the 'superset' value.
The update command description omits the "for built-in chat" explanation that appears in the create command (line 852). While brevity is reasonable for update commands, adding this context would help users who read the update docs first.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/docs/content/docs/cli/cli-reference.mdx` at line 883, Update the flag description for the "--agent <agent>" entry so it includes the same "for built-in chat" context used in the create command; specifically modify the object with key flag: "--agent <agent>" (description currently "New host agent presetId, instance UUID, or 'superset'.") to append or revise the text to mention that 'superset' is for the built-in chat, mirroring the create command wording for consistency.
852-852: ⚡ Quick winImprove consistency and discoverability of preset IDs.
Two minor improvements:
Formatting consistency: The default value
claudeshould be formatted consistently with'superset'. Consider:Default: 'claude'.Preset discovery: Users may not know what preset IDs are valid beyond the examples. Consider adding a note directing them to
superset agents list --localto discover configured agents.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/docs/content/docs/cli/cli-reference.mdx` at line 852, Update the "--agent <agent>" flag entry so the default value is formatted consistently as 'claude' (match the existing quoting for 'superset') and append a short note about preset discovery pointing users to run "superset agents list --local" to see configured preset IDs; update the flag description string (the object with key flag: "--agent <agent>") to include "Default: 'claude'." and add a trailing sentence like "Use `superset agents list --local` to discover configured preset IDs." so users can find valid presets.packages/cli/CLI_SPEC_TARGET.md (1)
605-606: ⚡ Quick winConsider adding guidance on discovering valid preset IDs.
The flag description mentions "Host agent presetId" as an option, but users may not know what preset IDs are available. Consider adding a reference to help users discover them, such as:
To see available preset IDs on a host, use
superset agents list --host <id>.Additionally, for consistency with the special literal value
superset(backtick-formatted), consider formatting the default valueclaudethe same way:`claude`.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/CLI_SPEC_TARGET.md` around lines 605 - 606, Update the `--agent <agent>` flag description to (1) include a short hint on how to discover valid host agent preset IDs (e.g., reference a command like "use superset agents list --host <id> to view available preset IDs") and (2) format the default value `claude` with backticks to match the existing `superset` literal; edit the text for the `--agent <agent>` flag in the CLI spec to incorporate both changes and keep the wording concise.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@apps/docs/content/docs/cli/cli-reference.mdx`:
- Line 883: Update the flag description for the "--agent <agent>" entry so it
includes the same "for built-in chat" context used in the create command;
specifically modify the object with key flag: "--agent <agent>" (description
currently "New host agent presetId, instance UUID, or 'superset'.") to append or
revise the text to mention that 'superset' is for the built-in chat, mirroring
the create command wording for consistency.
- Line 852: Update the "--agent <agent>" flag entry so the default value is
formatted consistently as 'claude' (match the existing quoting for 'superset')
and append a short note about preset discovery pointing users to run "superset
agents list --local" to see configured preset IDs; update the flag description
string (the object with key flag: "--agent <agent>") to include "Default:
'claude'." and add a trailing sentence like "Use `superset agents list --local`
to discover configured preset IDs." so users can find valid presets.
In `@packages/cli/CLI_SPEC_TARGET.md`:
- Around line 605-606: Update the `--agent <agent>` flag description to (1)
include a short hint on how to discover valid host agent preset IDs (e.g.,
reference a command like "use superset agents list --host <id> to view available
preset IDs") and (2) format the default value `claude` with backticks to match
the existing `superset` literal; edit the text for the `--agent <agent>` flag in
the CLI spec to incorporate both changes and keep the wording concise.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 39acb1a1-eafe-4594-beaf-9af148f3b9e0
📒 Files selected for processing (4)
apps/docs/content/docs/cli/cli-reference.mdxapps/docs/content/docs/sdk/reference.mdxpackages/cli/CLI_SPEC_TARGET.mdpackages/db/drizzle/0051_replace_automation_agent_config_with_agent_id.sql
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/db/drizzle/0051_replace_automation_agent_config_with_agent_id.sql
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/db/drizzle/0051_replace_automation_agent_config_with_agent_id.sql">
<violation number="1" location="packages/db/drizzle/0051_replace_automation_agent_config_with_agent_id.sql:2">
P2: The `EXISTS` guard does not make this update safe when `agent_config` is absent; the statement can still fail before execution because it directly references the dropped column.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
There was a problem hiding this comment.
3 issues found across 22 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/page.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/page.tsx:485">
P2: Auto-targeted automations are resolved against the viewer’s local host when rendering `AgentCell`, which can show incorrect agent labels/icons compared to the actual dispatch host.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/$automationId/components/AutomationDetailSidebar/AutomationDetailSidebar.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/$automationId/components/AutomationDetailSidebar/AutomationDetailSidebar.tsx:152">
P1: Selecting an agent now stores a host-specific agent ID but does not persist the selected host, so runs can be dispatched to a different host where that ID does not exist.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx:104">
P2: Template prefill can run repeatedly when agent choices refresh, because `applyTemplate` depends on `hostAgents`; that can reset user-edited form fields while the dialog is open.
(Based on your team's feedback about narrowing React effect dependencies to required fields.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| agentId={automation.agentConfig.id} | ||
| label={automation.agentConfig.label} | ||
| agentId={automation.agent} | ||
| hostId={automation.targetHostId ?? null} |
There was a problem hiding this comment.
P2: Auto-targeted automations are resolved against the viewer’s local host when rendering AgentCell, which can show incorrect agent labels/icons compared to the actual dispatch host.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/page.tsx, line 485:
<comment>Auto-targeted automations are resolved against the viewer’s local host when rendering `AgentCell`, which can show incorrect agent labels/icons compared to the actual dispatch host.</comment>
<file context>
@@ -481,8 +481,8 @@ function AutomationsPage() {
- agentId={automation.agentConfig.id}
- label={automation.agentConfig.label}
+ agentId={automation.agent}
+ hostId={automation.targetHostId ?? null}
/>
</span>
</file context>
… fallback The Team scope tab on the automations page lists teammates' automations, which usually pin `targetHostId` to a host the current user has no tRPC access to. `useV2AgentChoices(hostUrl)` returns empty for those, and the old fallback showed the raw `agent` id (e.g. "claude") in the cell. Fall through to `getPresetById` from `@superset/shared/host-agent-presets` when the host query has no match. Resolves to the seeded label for any presetId-shaped agent value (`claude` -> "Claude", `codex` -> "Codex", etc.), which is the 99% case. Custom non-preset agents pinned to a cross-host automation still show the raw id, but that's rare. Apply the same fallback in `AgentPicker` so the trigger label is sensible before the host query lands and for the same cross-host edge case.
- Sidebar: when changing the agent on an auto-routed automation, also persist `targetHostId` to the host the picker was scoped against. Otherwise a host-specific instance UUID could be written to a row that dispatches to a different online host, which wouldn't have that id. (cubic P1) - Migration: wrap the backfill UPDATE in a `DO $$ ... EXECUTE ... $$` block so the `agent_config ->> 'id'` reference isn't parsed when re-applying the migration against a DB where the column has already been dropped. The earlier `EXISTS (information_schema)` guard avoided execution but not parse-time validation. (cubic P2) - CreateAutomationDialog: split the template prefill into a one-shot scalar-fields effect (refs-tracked) and a separate agent-matching effect that runs only when `hostAgents` first resolves. Stops the whole prefill from re-firing and clobbering user edits when the agent-list query refreshes mid-session. (cubic P2)
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx:96">
P2: Template selection from the gallery no longer applies the template’s preferred agent, causing automations to run with the wrong agent.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
| const applyTemplate = useCallback((template: AutomationTemplate) => { | ||
| setName(template.name); | ||
| setPrompt(template.prompt); | ||
| if (template.agentType) setAgentType(template.agentType); | ||
| if (template.rrule) setRrule(template.rrule); | ||
| }, []); |
There was a problem hiding this comment.
P2: Template selection from the gallery no longer applies the template’s preferred agent, causing automations to run with the wrong agent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/components/CreateAutomationDialog/CreateAutomationDialog.tsx, line 96:
<comment>Template selection from the gallery no longer applies the template’s preferred agent, causing automations to run with the wrong agent.</comment>
<file context>
@@ -87,30 +87,44 @@ export function CreateAutomationDialog({
+ const appliedTemplateRef = useRef<AutomationTemplate | null>(null);
+ const appliedAgentForTemplateRef = useRef<AutomationTemplate | null>(null);
+
+ const applyTemplate = useCallback((template: AutomationTemplate) => {
+ setName(template.name);
+ setPrompt(template.prompt);
</file context>
| const applyTemplate = useCallback((template: AutomationTemplate) => { | |
| setName(template.name); | |
| setPrompt(template.prompt); | |
| if (template.agentType) setAgentType(template.agentType); | |
| if (template.rrule) setRrule(template.rrule); | |
| }, []); | |
| const applyTemplate = useCallback( | |
| (template: AutomationTemplate) => { | |
| setName(template.name); | |
| setPrompt(template.prompt); | |
| if (template.agentType) { | |
| const match = hostAgents.find( | |
| (option) => | |
| option.id === template.agentType || | |
| option.iconId === template.agentType, | |
| ); | |
| if (match) setAgent(match.id); | |
| } | |
| if (template.rrule) setRrule(template.rrule); | |
| }, | |
| [hostAgents], | |
| ); |
Tip: Review your code locally with the cubic CLI to iterate faster.
Changes since alpha.9: - workspaces.list accepts `projectId`, `projectName`, and `search` to filter the listing — matches the desktop list view. (#4455) - chore(deps): pin exact dev versions (`bun-types` 1.3.11, `typescript` 5.9.3). (#4436) - BREAKING: `automations.create` / `automations.update` / `AutomationSummary` rename `agentConfig` -> `agent` (string id of a host agent instance or preset, e.g. `claude`, `codex`, `superset`). The `AgentConfig` type is removed from the public surface. The host now resolves the live agent config on every run, so client-side snapshots no longer drift from `Settings → Agents` edits. (#4481) After merge: `cd packages/sdk && bun run build && cd dist && npm publish --access public`.
Changes since v0.2.15: - workspaces: `superset workspaces list` table now shows the workspace ID column. (#4463) - docs: VHS demo walkthrough recording added under `packages/cli/demo/`. (#4461) - auth: `superset auth login --api-key sk_live_…` stores an API key at `~/.superset/config.json` instead of running the OAuth flow; `whoami` and `logout` updated to recognize stored keys. (#4472) - hosts: `superset hosts list` shows `local` for the host machine you're invoking from, distinct from `online`/`no`. (#4476) - automations: `--agent` is now the host agent instance/preset id (e.g. `claude`, `codex`, `superset`) and is resolved live from the host on every run. The `--agent-config-file` flag is removed; create/update rename `agentConfig` -> `agent` end-to-end. (#4481) Push cli-v0.2.16 after this lands to fire the release pipeline.
Summary
Automation dispatch was reading a
ResolvedAgentConfigsnapshot stored on the row at create time, sourced from the desktop's local v1 settings (electronTrpc.settings.getAgentPresets). That source is disjoint from the v2 host service'shost_agent_configstable thatSettings → Agentsnow reads and writes, so v2 preset edits silently never reached automation runs.This PR replaces the snapshot with an
agentid and delegates to the host'sagents.runover the relay. The host resolves the currentHostAgentConfigfrom its own DB (instance id first, thenpresetIdfallback) and launches chat (agent === "superset") or terminal with the live command/args/env. Edits in v2 settings now take effect on the next run, with no client-side snapshot to drift.What changed
packages/db): replaceautomations.agent_config jsonbwithagent text not null. Migration0051adds the column, backfills fromagent_config->>'id', flipsNOT NULL, then drops the old column.packages/trpc/src/router/automation/dispatch.ts): singleagents.runcall viarelayMutationinstead of branching onkindand building shell commands server-side. Chat session rows now created host-side byrunChatAgent.agentConfig→agentend-to-end; drop theAgentConfigtype.AgentPickerandAgentCellsource fromuseV2AgentChoices(useHostUrl(hostId)).CreateAutomationDialogandAutomationDetailSidebarpasstargetHostIdand submitagent: <id>.Migration notes
Existing rows backfill to the slug their snapshot used (
claude,codex), which falls through the host'sresolveHostAgentConfigpresetIdpath → lowest-displayOrdermatchinghost_agent_configsrow on the target host. New automations created via the picker pin to aHostAgentConfig.id(UUID) directly.Edge case: a user who has deleted every stock
claude/codexrow inhost_agent_configs(only custom agents remain) will see their pre-migration automations throwNo host agent config matching '<slug>'on next fire. They re-pick the agent on the detail page to fix it.Bonus
Chat-kind automations used to land with
title = automation.name("Daily standup digest"repeated forever). Now the chat runtime auto-titles from the first user message (generateTitleFromMessageinpackages/chat/src/server/trpc/utils/runtime/runtime.ts:523), so each run gets a title summarizing its prompt content.Test plan
bun run typecheckandbun run lintclean (verified locally).Settings → Agents; openNew automation→ picker reflects the edit.agentcolumn is a host-agent UUID (not a slug).supersetagent → chat session created host-side, auto-title lands.Summary by cubic
Fixes automations using stale agent snapshots by resolving agents live on the host at run time. Replaces the stored
agent_configsnapshot with anagentid and dispatches viaagents.run, so edits in Settings → Agents take effect on the next run.Refactors
automations.agent_configwithagent text; migration0051backfills fromagent_config->>'id'.agents.runcall over the relay; remove server-side command building and chat-session writes. Host resolves current config by instance id orpresetId(includingsuperset) and launches chat/terminal.agentConfig→agentacross tRPC,SDK,CLI, andMCP; remove theAgentConfigtype. CLI:--agentaccepts host agent UUID,presetId, orsuperset; drop--agent-config-file.AgentPicker/AgentCellsource fromuseV2AgentChoices(useHostUrl(hostId)); creation/update flows passtargetHostIdand persist theagentid. When changing an auto-routed automation, also settargetHostIdto the picker's host to avoid cross-host UUID mismatches. Add label/icon fallback viagetPresetByIdfrom@superset/shared/host-agent-presetswhen host data is unavailable; split template prefill to avoid clobbering user edits as host agents load.Migration
claude,codex), defaulting toclaudeviaCOALESCEwhen the snapshot id is missing. Guarded withIF [NOT] EXISTSchecks and wraps the backfill in aDO $$ ... EXECUTE ... $$block for safe re-apply.0051; no other action required.Written for commit e26c8ef. Summary will update on new commits.
Summary by CodeRabbit
superset) instead of embedded agent configuration objectsautomations create/updateunify--agentto accept host agent id, presetId, orsuperset; removed config-file optionagentfield (backfilled) and remove the old agent config columnagentConfigwithagentagentfield and flag behavior