Skip to content

feat(extension): workflow params, honest dry runs, and actionable errors - #5099

Merged
iscekic merged 12 commits into
mainfrom
feat/extension-workflows-ux
Aug 6, 2026
Merged

feat(extension): workflow params, honest dry runs, and actionable errors#5099
iscekic merged 12 commits into
mainfrom
feat/extension-workflows-ux

Conversation

@iscekic

@iscekic iscekic commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

UX and reliability pass: extension workflows

Workflows get first-class parameters, a readable approval card, a manual run form, actionable errors, and honest dry runs. Every behavioral decision was checked against live kilo-auto/efficient runs — the weakest model we intend to support — plus Playwright e2e.

Headline: on "run my flight workflow for ZRH to NRT", main needed a median of 6 tool calls and never succeeded on the first run_workflow (0/4); this branch one-shots it 4/4 at 1 call. Creating a reusable, parameterized workflow went from impossible (no params support) to 4/4.

Why the old flow failed weak models

Two design traps, both found by watching real runs:

  1. Inputs were undiscoverable. run_workflow accepted an input object, but nothing declared what a workflow accepted, and input only reached the script as state.input on the first page — a navigation silently dropped it. Agents read the whole script through get_workflow to guess key names, then guessed wrong.
  2. Dry runs punished correct scripts. A dry run records clicks and fills instead of performing them, so any workflow that reads content its own actions produce (nearly all of them) hit a hard error. Agents concluded the workflow was broken and re-saved it in a loop — up to 5 save_workflow calls for one workflow.

What changed

Parameters

  • A workflow declares params: name, description, example, required.
  • Script contract is now async ({ page, state, input }). input is re-injected on every page, so navigations no longer lose it. state.input still mirrors it on page one for existing scripts.
  • run_workflow refuses to start when a required param is missing, naming each missing value with its description and example, and echoing the exact input object to retry with.
  • Params appear in the workflows index, search_workflows results, and on the approval card.
  • Manual runs open a small form when a workflow declares params; required values gate the Run button.

Honest dry runs

  • A selector miss after the first recorded action reports success with the recorded actions and an explanation. A miss before any action stays a hard failure, so genuinely wrong selectors are still caught.
  • Same for a dry-run script that returns nothing after recording actions. A real run that returns nothing still fails.
  • page.waitFor(selector, timeoutMs?) handles dynamic content; dry runs record the wait rather than polling.

Actionable errors

Every workflow error now says what happened and what to do next: out-of-scope names the tab URL and the scope, unapproved says to re-save for approval, invalid returns echo the value and both valid shapes, bad tool arguments carry zod field details instead of a bare "Invalid arguments", the safe-mode gate names the exact toggle, and failures are prefixed with the workflow name.

Discovery

search_workflows with a query now searches every site (in-scope results first) and returns inScope and startUrl per result, so "run my flights workflow" works from any tab. A relative startUrl (/, /search) resolves against the scope instead of being rejected.

Fixes found along the way

  • Deleting a workflow needs an arming click plus a confirm click, instead of firing on first click.
  • run_workflow input is now size-bounded like navigation state. It is embedded in the injected page code on every page, but only state was checked.
  • The workflow row moved to its own file, which let two obscure type aliases (Awaited<ReturnType<...>>, added only to dodge the import-count lint cap) become a plain type import.
  • vitest.config.ts never matched entrypoints/**/*.test.tsx, so the workflow-settings and conversation-events suites — 25 tests — existed but never ran. They run now, with the jsdom docblock and testing-library cleanup they needed.

Data: live A/B on kilo-auto/efficient

N=4 per scenario per arm, against a local flights fixture whose results render 1.2 s after the search click (so a script must wait). Real gateway, real model, fresh browser profile per run, approval cards auto-approved.

Scenario old (main) new (this PR)
Create: workflow saved and approved 4/4 4/4
Create: declares runtime inputs 0/4 — no params support 4/4, with examples
Run by name with values: succeeds on the first run_workflow 0/4 4/4
Run: median tool calls 6 1
Run: median wall clock 41 s 21 s
Recover from a broken selector: results delivered 0/4 — 3 stalled asking about approval 3/4, plus 1 correctly waiting for re-approval
Honest reply when no workflow exists 1/1: offers to create one, no fabricated run

Dry-run fix measured separately, same scenario, before and after:

Create-a-workflow round tool errors save_workflow calls tool calls
before 1, 7, 0, 0 2, 5, 1, 1 11, 21, 13, 7
after 0, 0, 0, 0 1, 1, 1, 1 9, 4, 7, 6

What main actually did on "run my workflow for ZRH→NRT": call get_workflow, read the whole script to guess the input keys, run it, get empty results because nothing waits for async content, then re-run or scrape the page with eval. This branch reads the params from the index and one-shots it.

