* feat(frontend): group the AI agent step form and edit saved agents in a modal
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: edit a saved AI agent through its own resource draft
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: drop the agent fork-for-edit session now that edits live in a draft
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: edit ai_agent resources from the resources page with the agent editor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: send a standalone agent's brain from the module when testing a step
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: keep the agent draft faithful to the resource it deploys to
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore: add the sqlx cache entry for the eval subject rename
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: share the module insert between the graph and the agent editor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): open evals inside the agent editor, actions in its header
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): add tools from the agent editor and lighten its test pane
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): open an ai_agent deep link in the agent editor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): drop the failed result badge on a step that never ran
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): head the agent editor's levels with a back control
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): drop connect and fill inputs from the agent editor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): lighten the agent editor's run panel
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): stop a nested agent tool's config reading as AI-filled
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): offer only AI or static on an agent tool's inputs
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): give a saved agent's tool editor a static-only surface
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): open an agent tool in a drawer beside the agent
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(frontend): hide unset agent config in the run form
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(frontend): share the input forms' pickers and s3 lookup
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(frontend): drop a dead agent-editor export and fix two stale comments
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): reach an ai_agent's resource-level settings and copilot
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): open an ai_agent's resource view as JSON, not the generic form
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): address review findings on the agent editor's draft and streaming
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): close the agent editor on a version restore, as the resource editor does
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): stop the provider picker auto-writing a kind, and clear review nits
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(frontend): drop the fork-for-edit leftovers from the agent card
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): mount the agent editor in the dev flow editor and guard the deep-link race
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): deploy the agent config that was submitted, and refuse one no run could use
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(frontend): build the agent editor's rows from the design-system button
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): keep a draft-only agent's draft, and let a blank MCP summary deploy
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): guard read-only agents, incomplete MCP tools and duplicate editor mounts
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: read-only agent editor, linked-card refresh, atomic eval rename
* fix: eval rename needs the privileged pool, per-workspace write access
* fix(frontend): drop the agent editor target when its mount goes away
* refactor: drop the agent rename work from this PR, unban the bindable defaults
* fix(frontend): refuse a renaming deploy and drop the copilot from static-only fields
* fix(frontend): mirror the worker's streaming rule and scope agent writes to their target
* fix(frontend): read runtime streaming as off and reset the drawer's json view
* fix(frontend): read an unsettled output_type as non-streaming too
* fix(frontend): let the showing modal claim an agent opened from inside it
* fix(frontend): keep in-flight edits, tool replacements and every linked step in sync
* fix(frontend): keep attachments in the run form and bind the agent ref to its tools
* fix(frontend): preview the agent as authored and re-evaluate step args on run
* fix(frontend): scope agent-editor ownership to the flow's workspace
* fix(frontend): drop the tool drill-in where there is no graph to select on
* fix(frontend): require a provider kind and keep one resource editor open at a time
* fix(frontend): keep legacy nulls, static-only text literal, and the handover anchor
* test(worker): pin the agent streaming default
* fix(frontend): let an AI-fillable input be switched to static
* fix(frontend): report agent editor background failures instead of floating them
* fix(frontend): keep the version pane's path alive while the editor closes
* fix(frontend): clear the anchor-keep flag at the start of each drawer session
* fix(frontend): preview the agent without its synthetic path, refresh the baseline on external writes
* refactor(frontend): drop the unverifiable baseline refresh, state the synthetic-path rule
* fix(frontend): keep the synthetic path out of agent tool test runs too
* refactor(frontend): host the agent editor under the agent's own path
* fix(frontend): mark an agent editor's host explicitly instead of inferring it from the path
* fix(frontend): discard linked-agent responses from before a deploy
* fix(frontend): keep a flow mount from claiming an agent editor's nested target
* feat(frontend): keep an agent used as a tool inside the agent being edited
* fix(frontend): reserve the agent editor's root module id
* docs(frontend): record why the agent editor previews under the agent's path
* fix(frontend): refuse to open or deploy a resource that is not an agent
* docs(frontend): put the scope-migration comment on the function it describes
* fix(frontend): refuse an agent path whose resource type is not proven
* fix(frontend): recheck the resource type before deploying, and keep expressions off static-only inputs
* fix(frontend): lazy-load the agent editor and slide its levels like the evals pane
* refactor: drop unreachable non-list tools check from agent deploy
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): clear text-only agent fields on image output, reserve the root id
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): keep the agent editor usable for a non-list tools value
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): stop the parked eval run list from taking arrow keys
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): report a non-list tools value on deploy instead of throwing
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): keep temperature editable for image output
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): skip non-object tool entries when rendering an agent
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): guard tool entry reads instead of copying the tool array
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): key tool rows by position so duplicate ids render
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): follow the operating workspace in step input forms
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): rethrow auth errors and wire remaining variable pickers
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(frontend): use runed watch for picker workspace reloads
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): mint password secrets in the operating workspace
A `password: true` string argument is rendered by PasswordArgInput, which
mints an ephemeral secret variable on the first keystroke and rebinds the
argument to `$var:<path>`. It minted into `$workspaceStore` — the globally
active navigation workspace.
Session editors operate on a different, possibly forked workspace without
switching `$workspaceStore`, and thread that operating workspace explicitly
as a `workspace` prop. When the two diverged the secret landed where the
user was merely looking while the job ran elsewhere, and the backend failed
with `Variable not found`.
Add the `workspace` prop to PasswordArgInput and thread it through every hop
between a form mount and the minting field, plus the entry points that supply
it. Track `mintedIn` so updates target where the variable actually lives, and
re-mint when the operating workspace moves after a path already exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): keep a password field consistent with its argument
A parent can replace the whole args object without remounting this field —
previewing a saved input, say — leaving `path` and `password` describing a
secret the argument no longer points at. Minting from them then copies the
old plaintext over the replacement, and the replacement is lost.
State that rule once as `argReplaced` and gate every mint on it. The
replacement can also land while the create is in flight, so the bound value
is captured before the request and re-checked after it resolves; the variable
that mint produced was never referenced, so it is deleted outright. A mint
that ends without binding re-seeds `password` from what the argument now
holds, so the field stops displaying a secret that will not be submitted and
a later workspace move cannot re-mint the stale plaintext. `updateValue`
returns early before anything is minted, since its 404 retry would otherwise
bind over a replacement it cannot see.
A failed initial mint now raises a toast rather than passing silently, which
also removes the component's last unhandled rejection.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(frontend): guard workspace forwarding to PasswordArgInput
Every hop between the form a caller mounts and the PasswordArgInput that
mints the secret must forward `workspace`, and so must the entry points that
supply it. A hop that drops the prop falls back to the navigation workspace
while the top-level case keeps passing, and no typechecker catches it because
every hop declares `workspace?: string | undefined`.
The forwarded expression is checked rather than the prop's presence, so
`workspace={$workspaceStore}` and `workspace={undefined}` fail. Two ways the
scan could stop guarding without failing are asserted too: an unterminated
mount raises instead of swallowing the rest of the file, and the number of
mounts parsed must equal the number of tag occurrences, so a mount written
inline rather than at the start of a line fails loudly.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): list and create variables in the operating workspace
* fix(frontend): surface and bound a failed recovery mint
* test(frontend): end a mount at the first line closing it
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: redact GitHub App tokens and Slack OAuth secret for non-admins
`GET /workspaces/get_settings` returned the full `git_app_installations`
JSONB to any workspace member. That column caches the GitHub App JWT and
installation token used by git-sync; the installation token is refreshed
on every git-sync action and valid for ~55 minutes, so the value sitting
in the DB is essentially always live. Null it out for non-admins,
matching the existing `slack_oauth_client_secret` redaction.
The tarball export's v2 settings format (added in #8935) included
`slack_oauth_client_secret` with no admin gating, regressing the same
redaction. Mirror the admin check there.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: split get_settings into admin-only + public endpoint
Adds `WorkspacePublicSettings` and `GET /workspaces/get_public_settings`,
which returns only fields safe for any workspace member to read
(workspace_id, slack/teams team identity, mute_critical_alerts, deploy_ui,
large_file_storage, datatable). `get_settings` is now admin-only via
`require_admin`.
Migrates frontend callers: every caller that read non-sensitive fields
(deploy_ui on trigger pages, mute_critical_alerts on the root layout, slack
team identity for handler pickers, etc.) now uses `getPublicSettings`. The
admin-managed settings UI, git-sync admin context, operator settings,
checkout polling, and full settings page stay on `getSettings`.
This replaces the field-level redactions added in the previous commit:
the type system itself defines the public surface, so adding a sensitive
column to `workspace_settings` no longer defaults to leaking — it stays
out of `WorkspacePublicSettings` unless explicitly added.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* worker: support AIAgent tools in AI executor
* worker: complete nested AIAgent tool execution path
* worker: inline AIAgent tool schema usage
* fix agent action
* frontend: add AI Agent as tool type in flow builder
Add the ability to insert a nested AI Agent as a tool within another
AI Agent step. Includes type definitions, factory function, graph icon,
insert/event wiring, and a dedicated editor component.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: remove AiAgentToolEditor, reuse FlowModuleComponent for AI agent tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: populate all input transforms for nested AI agent tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: avoid missing v2_job_status error for nested AI agent tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* sqlx
* nit
* refactor: cleanup nested AI agent tool implementation
- Add max nesting depth guard (5) on parent chain traversal
- Reject 3+ level nesting explicitly with clear error message
- Remove unnecessary flow_step_id tuple scaffolding in tool dispatch
- Consolidate get_value() calls using borrow in first match
- Replace unsafe `as unknown as FlowModule` casts with agentToolToFlowModule()
- Simplify toolKind ternary chain with .includes() lookup
- Fix leftover over-indentation from tuple removal
- Remove duplicate doc comment on is_completed_input_transform
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: pass flow_step_id and flow_job_id overrides to run_agent for nested AI agents
For nested AI agent tools, job.flow_step_id is None and job.parent_job
points to the parent agent instead of the flow. This caused memory
read/write and flow context resolution to silently fail.
handle_ai_agent_job already computes the correct flow_step_id (via
runnable_path fallback) and flow_job_id (via parent chain traversal).
This change threads those values through run_agent and
ToolExecutionContext so all downstream consumers use the correct IDs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* cleaning
* cleaning
* move const
* fix
* refactor: replace defaultToAi boolean with allowedAiTransforms whitelist
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: propagate root_job at push time, remove flow_job_id_override
Instead of threading flow_job_id_override through run_agent and
get_flow_context, propagate root_job and flow_innermost_root_job
when pushing tool jobs so nested AI agents can find the flow
job naturally via the existing job fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: simplify nested AI agent parent chain walk-up
Replace the generic depth-limited loop with a single-level check since
only flow → agent → nested agent tool is supported. Remove
MAX_AGENT_NESTING_DEPTH constant and flatten the module lookup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: reject 3+ level nested AI agent tools before job creation
Check at the parent agent level whether a nested AIAgent tool contains
AIAgent sub-tools. If so, return a fatal error immediately, preventing
the sub-job from being created and avoiding retry loops.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve deadlock in nested AI agent tool execution
Replace channel forwarding with inline DB writes for tool job
completions. Nested agents used bounded(1) channels where a sub-tool's
forwarded result would fill the parent channel, leaving no room for the
agent's own completion — causing a deadlock. Writing directly via
add_completed_job/add_completed_job_error bypasses the channel entirely.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* add in frontend
* draft openai handling
* upload to s3
* simpler output
* return s3 directly if any
* low quality
* implement for gemini
* handle imagen model
* handle image input
* cleaning
* remove base64 from output
* cleaning
* fix timeout
* handle openrouter
* remove log
* allow image input when creating image
* cleaning
* increase stack size
* inline everything
* revert stack size
* refactor: move AI executor types to separate module
- Created ai module structure with types.rs
- Moved all type definitions from ai_executor.rs to ai/types.rs
- No functional changes, just code organization
* refactor: add QueryBuilder trait and provider detection utilities
- Created QueryBuilder trait for abstracting provider-specific logic
- Added helper functions for provider detection (is_anthropic_provider)
- Implemented placeholder QueryBuilder for all providers
- Updated OpenAIRequest to use slices instead of Vec references
- All providers now have QueryBuilder implementations (using default for now)
* feat: implement OpenAI query builder with image support foundation
- Created proper OpenAI query builder implementation
- Added image_handler module for S3 upload/download utilities
- Separated text and image request building logic
- Added prepare_messages_for_api to handle S3Object conversion
- Foundation laid for supporting tools with image output
* refactor(ai): complete AI executor refactoring with query builder pattern
- Created modular structure under ai/ module
- Moved all types to ai/types.rs
- Created QueryBuilder trait for provider abstraction
- Implemented OpenAI query builder with image+tools support
- Added unified agent runner supporting both text and image outputs with tools
- Refactored run_agent to delegate to new unified implementation
- Added image handler utilities for S3 operations
- Improved code organization and maintainability
* cleaning
* feat(ai): implement remaining provider query builders
- Added Anthropic query builder with proper message conversion
- Added Google AI query builder with Gemini API support
- Added OpenRouter query builder delegating to OpenAI for compatibility
- Added missing Anthropic and Gemini types to types.rs
- Fixed type references and compilation errors
- All providers now support the unified query builder interface
* fixes
* fixes
* mime type + cleaning
* image to images
* handle mutlitple images
* fix
* remove agent_runner file
* clean query builder logic
* cleaning
* cleaning
* hide structured_output based on output type
* fix
* user images and not nested
* better descriptions