Screenshots

Approval card, before and after:

before

after

Manual run parameter form, and a failed run:

run form

failed run

Verification

  • pnpm --filter kilo-extension verify — typecheck, lint, 947 unit tests
  • pnpm --filter kilo-extension build and build:firefox
  • pnpm --filter kilo-extension e2e:chrome — 117 passed, including a new parameterized-workflow scenario covering the run form, waitFor, and the missing-input error
  • pnpm --filter kilo-extension e2e:firefox — 35/35 (first attempt hit the documented newSession load flake; retry clean)
  • Live probes: 24 model-driven runs across both arms, plus 8 create-round runs validating the dry-run fix, plus mechanical replay of every saved workflow

The live-probe harness is not committed: it needs a personal gateway token and would run in CI. Its findings are encoded in the tests and error strings above.

iscekic added 9 commits August 6, 2026 15:24
- Title and explainer on the card (save vs update intent is explicit)
- Show the workflow description, the one human-readable field
- Wrap script lines instead of clipping them at the card edge
- Cap card height; script scrolls, approve/reject stay visible
- Plain labels: Runs on / Starts at
Workflows declare params (name, description, example, required).

- Script contract: async ({ page, state, input }); input is re-injected
  on every page, so runs never lose inputs across navigations
- run_workflow validates required params and returns an actionable
  error naming each missing value with description and example
- Non-object input fails fast with the expected shape
- Params surface in the workflows index, search_workflows results,
  and the approval card (Inputs section)
- Manual runs prompt for params with a small form; required values
  gate the Run button
- page.waitFor(selector, timeoutMs) polls for dynamic content;
  dry runs record the wait instead of polling
- Settings rows show the workflow description; empty state tells
  users they can ask Kilo to create a workflow
Every workflow error now says what happened and what to do next:

- Out-of-scope run: names the tab URL and the scope, suggests
  navigating or setting a startUrl
- Unapproved script: says to re-save so the user can approve
- Invalid script return: echoes the returned value and both valid
  shapes; navigate-without-state gets its own message
- Bad tool arguments: zod field-level details instead of a bare
  "Invalid arguments"
- Workflow not found: points at search_workflows
- Safe-mode gate: names the exact toggle
- Page limit: names the limit and the likely navigation loop

search_workflows with a query now searches all sites (in-scope
results first) and returns inScope/startUrl per result, so agents
can find and run a workflow saved for another site.
Covers the manual run form (required-value gating), input reaching
the script, page.waitFor bridging async results, and the
missing-required-input error text an agent sees.
- Deleting a workflow now takes two clicks: the first arms a red
  confirm state (reset on blur), the second deletes
- vitest include missed entrypoints/**/*.test.tsx, so the
  workflow-settings and conversation-events suites (25 tests) never
  ran; include them, add the jsdom docblock the events suite was
  missing, and register testing-library cleanup (globals are off, so
  RTL cannot self-register and renders leaked across tests)
- Update the revived suites for the current copy and the delete
  confirmation
…fusion

Live probes with kilo-auto/efficient surfaced two failure modes:

- Scripts read results a dry run never renders (clicks are recorded,
  not performed) and fall through returning undefined. The
  invalid-value error now explains dry-run semantics and the fix.
- Models write workflow page helpers (page.click) inside eval. The
  eval description now says helpers exist only in workflow scripts.
…scripts

A dry run records clicks and fills instead of performing them, so any
workflow that reads content its own actions produce hit a hard error
and looked broken. Live probes showed agents then re-saving and
re-running the same correct workflow in a loop.

- A selector miss after the first recorded action reports success with
  the recorded actions and a note; a miss before any action stays a
  real failure, so wrong selectors are still caught
- Same for a dry-run script that returns nothing after recording
  actions; a real run returning nothing still fails
- run_workflow's description states this and says not to edit a
  workflow because a dry run stopped there
- A relative startUrl ("/", "/search") resolves against the scope
  instead of being rejected; the rejection message now shows the
  expected form and the received value
A run result carried only pagesVisited and result, so the transcript
header showed a raw tab id and the model had to remember which
workflow it ran. Successful results now carry workflowName, and
failures read: Workflow "X" failed: <reason>.
@iscekic iscekic self-assigned this Aug 6, 2026
…liases

The settings file was at the import and line caps, which forced two
obscure type aliases (Awaited<ReturnType<...>>) in place of a plain
type import. The row is self-contained, so it moves to its own file
and owns its delete confirmation and param form. WorkflowRunPrompt
now takes a name and params instead of a whole workflow.
@iscekic

iscekic commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Final validation on the shipped build

Three more live kilo-auto/efficient rounds after the dry-run fix and the row extraction, N=3 each:

Scenario Result
Run by name with values 3/3 one-shot, 0 tool errors, prices delivered
Recover from a broken selector 3/3 diagnosed and fixed; 2 delivered results, 1 correctly stopped to await re-approval
No workflow exists 3/3 honest — offers to create one, never fabricates a run

The dry-run fix is the largest single improvement measured. Same create-a-workflow scenario, N=4, before and after:

tool errors save_workflow calls tool calls
before 1, 7, 0, 0 2, 5, 1, 1 11, 21, 13, 7
after 0, 0, 0, 0 1, 1, 1, 1 9, 4, 7, 6

Before the fix, agents read a dry-run stop as a broken workflow and re-saved the same correct script up to five times.

Input is embedded in the injected page code on every page, but only
navigation state was size-checked. Oversized or unserializable input
now fails before any navigation, with the limit named.
Comment thread apps/extension/entrypoints/sidepanel/agent-workflow-tool-runtime.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of the new commit (2 files, 61 changed lines): the previously flagged empty-search message is fully resolved — formatEmptySearchMessage now matches searchAgentWorkflows semantics in all three empty-result cases (nothing saved, query missed across every site, no in-scope workflows), with focused tests covering each case including blank queries.

Files Reviewed (2 files)
  • apps/extension/entrypoints/sidepanel/agent-workflow-tool-runtime.ts
  • apps/extension/entrypoints/sidepanel/agent-workflow-tool-runtime.test.ts
Previous Review Summary (commit 8dee466)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 8dee466)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

One misleading empty-result message in search_workflows can send the model into a retry loop when a query was already provided; the rest of the workflow params / dry-run / error-message changes are well-tested and consistent.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
apps/extension/entrypoints/sidepanel/agent-workflow-tool-runtime.ts 241 Empty-result message tells the model to "search with a query" even when a query was already given (query search already covers all sites), and counts in-scope non-matching workflows as "for other sites"
Files Reviewed (25 files)
  • apps/extension/AGENTS.md
  • apps/extension/entrypoints/sidepanel/agent-chat-panel.tsx
  • apps/extension/entrypoints/sidepanel/agent-conversation-events.test.tsx
  • apps/extension/entrypoints/sidepanel/agent-workflow-tool-runtime.test.ts
  • apps/extension/entrypoints/sidepanel/agent-workflow-tool-runtime.ts - 1 issue
  • apps/extension/entrypoints/sidepanel/collapsible-code-block.tsx
  • apps/extension/entrypoints/sidepanel/pending-approval.ts
  • apps/extension/entrypoints/sidepanel/pending-workflow-save-card.tsx
  • apps/extension/entrypoints/sidepanel/workflow-row.tsx
  • apps/extension/entrypoints/sidepanel/workflow-run-prompt.tsx
  • apps/extension/entrypoints/sidepanel/workflow-settings-state.test.ts
  • apps/extension/entrypoints/sidepanel/workflow-settings-state.ts
  • apps/extension/entrypoints/sidepanel/workflow-settings.test.tsx
  • apps/extension/entrypoints/sidepanel/workflow-settings.tsx
  • apps/extension/src/shared/agent-llm-harness.test.ts
  • apps/extension/src/shared/agent-llm-harness.ts
  • apps/extension/src/shared/agent-workflow-runner.test.ts
  • apps/extension/src/shared/agent-workflow-runner.ts
  • apps/extension/src/shared/agent-workflows-storage.test.ts
  • apps/extension/src/shared/agent-workflows-storage.ts
  • apps/extension/src/shared/agent-workflows.test.ts
  • apps/extension/src/shared/agent-workflows.ts
  • apps/extension/tests/e2e/workflows.test.ts
  • apps/extension/vitest.config.ts
  • apps/extension/vitest.setup.ts

Reviewed by kimi-k3 · Input: 45.2K · Output: 5.6K · Cached: 369.7K

Review guidance: REVIEW.md from base branch main

…missed

An empty search told the model to "search with a query to find them"
even when it had just searched with one, inviting the same call again.
A query already covers every site, so that branch now says the search
was exhaustive and offers the next useful move. The no-query branch
keeps the query suggestion, and neither branch claims the misses
belong to other sites.
@iscekic

iscekic commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Verified the review fix with the model, not just unit tests: three live kilo-auto/efficient runs asking to run a workflow that does not exist.

Each run now settles in at most two search_workflows calls — one scoped listing, then one query search that widens to every site — and then reports honestly or offers to build the workflow. No run repeated the same search, which was the retry loop the old message invited.

@iscekic
iscekic merged commit 9274d14 into main Aug 6, 2026
20 checks passed
@iscekic
iscekic deleted the feat/extension-workflows-ux branch August 6, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants