Compare commits

..

No commits in common. "main" and "v1.0.40" have entirely different histories.

1572 changed files with 32143 additions and 252685 deletions

View file

@ -4,9 +4,6 @@
**/traces
**/inputs
**/har
**/downloads
**/browser_sessions
**/credential_vault
**/.git
**/.github
**/.mypy_cache

View file

@ -86,9 +86,8 @@ YUTORI_TOOL_SET=""
# LLM_KEY: The chosen language model to use. This should be one of the models
# provided by the enabled LLM providers (e.g., OPENAI_GPT5_5, OPENAI_GPT5_4,
# ANTHROPIC_CLAUDE5_FABLE, ANTHROPIC_CLAUDE4.7_OPUS, ANTHROPIC_CLAUDE4.6_SONNET,
# GEMINI_3_PRO, BEDROCK_ANTHROPIC_CLAUDE5_FABLE_INFERENCE_PROFILE,
# BEDROCK_ANTHROPIC_CLAUDE5_FABLE_WITH_FALLBACK).
# ANTHROPIC_CLAUDE4.7_OPUS, ANTHROPIC_CLAUDE4.6_SONNET, GEMINI_3_PRO,
# BEDROCK_ANTHROPIC_CLAUDE4.7_OPUS_INFERENCE_PROFILE).
# See docs: https://www.skyvern.com/docs/self-hosted/llm-configuration
LLM_KEY=""
# a cheaper LLM providers to help finishing some small tasks, like custom selection or svg conversion. If empty, it will be the same as LLM_KEY
@ -139,29 +138,12 @@ ANALYTICS_ID="anonymous"
# OP_SERVICE_ACCOUNT_TOKEN: API token for 1Password integration
OP_SERVICE_ACCOUNT_TOKEN=""
# =============================================================================
# SKYVERN CREDENTIAL VAULT CONFIGURATION
# =============================================================================
# Built-in encrypted local vault used by default by the OSS Docker Compose setup.
# Docker also sets ENABLE_LOCAL_CREDENTIAL_VAULT=true and persists this under /data/credential_vault.
CREDENTIAL_VAULT_TYPE=skyvern
ENABLE_LOCAL_CREDENTIAL_VAULT=true
# LOCAL_CREDENTIAL_VAULT_PATH=/data/credential_vault
# Production self-hosters should set LOCAL_CREDENTIAL_VAULT_KEY from an external secret store.
# If unset, Skyvern generates .fernet_key inside LOCAL_CREDENTIAL_VAULT_PATH; backups or host-dir
# exposure of that directory include both encrypted items and the key needed to decrypt them.
# LOCAL_CREDENTIAL_VAULT_KEY=
# The OSS Docker volumes for browser_sessions/ and downloads/ can contain plaintext cookies,
# session tokens, and downloaded files. Treat them as sensitive and exclude them from casual backups.
# Enable recording skyvern logs as artifacts
ENABLE_LOG_ARTIFACTS=false
# =============================================================================
# SKYVERN BITWARDEN CONFIGURATION
# =============================================================================
# Set CREDENTIAL_VAULT_TYPE=bitwarden to use Bitwarden or vaultwarden instead
# of the built-in Skyvern credential vault.
# Your organization ID in official Bitwarden server or vaultwarden (if using organizations)
SKYVERN_AUTH_BITWARDEN_ORGANIZATION_ID=your-org-id-here
@ -196,27 +178,3 @@ SKYVERN_AUTH_BITWARDEN_CLIENT_SECRET=your-client-secret-here
# Optional: override Redis URL specifically for notifications (falls back to REDIS_URL)
# NOTIFICATION_REDIS_URL=
# REDIS_URL=redis://localhost:6379/0
# =============================================================================
# OPTIONAL: GCP SELF-HOSTED DEPLOYMENT
# =============================================================================
# Uncomment to run Skyvern on Google Cloud with GCS storage and Secret Manager
# credentials. Auth uses Application Default Credentials (Workload Identity on
# GKE, or GOOGLE_APPLICATION_CREDENTIALS pointing at a service-account key).
# Store artifacts/screenshots/browser sessions/uploads in Google Cloud Storage
# SKYVERN_STORAGE_TYPE=gcs
# GCS_PROJECT_ID=your-gcp-project-id
# GCS_BUCKET_ARTIFACTS=skyvern-artifacts
# GCS_BUCKET_SCREENSHOTS=skyvern-screenshots
# GCS_BUCKET_BROWSER_SESSIONS=skyvern-browser-sessions
# GCS_BUCKET_UPLOADS=skyvern-uploads
# Service account that signs GCS download URLs; required only under Workload
# Identity, where the runtime SA has no signing key of its own.
# GCS_SIGNER_SA_EMAIL=skyvern@your-gcp-project-id.iam.gserviceaccount.com
# Store workflow credentials in GCP Secret Manager instead of Bitwarden
# CREDENTIAL_VAULT_TYPE=gcp
# GCP_CREDENTIAL_VAULT_PROJECT_ID=your-gcp-project-id
# Secret-id prefix; must be unique per Skyvern deployment sharing a project
# GCP_CREDENTIAL_VAULT_PREFIX=skyvern-cred-

View file

@ -12,53 +12,37 @@ jobs:
check-version-change:
runs-on: ubuntu-latest
outputs:
should_publish: ${{ steps.check.outputs.should_publish }}
version_changed: ${{ steps.check.outputs.version_changed }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 2
persist-credentials: false
- name: Decide whether to publish
- name: Check if version changed
id: check
run: |
# Compare current vs previous commit, AND check PyPI for the new version.
# Without the PyPI check, reverts (1.0.37 -> 1.0.36) and re-bumps after a
# revert (1.0.36 -> 1.0.37, where 1.0.37 was already published) both look
# like "version changed" and crash the publish step with HTTP 400.
# Get version from current pyproject.toml
CURRENT_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
# Get version from previous commit
git checkout HEAD^1
PREVIOUS_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
if [ "$CURRENT_VERSION" = "$PREVIOUS_VERSION" ]; then
echo "Version unchanged at $CURRENT_VERSION; skipping publish."
echo "should_publish=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION."
# Treat both skyvern and skyvern-ui as already-published only if both
# are present on PyPI at the new version. If either is missing (e.g.
# the first release of a new sibling package), proceed.
PYPI_INDEX="https://pypi.org/pypi"
SKYVERN_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PYPI_INDEX/skyvern/$CURRENT_VERSION/json")
SKYVERN_UI_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PYPI_INDEX/skyvern-ui/$CURRENT_VERSION/json")
echo "PyPI lookup: skyvern=$SKYVERN_STATUS skyvern-ui=$SKYVERN_UI_STATUS"
if [ "$SKYVERN_STATUS" = "200" ] && [ "$SKYVERN_UI_STATUS" = "200" ]; then
echo "Both packages already at $CURRENT_VERSION on PyPI; skipping publish."
echo "should_publish=false" >> $GITHUB_OUTPUT
if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then
echo "Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION"
echo "version_changed=true" >> $GITHUB_OUTPUT
else
echo "should_publish=true" >> $GITHUB_OUTPUT
echo "Version remained at $CURRENT_VERSION"
echo "version_changed=false" >> $GITHUB_OUTPUT
fi
run-ci:
needs: check-version-change
if: needs.check-version-change.outputs.should_publish == 'true'
if: needs.check-version-change.outputs.version_changed == 'true'
uses: ./.github/workflows/ci.yml
build-sdk:
runs-on: ubuntu-latest
needs: [check-version-change, run-ci]
if: needs.check-version-change.outputs.should_publish == 'true'
if: needs.check-version-change.outputs.version_changed == 'true'
steps:
- name: Check out Git repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@ -139,12 +123,7 @@ jobs:
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
packages-dir: dist/skyvern-ui/
# Defense-in-depth: if check-version-change ever misses an edge case
# (e.g. half-published release where one sibling landed), skip files
# already on PyPI rather than failing the whole publish step.
skip-existing: true
- name: Publish skyvern to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
packages-dir: dist/skyvern/
skip-existing: true

2
.gitignore vendored
View file

@ -162,7 +162,6 @@ ig_*
# Skyvern ignores
browser_sessions/
credential_vault/
videos/
skyvern/artifacts/
artifacts/
@ -190,7 +189,6 @@ tags.temp.tmp
# copy of Chrome user profile from Chrome >= 136
tmp/user_data_dir
tmp/
# Claude
.claude/*

View file

@ -132,6 +132,3 @@ repos:
rev: v0.17.2
hooks:
- id: yamlfmt
# Helm chart templates contain Go templating ({{ ... }}) and are not
# valid standalone YAML, so yamlfmt cannot parse them.
exclude: ^k8s/charts/.*/templates/

View file

@ -11,7 +11,6 @@ RUN uv pip compile pyproject.toml --extra server --python-version 3.11 -o requir
FROM python:3.11-slim-bookworm
WORKDIR /app
COPY --from=requirements-stage /tmp/requirements.txt /app/requirements.txt
COPY ./skyvern/forge/sdk/utils/tesseract_languages.py /tmp/tesseract_languages.py
RUN pip install --upgrade pip setuptools wheel
# --no-deps: requirements.txt is fully resolved by uv, including the
# pyproject overrides that loosen litellm's jsonschema==4.23.0 pin.
@ -19,13 +18,7 @@ RUN pip install --upgrade pip setuptools wheel
RUN pip install --no-cache-dir --no-deps -r requirements.txt
RUN playwright install-deps
RUN playwright install
RUN apt-get update && \
apt-get install -y xauth x11-apps netpbm gpg ca-certificates x11vnc tesseract-ocr $(python /tmp/tesseract_languages.py --apt-packages) && \
tesseract --version && \
rm /tmp/tesseract_languages.py && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN apt-get install -y xauth x11-apps netpbm gpg ca-certificates x11vnc && apt-get clean
RUN pip install --no-cache-dir websockify
COPY .nvmrc /app/.nvmrc
@ -54,9 +47,6 @@ ENV VIDEO_PATH=/data/videos
ENV HAR_PATH=/data/har
ENV LOG_PATH=/data/log
ENV ARTIFACT_STORAGE_PATH=/data/artifacts
ENV DOWNLOAD_PATH=/data/downloads
ENV BROWSER_SESSION_BASE_PATH=/data/browser_sessions
ENV LOCAL_CREDENTIAL_VAULT_PATH=/data/credential_vault
# cache tiktoken
RUN python /app/scripts/load_tiktoken.py

View file

@ -5,9 +5,7 @@ WORKDIR /app
# Copy dependency files first for better Docker layer caching
COPY ./skyvern-frontend/package.json ./skyvern-frontend/package-lock.json ./
# --include=dev keeps tsc/vite installed even if NODE_ENV=production is inherited
# from the host/build env (npm omits devDependencies otherwise -> "tsc: not found").
RUN npm ci --include=dev
RUN npm ci
# Copy source code
COPY ./skyvern-frontend/ ./

View file

@ -1,45 +0,0 @@
"""add audio_artifact_id to workflow_copilot_chat_messages
Revision ID: a8c4e2f9d6b1
Revises: f3a9c2b7e1d4
Create Date: 2026-06-11T20:30:00.000000+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "a8c4e2f9d6b1"
down_revision: Union[str, None] = "f3a9c2b7e1d4"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
conn = op.get_bind()
inspector = sa.inspect(conn)
columns = {col["name"] for col in inspector.get_columns("workflow_copilot_chat_messages")}
if "audio_artifact_id" in columns:
return
if conn.dialect.name == "postgresql":
op.execute(sa.text("SET lock_timeout = '2s'"))
op.add_column(
"workflow_copilot_chat_messages",
sa.Column("audio_artifact_id", sa.String(), nullable=True),
)
def downgrade() -> None:
conn = op.get_bind()
inspector = sa.inspect(conn)
columns = {col["name"] for col in inspector.get_columns("workflow_copilot_chat_messages")}
if "audio_artifact_id" not in columns:
return
op.drop_column("workflow_copilot_chat_messages", "audio_artifact_id")

View file

@ -1,65 +0,0 @@
"""add workflow_copilot_completion_criteria_sets table
Revision ID: 218048b68412
Revises: a8c4e2f9d6b1
Create Date: 2026-06-14T22:21:36.243898+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "218048b68412"
down_revision: Union[str, None] = "a8c4e2f9d6b1"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
_TABLE = "workflow_copilot_completion_criteria_sets"
def upgrade() -> None:
conn = op.get_bind()
inspector = sa.inspect(conn)
if inspector.has_table(_TABLE):
return
if conn.dialect.name == "postgresql":
op.execute(sa.text("SET lock_timeout = '2s'"))
op.create_table(
_TABLE,
sa.Column("completion_criteria_set_id", sa.String(), nullable=False),
sa.Column("organization_id", sa.String(), nullable=False),
sa.Column("workflow_copilot_chat_id", sa.String(), nullable=False),
sa.Column("goal_epoch", sa.Integer(), nullable=False),
sa.Column("status", sa.String(), nullable=False),
sa.Column("criteria", sa.JSON(), nullable=False),
sa.Column("source_turn_id", sa.String(), nullable=True),
sa.Column("source_goal_text", sa.UnicodeText(), nullable=True),
sa.Column("consecutive_all_no_evidence", sa.Integer(), nullable=False),
sa.Column("tripwire_fired", sa.Boolean(), nullable=False),
sa.Column("last_fully_satisfied_workflow_yaml", sa.UnicodeText(), nullable=True),
sa.Column("superseded_by_set_id", sa.String(), nullable=True),
sa.Column("superseded_at", sa.DateTime(), nullable=True),
sa.Column("supersede_reason", sa.String(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("modified_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("completion_criteria_set_id"),
)
op.create_index("wcccs_org_chat_index", _TABLE, ["organization_id", "workflow_copilot_chat_id"])
if conn.dialect.name == "postgresql":
op.execute(sa.text("SET lock_timeout = '0'"))
def downgrade() -> None:
conn = op.get_bind()
inspector = sa.inspect(conn)
if not inspector.has_table(_TABLE):
return
op.drop_index("wcccs_org_chat_index", table_name=_TABLE)
op.drop_table(_TABLE)

View file

@ -1,33 +0,0 @@
"""add sequential_key lookup index on workflow_runs
Revision ID: cfc2318acdf9
Revises: 218048b68412
Create Date: 2026-06-16T08:03:01.419039+00:00
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "cfc2318acdf9"
down_revision: Union[str, None] = "218048b68412"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute("SET statement_timeout = '24h';")
op.execute("""
CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_workflow_runs_sequential_key_lookup
ON workflow_runs (workflow_permanent_id, sequential_key, queued_at)
WHERE status IN ('queued', 'running', 'paused') AND browser_session_id IS NULL
""")
op.execute("RESET statement_timeout;")
def downgrade() -> None:
with op.get_context().autocommit_block():
op.execute("DROP INDEX CONCURRENTLY IF EXISTS ix_workflow_runs_sequential_key_lookup")

View file

@ -1,30 +0,0 @@
"""add browser_profile_loaded to persistent_browser_sessions
Revision ID: 1fee32b3d7c6
Revises: cfc2318acdf9
Create Date: 2026-06-16T12:53:28.144752+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "1fee32b3d7c6"
down_revision: Union[str, None] = "cfc2318acdf9"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column(
"persistent_browser_sessions",
sa.Column("browser_profile_loaded", sa.Boolean(), server_default=sa.true(), nullable=False),
)
def downgrade() -> None:
op.drop_column("persistent_browser_sessions", "browser_profile_loaded")

View file

@ -1,28 +0,0 @@
"""add downloaded_file_count to workflow_run_blocks
Revision ID: 58b0ced36529
Revises: 1fee32b3d7c6
Create Date: 2026-06-19T20:44:46.548990+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "58b0ced36529"
down_revision: Union[str, None] = "1fee32b3d7c6"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.execute("SET LOCAL lock_timeout = '5s'")
op.add_column("workflow_run_blocks", sa.Column("downloaded_file_count", sa.Integer(), nullable=True))
def downgrade() -> None:
op.drop_column("workflow_run_blocks", "downloaded_file_count")

View file

@ -1,37 +0,0 @@
"""add copilot chat history indexes
Revision ID: 61c9acf9f3ba
Revises: 58b0ced36529
Create Date: 2026-06-22 11:22:49.851792+00:00
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "61c9acf9f3ba"
down_revision: Union[str, None] = "58b0ced36529"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute("SET statement_timeout = '1h';")
op.execute("""
CREATE INDEX CONCURRENTLY IF NOT EXISTS wccm_org_chat_index
ON workflow_copilot_chat_messages (organization_id, workflow_copilot_chat_id);
""")
op.execute("""
CREATE INDEX CONCURRENTLY IF NOT EXISTS wcc_org_created_at_index
ON workflow_copilot_chats (organization_id, created_at);
""")
op.execute("RESET statement_timeout;")
def downgrade() -> None:
with op.get_context().autocommit_block():
op.execute("DROP INDEX CONCURRENTLY IF EXISTS wccm_org_chat_index;")
op.execute("DROP INDEX CONCURRENTLY IF EXISTS wcc_org_created_at_index;")

View file

@ -1,54 +0,0 @@
"""add tag values table
Revision ID: 2c76348b4e7a
Revises: 61c9acf9f3ba
Create Date: 2026-06-24T16:30:09.366617+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "2c76348b4e7a"
down_revision: Union[str, None] = "61c9acf9f3ba"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.create_table(
"tag_values",
sa.Column("tag_value_id", sa.String(), nullable=False),
sa.Column("organization_id", sa.String(), nullable=False),
sa.Column("key", sa.String(), nullable=False),
sa.Column("value", sa.String(), nullable=False),
sa.Column("color", sa.String(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("modified_at", sa.DateTime(), nullable=False),
sa.Column("deleted_at", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(
["organization_id"],
["organizations.organization_id"],
),
sa.PrimaryKeyConstraint("tag_value_id"),
)
op.create_index(
"ix_tag_values_org_key_value_active",
"tag_values",
["organization_id", "key", "value"],
unique=True,
postgresql_where=sa.text("deleted_at IS NULL"),
)
def downgrade() -> None:
op.drop_index(
"ix_tag_values_org_key_value_active",
table_name="tag_values",
postgresql_where=sa.text("deleted_at IS NULL"),
)
op.drop_table("tag_values")

View file

@ -1,27 +0,0 @@
"""add browser_profile_key to workflows
Revision ID: 45128d67bc1a
Revises: 2c76348b4e7a
Create Date: 2026-06-25T00:17:17.827852+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "45128d67bc1a"
down_revision: Union[str, None] = "2c76348b4e7a"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column("workflows", sa.Column("browser_profile_key", sa.String(), nullable=True))
def downgrade() -> None:
op.drop_column("workflows", "browser_profile_key")

View file

@ -1,36 +0,0 @@
"""add proxy pin fields
Revision ID: a1cad008154a
Revises: 45128d67bc1a
Create Date: 2026-06-26T19:26:11.232122+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "a1cad008154a"
down_revision: Union[str, None] = "45128d67bc1a"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.execute("SET LOCAL lock_timeout = '5s'")
op.add_column("credentials", sa.Column("proxy_location", sa.String(), nullable=True))
op.add_column("credentials", sa.Column("proxy_session_id", sa.String(), nullable=True))
op.add_column("browser_profiles", sa.Column("proxy_location", sa.String(), nullable=True))
op.add_column("browser_profiles", sa.Column("proxy_session_id", sa.String(), nullable=True))
op.add_column("persistent_browser_sessions", sa.Column("proxy_session_id", sa.String(), nullable=True))
def downgrade() -> None:
op.drop_column("persistent_browser_sessions", "proxy_session_id")
op.drop_column("browser_profiles", "proxy_session_id")
op.drop_column("browser_profiles", "proxy_location")
op.drop_column("credentials", "proxy_session_id")
op.drop_column("credentials", "proxy_location")

View file

@ -1,67 +0,0 @@
"""managed browser profiles for save and reuse session
Revision ID: ecf365563e98
Revises: a1cad008154a
Create Date: 2026-07-02T23:17:33.611632+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "ecf365563e98"
down_revision: Union[str, None] = "a1cad008154a"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column(
"browser_profiles",
sa.Column("is_managed", sa.Boolean(), server_default=sa.text("false"), nullable=False),
)
op.add_column("browser_profiles", sa.Column("workflow_permanent_id", sa.String(), nullable=True))
op.add_column("browser_profiles", sa.Column("browser_profile_key_digest", sa.String(), nullable=True))
op.create_index(
"uq_browser_profiles_org_name_user",
"browser_profiles",
["organization_id", "name"],
unique=True,
postgresql_where=sa.text("is_managed = false"),
sqlite_where=sa.text("is_managed = false"),
)
op.create_index(
"uq_browser_profiles_managed_segment",
"browser_profiles",
["organization_id", "workflow_permanent_id", "browser_profile_key_digest"],
unique=True,
# Exclude soft-deleted rows so deleting a managed profile doesn't tombstone the
# segment — the next run re-creates it instead of colliding on the unique key.
postgresql_where=sa.text("is_managed = true AND deleted_at IS NULL"),
sqlite_where=sa.text("is_managed = true AND deleted_at IS NULL"),
)
op.create_index("idx_browser_profiles_wpid", "browser_profiles", ["workflow_permanent_id"], unique=False)
op.drop_constraint("uc_org_browser_profile_name", "browser_profiles", type_="unique")
def downgrade() -> None:
op.execute("DELETE FROM browser_profiles WHERE is_managed = true")
op.create_unique_constraint("uc_org_browser_profile_name", "browser_profiles", ["organization_id", "name"])
op.drop_index("idx_browser_profiles_wpid", table_name="browser_profiles")
op.drop_index(
"uq_browser_profiles_managed_segment",
table_name="browser_profiles",
postgresql_where=sa.text("is_managed = true AND deleted_at IS NULL"),
)
op.drop_index(
"uq_browser_profiles_org_name_user",
table_name="browser_profiles",
postgresql_where=sa.text("is_managed = false"),
)
op.drop_column("browser_profiles", "browser_profile_key_digest")
op.drop_column("browser_profiles", "workflow_permanent_id")
op.drop_column("browser_profiles", "is_managed")

View file

@ -1,30 +0,0 @@
"""add enable_self_healing to workflows
Revision ID: 2ac47bc1c075
Revises: ecf365563e98
Create Date: 2026-07-03T03:33:20.863940+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "2ac47bc1c075"
down_revision: Union[str, None] = "ecf365563e98"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column(
"workflows",
sa.Column("enable_self_healing", sa.Boolean(), nullable=False, server_default=sa.false()),
)
def downgrade() -> None:
op.drop_column("workflows", "enable_self_healing")

View file

@ -1,49 +0,0 @@
"""add workflow run credential selections
Revision ID: aff1632dc377
Revises: 2ac47bc1c075
Create Date: 2026-07-03T18:53:23.324057+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "aff1632dc377"
down_revision: Union[str, None] = "2ac47bc1c075"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column("credential_parameters", sa.Column("credential_ids", sa.JSON(), nullable=True))
op.add_column("credential_parameters", sa.Column("selection_strategy", sa.String(), nullable=True))
op.create_table(
"workflow_run_credential_selections",
sa.Column("selection_id", sa.String(), nullable=False),
sa.Column("organization_id", sa.String(), nullable=False),
sa.Column("workflow_run_id", sa.String(), nullable=False),
sa.Column("workflow_permanent_id", sa.String(), nullable=False),
sa.Column("parameter_key", sa.String(), nullable=False),
sa.Column("credential_id", sa.String(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("selection_id"),
sa.UniqueConstraint("workflow_run_id", "parameter_key", name="uq_wrcs_workflow_run_parameter_key"),
)
op.create_index(
"idx_wrcs_lru_lookup",
"workflow_run_credential_selections",
["organization_id", "workflow_permanent_id", "parameter_key", "credential_id", "created_at"],
unique=False,
)
def downgrade() -> None:
op.drop_index("idx_wrcs_lru_lookup", table_name="workflow_run_credential_selections")
op.drop_table("workflow_run_credential_selections")
op.drop_column("credential_parameters", "selection_strategy")
op.drop_column("credential_parameters", "credential_ids")

View file

@ -1,51 +0,0 @@
"""browser profile name unique partial on deleted_at
Revision ID: e2d87251fee8
Revises: aff1632dc377
Create Date: 2026-07-07T15:35:51.042177+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "e2d87251fee8"
down_revision: Union[str, None] = "aff1632dc377"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.drop_index(
"uq_browser_profiles_org_name_user",
table_name="browser_profiles",
postgresql_where=sa.text("is_managed = false"),
)
op.create_index(
"uq_browser_profiles_org_name_user",
"browser_profiles",
["organization_id", "name"],
unique=True,
postgresql_where=sa.text("is_managed = false AND deleted_at IS NULL"),
sqlite_where=sa.text("is_managed = false AND deleted_at IS NULL"),
)
def downgrade() -> None:
op.drop_index(
"uq_browser_profiles_org_name_user",
table_name="browser_profiles",
postgresql_where=sa.text("is_managed = false AND deleted_at IS NULL"),
)
op.create_index(
"uq_browser_profiles_org_name_user",
"browser_profiles",
["organization_id", "name"],
unique=True,
postgresql_where=sa.text("is_managed = false"),
sqlite_where=sa.text("is_managed = false"),
)

View file

@ -1,31 +0,0 @@
"""add pin_saved_session_ip to workflows
Revision ID: 5b618ec6dce6
Revises: e2d87251fee8
Create Date: 2026-07-07T21:21:37.964227+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "5b618ec6dce6"
down_revision: Union[str, None] = "e2d87251fee8"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.execute("SET LOCAL lock_timeout = '5s'")
op.add_column(
"workflows",
sa.Column("pin_saved_session_ip", sa.Boolean(), nullable=False, server_default=sa.false()),
)
def downgrade() -> None:
op.drop_column("workflows", "pin_saved_session_ip")

View file

@ -1,133 +0,0 @@
"""add workflow run tag events table
Revision ID: bec06d149264
Revises: 5b618ec6dce6
Create Date: 2026-07-08T17:54:31.712270+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "bec06d149264"
down_revision: Union[str, None] = "5b618ec6dce6"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.create_table(
"workflow_run_tag_events",
sa.Column("tag_event_id", sa.String(), nullable=False),
sa.Column("workflow_run_id", sa.String(), nullable=False),
sa.Column("organization_id", sa.String(), nullable=False),
sa.Column("key", sa.String(), nullable=True),
sa.Column("value", sa.String(), nullable=True),
sa.Column("event_type", sa.String(), nullable=False),
sa.Column("set_at", sa.DateTime(), nullable=False),
sa.Column("set_by", sa.String(), nullable=False),
sa.Column("source", sa.String(), nullable=False),
sa.Column("caller_type", sa.String(), nullable=True),
sa.Column("superseded_at", sa.DateTime(), nullable=True),
sa.Column("inherited_from_tag_event_id", sa.String(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("modified_at", sa.DateTime(), nullable=False),
sa.CheckConstraint("event_type IN ('set', 'delete')", name="ck_workflow_run_tag_events_event_type"),
sa.CheckConstraint(
"source IN ('manual', 'bulk_apply', 'backfill', 'inherited', 'import', 'system')",
name="ck_workflow_run_tag_events_source",
),
sa.CheckConstraint(
"caller_type IS NULL OR caller_type IN ('user', 'api_key', 'system')",
name="ck_workflow_run_tag_events_caller_type",
),
sa.CheckConstraint(
"event_type != 'set' OR value IS NOT NULL",
name="ck_workflow_run_tag_events_set_has_value",
),
sa.ForeignKeyConstraint(
["workflow_run_id"],
["workflow_runs.workflow_run_id"],
),
sa.ForeignKeyConstraint(
["organization_id"],
["organizations.organization_id"],
),
sa.PrimaryKeyConstraint("tag_event_id"),
)
op.create_index(
"workflow_run_tag_events_org_wr_set_at_idx",
"workflow_run_tag_events",
["organization_id", "workflow_run_id", "set_at"],
unique=False,
)
op.create_index(
"workflow_run_tag_events_org_key_value_active_idx",
"workflow_run_tag_events",
["organization_id", "key", "value"],
unique=False,
postgresql_include=["workflow_run_id"],
postgresql_where=sa.text("superseded_at IS NULL AND event_type = 'set'"),
)
op.create_index(
"workflow_run_tag_events_org_value_active_idx",
"workflow_run_tag_events",
["organization_id", "value"],
unique=False,
postgresql_include=["workflow_run_id"],
postgresql_where=sa.text("superseded_at IS NULL AND event_type = 'set'"),
)
op.create_index(
"workflow_run_tag_events_org_set_at_idx",
"workflow_run_tag_events",
["organization_id", "set_at"],
unique=False,
)
op.create_index(
"workflow_run_tag_events_active_grouped_unique",
"workflow_run_tag_events",
["organization_id", "workflow_run_id", "key"],
unique=True,
postgresql_where=sa.text("superseded_at IS NULL AND event_type = 'set' AND key IS NOT NULL"),
sqlite_where=sa.text("superseded_at IS NULL AND event_type = 'set' AND key IS NOT NULL"),
)
op.create_index(
"workflow_run_tag_events_active_label_unique",
"workflow_run_tag_events",
["organization_id", "workflow_run_id", "value"],
unique=True,
postgresql_where=sa.text("superseded_at IS NULL AND event_type = 'set' AND key IS NULL"),
sqlite_where=sa.text("superseded_at IS NULL AND event_type = 'set' AND key IS NULL"),
)
def downgrade() -> None:
op.drop_index(
"workflow_run_tag_events_active_label_unique",
table_name="workflow_run_tag_events",
postgresql_where=sa.text("superseded_at IS NULL AND event_type = 'set' AND key IS NULL"),
)
op.drop_index(
"workflow_run_tag_events_active_grouped_unique",
table_name="workflow_run_tag_events",
postgresql_where=sa.text("superseded_at IS NULL AND event_type = 'set' AND key IS NOT NULL"),
)
op.drop_index("workflow_run_tag_events_org_set_at_idx", table_name="workflow_run_tag_events")
op.drop_index(
"workflow_run_tag_events_org_value_active_idx",
table_name="workflow_run_tag_events",
postgresql_include=["workflow_run_id"],
postgresql_where=sa.text("superseded_at IS NULL AND event_type = 'set'"),
)
op.drop_index(
"workflow_run_tag_events_org_key_value_active_idx",
table_name="workflow_run_tag_events",
postgresql_include=["workflow_run_id"],
postgresql_where=sa.text("superseded_at IS NULL AND event_type = 'set'"),
)
op.drop_index("workflow_run_tag_events_org_wr_set_at_idx", table_name="workflow_run_tag_events")
op.drop_table("workflow_run_tag_events")

View file

@ -1,108 +0,0 @@
"""add heal episodes and proposals
Revision ID: e1227914ecfe
Revises: bec06d149264
Create Date: 2026-07-09T22:47:11.311011+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "e1227914ecfe"
down_revision: Union[str, None] = "bec06d149264"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.create_table(
"heal_episodes",
sa.Column("heal_episode_id", sa.String(), nullable=False),
sa.Column("organization_id", sa.String(), nullable=False),
sa.Column("workflow_permanent_id", sa.String(), nullable=False),
sa.Column("workflow_id", sa.String(), nullable=False),
sa.Column("workflow_run_id", sa.String(), nullable=False),
sa.Column("workflow_run_block_id", sa.String(), nullable=False),
sa.Column("block_label", sa.String(), nullable=False),
sa.Column("engine", sa.String(), nullable=False),
sa.Column("status", sa.String(), nullable=False),
sa.Column("skip_reason", sa.String(), nullable=True),
sa.Column("block_prompt", sa.UnicodeText(), nullable=True),
sa.Column("block_code", sa.UnicodeText(), nullable=True),
sa.Column("block_steps", sa.JSON(), nullable=True),
sa.Column("snapshot_available", sa.Boolean(), nullable=False, server_default=sa.false()),
sa.Column("convergence_eligible", sa.Boolean(), nullable=False, server_default=sa.false()),
sa.Column("parameter_binding_keys", sa.JSON(), nullable=True),
sa.Column("exception_class", sa.String(), nullable=True),
sa.Column("failing_line", sa.Integer(), nullable=True),
sa.Column("matched_step_index", sa.Integer(), nullable=True),
sa.Column("failure_message", sa.UnicodeText(), nullable=True),
sa.Column("escalation_task_id", sa.String(), nullable=True),
sa.Column("wall_clock_ms", sa.Integer(), nullable=True),
sa.Column("action_count", sa.Integer(), nullable=True),
sa.Column("output_obligation", sa.String(), nullable=True),
sa.Column("dom_snapshot_artifact_id", sa.String(), nullable=True),
sa.Column("scout_transcript_artifact_id", sa.String(), nullable=True),
sa.Column("screenshot_artifact_id", sa.String(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("modified_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("heal_episode_id"),
)
op.create_index(
"he_org_wpid_index",
"heal_episodes",
["organization_id", "workflow_permanent_id", "created_at"],
unique=False,
)
op.create_index("he_org_created_at_index", "heal_episodes", ["organization_id", "created_at"], unique=False)
op.create_index("he_org_wrid_index", "heal_episodes", ["organization_id", "workflow_run_id"], unique=False)
op.create_table(
"workflow_heal_proposals",
sa.Column("heal_proposal_id", sa.String(), nullable=False),
sa.Column("organization_id", sa.String(), nullable=False),
sa.Column("workflow_permanent_id", sa.String(), nullable=False),
sa.Column("block_label", sa.String(), nullable=False),
sa.Column("candidate_definition", sa.JSON(), nullable=False),
sa.Column("provenance", sa.JSON(), nullable=True),
sa.Column("episode_ids", sa.JSON(), nullable=False),
sa.Column("rendered_diff", sa.UnicodeText(), nullable=True),
sa.Column("base_version", sa.Integer(), nullable=False),
sa.Column("base_definition_hash", sa.String(), nullable=False),
sa.Column("status", sa.String(), nullable=False, server_default=sa.text("'proposed'")),
sa.Column("adopted_workflow_id", sa.String(), nullable=True),
sa.Column("episode_window", sa.String(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("modified_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("heal_proposal_id"),
)
op.create_index(
"hp_org_wpid_index",
"workflow_heal_proposals",
["organization_id", "workflow_permanent_id"],
unique=False,
)
op.add_column(
"organizations",
sa.Column("selfheal_screenshot_capture_enabled", sa.Boolean(), nullable=False, server_default=sa.false()),
)
op.add_column("organizations", sa.Column("selfheal_artifact_retention_days", sa.Integer(), nullable=True))
def downgrade() -> None:
op.drop_column("organizations", "selfheal_artifact_retention_days")
op.drop_column("organizations", "selfheal_screenshot_capture_enabled")
op.drop_index("hp_org_wpid_index", table_name="workflow_heal_proposals")
op.drop_table("workflow_heal_proposals")
op.drop_index("he_org_wrid_index", table_name="heal_episodes")
op.drop_index("he_org_created_at_index", table_name="heal_episodes")
op.drop_index("he_org_wpid_index", table_name="heal_episodes")
op.drop_table("heal_episodes")

View file

@ -38,9 +38,6 @@ services:
- ./videos:/data/videos
- ./har:/data/har
- ./log:/data/log
- ./downloads:/data/downloads
- ./browser_sessions:/data/browser_sessions
- ./credential_vault:/data/credential_vault
# Generated credentials allow the UI to pick up the local API key on first startup.
- ./.skyvern:/app/.skyvern
# Uncomment the following two lines if you want to connect to any local changes
@ -65,11 +62,6 @@ services:
- BROWSER_REMOTE_DEBUGGING_URL=${BROWSER_REMOTE_DEBUGGING_URL:-http://127.0.0.1:9222}
- BROWSER_REMOTE_DEBUGGING_HOST_HEADER=${BROWSER_REMOTE_DEBUGGING_HOST_HEADER:-}
- BROWSER_CDP_CONNECT_TIMEOUT_MS=${BROWSER_CDP_CONNECT_TIMEOUT_MS:-120000}
- DOWNLOAD_PATH=/data/downloads
- BROWSER_SESSION_BASE_PATH=/data/browser_sessions
- CREDENTIAL_VAULT_TYPE=${CREDENTIAL_VAULT_TYPE:-skyvern}
- ENABLE_LOCAL_CREDENTIAL_VAULT=${ENABLE_LOCAL_CREDENTIAL_VAULT:-true}
- LOCAL_CREDENTIAL_VAULT_PATH=/data/credential_vault
- ENABLE_CODE_BLOCK=true
# --- Control your own browser (Chrome/Chromium) ---
# Prefer Chrome's chrome://inspect/#remote-debugging flow for an existing profile.

File diff suppressed because it is too large Load diff

View file

@ -1,114 +0,0 @@
![Skyvern Changelog — June 2026](images/2026-06-00-header.png)
# Skyvern Changelog — June 2026
*Everything we shipped in June 2026 — weeks of June 1, 8, 15, and 22.*
---
## Workflows Are Now Agents
We renamed **Workflows to Agents** across the product, the public API, and the docs. The API now lives under **`/agents`** (e.g. `POST /v1/run/agents`) and accepts an **`agent_id`** alias for `workflow_id` — same `wpid_` value, echoed back in responses. It's fully backwards-compatible: your existing `workflow_id` inputs, `/workflows` endpoints, SDK methods, and bookmarked doc URLs all keep working.
![Workflows are now Agents — the app renamed to Agents with the public API served under /agents and an agent_id alias for workflow_id, shown as backwards-compatible](images/2026-06-01-agents-rename.png)
---
## Workflow Studio (Preview)
Meet the next-generation editor. **Workflow Studio** is an opt-in preview that reimagines how you build: a refreshed **build canvas**, an **integrated live browser view**, and a built-in **run viewer** — all in one place. Take control of the live browser, center the viewport, and watch a run unfold without leaving the editor. Flip it on from Feature Preview in Settings.
![Workflow Studio preview — a redesigned editor with a build canvas on the left, an integrated live browser view in the center, and a run viewer panel, with a take-control button on the live browser](images/2026-06-02-workflow-studio.png)
---
## Code-First Code Blocks
For when you want to drop to code. Code blocks now have a **dedicated code editor** with **syntax highlighting**, **Jinja parameter hints**, and a **run timeline** that records every action — including `page.evaluate(...)` calls. A **Build ↔ Code toggle** flips between visual and code editing, block labels and extraction output render correctly, and Copilot-generated code blocks support a **typed extraction schema** and fill logins with your stored credentials.
![Code-first code block — a dedicated code editor with syntax highlighting and Jinja parameter hints, a Build/Code toggle in the header, and a run timeline listing each action taken](images/2026-06-03-code-first.png)
---
## Copilot: Ask, Build, Code — With Memory
The Copilot got sharper about intent and context. It now **routes every request by mode****Ask** answers without touching your workflow, **Build** creates or edits it, **Code** generates executable code blocks. It **remembers the full chat history per workflow**, so prior context and decisions carry forward as you keep building. And its **repair loop is honest now**: when consecutive fixes make no verified progress, it stops after three tries and escalates cleanly — draft preserved — instead of spinning.
![Workflow Copilot — a chat with an Ask / Build / Code mode selector, per-workflow chat history in a side rail, and a repair loop that halts after three attempts with an honest escalation message](images/2026-06-04-copilot.png)
---
## A Bigger Model Menu
More choice for every run. New this month: **DeepSeek**, **Xiaomi MiMo** (and **MiMo-V2.5** via OpenRouter), **Gemini 3.5 Flash**, **GPT-5-mini** (your own OpenAI key or via OpenRouter), and **Claude Opus 4.8** (Anthropic + Bedrock). OpenRouter runs now track token counts and cost like every other provider. (Gemini 2.5 Pro/Flash are deprecated; CUA and the Claude Opus family are now enterprise-only.)
![Model dropdown — an expanded model picker listing DeepSeek, Xiaomi MiMo, Gemini 3.5 Flash, GPT-5-mini, and Claude Opus 4.8, with an OpenRouter cost-tracking badge](images/2026-06-05-models.png)
---
## Credentials & OTP, Everywhere
Logins that just work. Pull credentials straight from **1Password** (with an item picker) or **Bitwarden**, look up one-time passcodes from **Gmail** via OAuth, and **fill OTP at runtime** from your stored TOTP secrets — even inside code blocks. Expired credential sessions **auto re-save**, and you can **clear a saved credential** without deleting and recreating it.
![Credentials setup — a credential picker offering 1Password and Bitwarden vault items, a Gmail-backed OTP lookup option, and a runtime OTP fill using a stored TOTP secret](images/2026-06-06-credentials.png)
---
## Multi-Tab Browser Control
Automations that span tabs. The autonomous agent loop can now **open and work across multiple browser tabs** at once — following new-tab links and handling pop-ups — so flows that jump between tabs no longer dead-end.
![Multi-tab browser control — an agent working across two browser tabs at once, following a link that opened a second tab and acting in both](images/2026-06-07-multi-tab.png)
---
## Self-Host on Google Cloud
Full GCP-native deployments. Self-hosted Skyvern now supports **Google Cloud Storage** for artifacts and **GCP Secret Manager** as a vault backend — so you can run the whole stack on Google Cloud with your own storage and secrets.
![Self-hosting on GCP — a deployment config showing Google Cloud Storage selected for artifacts and GCP Secret Manager selected as the vault backend](images/2026-06-08-gcp-self-host.png)
---
## Quick List — June
**New features**
- **Analytics drill-down & tag filters** — period-aware per-agent drill-down (avg duration, credits/run, period-over-period trends) plus filtering runs and metrics by workflow tag
- **Agent Directory Tree** — the agents/folders list is now a navigable tree for nested hierarchies
- **Self-serve editor onboarding** — a get-started modal, a 4-stop guided tour, and smart empty states for faster time-to-first-automation
- **Bulk actions on the agents list** — select multiple agents and act on them at once
- **Label management in Settings** — rename, recolor, and soft-delete tags
- **Browser profiles in MCP sessions** — associate a browser profile with MCP-driven runs for credential/state reuse
- **Feature Preview panel in Settings** — opt into upcoming features before they're fully released
- **Workflow schedules in open source** — self-hosted deployments now support schedules
- **Uncapped workflow runtime** — configure long-running automations with no maximum time limit
**Improvements**
- Structured output is verified against its schema before a run is marked complete
- Tasks return a best-effort **partial deliverable** when they exhaust their credit budget
- The task planner prefers **loop-based tasks** for repeated same-shaped work, and can now emit EXTRACT_INFORMATION / GOTO_URL actions
- The agent **acts immediately on stalled pages** and **halts reliably on terminal blockers** instead of spinning
- Remote browser sessions capped at 4 hours; debug sessions stay open after a run so you can inspect the final page
- End-state screenshots captured for **every open tab** at completion; per-run recordings clipped to the run window and stored as downloadable artifacts
- Run app + recording URLs surfaced in MCP and CLI responses; run-ID search fallback on the runs list
- Batched cache invalidation on workflow save (removed a per-block N+1) for faster saves on large workflows
- OpenRouter cost tracking; analytics tag filters accept labels, groups, or `group:label`
- Improved authenticator-2FA credential setup; PDF field mapping by positional labels
- Copilot data-write blocks default to `continue_on_failure=false`; code-repair progress shown as quiet indicators
**Bug fixes**
- Fixed the workflow builder freezing on an infinite FlowRenderer layout loop
- Fixed the code editor crashing (stack overflow) on large or deeply-nested JSON
- Fixed the app failing to load after a deploy from stale chunks — it now auto-recovers with a cache-busting reload
- Fixed login credentials not reattaching after editing a workflow via MCP
- Fixed conditional routing not being preserved when loading a v1-format workflow
- Fixed the active run context being lost when switching organizations
- Fixed multi-step logins not advancing when credentials filled JavaScript-gated fields
- Fixed scanned PDFs being parsed as a single page instead of page-by-page
- Fixed shadow-DOM text nodes being missed during element text extraction
- Fixed runs not stopping when a configured time limit was exceeded
- Fixed credits not recorded when data was captured inside a Navigate block
- Fixed telephone inputs dropping digits on bare NANP-format numbers
- Fixed persistent sessions reporting a stale cached session as live
- Fixed FileUpload blocks silently skipping when no file was provided (now a clean no-op)
- Fixed the block library drawer overflowing its container in the workflow builder

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 KiB

View file

@ -13,9 +13,6 @@ keywords:
<div className="cl-page">
<div className="cl-sidebar">
<p className="cl-sidebar-title">TIMELINE</p>
<a href="#week-jun-30-2026" className="cl-sidebar-link">Week of Jun 30, 2026</a>
<a href="#week-jun-22-2026" className="cl-sidebar-link">Week of Jun 22, 2026</a>
<a href="#week-jun-15-2026" className="cl-sidebar-link">Week of Jun 15, 2026</a>
<a href="#week-jun-8-2026" className="cl-sidebar-link">Week of Jun 8, 2026</a>
<a href="#week-jun-1-2026" className="cl-sidebar-link">Week of Jun 1, 2026</a>
<a href="#week-may-25-2026" className="cl-sidebar-link">Week of May 25, 2026</a>
@ -23,168 +20,31 @@ keywords:
<a href="#week-may-11-2026" className="cl-sidebar-link">Week of May 11, 2026</a>
<a href="#week-may-4-2026" className="cl-sidebar-link">Week of May 4, 2026</a>
<a href="#week-apr-27-2026" className="cl-sidebar-link">Week of Apr 27, 2026</a>
<a href="#week-apr-20-2026" className="cl-sidebar-link">Week of Apr 20, 2026</a>
<a href="#week-apr-13-2026" className="cl-sidebar-link">Week of Apr 13, 2026</a>
<a href="#week-feb-23-2026" className="cl-sidebar-link">Week of Feb 23, 2026</a>
<a href="#week-feb-16-2026" className="cl-sidebar-link">Week of Feb 16, 2026</a>
<a href="#week-feb-9-2026" className="cl-sidebar-link">Week of Feb 9, 2026</a>
<a href="#week-feb-2-2026" className="cl-sidebar-link">Week of Feb 2, 2026</a>
<a href="#week-jan-26-2026" className="cl-sidebar-link">Week of Jan 26, 2026</a>
<a href="#week-jan-19-2026" className="cl-sidebar-link">Week of Jan 19, 2026</a>
<a href="#week-jan-5-2026" className="cl-sidebar-link">Week of Jan 5, 2026</a>
<a href="#v1-0-24" className="cl-sidebar-link">v1.0.24 — Apr 21, 2026</a>
<a href="#v1-0-23" className="cl-sidebar-link">v1.0.23 — Apr 14, 2026</a>
<a href="#v1-0-22" className="cl-sidebar-link">v1.0.22 — Feb 26, 2026</a>
<a href="#v1-0-21" className="cl-sidebar-link">v1.0.21 — Feb 24, 2026</a>
<a href="#v1-0-20" className="cl-sidebar-link">v1.0.20 — Feb 21, 2026</a>
<a href="#v1-0-19" className="cl-sidebar-link">v1.0.19 — Feb 20, 2026</a>
<a href="#v1-0-18" className="cl-sidebar-link">v1.0.18 — Feb 19, 2026</a>
<a href="#v1-0-17" className="cl-sidebar-link">v1.0.17 — Feb 19, 2026</a>
<a href="#v1-0-16" className="cl-sidebar-link">v1.0.16 — Feb 19, 2026</a>
<a href="#v1-0-15" className="cl-sidebar-link">v1.0.15 — Feb 17, 2026</a>
<a href="#v1-0-14" className="cl-sidebar-link">v1.0.14 — Feb 17, 2026</a>
<a href="#v1-0-13" className="cl-sidebar-link">v1.0.13 — Feb 10, 2026</a>
<a href="#v1-0-12" className="cl-sidebar-link">v1.0.12 — Feb 3, 2026</a>
<a href="#v1-0-11" className="cl-sidebar-link">v1.0.11 — Jan 29, 2026</a>
<a href="#v1-0-10" className="cl-sidebar-link">v1.0.10 — Jan 22, 2026</a>
<a href="#v1-0-9" className="cl-sidebar-link">v1.0.9 — Jan 20, 2026</a>
<a href="#v1-0-8" className="cl-sidebar-link">v1.0.8 — Jan 9, 2026</a>
</div>
<div className="cl-content">
<h1 className="cl-title">Changelog</h1>
<p className="cl-subtitle">New features, improvements, and fixes in Skyvern</p>
<a id="week-jun-30-2026"></a>
<Update label="Week of June 30, 2026" description="Workflow Studio UX improvements, faster enrichment, code-block self-heal, extraction accuracy, bug fixes">
## Improvements
- **Block Type Shown in Workflow Studio Header** — The block detail panel in Workflow Studio now displays the block type in the header, making it easier to identify what kind of block you're editing at a glance. ([#6956](https://github.com/Skyvern-AI/skyvern/pull/6956))
- **Improved Extraction Accuracy for Absent Data** — The agent now better handles cases where requested output is genuinely absent from the page, reducing false positives in extraction results. ([#6957](https://github.com/Skyvern-AI/skyvern/pull/6957))
- **Data-Only Validation Skips Page Evidence** — Validation steps that check only extracted data no longer capture page screenshots when they aren't needed, reducing unnecessary work during runs. ([#6960](https://github.com/Skyvern-AI/skyvern/pull/6960))
- **Copilot Block Runs Routed to UI Worker** — Copilot block runs are now dispatched to a dedicated UI worker, keeping the editor responsive during live automation. ([#6962](https://github.com/Skyvern-AI/skyvern/pull/6962))
- **Clearer Invalid JSON Errors in Workflow Editor** — The workflow editor now surfaces a descriptive error when a parameter contains invalid JSON, making it faster to diagnose and fix configuration mistakes. ([#6963](https://github.com/Skyvern-AI/skyvern/pull/6963))
- **"Ignore System Prompt" Moved to Advanced Settings** — The "Ignore system prompt" option has been relocated to the advanced settings panel, reducing clutter in the default block configuration view. ([#6964](https://github.com/Skyvern-AI/skyvern/pull/6964))
- **Empty Extra HTTP Headers Hidden** — Extra HTTP header fields that are empty no longer appear in block configurations, reducing visual noise in the workflow editor. ([#6966](https://github.com/Skyvern-AI/skyvern/pull/6966))
- **Block Selection Persisted in URL** — The selected block in Workflow Studio is now encoded in the URL, so your selection is preserved on reload and can be shared via link. ([#6969](https://github.com/Skyvern-AI/skyvern/pull/6969))
- **Code-Block Self-Heal Minimum Retry Floor** — The code-block self-healing loop now enforces a minimum number of repair attempts before giving up, preventing premature failures on recoverable errors. ([#6971](https://github.com/Skyvern-AI/skyvern/pull/6971))
- **Faster Live-Draft Enrichment** — Workflow recording live-draft enrichment now uses a faster model and a shorter debounce interval, making draft suggestions appear noticeably sooner while recording. ([#6973](https://github.com/Skyvern-AI/skyvern/pull/6973))
## Bug fixes
- Fixed syntax highlighting disappearing when viewing large webhook payloads. ([#6967](https://github.com/Skyvern-AI/skyvern/pull/6967))
- Fixed LLM blocks not enforcing their configured output schema, which could allow malformed data to propagate to downstream blocks. ([#6965](https://github.com/Skyvern-AI/skyvern/pull/6965))
</Update>
<a id="week-jun-22-2026"></a>
<Update label="Week of June 22, 2026" description="Workflow Studio preview, new models, label management, Gmail OTP, Copilot improvements, analytics filters, bug fixes">
## New features
- **Gmail-Backed OTP Lookup** — Agents can now retrieve one-time passwords from Gmail via OAuth, enabling automated login flows that depend on email-delivered verification codes. ([#6769](https://github.com/Skyvern-AI/skyvern/pull/6769))
- **Copilot Chat History per Workflow** — The Copilot now retains its full conversation history scoped to each individual workflow, so prior context and decisions are preserved as you continue building. ([#6767](https://github.com/Skyvern-AI/skyvern/pull/6767))
- **Code-First Editor Polish** — Code-first code blocks now display block labels, correctly render extraction output, and include a Build-vs-Code toggle for switching between visual and code editing modes. ([#6771](https://github.com/Skyvern-AI/skyvern/pull/6771))
- **Workflow Studio (Preview)** — A redesigned workflow editor with a refreshed build canvas, integrated browser view, and run viewer is now available as an opt-in preview, giving teams an early look at the next-generation authoring experience. ([#6782](https://github.com/Skyvern-AI/skyvern/pull/6782))
- **DeepSeek and Xiaomi MiMo Models** — DeepSeek and Xiaomi MiMo are now available in the model dropdown for agent runs. Gemini 2.5 Pro and 2.5 Flash have been deprecated. ([#6831](https://github.com/Skyvern-AI/skyvern/pull/6831))
- **Label Management Settings** — Labels can now be renamed, recolored, and soft-deleted directly from the Settings page, making it easier to maintain a clean tag taxonomy. ([#6830](https://github.com/Skyvern-AI/skyvern/pull/6830))
- **Browser Profiles in MCP Sessions** — Browser sessions initiated via MCP can now be associated with a browser profile, enabling credential and state reuse across MCP-driven runs. ([#6798](https://github.com/Skyvern-AI/skyvern/pull/6798))
- **Workflow-Tag Filter on Analytics** — The analytics dashboard now supports filtering runs and metrics by workflow tag. ([#6780](https://github.com/Skyvern-AI/skyvern/pull/6780))
## Improvements
- **Copilot Repair Shows Quiet Progress** — The Copilot's code-seam repair steps now appear as compact progress indicators instead of repeated chat messages, keeping the conversation readable during long repairs. ([#6746](https://github.com/Skyvern-AI/skyvern/pull/6746))
- **Collapsible Sidebar with Avatar** — The left sidebar now features an edge toggle, a closed-state user avatar, and a smooth open/close animation for a more polished navigation experience. ([#6754](https://github.com/Skyvern-AI/skyvern/pull/6754))
- **Structured Output Verified Before Completion** — Runs that produce structured data now verify the output satisfies the configured schema before marking the run as successfully complete. ([#6751](https://github.com/Skyvern-AI/skyvern/pull/6751))
- **MCP Workflow Overlap Settings Preserved** — Overlap settings configured via MCP are now retained correctly when the workflow is subsequently edited, preventing unintentional resets. ([#6748](https://github.com/Skyvern-AI/skyvern/pull/6748))
- **Browser Profile List Sorted Newest First** — The browser profile picker now orders profiles with the most recently created at the top, making recent profiles easier to find and select. ([#6739](https://github.com/Skyvern-AI/skyvern/pull/6739))
- **Remote Browser Sessions Capped at Four Hours** — Remote browser sessions now have a maximum duration of four hours, preventing orphaned sessions from consuming resources indefinitely.
- **Debug Sessions Stay Open After Runs** — Browser sessions launched from the workflow editor in debug mode now remain alive after a run completes, so you can inspect the final page state without the session closing immediately. ([#6832](https://github.com/Skyvern-AI/skyvern/pull/6832))
- **GPT-5 Mini via OpenRouter** — GPT-5 Mini is now available as a selectable model via OpenRouter. ([#6816](https://github.com/Skyvern-AI/skyvern/pull/6816))
- **Extraction and AI Steps in Code-Block View** — Extraction and AI steps are now surfaced in the code-block run view, filling a gap in execution visibility. ([#6823](https://github.com/Skyvern-AI/skyvern/pull/6823))
- **Color Palette for Grouped Workflow Tags** — Tags that belong to a group are now displayed with a curated color palette, making it easier to distinguish tag groups at a glance. ([#6801](https://github.com/Skyvern-AI/skyvern/pull/6801))
- **Improved Authenticator 2FA Credential Setup** — The credential setup flow for authenticator-based 2FA has been improved with clearer guidance and a smoother configuration experience. ([#6783](https://github.com/Skyvern-AI/skyvern/pull/6783))
- **Copilot Data-Write Blocks Default to Safe Failure Mode** — New Copilot-generated data-write blocks now default to `continue_on_failure=false`, preventing downstream blocks from running on bad data. ([#6779](https://github.com/Skyvern-AI/skyvern/pull/6779))
- **End-State Screenshots for All Open Tabs** — Task runs now capture end-state screenshots for every open tab at completion, giving a full snapshot of the browser state. ([#6777](https://github.com/Skyvern-AI/skyvern/pull/6777))
- **Partial Deliverable on Budget Exhaustion** — Tasks that hit their credit budget now return the best-effort deliverable collected so far instead of failing without output. ([#6778](https://github.com/Skyvern-AI/skyvern/pull/6778))
- **Planner Supports EXTRACT_INFORMATION and GOTO_URL** — The task planner can now emit EXTRACT_INFORMATION and GOTO_URL actions, expanding the set of strategies available during automated planning. ([#6800](https://github.com/Skyvern-AI/skyvern/pull/6800))
- **Copilot Completion Recognition Improvements** — The Copilot completion recognizer is now more authoritative and evidence-backed, with stable criteria counts and more reliable recognition across runs. ([#6789](https://github.com/Skyvern-AI/skyvern/pull/6789))
- **Copilot Code Repair Loop Reliability** — The Copilot code authoring repair loop is now more reliable, reducing cases where stuck repairs would stall progress. ([#6792](https://github.com/Skyvern-AI/skyvern/pull/6792))
- **Workflow Studio Browser and Panel Improvements** — The Workflow Studio editor now supports take-control and viewport centering for the live browser, with alignment and toolbar polish for the run/editor panels. ([#6810](https://github.com/Skyvern-AI/skyvern/pull/6810))
- **Analytics Tag Filters Accept Flexible Input Shapes** — Analytics tag filters now accept labels, groups, or `group:label` pairs in all three supported filter shapes. ([#6802](https://github.com/Skyvern-AI/skyvern/pull/6802))
- **Run ID Search Fallback on Runs List** — The runs list now falls back to searching by run ID when no parameter-value match is found, making it easier to locate specific runs. ([#6788](https://github.com/Skyvern-AI/skyvern/pull/6788))
## Bug fixes
- Fixed login credentials not being reattached after editing a workflow via MCP, which could cause credential-gated runs to fail. ([#6761](https://github.com/Skyvern-AI/skyvern/pull/6761))
- Fixed conditional routing logic not being preserved when loading a workflow stored in the v1 format. ([#6760](https://github.com/Skyvern-AI/skyvern/pull/6760))
- Fixed the active run context being lost when switching between organizations, requiring a page refresh to restore the correct run view. ([#6753](https://github.com/Skyvern-AI/skyvern/pull/6753))
- Fixed browser connection settings defined in a TaskV2 block not being forwarded to child task runs. ([#6750](https://github.com/Skyvern-AI/skyvern/pull/6750))
- Fixed telephone number inputs dropping digits when bare NANP-format phone numbers were typed. ([#6826](https://github.com/Skyvern-AI/skyvern/pull/6826))
- Fixed the workflow builder freezing due to an infinite FlowRenderer dimension/layout feedback loop. ([#6824](https://github.com/Skyvern-AI/skyvern/pull/6824))
- Fixed the code editor crashing with a stack overflow when opened on large or deeply-nested JSON. ([#6817](https://github.com/Skyvern-AI/skyvern/pull/6817))
- Fixed the app failing to load after a deploy due to stale webpack chunks; the app now auto-recovers via a cache-busting reload. ([#6818](https://github.com/Skyvern-AI/skyvern/pull/6818))
- Fixed runs returning an incorrect terminal outcome when the extraction schema had been modified after the initial recording. ([#6815](https://github.com/Skyvern-AI/skyvern/pull/6815))
- Fixed MCP workflow edits dropping non-credential parameter-to-block links. ([#6813](https://github.com/Skyvern-AI/skyvern/pull/6813))
- Fixed address bar paste not working in the VNC streaming view. ([#6812](https://github.com/Skyvern-AI/skyvern/pull/6812))
- Fixed newly added workflow blocks not receiving focus in the editor. ([#6809](https://github.com/Skyvern-AI/skyvern/pull/6809))
- Fixed Copilot code-block file downloads being written to the wrong directory. ([#6797](https://github.com/Skyvern-AI/skyvern/pull/6797))
- Fixed parameter-value search not appearing in the v2 runs list. ([#6796](https://github.com/Skyvern-AI/skyvern/pull/6796))
- Fixed extra actions firing after selecting a combobox option during text entry. ([#6787](https://github.com/Skyvern-AI/skyvern/pull/6787))
- Fixed shadow DOM text nodes being missed during element text extraction. ([#6791](https://github.com/Skyvern-AI/skyvern/pull/6791))
- Fixed persistent browser sessions incorrectly reporting a cached session as live when it had become stale.
</Update>
<a id="week-jun-15-2026"></a>
<Update label="Week of June 15, 2026" description="Code-first code block editor, analytics drill-down, copilot repair loop, OTP fill, uncapped runtimes, voice input fixes">
## New features
- **OTP Fill at Runtime in Code-Block Sandbox** — Code blocks can now fill OTP fields using your stored TOTP credentials at runtime, enabling fully automated two-factor authentication flows without manual input. ([#6571](https://github.com/Skyvern-AI/skyvern/pull/6571))
- **Uncapped Workflow Runtime** — Workflows can now be configured to run without a maximum time limit, enabling long-running automations that previously timed out. ([#6567](https://github.com/Skyvern-AI/skyvern/pull/6567))
- **Code-First Code Block Editor** — Code blocks now feature a dedicated code editor with syntax highlighting, Jinja parameter hints, and a run timeline showing each action taken, giving developers full visibility and control over code-driven automations. ([#6604](https://github.com/Skyvern-AI/skyvern/pull/6604))
- **Period-Aware Analytics Drill-Down** — The agent analytics drill-down panel now respects the selected time period (7/30/90/365 days or custom range) and shows per-agent average duration, credits per run, and period-over-period trends.
- **Agent Directory Tree** — The agents and folders list has been redesigned as a navigable directory tree, making it easier to browse and organize agents across nested folder hierarchies. ([#6631](https://github.com/Skyvern-AI/skyvern/pull/6631))
- **GCP Self-Host: Storage and Vault** — Self-hosted deployments on Google Cloud Platform now support GCS artifact storage and GCP Secret Manager vault, enabling full GCP-native deployments. ([#6632](https://github.com/Skyvern-AI/skyvern/pull/6632))
- **1Password Credential Picker** — Credential inputs now include an item picker for 1Password vaults, letting you browse and select vault items directly during credential setup. ([#6651](https://github.com/Skyvern-AI/skyvern/pull/6651))
- **Typed Extraction Schema for Copilot Code Blocks** — Copilot-generated code blocks now support a typed data extraction schema (matching `data_schema` parity), enabling structured data output from code-block automations. ([#6674](https://github.com/Skyvern-AI/skyvern/pull/6674))
- **SDK-Backed Remote Browser Provider** — Agents can now connect to external browser providers via SDK, enabling tighter integration with third-party remote browser services. ([#6687](https://github.com/Skyvern-AI/skyvern/pull/6687))
- **Bitwarden Credential Integration** — Saved credentials can now be sourced directly from Bitwarden vaults, bringing Bitwarden to full UI and functional parity with other supported credential providers. ([#6729](https://github.com/Skyvern-AI/skyvern/pull/6729))
## Improvements
- **Smarter Loop Planning for Repeated Work** — The task planner now prefers loop-based tasks when it detects repeated same-shaped operations, improving throughput on multi-item automation runs. ([#6575](https://github.com/Skyvern-AI/skyvern/pull/6575))
- **Agent Auto-Acts on Stalled Pages** — When the agent re-evaluates a page that hasn't changed, it now applies steering heuristics and acts immediately instead of stalling. ([#6577](https://github.com/Skyvern-AI/skyvern/pull/6577))
- **Final Answer Surfaced Directly** — The task agent now returns the final deliverable directly instead of spinning through redundant synthesis steps, reducing latency on completed tasks. ([#6574](https://github.com/Skyvern-AI/skyvern/pull/6574))
- **Copilot Stops Stuck Repair Loops** — The Workflow Copilot now detects when consecutive repairs make no verified progress, halts after three attempts, and presents an honest escalation with the draft preserved for review. ([#6608](https://github.com/Skyvern-AI/skyvern/pull/6608))
- **Voice Input Microphone Permission** — The browser microphone permission prompt now appears correctly when using voice dictation in the Prompt Box and Workflow Copilot. ([#6601](https://github.com/Skyvern-AI/skyvern/pull/6601))
- **`page.evaluate` Recorded in Code Block Timeline** — JavaScript `page.evaluate(...)` calls made inside code blocks are now recorded as actions in the run timeline, filling a gap in execution visibility. ([#6600](https://github.com/Skyvern-AI/skyvern/pull/6600))
- **Active Run Shown on Browser Session Page** — Browser session detail pages now display which workflow run currently occupies the session, making it easier to understand session usage. ([#6691](https://github.com/Skyvern-AI/skyvern/pull/6691))
- **Distinct Visual for Terminate Actions** — Terminate actions now appear with a distinct amber triangle icon in the run timeline, making it easy to spot where a run was stopped. ([#6693](https://github.com/Skyvern-AI/skyvern/pull/6693))
- **PDF Field Mapping by Positional Labels** — PDF form filling now uses positional field labels to improve accuracy when mapping data to form fields with ambiguous names. ([#6663](https://github.com/Skyvern-AI/skyvern/pull/6663))
- **MiMo-V2.5 Model Support via OpenRouter** — Added Xiaomi MiMo-V2.5 as a selectable LLM via OpenRouter for agent runs. ([#6636](https://github.com/Skyvern-AI/skyvern/pull/6636))
- **Settings "API Keys" Renamed to "General"** — The "API Keys" section in Settings has been renamed to "General" to better reflect its full scope of configuration options. ([#6642](https://github.com/Skyvern-AI/skyvern/pull/6642))
- **Run URLs Surfaced in MCP and CLI** — The agent app URL and session recording URLs are now included in run responses returned via MCP and the CLI, making it easier to link directly to a live run. ([#6719](https://github.com/Skyvern-AI/skyvern/pull/6719))
- **Email OTP Credentials Supported in Copilot** — The Copilot can now use stored email OTP credentials when generating and validating code-block automations that require email-based authentication. ([#6716](https://github.com/Skyvern-AI/skyvern/pull/6716))
- **Auto-Fill Default Google Account for Sheets Blocks** — Google Sheets blocks now automatically select the default linked Google account, reducing manual setup when configuring Sheets integrations. ([#6715](https://github.com/Skyvern-AI/skyvern/pull/6715))
- **Copilot Output Accurately Reflects Verified Results** — The Copilot no longer under-reports successfully verified output, ensuring the run summary accurately reflects what was accomplished. ([#6710](https://github.com/Skyvern-AI/skyvern/pull/6710))
- **Runs Halt Reliably on Terminal Blockers** — When the agent encounters strong evidence of an unresolvable blocker (such as a persistent challenge page), it now stops immediately rather than continuing to attempt further actions. ([#6714](https://github.com/Skyvern-AI/skyvern/pull/6714))
- **Workflow Save Performance Improved** — Cache invalidation on workflow save is now batched, eliminating a per-block N+1 query pattern that slowed saves for large workflows. ([#6718](https://github.com/Skyvern-AI/skyvern/pull/6718))
## Bug fixes
- Fixed the Run Inputs panel collapsing unexpectedly when filtering Run History. ([#6573](https://github.com/Skyvern-AI/skyvern/pull/6573))
- Fixed the agent incorrectly retrying click fallbacks after an action dispatch timed out, causing unintended interactions. ([#6569](https://github.com/Skyvern-AI/skyvern/pull/6569))
- Fixed session recordings and page interpretation not resuming correctly after navigation, causing missed recording segments. ([#6568](https://github.com/Skyvern-AI/skyvern/pull/6568))
- Fixed code block runs not generating a recording entry, causing the Recording tab to appear empty. ([#6607](https://github.com/Skyvern-AI/skyvern/pull/6607))
- Fixed code block action screenshots not appearing on the run Overview page. ([#6605](https://github.com/Skyvern-AI/skyvern/pull/6605))
- Fixed long usernames or email addresses hiding the sidebar collapse button on desktop.
- Fixed the FileParserBlock crashing instead of surfacing a clear error when it encountered an unparseable file. ([#6657](https://github.com/Skyvern-AI/skyvern/pull/6657))
- Fixed the Inputs field missing from the Code view of code-first code blocks. ([#6633](https://github.com/Skyvern-AI/skyvern/pull/6633))
- Fixed Copilot-synthesized download blocks not downloading the file during code replay. ([#6637](https://github.com/Skyvern-AI/skyvern/pull/6637), [#6638](https://github.com/Skyvern-AI/skyvern/pull/6638))
- Fixed multi-step login flows not advancing when credentials were filled into JavaScript-gated fields. ([#6650](https://github.com/Skyvern-AI/skyvern/pull/6650))
- Fixed an infinite update loop on the workflow build page when certain block configurations were active. ([#6660](https://github.com/Skyvern-AI/skyvern/pull/6660))
- Fixed adopted-session file downloads not being available as run artifacts after the session ended. ([#6679](https://github.com/Skyvern-AI/skyvern/pull/6679))
- Fixed the run timeline showing action and step rows for non-code blocks when they should only appear for code blocks. ([#6675](https://github.com/Skyvern-AI/skyvern/pull/6675))
- Fixed workflow run actions not appearing in the timeline for all block types.
- Fixed workflow runs not stopping when a configured time limit was exceeded. ([#6677](https://github.com/Skyvern-AI/skyvern/pull/6677))
- Fixed the analytics plan-tier badge showing the incorrect tier for some organizations.
- Fixed credits not being recorded when data was captured inside a Navigate block in task runs. ([#6698](https://github.com/Skyvern-AI/skyvern/pull/6698))
- Fixed Copilot directory mode synthesizing incorrect code bindings. ([#6702](https://github.com/Skyvern-AI/skyvern/pull/6702))
- Fixed FileUpload blocks silently skipping the upload when no file was provided, instead of recording a clean no-op and continuing. ([#6728](https://github.com/Skyvern-AI/skyvern/pull/6728))
- Fixed the product tour not launching when triggered from the "Take a tour" menu option. ([#6727](https://github.com/Skyvern-AI/skyvern/pull/6727))
- Fixed scanned PDF files being parsed as a single large page rather than processed page-by-page, which caused extraction to miss content on multi-page documents. ([#6722](https://github.com/Skyvern-AI/skyvern/pull/6722))
- Fixed the block library drawer overflowing its container bounds in the workflow builder. ([#6721](https://github.com/Skyvern-AI/skyvern/pull/6721))
</Update>
<a id="week-jun-8-2026"></a>
<Update label="Week of June 8, 2026" description="Multi-tab agent control, Gemini 3.5 Flash, GCP Vault, bulk agent actions, credential clears, code-block credentials">
<Update label="Week of June 8, 2026" description="Editor onboarding, Copilot modes, GCS storage, API docs split, OTP fixes">
## New features
@ -192,16 +52,6 @@ keywords:
- **GCS Storage Backend** — Added Google Cloud Storage as an artifact storage backend for self-hosted deployments. ([#6442](https://github.com/Skyvern-AI/skyvern/pull/6442))
- **Key-Optional Labels and Flexible Tag Filters** — Tags now support key-optional labels, and tag filter queries accept more flexible formats for richer agent organization. ([#6458](https://github.com/Skyvern-AI/skyvern/pull/6458))
- **Self-Serve Editor Onboarding** — New users in the workflow editor now see a get-started modal with intent routing and template cards, a 4-stop guided product tour, and smart empty states on the dashboard and runs pages to accelerate time-to-first-automation. ([#6475](https://github.com/Skyvern-AI/skyvern/pull/6475))
- **Multi-Tab Browser Control** — The autonomous agent loop can now open and interact with multiple browser tabs simultaneously, enabling workflows that span across new tab links and pop-ups. ([#6507](https://github.com/Skyvern-AI/skyvern/pull/6507))
- **Gemini 3.5 Flash Model Support** — Added Google Gemini 3.5 Flash as a selectable LLM for agent runs, providing a faster inference option. ([#6505](https://github.com/Skyvern-AI/skyvern/pull/6505))
- **GCP Secret Manager Vault Adapter** — Added Google Cloud Secret Manager as a supported vault backend for storing and retrieving secrets in self-hosted deployments. ([#6488](https://github.com/Skyvern-AI/skyvern/pull/6488))
- **Bulk Actions on Agents List** — You can now select multiple agents at once and apply bulk actions directly from the agents list page. ([#6485](https://github.com/Skyvern-AI/skyvern/pull/6485))
- **Self-Service Credential Clears** — Saved credentials can now be individually cleared from the credentials settings page without deleting and recreating them. ([#6480](https://github.com/Skyvern-AI/skyvern/pull/6480))
- **Per-Run Session Recordings as Artifacts** — Recordings are now clipped to each run's time window and stored as downloadable run-scoped artifacts. ([#6479](https://github.com/Skyvern-AI/skyvern/pull/6479))
- **Credential-Aware Scouting in Code-Block Mode** — The Copilot now uses saved credentials while scouting pages for code-block generation and produces credential-typed code that fills login fields using your stored credentials. ([#6521](https://github.com/Skyvern-AI/skyvern/pull/6521))
- **Claude Fable 5 Model Support** — Added Claude Fable 5 as a selectable model for agent runs. ([#6539](https://github.com/Skyvern-AI/skyvern/pull/6539))
- **Live Browser Recording with Instant Placeholders** — Session recordings now commit navigation and wait blocks immediately as placeholders during live recording, with support for capture pause and draft interpretation. ([#6561](https://github.com/Skyvern-AI/skyvern/pull/6561))
- **Act-and-Observe Copilot Scouting** — The Copilot's page scouting now uses act-and-observe interactions that return bounded page evidence alongside gathered actions, improving code-block generation accuracy. ([#6533](https://github.com/Skyvern-AI/skyvern/pull/6533))
## Improvements
@ -214,13 +64,6 @@ keywords:
- **Block Title Editable from Settings Sidebar** — Workflow block titles can now be edited from the right-hand settings sidebar, without needing to use the inline editor on the canvas. ([#6443](https://github.com/Skyvern-AI/skyvern/pull/6443))
- **API Reference Split into Agents and Runs Sections** — The public API reference is now organized into separate Agents and Runs sections for easier navigation. ([#6448](https://github.com/Skyvern-AI/skyvern/pull/6448))
- **Label-First Tag Editor and Flexible Tag Filter UI** — The tag editor now accepts standalone labels or `group:label` format in a single input, and the filter bar supports filtering by label, group, or group:label for richer agent organization. ([#6470](https://github.com/Skyvern-AI/skyvern/pull/6470))
- **2FA Field Progressive Disclosure in Login Block** — Two-factor authentication fields in the login workflow block are now collapsed by default and expanded on demand, reducing visual noise for workflows that don't use 2FA. ([#6490](https://github.com/Skyvern-AI/skyvern/pull/6490))
- **Persistent Browser Session Profile Generation Now Opt-In** — Automatic browser profile generation during persistent sessions is now opt-in, giving you explicit control over when profiles are captured. ([#6489](https://github.com/Skyvern-AI/skyvern/pull/6489))
- **Icon-Only Status Pills on Mobile Viewports** — Status badges now render in a compact icon-only format on small screen widths, preventing layout overflow on mobile devices. ([#6481](https://github.com/Skyvern-AI/skyvern/pull/6481))
- **Hardened Webhook Delivery** — Workflow webhook delivery is now more reliable, with improved handling of transient failures and prioritized delivery ordering. ([#6478](https://github.com/Skyvern-AI/skyvern/pull/6478))
- **Reduced Live Recording Draft Latency** — Live session recordings now appear almost instantly with placeholder entries, with final blocks committed after a short debounce window. ([#6553](https://github.com/Skyvern-AI/skyvern/pull/6553))
- **Copilot Stops on Terminal Blockers** — The Copilot now correctly halts when it reaches a condition that blocks further progress, preventing runaway sessions. ([#6535](https://github.com/Skyvern-AI/skyvern/pull/6535))
- **Copilot Chat Shows Run Outcome Verdict** — The Copilot chat timeline now reflects the actual run outcome verdict, giving a more accurate view of what the agent accomplished. ([#6532](https://github.com/Skyvern-AI/skyvern/pull/6532))
## Bug fixes
@ -229,23 +72,6 @@ keywords:
- Fixed persistent-session screen recordings not appearing in the run view when the recording timestamp fell outside the run's time window. ([#6454](https://github.com/Skyvern-AI/skyvern/pull/6454))
- Fixed Copilot inconsistently asking for a URL before running discovery when the user had only named a site without providing one. ([#6473](https://github.com/Skyvern-AI/skyvern/pull/6473))
- Fixed Copilot asking for a URL after already successfully navigating to the requested site during discovery. ([#6472](https://github.com/Skyvern-AI/skyvern/pull/6472))
- Fixed self-hosted deployments rejecting requests with "Invalid credentials" (HTTP 403) when the Docker UI injected an API key that the container's server incorrectly filtered out.
- Fixed persistent session recordings not being finalized when the video encoding process outlasted the session's cleanup window. ([#6519](https://github.com/Skyvern-AI/skyvern/pull/6519))
- Fixed the saved credentials status indicator not updating until the page was manually refreshed. ([#6517](https://github.com/Skyvern-AI/skyvern/pull/6517))
- Fixed the workflow run timeline rendering blocks in branch-tree order instead of execution order, producing a confusing step sequence. ([#6515](https://github.com/Skyvern-AI/skyvern/pull/6515))
- Fixed downloaded files inside loop blocks being shared across all iterations instead of scoped to the block that downloaded them. ([#6487](https://github.com/Skyvern-AI/skyvern/pull/6487))
- Fixed session recordings defaulting to an incorrect resolution; recordings now use Playwright's default viewport size. ([#6524](https://github.com/Skyvern-AI/skyvern/pull/6524))
- Fixed Copilot failing to recognize and use a saved credential when the credential ID was typed with a space or hyphen separator instead of an underscore; malformed IDs are now normalized and resolved correctly. ([#6529](https://github.com/Skyvern-AI/skyvern/pull/6529))
- Fixed Copilot chat showing workflow blocks as completed even when the overall run goal was not demonstrated — the recorded run outcome verdict is now surfaced in the chat narrative. ([#6528](https://github.com/Skyvern-AI/skyvern/pull/6528))
- Fixed Copilot browser sessions incorrectly blocking localhost navigation during page scouting, which could prevent evidence from being gathered from locally-served pages. ([#6527](https://github.com/Skyvern-AI/skyvern/pull/6527))
- Fixed single-element JSON arrays being unwrapped to a scalar when passed as workflow run inputs. ([#6560](https://github.com/Skyvern-AI/skyvern/pull/6560))
- Fixed code blocks silently dropping non-dict return values instead of passing them through to downstream blocks. ([#6556](https://github.com/Skyvern-AI/skyvern/pull/6556))
- Fixed the task agent ignoring the `max_iterations` override, causing runs to exceed their configured step limit. ([#6544](https://github.com/Skyvern-AI/skyvern/pull/6544))
- Fixed generated workflow parameter keys being duplicated, which caused a hard failure when the workflow was run. ([#6543](https://github.com/Skyvern-AI/skyvern/pull/6543))
- Fixed task runs completing successfully even when a required sub-goal had not been satisfied. ([#6546](https://github.com/Skyvern-AI/skyvern/pull/6546))
- Fixed the credential-clear confirmation dialog being hidden behind other overlays. ([#6542](https://github.com/Skyvern-AI/skyvern/pull/6542))
- Fixed click actions on elements wrapped in disabled containers not being retargeted to the clickable child element. ([#6540](https://github.com/Skyvern-AI/skyvern/pull/6540))
- Fixed Copilot loop detector incorrectly triggering at block dispatch boundaries. ([#6550](https://github.com/Skyvern-AI/skyvern/pull/6550))
</Update>
@ -435,8 +261,8 @@ keywords:
</Update>
<a id="week-apr-20-2026"></a>
<Update label="Week of April 20, 2026" description="Copilot reliability, run recordings, 2FA improvements, and more">
<a id="v1-0-24"></a>
<Update label="v1.0.24 — April 21, 2026" description="Copilot reliability, run recordings, 2FA improvements, and more">
## Improvements
@ -453,8 +279,8 @@ keywords:
</Update>
<a id="week-apr-13-2026"></a>
<Update label="Week of April 13, 2026" description="Saudi Arabia proxy, workflow error mapping, smarter caching, MCP OAuth, AI output summaries, and reliability fixes">
<a id="v1-0-23"></a>
<Update label="v1.0.23 — April 14, 2026" description="Saudi Arabia proxy, workflow error mapping, smarter caching, MCP OAuth, AI output summaries, and reliability fixes">
## New features
@ -498,35 +324,38 @@ keywords:
</Update>
<a id="week-feb-23-2026"></a>
<Update label="Week of February 23, 2026" description="Adaptive caching, Workflow Trigger block, CLI signup, reserved parameters, new LLM models">
<a id="v1-0-22"></a>
<Update label="v1.0.22 — February 26, 2026" description="Adaptive caching, Workflow Trigger block, CLI signup, and more">
## New features
- **Adaptive Caching (Script Generation)** — Skyvern can now learn from repeated workflow runs and generate cached scripts that speed up future executions. When a block runs successfully, Skyvern records a reusable script and replays it on subsequent runs, falling back to the AI agent only if the script fails. ([#4908](https://github.com/Skyvern-AI/skyvern/pull/4908), [#4916](https://github.com/Skyvern-AI/skyvern/pull/4916), [#4917](https://github.com/Skyvern-AI/skyvern/pull/4917), [#4920](https://github.com/Skyvern-AI/skyvern/pull/4920), [#4922](https://github.com/Skyvern-AI/skyvern/pull/4922), [#4931](https://github.com/Skyvern-AI/skyvern/pull/4931))
- **Workflow Trigger Block** — A new block type that lets one workflow trigger another from within the workflow editor. Chain workflows together as building blocks for complex automations. ([#4885](https://github.com/Skyvern-AI/skyvern/pull/4885))
- **Browser-based CLI Signup** — New users can sign up for Skyvern Cloud directly from the CLI. Running `skyvern signup` opens a browser flow that creates your account and stores your API key locally. ([#4925](https://github.com/Skyvern-AI/skyvern/pull/4925))
- **Interactive ngrok Tunnel** — `skyvern browser serve` can now create an ngrok tunnel automatically, making it easy to expose your local browser to Skyvern Cloud without manual network configuration. ([#4924](https://github.com/Skyvern-AI/skyvern/pull/4924))
- **South Korea Proxy Location** — Added `RESIDENTIAL_KR` as a proxy geolocation option for automations targeting South Korean websites. ([#4918](https://github.com/Skyvern-AI/skyvern/pull/4918))
- **Downloaded Files Tab** — Browser session detail pages now include a Downloaded Files tab for viewing and accessing files captured during a session. ([#4911](https://github.com/Skyvern-AI/skyvern/pull/4911))
- **CDP Proxy Authentication** — Remote browser connections now support authenticated proxies via the CDP `Fetch.authRequired` protocol. ([#4936](https://github.com/Skyvern-AI/skyvern/pull/4936))
- **Remote Browser Download Interception** — File downloads triggered by XHR requests and other non-navigation events are now captured when using remote CDP browser connections. ([#4906](https://github.com/Skyvern-AI/skyvern/pull/4906), [#4921](https://github.com/Skyvern-AI/skyvern/pull/4921), [#4934](https://github.com/Skyvern-AI/skyvern/pull/4934))
- **`current_date` Reserved Parameter** — Workflows now have a built-in `current_date` parameter that resolves to today's date automatically. ([#4854](https://github.com/Skyvern-AI/skyvern/pull/4854))
- **Reserved Parameters in Block Editor** — The workflow block parameter picker now shows reserved system parameters, making them discoverable without memorizing names. ([#4857](https://github.com/Skyvern-AI/skyvern/pull/4857))
- **Natural Language Loop `data_schema`** — Loop blocks driven by natural language extraction now support a `data_schema` field for consistent output structure. ([#4851](https://github.com/Skyvern-AI/skyvern/pull/4851))
- **Gemini 3.1 Pro and Inception Mercury-2** — Two new LLM engine options for your automations. ([#4847](https://github.com/Skyvern-AI/skyvern/pull/4847))
- **MCP Server on API Service** — The MCP remote server is now mounted at `/mcp` on the existing API, eliminating the need for a separate process. ([#4843](https://github.com/Skyvern-AI/skyvern/pull/4843))
## Improvements
- **Renamed "Login-Free" to "Saved Profile"** — Clearer terminology throughout the UI. Browser session checkbox wording has also been improved. ([#4914](https://github.com/Skyvern-AI/skyvern/pull/4914))
- **TOTP Verification Improvements** — Better timer handling, expired code retry, 2FA banner suppression, and more reliable goal-text extraction during TOTP flows. ([#4860](https://github.com/Skyvern-AI/skyvern/pull/4860))
- **TOTP Webhook includes `workflow_permanent_id`** — Easier to identify which workflow is requesting a 2FA code. ([#4871](https://github.com/Skyvern-AI/skyvern/pull/4871))
- **Consolidated Gemini 3 Pro Model Key** — `VERTEX_GEMINI_3_PRO` now auto-resolves to the latest version, so you no longer need to update config when Google releases point versions. ([#4926](https://github.com/Skyvern-AI/skyvern/pull/4926))
- **Browser Rotation for Remote Connections** — Remote browser environments now support context rotation, improving reliability for long-running automations. ([#4929](https://github.com/Skyvern-AI/skyvern/pull/4929))
- Workflow save validation (HTTP 422) now returns clear, field-level error messages. ([#4852](https://github.com/Skyvern-AI/skyvern/pull/4852))
- Browser launch errors now show actionable messages instead of raw exceptions. ([#4844](https://github.com/Skyvern-AI/skyvern/pull/4844))
- Cloud LLM fallback overrides are now properly applied when selecting models. ([#4839](https://github.com/Skyvern-AI/skyvern/pull/4839))
## Bug fixes
@ -538,53 +367,151 @@ keywords:
- MCP remote auth token comparison no longer fails on encrypted tokens. ([#4863](https://github.com/Skyvern-AI/skyvern/pull/4863))
- `skyvern quickstart` now handles local PostgreSQL without a pre-existing `skyvern` role. ([#4878](https://github.com/Skyvern-AI/skyvern/pull/4878))
- WebSocket URLs through ngrok tunnels now rewrite correctly for live browser streaming. ([#4927](https://github.com/Skyvern-AI/skyvern/pull/4927))
</Update>
<a id="v1-0-21"></a>
<Update label="v1.0.21 — February 24, 2026" description="Reserved parameters, data_schema for loops, new LLM models">
## New features
- **`current_date` Reserved Parameter** — Workflows now have a built-in `current_date` parameter that resolves to today's date automatically. ([#4854](https://github.com/Skyvern-AI/skyvern/pull/4854))
- **Reserved Parameters in Block Editor** — The workflow block parameter picker now shows reserved system parameters, making them discoverable without memorizing names. ([#4857](https://github.com/Skyvern-AI/skyvern/pull/4857))
- **Natural Language Loop `data_schema`** — Loop blocks driven by natural language extraction now support a `data_schema` field for consistent output structure. ([#4851](https://github.com/Skyvern-AI/skyvern/pull/4851))
- **Gemini 3.1 Pro and Inception Mercury-2** — Two new LLM engine options for your automations. ([#4847](https://github.com/Skyvern-AI/skyvern/pull/4847))
- **MCP Server on API Service** — The MCP remote server is now mounted at `/mcp` on the existing API, eliminating the need for a separate process. ([#4843](https://github.com/Skyvern-AI/skyvern/pull/4843))
## Improvements
- Workflow save validation (HTTP 422) now returns clear, field-level error messages. ([#4852](https://github.com/Skyvern-AI/skyvern/pull/4852))
- Browser launch errors now show actionable messages instead of raw exceptions. ([#4844](https://github.com/Skyvern-AI/skyvern/pull/4844))
- Cloud LLM fallback overrides are now properly applied when selecting models. ([#4839](https://github.com/Skyvern-AI/skyvern/pull/4839))
## Bug fixes
- Conditional blocks no longer crash when all branches use Jinja templates. ([#4849](https://github.com/Skyvern-AI/skyvern/pull/4849))
- Conditional blocks no longer misinterpret resolved Jinja template variables. ([#4841](https://github.com/Skyvern-AI/skyvern/pull/4841))
</Update>
<a id="week-feb-16-2026"></a>
<Update label="Week of February 16, 2026" description="Unified Browser Task block, browser profile testing, 2FA detection, Skills package, Claude Opus 4.6, MCP block discovery">
<a id="v1-0-20"></a>
<Update label="v1.0.20 — February 21, 2026" description="Browser profile testing UI and 2FA verification flow">
## New features
- **Browser Profile Testing UI (Frontend)** — The Browsers page now lets you launch a test run against a saved browser profile to confirm its login state is still valid before scheduling production workflows. ([#4833](https://github.com/Skyvern-AI/skyvern/pull/4833))
- **2FA Verification Code UI and Notifications** — When a workflow pauses for a 2FA code, the UI now shows a clear prompt and pushes a notification so you don't miss it. ([#4787](https://github.com/Skyvern-AI/skyvern/pull/4787))
- **Gated Admin Impersonation for MCP API Keys** — Adds gated admin controls so support engineers can impersonate an organization scope when troubleshooting MCP-based automations, with audit-friendly access controls. ([#4822](https://github.com/Skyvern-AI/skyvern/pull/4822))
- **Browser Profile Testing** — Test browser profiles directly from the UI to verify that saved login sessions are still valid. Workflows can also run with a saved browser profile, enabling login-free automations that reuse authenticated sessions. ([#4818](https://github.com/Skyvern-AI/skyvern/pull/4818))
- **Skyvern Skills Package** — The new `skyvern skill` CLI command provides pre-built workflow templates and reference documentation for common automation patterns. ([#4817](https://github.com/Skyvern-AI/skyvern/pull/4817))
- **Clear Cached Scripts API** — A new endpoint lets you clear cached automation scripts for a workflow when a target website changes and you want to force Skyvern to re-learn. ([#4809](https://github.com/Skyvern-AI/skyvern/pull/4809))
- **Automatic 2FA Detection Without TOTP Credentials** — Skyvern now detects when a website asks for a 2FA code even without pre-configured TOTP credentials. The system pauses and waits for you to provide the code via the UI or webhook. ([#4786](https://github.com/Skyvern-AI/skyvern/pull/4786))
- **Full CLI Parity with MCP** — The CLI now supports browser session management, credential management, block operations, and enhanced workflow commands. Everything available through MCP is now accessible from the command line. ([#4789](https://github.com/Skyvern-AI/skyvern/pull/4789), [#4792](https://github.com/Skyvern-AI/skyvern/pull/4792), [#4793](https://github.com/Skyvern-AI/skyvern/pull/4793))
- **Claude Opus 4.6 CUA Support** — Anthropic's Claude Opus 4.6 is now available as a Computer Use Agent model for driving browser interactions. ([#4780](https://github.com/Skyvern-AI/skyvern/pull/4780))
- **Anthropic Claude Opus 4.6** — Added Claude Opus 4.6 as an available LLM engine for web automation tasks. ([#4777](https://github.com/Skyvern-AI/skyvern/pull/4777), [#4778](https://github.com/Skyvern-AI/skyvern/pull/4778))
- **Unified Browser Task Block** — The Navigation V1 and Task V2 blocks have been merged into a single **Browser Task** block. The block now exposes both engines as a dropdown, simplifying the workflow editor and reducing block-type confusion. ([#4695](https://github.com/Skyvern-AI/skyvern/pull/4695))
- **Block Discovery MCP Tools** — Added MCP tools that let AI assistants enumerate available block types and their schemas, plus richer instructions for building workflows through MCP. ([#4683](https://github.com/Skyvern-AI/skyvern/pull/4683))
- **Edit Existing Credentials API** — A new endpoint lets you update credential values without deleting and recreating them, preserving credential IDs across changes. ([#4693](https://github.com/Skyvern-AI/skyvern/pull/4693))
- **Filter Workflow Runs by Error Code and Search Key** — The workflow runs list endpoint now supports `error_code` and `search_key` query parameters for narrowing down failed runs. ([#4694](https://github.com/Skyvern-AI/skyvern/pull/4694))
- **GPT-5 Mini in Model Selector** — GPT-5 Mini is now selectable as an LLM engine. GPT-5 has been hidden in favor of more reliable variants. ([#4686](https://github.com/Skyvern-AI/skyvern/pull/4686))
## Improvements
- **2FA Backend Cleanup** — Refactored the 2FA pipeline to make code delivery more reliable across both the UI form and the API endpoint. ([#4826](https://github.com/Skyvern-AI/skyvern/pull/4826))
</Update>
<a id="v1-0-19"></a>
<Update label="v1.0.19 — February 20, 2026" description="Admin impersonation controls and configuration improvements">
## New features
- **Gated Admin Impersonation for MCP API Keys** — Adds gated admin controls so support engineers can impersonate an organization scope when troubleshooting MCP-based automations, with audit-friendly access controls. ([#4822](https://github.com/Skyvern-AI/skyvern/pull/4822))
## Improvements
- **Environment Variables Override Mounted API Keys** — In Skyvern's Streamlit-based deployment, env vars now take precedence over keys baked into the image, making local overrides simpler. ([#4824](https://github.com/Skyvern-AI/skyvern/pull/4824))
- The workflow run timeline now properly displays loop iterations and conditional branches, making complex runs easier to debug. ([#4782](https://github.com/Skyvern-AI/skyvern/pull/4782))
- **Block Failure Reasons Surfaced in UI** — Every block type now shows its `failure_reason` in the run timeline, making debugging non-obvious failures faster. ([#4672](https://github.com/Skyvern-AI/skyvern/pull/4672))
- **Webhook Payload Truncation in Logs** — Large webhook payloads are now truncated in log entries, keeping logs readable without losing context. ([#4701](https://github.com/Skyvern-AI/skyvern/pull/4701))
</Update>
<a id="v1-0-18"></a>
<Update label="v1.0.18 — February 19, 2026" description="Browser profile testing and saved-profile workflows">
## New features
- **Browser Profile Testing** — Test browser profiles directly from the UI to verify that saved login sessions are still valid. Workflows can also run with a saved browser profile, enabling login-free automations that reuse authenticated sessions. ([#4818](https://github.com/Skyvern-AI/skyvern/pull/4818))
## Bug fixes
- When both a TOTP credential and a webhook are configured for 2FA, the credential is now correctly prioritized. ([#4811](https://github.com/Skyvern-AI/skyvern/pull/4811))
</Update>
<a id="v1-0-17"></a>
<Update label="v1.0.17 — February 19, 2026" description="Skills package and cached script management">
## New features
- **Skyvern Skills Package** — The new `skyvern skill` CLI command provides pre-built workflow templates and reference documentation for common automation patterns. ([#4817](https://github.com/Skyvern-AI/skyvern/pull/4817))
- **Clear Cached Scripts API** — A new endpoint lets you clear cached automation scripts for a workflow when a target website changes and you want to force Skyvern to re-learn. ([#4809](https://github.com/Skyvern-AI/skyvern/pull/4809))
</Update>
<a id="v1-0-16"></a>
<Update label="v1.0.16 — February 19, 2026" description="2FA detection, full CLI parity, Claude Opus 4.6 CUA">
## New features
- **Automatic 2FA Detection Without TOTP Credentials** — Skyvern now detects when a website asks for a 2FA code even without pre-configured TOTP credentials. The system pauses and waits for you to provide the code via the UI or webhook. ([#4786](https://github.com/Skyvern-AI/skyvern/pull/4786))
- **Full CLI Parity with MCP** — The CLI now supports browser session management, credential management, block operations, and enhanced workflow commands. Everything available through MCP is now accessible from the command line. ([#4789](https://github.com/Skyvern-AI/skyvern/pull/4789), [#4792](https://github.com/Skyvern-AI/skyvern/pull/4792), [#4793](https://github.com/Skyvern-AI/skyvern/pull/4793))
- **Claude Opus 4.6 CUA Support** — Anthropic's Claude Opus 4.6 is now available as a Computer Use Agent model for driving browser interactions. ([#4780](https://github.com/Skyvern-AI/skyvern/pull/4780))
## Improvements
- The workflow run timeline now properly displays loop iterations and conditional branches, making complex runs easier to debug. ([#4782](https://github.com/Skyvern-AI/skyvern/pull/4782))
## Bug fixes
- Fixed conditional blocks evaluating the wrong value after Jinja template rendering. ([#4801](https://github.com/Skyvern-AI/skyvern/pull/4801))
- Fixed MFA resolution priority when both TOTP credentials and webhooks are configured. ([#4800](https://github.com/Skyvern-AI/skyvern/pull/4800))
</Update>
<a id="v1-0-15"></a>
<Update label="v1.0.15 — February 17, 2026" description="Claude Opus 4.6 model support">
## New features
- **Anthropic Claude Opus 4.6** — Added Claude Opus 4.6 as an available LLM engine for web automation tasks. ([#4777](https://github.com/Skyvern-AI/skyvern/pull/4777), [#4778](https://github.com/Skyvern-AI/skyvern/pull/4778))
</Update>
<a id="v1-0-14"></a>
<Update label="v1.0.14 — February 17, 2026" description="Unified Browser Task block, MCP block discovery, and credential editing">
## New features
- **Unified Browser Task Block** — The Navigation V1 and Task V2 blocks have been merged into a single **Browser Task** block. The block now exposes both engines as a dropdown, simplifying the workflow editor and reducing block-type confusion. ([#4695](https://github.com/Skyvern-AI/skyvern/pull/4695))
- **Block Discovery MCP Tools** — Added MCP tools that let AI assistants enumerate available block types and their schemas, plus richer instructions for building workflows through MCP. ([#4683](https://github.com/Skyvern-AI/skyvern/pull/4683))
- **Edit Existing Credentials API** — A new endpoint lets you update credential values without deleting and recreating them, preserving credential IDs across changes. ([#4693](https://github.com/Skyvern-AI/skyvern/pull/4693))
- **Filter Workflow Runs by Error Code and Search Key** — The workflow runs list endpoint now supports `error_code` and `search_key` query parameters for narrowing down failed runs. ([#4694](https://github.com/Skyvern-AI/skyvern/pull/4694))
- **GPT-5 Mini in Model Selector** — GPT-5 Mini is now selectable as an LLM engine. GPT-5 has been hidden in favor of more reliable variants. ([#4686](https://github.com/Skyvern-AI/skyvern/pull/4686))
## Improvements
- **Block Failure Reasons Surfaced in UI** — Every block type now shows its `failure_reason` in the run timeline, making debugging non-obvious failures faster. ([#4672](https://github.com/Skyvern-AI/skyvern/pull/4672))
- **Webhook Payload Truncation in Logs** — Large webhook payloads are now truncated in log entries, keeping logs readable without losing context. ([#4701](https://github.com/Skyvern-AI/skyvern/pull/4701))
## Bug fixes
- Fixed the cursor jumping to the end while editing conditional expressions in the workflow editor. ([#4682](https://github.com/Skyvern-AI/skyvern/pull/4682))
- Fixed dotted Jinja variables (e.g. `{{ block_output.field }}`) rendering incorrectly in the expression preview. ([#4684](https://github.com/Skyvern-AI/skyvern/pull/4684))
- The API now returns 404 instead of 500 when a workflow is not found. ([#4699](https://github.com/Skyvern-AI/skyvern/pull/4699))
</Update>
<a id="week-feb-9-2026"></a>
<Update label="Week of February 9, 2026" description="Cached scripts for control-flow blocks, OTEL, and workflow editor polish">
<a id="v1-0-13"></a>
<Update label="v1.0.13 — February 10, 2026" description="Cached scripts for control-flow blocks, OTEL, and workflow editor polish">
## New features
@ -614,8 +541,8 @@ keywords:
</Update>
<a id="week-feb-2-2026"></a>
<Update label="Week of February 2, 2026" description="Task V2 termination, ForLoop caching, and cleaner conditional debugging">
<a id="v1-0-12"></a>
<Update label="v1.0.12 — February 3, 2026" description="Task V2 termination, ForLoop caching, and cleaner conditional debugging">
## New features
@ -647,8 +574,8 @@ keywords:
</Update>
<a id="week-jan-26-2026"></a>
<Update label="Week of January 26, 2026" description="Workflow Copilot GA, Browser Sessions v2 frontend, and TOTP improvements">
<a id="v1-0-11"></a>
<Update label="v1.0.11 — January 29, 2026" description="Workflow Copilot GA, Browser Sessions v2 frontend, and TOTP improvements">
## New features
@ -678,32 +605,26 @@ keywords:
</Update>
<a id="week-jan-19-2026"></a>
<Update label="Week of January 19, 2026" description="Browser Sessions v2, Print PDF block, Copilot streaming, general text CAPTCHA, HTTP file downloads">
<a id="v1-0-10"></a>
<Update label="v1.0.10 — January 22, 2026" description="Browser Sessions v2 backend, general text CAPTCHA, and TOTP webhook retries">
## New features
- **Browser Sessions v2 (Backend)** — A new persistent browser session architecture with cleaner lifecycle management, better resource reuse, and improved reliability for long-running automations. The frontend follows in v1.0.11. ([#4515](https://github.com/Skyvern-AI/skyvern/pull/4515))
- **General Text CAPTCHA Solver** — Skyvern can now solve a wider range of text-based CAPTCHAs without site-specific configuration. ([#4517](https://github.com/Skyvern-AI/skyvern/pull/4517))
- **TOTP Webhook Retries** — TOTP webhook delivery now retries automatically on transient failures, reducing the rate of "no code received" errors during 2FA flows. ([#4518](https://github.com/Skyvern-AI/skyvern/pull/4518))
- **Block-Label Validation in `run_blocks`** — The `run_blocks` endpoint now validates block labels up front, returning a clear error instead of failing partway through execution. ([#4500](https://github.com/Skyvern-AI/skyvern/pull/4500))
- **Print PDF Block** — A new workflow block that prints the current page to PDF and saves it to your run's downloaded files, useful for archiving receipts, invoices, and reports. ([#4452](https://github.com/Skyvern-AI/skyvern/pull/4452))
- **HTTP File Downloads** — The HTTP Request block can now download files directly via HTTP (not just GET JSON), enabling workflows that pull artifacts from authenticated APIs. ([#4440](https://github.com/Skyvern-AI/skyvern/pull/4440))
- **Workflow Copilot Streaming with Cancel** — The Workflow Copilot now streams its responses with a cancel button, so you can stop generation mid-stream if you want to refine your prompt. ([#4437](https://github.com/Skyvern-AI/skyvern/pull/4437), [#4456](https://github.com/Skyvern-AI/skyvern/pull/4456))
- **"Execute on Any Outcome" (Finally) Block Option** — Blocks can now be configured to run regardless of whether prior blocks succeeded, enabling cleanup steps and "always send report" workflows. ([#4443](https://github.com/Skyvern-AI/skyvern/pull/4443))
- **Persistent Browser Session Uptime Billing** — Billing v2 now meters persistent browser session uptime separately, with PostHog feature flags controlling rollout. ([#4444](https://github.com/Skyvern-AI/skyvern/pull/4444))
- **GPT-5 Mini Flex Engine** — Added GPT-5 Mini Flex as an available LLM engine. ([#4432](https://github.com/Skyvern-AI/skyvern/pull/4432))
- **Clipboard Copy over HTTP** — The browser now exposes clipboard copy functionality that works in HTTP-served browser sessions, not just HTTPS. ([#4446](https://github.com/Skyvern-AI/skyvern/pull/4446))
## Improvements
- **Confirmation Dialog Shows Affected Blocks** — Deleting a parameter or block now lists every block that references it, so you don't accidentally break downstream logic. ([#4519](https://github.com/Skyvern-AI/skyvern/pull/4519))
- **Workflow Updates Return 409 on Race** — Concurrent workflow updates now return HTTP 409 instead of silently overwriting each other. ([#4510](https://github.com/Skyvern-AI/skyvern/pull/4510))
- **Tiktoken Caching** — Tokenizer instances are now cached, cutting per-step overhead on token counting. ([#4521](https://github.com/Skyvern-AI/skyvern/pull/4521))
- **HTTP Block Boolean and Integer Templates** — The HTTP Request block now correctly passes boolean and integer template values without coercing them to strings. ([#4435](https://github.com/Skyvern-AI/skyvern/pull/4435))
- **Bitwarden and Azure Vault Tooltips** — Added inline help tooltips explaining how to set up Bitwarden and Azure Key Vault credential sources. ([#4447](https://github.com/Skyvern-AI/skyvern/pull/4447))
- **Smaller S3 Objects Use STANDARD Tier** — Storage cost optimization: small artifacts now use STANDARD instead of GLACIER_IR. ([#4453](https://github.com/Skyvern-AI/skyvern/pull/4453))
- **Reduced Max SVG Parsing** — Lowered the SVG parsing cap to prevent runaway memory usage on pathological pages. ([#4430](https://github.com/Skyvern-AI/skyvern/pull/4430))
## Bug fixes
@ -713,8 +634,39 @@ keywords:
</Update>
<a id="week-jan-5-2026"></a>
<Update label="Week of January 5, 2026" description="Workflow Copilot v1, Azure secret storage, and per-action billing">
<a id="v1-0-9"></a>
<Update label="v1.0.9 — January 20, 2026" description="Print PDF block, Workflow Copilot streaming, and persistent session billing">
## New features
- **Print PDF Block** — A new workflow block that prints the current page to PDF and saves it to your run's downloaded files, useful for archiving receipts, invoices, and reports. ([#4452](https://github.com/Skyvern-AI/skyvern/pull/4452))
- **HTTP File Downloads** — The HTTP Request block can now download files directly via HTTP (not just GET JSON), enabling workflows that pull artifacts from authenticated APIs. ([#4440](https://github.com/Skyvern-AI/skyvern/pull/4440))
- **Workflow Copilot Streaming with Cancel** — The Workflow Copilot now streams its responses with a cancel button, so you can stop generation mid-stream if you want to refine your prompt. ([#4437](https://github.com/Skyvern-AI/skyvern/pull/4437), [#4456](https://github.com/Skyvern-AI/skyvern/pull/4456))
- **"Execute on Any Outcome" (Finally) Block Option** — Blocks can now be configured to run regardless of whether prior blocks succeeded, enabling cleanup steps and "always send report" workflows. ([#4443](https://github.com/Skyvern-AI/skyvern/pull/4443))
- **Persistent Browser Session Uptime Billing** — Billing v2 now meters persistent browser session uptime separately, with PostHog feature flags controlling rollout. ([#4444](https://github.com/Skyvern-AI/skyvern/pull/4444))
- **GPT-5 Mini Flex Engine** — Added GPT-5 Mini Flex as an available LLM engine. ([#4432](https://github.com/Skyvern-AI/skyvern/pull/4432))
- **Clipboard Copy over HTTP** — The browser now exposes clipboard copy functionality that works in HTTP-served browser sessions, not just HTTPS. ([#4446](https://github.com/Skyvern-AI/skyvern/pull/4446))
## Improvements
- **HTTP Block Boolean and Integer Templates** — The HTTP Request block now correctly passes boolean and integer template values without coercing them to strings. ([#4435](https://github.com/Skyvern-AI/skyvern/pull/4435))
- **Bitwarden and Azure Vault Tooltips** — Added inline help tooltips explaining how to set up Bitwarden and Azure Key Vault credential sources. ([#4447](https://github.com/Skyvern-AI/skyvern/pull/4447))
- **Smaller S3 Objects Use STANDARD Tier** — Storage cost optimization: small artifacts now use STANDARD instead of GLACIER_IR. ([#4453](https://github.com/Skyvern-AI/skyvern/pull/4453))
- **Reduced Max SVG Parsing** — Lowered the SVG parsing cap to prevent runaway memory usage on pathological pages. ([#4430](https://github.com/Skyvern-AI/skyvern/pull/4430))
</Update>
<a id="v1-0-8"></a>
<Update label="v1.0.8 — January 9, 2026" description="Workflow Copilot v1, Azure secret storage, and per-action billing">
## New features

View file

@ -23,14 +23,14 @@ Open **Billing** in the sidebar to see available plans and your current balance.
<img src="/images/cloud/settings-billing-overview.png" alt="Billing page showing plan options and current balance" />
| Plan | Price | Credits | Estimated actions | Concurrent runs |
|------|-------|---------|-------------------|-----------------|
| **Free** | $0 | 5,000 one-time | ~200 | 1 |
| **Hobby** | $29/month | 30,000/month | ~1,200 | 10 |
| **Pro** | $149/month | 150,000/month | ~6,200 | 25 |
| Plan | Price | Credits/month | Estimated actions | Concurrent runs |
|------|-------|---------------|-------------------|-----------------|
| **Free** | $0 | 1,000 | ~170 | 1 |
| **Hobby** | $29/month | 30,000 | ~1,200 | 10 |
| **Pro** | $149/month | 150,000 | ~6,200 | 25 |
| **Enterprise** | Custom | Unlimited | Unlimited | Unlimited |
**Free** is available to all new accounts with a one-time credit grant and zero commitment. **Hobby** and **Pro** are monthly subscriptions. Click **Switch to Hobby** or **Switch to Pro** to change plans. Your remaining dollar balance is applied as a credit toward your first subscription payment.
**Free** is available to all new accounts with zero commitment. **Hobby** and **Pro** are monthly subscriptions. Click **Switch to Hobby** or **Switch to Pro** to change plans. Your remaining dollar balance is applied as a credit toward your first subscription payment.
<Note>
Need HIPAA compliance, SOC-2 reports, custom code blocks, or dedicated support? Use the **Book a call** link at the bottom of the Billing page to discuss **Enterprise**, or visit [skyvern.com/pricing](https://skyvern.com/pricing) for a complete feature comparison.
@ -40,7 +40,7 @@ Need HIPAA compliance, SOC-2 reports, custom code blocks, or dedicated support?
<AccordionGroup>
<Accordion title="Free: Try Skyvern with zero commitment">
**5,000 one-time credits** (~200 actions)
**1,000 credits/month** (~170 actions)
Perfect for evaluating Skyvern and prototyping automations.
@ -117,11 +117,11 @@ The **Your Current Balance** section shows your remaining dollar balance. Credit
## FAQ
<Accordion title="How do credits work?">
Each browser action the AI takes (clicking, typing, navigating, extracting) costs one credit. Credits are deducted in real time as runs execute. Free accounts receive a one-time credit grant. Paid plans include a monthly credit allowance that resets each billing cycle. Unused included credits don't roll over.
Each browser action the AI takes (clicking, typing, navigating, extracting) costs one credit. Credits are deducted in real time as runs execute. Your plan includes a monthly credit allowance that resets each billing cycle. Unused credits don't roll over.
</Accordion>
<Accordion title="Can I buy more credits?">
Yes. If you exhaust your included allowance, you can purchase additional credits from the Billing page. You can also upgrade to a higher plan for a larger monthly allowance.
Yes. If you exhaust your monthly allowance, you can purchase additional credits from the Billing page. You can also upgrade to a higher plan for a larger monthly allowance.
</Accordion>
<Accordion title="Can I cancel anytime?">

View file

@ -128,7 +128,7 @@ From the session detail page, click **Stop**. A confirmation dialog appears. The
Sessions also close automatically when their timeout expires, **even if a task is still running**. Set timeouts with enough margin for your longest expected task.
<Warning>
Closed sessions cannot be reopened, and a session only saves its browser state at close when profile generation is enabled for it. The **Save browser session** credential flow handles this automatically; for API-created sessions, see [Save a session's profile](/developers/optimization/browser-sessions#save-a-sessions-profile). If you need the same login state later, capture it as a [browser profile](/cloud/browser-management/browser-profiles) before closing.
Closed sessions cannot be reopened. If you need the same login state later, create a [browser profile](/cloud/browser-management/browser-profiles) before closing.
</Warning>
---

View file

@ -58,13 +58,9 @@ Browser-based blocks (Browser Task, Browser Action, Extraction, Login, File Down
| **Max Steps Override** | Cap the number of AI reasoning steps |
| **Disable Cache** | Skip cached script execution |
| **TOTP Identifier** | Link TOTP credentials for 2FA handling |
| **TOTP Verification URL** | Endpoint this block polls when it needs a 2FA code |
| **TOTP Verification URL** | Endpoint for fetching TOTP codes |
| **Error Code Mapping** | Map error conditions to custom error codes (JSON) |
<Note>
Set **TOTP Verification URL** on the block that may encounter 2FA. Top-level workflow run `totp_url` values do not automatically populate empty block fields.
</Note>
---
## Browser Automation

View file

@ -363,7 +363,7 @@ Restart Skyvern after setting these variables.
| Status stays Inactive | Verify the API base URL is a valid URL and the API token is not empty. The configuration is validated on save but does not make a live request to your server. |
| Credentials not created | Review your API logs for auth errors. Ensure the response includes an `id` field. Skyvern expects HTTP 200 for all operations. |
| Credentials not retrieved | Ensure the GET response includes all required fields for the credential type (`username` and `password` for passwords, all card fields for credit cards, `secret_value` for secrets). |
| Env config not working | Restart Skyvern after setting variables. Verify `CREDENTIAL_VAULT_TYPE=custom` is set and both URL and token are provided. Deployment defaults vary, so `custom` must be explicitly set for an external service. |
| Env config not working | Restart Skyvern after setting variables. Verify `CREDENTIAL_VAULT_TYPE=custom` is set and both URL and token are provided. The default vault type is `bitwarden`, so this variable must be explicitly set. |
---

View file

@ -39,8 +39,8 @@ The preferred method. Skyvern generates valid 6-digit codes on demand during log
<Step title="Select Authenticator App">
Choose **Authenticator App** from the three options.
</Step>
<Step title="Add your TOTP setup key">
Enter the secret key into the **Authenticator Key** field, or click **Scan QR** to upload a QR code image from the site's 2FA setup screen. Then click **Save**.
<Step title="Paste your TOTP secret key">
Enter the secret key into the **Authenticator Key** field and click **Save**.
<img src="/images/cloud/totp-setup-2.png" alt="Add Credential dialog with Authenticator App selected and the Authenticator Key field" />
</Step>
</Steps>
@ -53,7 +53,7 @@ The secret key is the base32-encoded string behind the QR code you'd normally sc
- **LastPass**: Edit the login → Advanced Settings → copy the TOTP secret
- **Site settings**: Many sites show a "Can't scan?" link during 2FA setup that reveals the text key
If you only have a QR code, use **Scan QR** in the credential form or paste the full `otpauth://totp/...?secret=BASE32KEY` URI.
If you only have a QR code, decode it to extract the `secret=` parameter from the `otpauth://totp/...?secret=BASE32KEY` URI.
</Accordion>
---
@ -171,13 +171,7 @@ This turns email-based 2FA into something nearly as automated as an authenticato
## Server-fetched codes (totp_url)
Instead of pushing codes to Skyvern, you can have Skyvern pull them from your server. Skyvern calls your endpoint only when the automation reaches a page that asks for a verification code.
<Warning>
For saved agents or workflows, configure the endpoint on the block that performs the login. Open the browser, action, file download, or login block and set **TOTP Verification URL**. Passing `totp_url` only in the top-level workflow run request does not automatically populate a block whose **TOTP Verification URL** is empty.
</Warning>
If the endpoint changes per run, create a workflow parameter such as `totp_url`, set the block's **TOTP Verification URL** to `{{ totp_url }}`, and pass the URL in `parameters` when starting the run.
Instead of pushing codes to Skyvern, you can have Skyvern pull them from your server. Pass `totp_url` when running a [task](/cloud/getting-started/run-from-code) or [agent](/cloud/building-agents/build-an-agent), and Skyvern will call your endpoint when it needs a verification code.
Your endpoint must accept a POST request with `task_id`, `workflow_run_id`, and `workflow_permanent_id` in the body, and return:
@ -252,7 +246,7 @@ curl -X GET "https://api.skyvern.com/v1/credentials/totp?totp_identifier=user@ex
| `otp_type` | Filter by `totp` or `magic_link` |
| `limit` | Number of records (default 50, max 200) |
Returns historical records ordered newest first, capped by `limit`. Runtime polling still only uses unexpired codes within `TOTP_LIFESPAN_MINUTES`.
Returns a list ordered newest first. Only codes created within the last 10 minutes (configurable via `TOTP_LIFESPAN_MINUTES`) are returned.
<CardGroup cols={2}>
<Card

View file

@ -1,306 +0,0 @@
# Workflow Build — Recent Activity History (SKY-11075)
Replace the bottom-of-canvas **dot/carousel indicator** on `/workflows/:id/build` with a
real recent-activity history view that's easy to scan and lets you jump directly to a
specific recent activity.
Status: **Final — recent activity is a compact run selector locked to the right
run-detail panel; the A/B/C style and placement preview toggles were removed. See
the Round 7 section below.**
---
## 1. Frame
- **Page:** Workflow editor, *build* mode — `/workflows/:id/build` (and the run-loaded
variant `/workflows/:wpid/:workflowRunId/:blockLabel/build`). The history surface only
renders in **debug/browser mode** (`showBrowser`), bottom-center over the canvas+code
pane. Redesign of an existing affordance.
- **Job to be done:** While iterating on a workflow you fire blocks/the workflow many times
in one debug session. Today those runs collapse into a row of identical ~16px dots whose
only metadata is a hover tooltip. You can't tell at a glance *what* ran, *whether it
passed*, *how long it took*, or *which mode* (agent vs cached code) — you must hover each
dot one by one. The job: make each recent run legible at a glance and jump straight to it.
- **Deciding constraint — what counts as "recent activity":** **Debug-session runs**
(confirmed with requester). Each entry = one block/workflow run triggered in the current
debug session, sourced from `GET /debug-session/{id}/runs` (`useDebugSessionRunsQuery`).
No new backend. Deliberately distinct from (a) the global **Runs** page and (b) the
right-side **run-detail timeline** (`DebuggerRun`/`DebuggerRunTimeline`) which shows the
*inside* of one selected run. This surface is the *list of runs*; selecting one drives the
existing detail rail — no overlapping history surfaces.
- **Open sub-question (requester):** is a dock/strip the best surface, or do we want a
"proper timeline"? → resolved via the concepts below; requester to pick direction.
- **Platform:** web.
- **North star references:** Databricks pipeline run-selector + event log; Vercel Activity feed.
### Data available per entry (`DebugSessionRun`) — today all hidden behind one dot
`block_label`, `status` (running/completed/failed/terminated/timed_out/canceled/queued/
skipped), `created_at` / `queued_at` / `started_at` / `finished_at` (→ duration + "x ago"),
`failure_reason`, `run_with` (agent | code), `code_gen`, `ai_fallback`, `workflow_run_id`,
`workflow_permanent_id`, `script_run_id`. Selecting → `navigate(/workflows/{wpid}/{workflow_run_id}/{block_label}/build)`.
---
## 2. DS inventory (read from code — Figma DS is thin by design)
| Need | Reuse (exists in repo) |
|---|---|
| Status glyph | `StatusDot` pattern (WorkflowRunTimelineBlockItem): `CheckCircledIcon text-success`, `CrossCircledIcon text-destructive`, `ReloadIcon animate-spin text-sky-400`, neutral `rounded-full bg-slate-600` |
| Block-type glyph | `WorkflowBlockIcon` (per `WorkflowBlockType`) |
| Type / mode pills | inline pill convention `rounded bg-slate-700/70 px-1.5 py-0.5 text-[10px] font-medium` |
| Relative time / duration | `formatMs(Date.now()-created).ago`, `formatDuration(toDuration(ms))`, `tabular-nums` |
| Tooltip | `Tip` (`@/components/Tip`) |
| Scroll container | `ScrollArea` (`@/components/ui/scroll-area`) |
| Dropdown / flyout | `Popover` (`@/components/ui/popover`) |
| Expand / collapse | `Collapsible` (`@/components/ui/collapsible`) |
| Buttons / badges | `Button`, `Badge` |
| Surface tokens | `bg-neutral-50 dark:bg-background`, `border-neutral-200 dark:border-neutral-800`, focus `focus-visible:ring-1 focus-visible:ring-white/40` |
Conventions: build-mode rails skew dark (`slate-*`); pills `text-[10px]`, `tabular-nums` for
numbers; every interactive row is a `<button>` with a visible focus ring.
## 3. Gap ledger (DS lacks → candidate Step-5 tickets)
- No dedicated **Status badge** component — status color/icon mapping is re-derived inline in
several places (`StatusDot`, run cards). Candidate: extract a shared `RunStatusBadge`.
- No **horizontal "activity strip"/run-history rail** primitive.
- (Append as concepts/implementation pressure the library.)
---
## 4. Inspiration (Mobbin — web)
1. **Databricks — pipeline "Update details"** — editor canvas with a **Run: {timestamp}
[status]** selector top-left, a detail panel (Update ID, *Status, Duration, Start time,
Run time, Completion date*), and a timestamped **Event log** below. Closest analog: a
build canvas whose run history is a compact selector + per-run metadata.
[link](https://mobbin.com/screens/2a53f9eb-b8e1-4958-afb7-f441acbdf414). a11y: the
selector must be a real listbox/menu button, not a click-only div.
2. **Vercel — Activity** — scannable vertical feed: each row = entity icon + plain-language
description + **relative time** ("42m"), grouped by date, left filter rail. Gold standard
for "legible at a glance."
[link](https://mobbin.com/screens/2f3056eb-a31a-4b3e-ae4b-1bcc1352117f). a11y: relative
time needs a `title`/`<time datetime>` with the absolute timestamp.
3. **WorkOS — Events** — master-detail: list (event + date/time) on the left, metadata on the
right. [link](https://mobbin.com/screens/a5b50327-60e0-4075-9cfd-7f383d18265c).
4. **Okta — System Log** / **Stripe — Events** / **Cloudflare — Observability** — dense log
tables (timestamp · type · message), some with a count-over-time bar.
[Okta](https://mobbin.com/screens/70f42b3f-1823-4cb2-872d-346ad3eab8dc) ·
[Stripe](https://mobbin.com/screens/5423e185-8374-4b6c-a4a0-2d3ae9adc1d0) ·
[Cloudflare](https://mobbin.com/screens/db64bfde-eac6-4786-8c29-62b98eb1a149). a11y:
color-coded status must pair with an icon/text, not color alone.
---
## 5. Concepts
All three keep the data scope (debug-session runs), keep the existing bottom-center anchor
over the canvas+code pane, and preserve the existing select→navigate behavior. They differ in
*how the history is surfaced*.
### Concept A — "Activity Strip" (in-place horizontal chips)
- **Thesis:** smallest change. Swap the row of bare dots for a row of legible **chips** in the
same pill, scrolling horizontally. Each chip: status glyph + block icon + block label +
"x ago"; duration/mode in tooltip. Click → navigate.
- **Lineage:** Databricks run-selector (canvas-anchored) + Vercel chip legibility.
- **DS:** `StatusDot`, `WorkflowBlockIcon`, pills, `ScrollArea`, `formatMs`. Same wrapper/position.
- **Tradeoffs:** + tiny diff, preserves muscle memory, fast. horizontal scroll is less
scannable than vertical; limited metadata per chip; long lists hide off-screen.
### Concept B — "Activity History panel" (collapsed timeline bar → expanding vertical list) — RECOMMENDED
- **Thesis:** the dock's **collapsed** state is a slim **status-segment bar** — one thin
segment per run, colored by status, newest on the right, labeled "N runs · last ✓ 2m ago".
That's the at-a-glance, time-ordered "timeline." Click/hover **expands upward** into a
**vertical, scannable list**: one rich row per run = status glyph · block icon · block name ·
type pill · mode pill (Agent/Code) · duration · "x ago", with a one-line failure reason on
failures. Newest first. Row click → navigate (existing behavior); current run highlighted.
- **Lineage:** Vercel Activity feed (legible rows) + Okta count-over-time bar (collapsed
status density) + WorkOS master-detail (list feeds the existing right-side detail rail).
- **DS:** `Popover`/`Collapsible` + `ScrollArea`, the `WorkflowRunTimelineBlockItem` row
vocabulary, `StatusDot`, pills. New: a small **status-segment bar** (gap-ledger item).
- **Tradeoffs:** + most legible (vertical rows, full metadata — directly satisfies the AC),
+ collapsed footprint stays tiny so the canvas isn't crowded, + serves *both* the
"scannable history" and "proper timeline" instincts, + scales to long lists.
one extra primitive (segment bar); popover sizing over the canvas needs care.
### Concept C — "Run Selector + Segmented Timeline" (header selector + bottom bar)
- **Thesis:** Databricks-style. A **run selector** near the header ("Run: Login ✓ · 2m ago ▾")
opens the activity list, *and* a persistent thin **segmented timeline bar** sits at the
bottom (each run a proportional, status-colored segment; hover→tooltip, click→navigate).
- **Lineage:** Databricks run-selector + Okta status-density bar.
- **DS:** `Popover`, segment bar (new), `StatusDot` colors.
- **Tradeoffs:** + strongest "timeline" metaphor, scales well. most net-new UI and two
surfaces (header + bottom) risk fragmenting the history; largest build/QA; header is
already busy in build mode.
## 6. Recommendation & Decision
**Recommend Concept B.** It's the most faithful to the acceptance criteria — each entry
legible at a glance with real metadata, and one click to jump to a run — while keeping the
collapsed footprint as small as today's dot pill. Its collapsed status-segment bar answers the
requester's "proper timeline?" instinct without splitting the history across two surfaces
(Concept C's main risk), and it reuses the established run-row vocabulary so it looks native.
Decision (2026-06-15): requester chose to **build all three (A, B, C) behind a live variant
toggle** so they can compare directly in the PR preview, then collapse to one winner later.
Implementation ships a `RecentActivity` orchestrator that renders the variant selected by a
persisted (localStorage + `?activityVariant=` URL seed) toggle; default = B.
## 7. Implementation (shipped)
New module `skyvern-frontend/src/routes/workflows/debugger/recentActivity/`:
- `useRecentActivity.ts` — data hook (debug-session runs, sorted asc; block-type map;
`navigateToRun` preserving the old select→navigate target, now also resolving nested loop
blocks for the "block still exists" guard).
- `runActivity.ts` / `RunGlyphs.tsx` — shared status/duration/time helpers + status/block glyphs.
- `RecentActivitySegmentBar.tsx`, `RecentActivityList.tsx` — shared primitives.
- `RecentActivityStrip.tsx` (A), `RecentActivityPanel.tsx` (B), `RecentActivityTimeline.tsx`
(C), `RecentActivityVariantToggle.tsx`, `RecentActivity.tsx` (orchestrator).
- `src/store/RecentActivityVariantStore.ts` — variant store.
- `Workspace.tsx` now renders `<RecentActivity />`; `DebuggerBlockRuns.tsx` deleted.
Verification: `tsc --noEmit` ✓, `eslint --max-warnings 0` ✓, `vite build` ✓. Live visual
verification of the populated state needs a debug session with ≥1 run (backend + browser
session) — delegated to the PR preview by design (the whole point of the A/B/C toggle).
### Gap ledger outcome (→ Step 5 DS ticket)
- Shipped one-off **`RecentActivitySegmentBar`** (status-segment/timeline-bar primitive) — DS has none.
- Re-derived **run-status → icon/color** inline (`RunGlyphs` + `STATUS_PILL_TONE`/`SEGMENT_FILL`)
because there's no shared `RunStatusBadge`.
- Filed **SKY-11082** (team Skyvern AI) to extract both primitives into the DS once the winning
variant is chosen. Pure helpers covered by `runActivity.test.ts`.
---
# Round 2 — Placement exploration (2026-06-16)
The A/B/C work settled the *surface design*; this round explores *where on the build page*
the surface lives. Orthogonal axis: any style (A/B/C) can sit at any placement.
## Frame (placement)
The surface only renders in **debug/browser mode** (`showBrowser`), inside the vertical
`Splitter`. Candidate regions and their occupants:
- **Left pane** = code (left half) + infinite canvas (right half). Bottom-left corner holds the
FlowRenderer controls (FitView/Lock/GlobalCollapse); top is overlaid by `WorkflowHeader`
(`absolute left-6 top-8 h-20`) and header-anchored sub-panels (`top-[8.5rem]`).
- **Right pane** (`skyvern-split-right`) = live browser + footer (copilot/power/reload); far
right edge holds the run-detail **timeline rail** (`w-[5rem]`, expandable, `z-[15]`).
**Deciding constraint:** a placement must not block the live browser, the canvas controls, or
the run-detail timeline, and should not fight the header/sub-panels. So placements are
pointer-events-transparent floating bands (only the dock itself captures clicks).
## Inspiration (Mobbin — web, node-canvas editors)
- **WRITER — Blueprints**: execution log as a full-width **bottom** panel (node rows + status +
Trace + duration). [link](https://mobbin.com/screens/2368f17d-4fcc-4590-912d-08cb39245dab)
- **n8n — Executions**: run history as a **left side panel** (status + duration, auto-refresh),
entered via a top **Editor | Executions** tab.
[link](https://mobbin.com/screens/d3af4413-04f9-4534-8aa4-01916d2b4910)
- **StackAI — Version history / Run Details**: **right side panel** co-located with the run
(status, timestamps, duration, tokens).
[link](https://mobbin.com/screens/cffa32bb-fa1e-43fa-913b-27001b35f395) ·
[link](https://mobbin.com/screens/bb0174f4-60aa-4e30-ac5f-73679b160f38)
- **Databricks** (round 1): top **run-selector** + bottom **event log** in one editor.
## Placement concepts
- **P1 — Bottom dock (baseline, current):** floating bottom-center over the canvas/code pane.
Lineage: WRITER bottom log + StackAI bottom toolbar. + canvas stays clear, controls reachable.
easy to miss; far from where the eye starts.
- **P2 — Top rail (under header):** pinned top-center over the canvas/code pane, just below the
header. Lineage: n8n Editor|Executions top tab + Databricks top run-selector. + high
discoverability, reads top-down, near run controls. competes with header sub-panels when one
is open; popover opens downward over the canvas.
- **P3 — Browser-pane header (co-located with run viewing):** pinned to the top of the right
(live-browser) pane, so picking a recent run sits beside viewing that run + its detail
timeline. Lineage: StackAI right-side run history/details. + strongest pick→view coherence
(directly answers the ticket's "reconcile with run viewing"). eats a strip of browser space;
must clear the far-right timeline rail.
**Recommendation:** **P3 (browser-pane header)** for coherence — it puts "which run" right next
to "this run's browser + timeline," which is the actual debugging loop — with **P1** as the
low-risk default if we want the canvas untouched. P2 is the most discoverable but the most
crowded.
**Decision (2026-06-16):** per the established pattern, build all three behind a **placement
toggle** (orthogonal to the A/B/C style toggle; persisted via localStorage + `?activityPlacement=`
URL seed; default `bottom`) so they can be compared live in the PR preview.
### Gap-ledger addition
- No shared **floating-dock anchor** convention for build-canvas overlays (each overlay
re-implements `absolute … pointer-events-none [&>*]:pointer-events-auto`). Minor; noted on
[[SKY-11082]] rather than a new ticket.
## Round 2 decision — style converges to one "Activity Timeline" (2026-06-16)
Requester: "I like C the most, and B for its compression and all the events in one place."
**Merge B + C into a single default surface** and drop A (and the A/B/C style toggle):
**Activity Timeline** — compact two-row dock:
- Row 1: a **run selector** ("Run: ✓ Login · 2m ago ▾") + a compression summary ("N runs ·
last ✓ 2m ago"). The selector opens **B's consolidated, scannable event list**
(`RecentActivityList`) — all events in one place.
- Row 2: a **persistent segmented timeline bar** (C) — proportional, status-colored; hover→tip,
click a segment → jump to that run.
This keeps C's timeline metaphor + direct segment navigation, with B's compression (compact
collapsed form) and single consolidated list. Built behind the **placement toggle** (bottom /
top / right) so location can still be compared in the PR preview.
## Round 3 decision — placement locked to browser-pane header (2026-06-16)
Requester locked the **browser-pane header (right)** placement. Removed the placement toggle +
store and the bottom/top anchors; `RecentActivity` now mounts unconditionally at the top of the
live-browser pane (`popoverSide="bottom"`). Rendered visual proof (mock runs) below — produced
via a throwaway standalone preview page, not committed.
![Activity Timeline preview](./activity-timeline-preview.png)
## Round 4 — re-added the 3-way placement toggle (2026-06-16)
Reverted the round-3 lock: requester wants to compare bottom / top / right **by eye** in the
preview rather than commit blind. Restored `RecentActivityPlacementStore` +
`RecentActivityPlacementToggle` and the three Workspace anchors (default `bottom`,
`?activityPlacement=` URL seed). Decide, then re-lock.
![Placement comparison](./activity-timeline-placements.png)
## Round 5 — drop floating, integrate the run selector (2026-06-16)
Real screenshots showed every floating placement collides with canvas/browser content and
duplicates the existing right-side run-detail panel (`DebuggerRunTimeline`: Actions/Steps/Credits
+ block/action timeline) — the overlap the ticket warned about. So: **remove the floating dock,
segmented bar, and placement toggle**, and make recent-activity a compact **run selector** that
opens the consolidated list. Two integration points behind a small mode toggle (default `panel`,
`?activityMode=panel|toolbar`):
- **panel:** selector at the top of the right run-detail panel (`DebuggerRun`) — co-located with
viewing; picking a run drives the panel below.
- **toolbar:** selector as a dropdown in the editor header toolbar (`EditorActionToolbar`).
Removed: `RecentActivity`, `RecentActivityTimeline`, `RecentActivitySegmentBar`,
`RecentActivityPlacementToggle`, `RecentActivityPlacementStore`. Added: `RecentActivityRunSelector`,
`RecentActivityIntegrationStore`, `RecentActivityIntegrationToggle`.
## Round 6 — timeline strip below the browser (2026-06-16)
Panel/toolbar selectors still read as bolted-on. Requester idea: dock the timeline as a
horizontal strip **below the live browser session window** — the browser is what you're
watching, so a run history beneath it is contextual (WRITER bottom log / Databricks event-log
lineage). Added as a third integration option (`browser`) on the mode toggle:
`RecentActivityStrip` — a scannable horizontal row of run chips (oldest→newest, auto-scrolled to
newest, click→jump) + an "N runs ▾" button opening the consolidated list. Mounted between the
browser stream and its footer in both VNC and CDP panels. Compare via the Panel/Toolbar/Browser
toggle (`?activityMode=panel|toolbar|browser`).
## Round 7 — placement locked to the run-detail panel; preview toggle removed (final, 2026-06-29)
Compared the three integrations live in the PR preview and **locked `panel`**: the run
selector mounts at the top of the right run-detail panel (`DebuggerRun`), co-located with
viewing a run, which reconciles the history surface with run viewing (the ticket's open
question) without a second overlapping surface. Removed the exploration scaffolding —
`RecentActivityIntegrationStore` (+test), `RecentActivityIntegrationToggle`, and
`RecentActivityStrip` — plus the toolbar/browser wirings in `WorkflowHeader`/`Workspace`.
`RecentActivityRunSelector` mounts unconditionally (it self-hides until the debug session has
≥1 run) and its now-single-use `variant`/`popoverSide` props were dropped. Run rows in the
consolidated list are disabled while a workflow run is in progress (navigation is a no-op
then), covered by `RecentActivityList.test.tsx`. Dead exports `SEGMENT_FILL` and
`getRunTooltip` (orphaned when the segment bar / strip were cut) were removed.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

View file

@ -40,8 +40,6 @@ Select the method that matches how you receive verification codes:
When you enable 2FA on a website, you're typically shown a QR code or a secret key. Store this secret with your credential, and Skyvern generates valid codes automatically.
In the Skyvern UI, paste the manual key or full `otpauth://` URI into **Authenticator Key**. You can also click **Scan QR** and upload a QR code image from the site's 2FA setup screen.
<CodeGroup>
```python Python
import os
@ -314,12 +312,6 @@ If `content` is longer than 10 characters, Skyvern's AI extracts the verificatio
Instead of pushing codes to Skyvern, you implement an endpoint that Skyvern polls until a code is available.
<Warning>
For saved workflows or agents, configure the endpoint on the specific browser block that can encounter 2FA. Use the block's **TOTP Verification URL** field, or set that field to a workflow parameter such as `{{ totp_url }}` and pass the value in `parameters` when starting the run.
Passing `totp_url` only at the top level of a workflow run does not automatically populate every block. If the block's **TOTP Verification URL** is empty, that block will not call your endpoint.
</Warning>
### Step 1: Implement the endpoint
Your endpoint must accept POST requests and return the verification code:
@ -363,9 +355,9 @@ def verify_skyvern_request(request, api_key: str) -> bool:
return hmac.compare_digest(signature, expected)
```
### Step 3: Configure the login or block
### Step 3: Configure the login
For a one-off login task, pass `totp_url` in the login request:
Pass `totp_url` when running your login:
<CodeGroup>
```python Python
@ -390,22 +382,6 @@ curl -X POST "https://api.skyvern.com/v1/run/tasks/login" \
```
</CodeGroup>
For a saved workflow or agent, configure the block that may see the 2FA prompt:
1. Open the browser, action, file download, or login block that performs the login.
2. Set **TOTP Verification URL** to your endpoint.
3. If the endpoint changes per run, create a workflow parameter such as `totp_url`, set the block's **TOTP Verification URL** to `{{ totp_url }}`, and pass the URL in the run's `parameters`.
```typescript TypeScript
const body: SkyvernRunWorkflowRequest = {
workflow_id: "wpid_123",
parameters: {
totp_url: "https://your-server.com/totp-webhook",
// other workflow parameters...
},
};
```
Skyvern polls your endpoint every 10 seconds until a code is returned or the timeout is reached.
---
@ -522,7 +498,7 @@ curl -X GET "https://api.skyvern.com/v1/credentials/totp?totp_identifier=user@ex
| `limit` | Number of records (default 50, max 200) |
<Note>
This endpoint returns historical records ordered newest first, capped by `limit`. Runtime polling still only uses unexpired codes within `TOTP_LIFESPAN_MINUTES`.
Skyvern only returns codes from the last 10 minutes (configurable via `TOTP_LIFESPAN_MINUTES`).
</Note>
---

View file

@ -24,84 +24,20 @@ Profiles are ideal when you:
## How profiles work
You can create a blank profile with only `name` and optional `description`, then pass that profile to future agent runs. Blank profiles are seeded from the configured default browser profile directory when available, with a minimal loadable profile skeleton as a fallback.
When an agent runs with `persist_browser_session=true`, Skyvern archives the browser state (cookies, storage, session files) after the run completes. This archiving happens asynchronously in the background. Once the archive is ready, you can create a profile from it, then pass that profile to future agent runs to restore the saved state.
For [browser sessions](/developers/optimization/browser-sessions), profile saving is opt-in: a session archives its state when it ends only if it has `generate_browser_profile` enabled or was started from a saved profile. See [Save a session's profile](/developers/optimization/browser-sessions#save-a-sessions-profile).
---
## Create a Browser Profile
Create a blank profile when you want a reusable profile ID before any browsing has happened. Create a sourced profile when you want to capture cookies, localStorage, and session files from a workflow run or browser session.
### Blank profile
<CodeGroup>
```python Python
import asyncio
from skyvern import Skyvern
async def main():
client = Skyvern(api_key="YOUR_API_KEY")
profile = await client.create_browser_profile(
name="fresh-profile",
description="Blank profile for future runs",
)
print(f"Profile created: {profile.browser_profile_id}")
asyncio.run(main())
```
```typescript TypeScript
import { Skyvern } from "@skyvern/client";
async function main() {
const client = new Skyvern({ apiKey: process.env.SKYVERN_API_KEY! });
const profile = await client.createBrowserProfile({
name: "fresh-profile",
description: "Blank profile for future runs",
});
console.log(`Profile created: ${profile.browser_profile_id}`);
}
main();
```
```bash cURL
curl -s -X POST "https://api.skyvern.com/v1/browser_profiles" \
-H "x-api-key: $SKYVERN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "fresh-profile",
"description": "Blank profile for future runs"
}'
```
</CodeGroup>
**Parameters:**
| Parameter | Type | Description |
|----------------------|--------|------------------------------------------------------------------------------------------------------------------------------|
| `name` | string | Required unique display name |
| `description` | string | Optional profile description |
| `browser_session_id` | string | Omit for a blank profile |
| `workflow_run_id` | string | Omit for a blank profile |
| `proxy_location` | string \| object | Optional pinned proxy location for the profile's residential ISP identity (e.g. `RESIDENTIAL_ISP`, or a `GeoTarget` object) |
| `proxy_session_id` | string | Optional advanced reuse key for the profile's pinned proxy identity |
### From an agent run
Create an agent with `persist_browser_session=true` in the agent definition, run it, wait for completion, then create a profile from the run. Session archiving happens asynchronously, so add brief retry logic when creating the profile.
<Note>
`persist_browser_session` must be set when **creating the agent**, not when running it. It is an agent definition property, not a runtime parameter.
</Note>
### From an agent run
<CodeGroup>
```python Python
import asyncio
@ -288,16 +224,12 @@ done
### From a browser session
You can also create a profile from a closed [Browser Session](/developers/optimization/browser-sessions) that was set to save its profile. Create the session with `generate_browser_profile` enabled (or [turn it on while the session is alive](/developers/optimization/browser-sessions#save-a-sessions-profile)), close the session, then pass the session ID instead of the workflow run ID.
You can also create a profile from a [Browser Session](/developers/optimization/browser-sessions) that was used inside an agent with `persist_browser_session=true`. After the agent run completes and the session is closed, pass the session ID instead of the workflow run ID.
<Warning>
Sessions do not save their profile by default. If the session did not have `generate_browser_profile` enabled and was not started from a saved profile, this call fails with a `400` error — retrying does not help. Update API scripts and integrations that create profiles from closed sessions; n8n users may need updated Skyvern nodes. Profiles created from workflow runs (`persist_browser_session=true`) are unaffected.
Only sessions that were part of an agent with `persist_browser_session=true` produce an archive. A session created with `create_browser_session()` alone does not archive its state. Archiving happens asynchronously after the session closes, so add retry logic.
</Warning>
<Note>
For opted-in sessions, the archive uploads asynchronously after the session closes, so keep brief retry logic for the transient "not persisted yet" window.
</Note>
<CodeGroup>
```python Python
import asyncio
@ -306,7 +238,7 @@ from skyvern import Skyvern
async def main():
client = Skyvern(api_key="YOUR_API_KEY")
# browser_session_id from a closed session created with generate_browser_profile enabled
# browser_session_id from a workflow run with persist_browser_session=true
session_id = "pbs_your_session_id"
# Create profile from the closed session (retry while archive uploads)
@ -334,7 +266,7 @@ import { Skyvern } from "@skyvern/client";
async function main() {
const client = new Skyvern({ apiKey: process.env.SKYVERN_API_KEY! });
// browser_session_id from a closed session created with generate_browser_profile enabled
// browser_session_id from a workflow run with persist_browser_session=true
const sessionId = "pbs_your_session_id";
// Create profile from the closed session (retry while archive uploads)
@ -363,17 +295,8 @@ main();
```
```bash cURL
# Create a session that saves its profile when it ends
SESSION_ID=$(curl -s -X POST "https://api.skyvern.com/v1/browser_sessions" \
-H "x-api-key: $SKYVERN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"generate_browser_profile": true}' | jq -r '.browser_session_id')
# ... run your tasks against $SESSION_ID here ...
# Close the session so the profile uploads
curl -s -X POST "https://api.skyvern.com/v1/browser_sessions/$SESSION_ID/close" \
-H "x-api-key: $SKYVERN_API_KEY"
# browser_session_id from a workflow run with persist_browser_session=true
SESSION_ID="pbs_your_session_id"
# Create profile (retry while session archives)
for i in {1..10}; do
@ -399,7 +322,7 @@ done
| Parameter | Type | Description |
|-----------|------|-------------|
| `name` | string | Required. Display name for the profile. Must be unique within your organization |
| `browser_session_id` | string | ID of the closed browser session (starts with `pbs_`). The session must have had `generate_browser_profile` enabled or been started from a saved profile |
| `browser_session_id` | string | ID of the closed browser session (starts with `pbs_`). The session must have been part of an agent with `persist_browser_session=true` |
| `description` | string | Optional description of the profile's purpose |
---
@ -824,57 +747,6 @@ In a real scenario, step 1 would be a login agent that authenticates with a site
---
## Pin a proxy identity
A browser profile can carry a pinned residential ISP proxy identity so that every run reusing the profile reaches the target site from the same IP and locale. Pinning is opt-in and uses two fields:
- `proxy_location` — set to `RESIDENTIAL_ISP` (or a `GeoTarget` object) to opt the profile into a static ISP identity.
- `proxy_session_id` — opaque Skyvern-managed sticky-session key. Omit it to let Skyvern generate one automatically when `proxy_location` is `RESIDENTIAL_ISP`.
When a run uses a profile that has a pinned proxy, Skyvern routes traffic through the same residential ISP identity the profile was last associated with, even if the run request does not set `proxy_location`.
### Set a pin at create time
<CodeGroup>
```python Python
profile = await client.create_browser_profile(
name="prod-salesforce-admin",
description="Pinned residential ISP identity",
proxy_location="RESIDENTIAL_ISP",
)
```
```bash cURL
curl -s -X POST "https://api.skyvern.com/v1/browser_profiles" \
-H "x-api-key: $SKYVERN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "prod-salesforce-admin",
"description": "Pinned residential ISP identity",
"proxy_location": "RESIDENTIAL_ISP"
}'
```
</CodeGroup>
### Rotate the pinned identity
Update the profile with `rotate_proxy_session_id: true` to ask Skyvern to mint a fresh sticky-session id while keeping the same `proxy_location`. Use this when the existing IP gets flagged or when you want to start a new identity for the same profile.
```bash cURL
curl -s -X PATCH "https://api.skyvern.com/v1/browser_profiles/$PROFILE_ID" \
-H "x-api-key: $SKYVERN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "rotate_proxy_session_id": true }'
```
To clear the pin entirely, set `proxy_location` to `null` in the same update call.
<Note>
`proxy_session_id` is only valid alongside `RESIDENTIAL_ISP`. Requests that supply `proxy_session_id` with any other `proxy_location` are rejected with a 400.
</Note>
---
## Best practices
### Use descriptive names

View file

@ -86,7 +86,6 @@ curl -X POST "https://api.skyvern.com/v1/browser_sessions" \
| `proxy_location` | string | Geographic proxy location (Cloud only). See [Proxy & Geolocation](/developers/going-to-production/proxy-geolocation) for available options |
| `browser_type` | string | Browser type: `chrome` or `msedge` |
| `extensions` | array | Extensions to install: `ad-blocker`, `captcha-solver` |
| `generate_browser_profile` | boolean | REST API only for now — not yet accepted by the SDK methods. Save the session's browser profile (cookies, localStorage, session files) when the session ends, so it can be turned into a reusable [browser profile](/developers/optimization/browser-profiles). Default: `false`. See [Save a session's profile](#save-a-sessions-profile) |
**Example response:**
@ -368,43 +367,6 @@ You cannot use both `browser_session_id` and `browser_profile_id` in the same re
---
## Save a session's profile
By default, a browser session does **not** save its browser profile when it ends. To capture the session's state (cookies, localStorage, session files) for reuse, opt in with `generate_browser_profile`. Python and TypeScript SDK support for this flag is rolling out; until your SDK version includes it, call the REST API directly (the Python SDK can also pass it via `request_options` with `additional_body_parameters={"generate_browser_profile": True}`).
```bash
curl -X POST "https://api.skyvern.com/v1/browser_sessions" \
-H "x-api-key: $SKYVERN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"timeout": 60,
"generate_browser_profile": true
}'
```
You can also toggle the flag on a live session. The value is read when the session ends, so the update takes effect as long as the session is still open. Updating a session that has already closed returns a `409` error.
```bash
curl -X PATCH "https://api.skyvern.com/v1/browser_sessions/pbs_abc123" \
-H "x-api-key: $SKYVERN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"generate_browser_profile": true}'
```
To confirm the setting on an existing session, fetch it with `GET /v1/browser_sessions/{browser_session_id}` — the response includes `generate_browser_profile`.
Once the session closes and its profile finishes uploading, turn it into a reusable profile with [`create_browser_profile`](/developers/optimization/browser-profiles#from-a-browser-session).
<Note>
Sessions started from a saved profile (`browser_profile_id`) always save their profile when they end, regardless of this flag. This does not update the original profile — to keep the refreshed login state, create a new profile from the closed session and use the new profile's ID in future runs.
</Note>
<Warning>
**Behavior change:** sessions previously saved their profile automatically. Creating a browser profile from a closed session now fails with a `400` error unless the session had `generate_browser_profile` enabled or was started from a profile — retrying does not help. Update API scripts and integrations that create profiles from closed sessions; n8n users may need updated Skyvern nodes. Creating profiles from workflow runs (`persist_browser_session`) is unaffected.
</Warning>
---
## Close a session
Close a session to release resources and stop billing. The browser shuts down immediately.
@ -732,7 +694,7 @@ Skyvern also offers [Browser Profiles](/developers/optimization/browser-profiles
| **Best for** | Back-to-back tasks, human-in-the-loop, real-time agents | Repeated logins, scheduled agents, shared auth state |
<Tip>
You can create a [Browser Profile](/developers/optimization/browser-profiles) from a completed session to save its authenticated state for future reuse. The session must have [`generate_browser_profile` enabled](#save-a-sessions-profile) or have been started from a profile.
You can create a [Browser Profile](/developers/optimization/browser-profiles) from a completed session to save its authenticated state for future reuse.
</Tip>
---

View file

@ -13,11 +13,11 @@ keywords:
- budget
---
Skyvern Cloud uses a **credit-based billing model**. Free accounts include a one-time credit grant. Paid plans include a monthly credit allowance that determines how many actions you can run.
Skyvern Cloud uses a **credit-based billing model**. Each plan includes a monthly credit allowance that determines how many actions you can run.
| Plan | Price | Actions Included |
|------|-------|------------------|
| Free | $0 | ~200 |
| Free | $0 | ~170 |
| Hobby | $29 | ~1,200 |
| Pro | $149 | ~6,200 |
| Enterprise | Custom | Unlimited |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View file

@ -19,7 +19,7 @@ profile = await client.create_browser_profile(
name="production-login",
workflow_run_id="wr_abc123",
)
print(profile.browser_profile_id) # bp_abc123
print(profile.browser_profile_id) # bpf_abc123
```
```typescript TypeScript
@ -27,7 +27,7 @@ const profile = await skyvern.createBrowserProfile({
name: "production-login",
workflow_run_id: "wr_abc123",
});
console.log(profile.browser_profile_id); // bp_abc123
console.log(profile.browser_profile_id); // bpf_abc123
```
</CodeGroup>
@ -38,7 +38,7 @@ console.log(profile.browser_profile_id); // bp_abc123
| `name` | `str` | Yes | Display name for the profile. |
| `description` | `str` | No | Optional description. |
| `workflow_run_id` | `str` | Conditional | The workflow run ID to snapshot. The run must have used `persist_browser_session=True`. Required if `browser_session_id` is not provided. |
| `browser_session_id` | `str` | Conditional | The browser session ID to snapshot. The session must have had `generate_browser_profile` enabled or been started from a saved profile. Required if `workflow_run_id` is not provided. |
| `browser_session_id` | `str` | Conditional | The browser session ID to snapshot. Required if `workflow_run_id` is not provided. |
| `request_options` | `RequestOptions` | No | Per-request configuration (see below). |
You must provide either `workflow_run_id` or `browser_session_id`.
@ -47,7 +47,7 @@ You must provide either `workflow_run_id` or `browser_session_id`.
| Field | Type | Description |
|-------|------|-------------|
| `browser_profile_id` | `str` | Unique ID. Starts with `bp_`. |
| `browser_profile_id` | `str` | Unique ID. Starts with `bpf_`. |
| `name` | `str` | Profile name. |
| `description` | `str \| None` | Profile description. |
| `created_at` | `datetime` | When the profile was created. |
@ -105,7 +105,7 @@ const result = await skyvern.runWorkflow({
</CodeGroup>
<Info>
Session archiving is asynchronous. If `create_browser_profile` fails immediately after a run or session ends, wait a few seconds and retry. A `400` error saying the session was not configured to generate a browser profile is permanent — recreate the session with `generate_browser_profile` enabled. See [Save a session's profile](/developers/optimization/browser-sessions#save-a-sessions-profile).
Session archiving is asynchronous. If `create_browser_profile` fails immediately after a workflow completes, wait a few seconds and retry.
</Info>
---

View file

@ -7,11 +7,11 @@ Delete a browser profile.
<CodeGroup>
```python Python
await client.delete_browser_profile("bp_abc123")
await client.delete_browser_profile("bpf_abc123")
```
```typescript TypeScript
await skyvern.deleteBrowserProfile("bp_abc123");
await skyvern.deleteBrowserProfile("bpf_abc123");
```
</CodeGroup>

View file

@ -7,12 +7,12 @@ Get a single profile by ID.
<CodeGroup>
```python Python
profile = await client.get_browser_profile("bp_abc123")
profile = await client.get_browser_profile("bpf_abc123")
print(profile.name)
```
```typescript TypeScript
const profile = await skyvern.getBrowserProfile("bp_abc123");
const profile = await skyvern.getBrowserProfile("bpf_abc123");
console.log(profile.name);
```
</CodeGroup>

View file

@ -33,14 +33,9 @@ console.log(session.browser_session_id); // pbs_abc123
| `proxy_location` | `ProxyLocation` | No | `None` | Route browser traffic through a geographic proxy. |
| `extensions` | `list[Extensions]` | No | `None` | Browser extensions to install. Options: `"ad-blocker"`, `"captcha-solver"`. |
| `browser_type` | `PersistentBrowserType` | No | `None` | Browser type. Options: `"chrome"`, `"msedge"`. |
| `browser_profile_id` | `str` | No | `None` | Load a browser profile (cookies, localStorage) into this session. ID starts with `bp_`. |
| `generate_browser_profile` | `bool` | No | `False` | REST API only for now — not yet accepted by the SDK methods (see note below). Save the session's browser profile when it ends so it can be turned into a reusable [browser profile](/sdk-reference/browser-profiles/create-browser-profile). Sessions started with `browser_profile_id` always save theirs. |
| `browser_profile_id` | `str` | No | `None` | Load a browser profile (cookies, localStorage) into this session. ID starts with `bpf_`. |
| `request_options` | `RequestOptions` | No | | Per-request configuration (see below). |
<Note>
`generate_browser_profile` is not yet accepted as a keyword by the SDK methods (watch the [changelog](/changelog) for SDK availability). Until SDK support lands, pass it from Python via `request_options` with `additional_body_parameters={"generate_browser_profile": True}` (see [Request options](#request-options) below); from TypeScript, call the REST API directly. You can also toggle it on a live session via `PATCH /v1/browser_sessions/{browser_session_id}` — the value is read when the session ends. See [Save a session's profile](/developers/optimization/browser-sessions#save-a-sessions-profile).
</Note>
### Returns `BrowserSessionResponse`
| Field | Type | Description |

View file

@ -26,7 +26,7 @@ for (const c of creds) {
|-----------|------|----------|---------|-------------|
| `page` | `int` | No | `None` | Page number. |
| `page_size` | `int` | No | `None` | Results per page. |
| `vault_type` | `CredentialVaultType` | No | `None` | Filter credentials by vault type (e.g. `"skyvern"`, `"custom"`, `"bitwarden"`, `"azure_vault"`). |
| `vault_type` | `CredentialVaultType` | No | `None` | Filter credentials by vault type (e.g. `"custom"`, `"bitwarden"`, `"azure_vault"`). |
| `request_options` | `RequestOptions` | No | `None` | Per-request configuration (see below). |
### Returns `list[CredentialResponse]`

View file

@ -56,7 +56,7 @@ for (const f of result.downloaded_files ?? []) {
| `max_steps_per_run` | `int` | No | `None` | Cap AI steps. |
| `extra_http_headers` | `dict[str, str]` | No | `None` | Additional HTTP headers. |
| `totp_identifier` | `str` | No | `None` | Identifier for TOTP verification. |
| `totp_url` | `str` | No | `None` | Endpoint Skyvern polls to fetch TOTP codes. |
| `totp_url` | `str` | No | `None` | URL to receive TOTP codes. |
| `browser_address` | `str` | No | `None` | Connect to a browser at this CDP address. |
| `max_screenshot_scrolling_times` | `int` | No | `None` | Number of screenshot scrolls. |
| `waitForCompletion` (TS only) | `boolean` | No | `false` | Block until the download finishes. |

View file

@ -48,7 +48,7 @@ console.log(result.status);
| `proxy_location` | `ProxyLocation` | No | `None` | Route browser traffic through a geographic proxy. |
| `webhook_url` | `str` | No | `None` | URL to receive a POST when the login finishes. |
| `totp_identifier` | `str` | No | `None` | Identifier for TOTP verification. |
| `totp_url` | `str` | No | `None` | Endpoint Skyvern polls to fetch TOTP codes. |
| `totp_url` | `str` | No | `None` | URL to receive TOTP codes. |
| `wait_for_completion` (Python) / `waitForCompletion` (TS) | `bool` | No | `False` | Block until the login finishes. |
| `timeout` | `float` | No | `1800` | Max wait time in seconds. |
| `extra_http_headers` | `dict[str, str]` | No | `None` | Additional HTTP headers. |

View file

@ -54,7 +54,7 @@ console.log(result.output);
| `webhook_url` | `str` | No | `None` | URL to receive a POST when the run finishes. |
| `error_code_mapping` | `dict[str, str]` | No | `None` | Map custom error codes to failure reasons. |
| `totp_identifier` | `str` | No | `None` | Identifier for TOTP verification. |
| `totp_url` | `str` | No | `None` | Endpoint Skyvern polls to fetch TOTP codes. |
| `totp_url` | `str` | No | `None` | URL to receive TOTP codes. |
| `title` | `str` | No | `None` | Display name for this run in the dashboard. |
| `model` | `dict` | No | `None` | Override the output model definition. |
| `user_agent` | `str` | No | `None` | Custom User-Agent header for the browser. |

View file

@ -50,7 +50,7 @@ console.log(result.output);
| `webhook_url` | `str` | No | `None` | URL to receive a POST when the run finishes. |
| `title` | `str` | No | `None` | Display name for this run in the dashboard. |
| `totp_identifier` | `str` | No | `None` | Identifier for TOTP verification. |
| `totp_url` | `str` | No | `None` | Workflow-level TOTP endpoint metadata. For saved agents/workflows, set **TOTP Verification URL** on the block that may encounter 2FA, or template that block field from a workflow parameter. |
| `totp_url` | `str` | No | `None` | URL to receive TOTP codes. |
| `template` | `bool` | No | `None` | Run a template workflow. |
| `user_agent` | `str` | No | `None` | Custom User-Agent header for the browser. |
| `extra_http_headers` | `dict[str, str]` | No | `None` | Additional HTTP headers injected into every browser request. |
@ -121,7 +121,7 @@ result = await client.run_workflow(
```python Python
result = await client.run_workflow(
workflow_id="wpid_daily_report",
browser_profile_id="bp_abc123",
browser_profile_id="bpf_abc123",
wait_for_completion=True,
)
```
@ -130,7 +130,7 @@ result = await client.run_workflow(
const result = await skyvern.runWorkflow({
body: {
workflow_id: "wpid_daily_report",
browser_profile_id: "bp_abc123",
browser_profile_id: "bpf_abc123",
},
waitForCompletion: true,
});

View file

@ -64,16 +64,6 @@ codex mcp get skyvern
If you previously configured Skyvern with an `x-api-key` header, run `codex mcp remove skyvern` first so Codex does not skip the OAuth handshake. You do not need to run `codex mcp login skyvern` after `add` — the OAuth flow runs inline. Use `codex mcp login skyvern` only to re-authenticate after a token expires or to request custom `--scopes`.
<Note>
If `codex mcp add` fails with `resource is not allowed for this authorization server`, your Codex version derived a trailing-slash `resource` from the URL. Pass the slashless resource explicitly:
```bash
codex mcp add skyvern --url https://api.skyvern.com/mcp/ --oauth-resource https://api.skyvern.com/mcp
```
Skyvern Cloud accepts both forms, so the flag is only needed on older self-hosted deployments.
</Note>
</Tab>
<Tab title="Cursor">
@ -140,19 +130,18 @@ claude mcp add-json skyvern '{"type":"http","url":"https://api.skyvern.com/mcp/"
3. When Claude Desktop opens the Skyvern installer preview, click **Install**.
4. In the **Configure Skyvern** dialog, paste your Skyvern API key.
5. Leave **Skyvern MCP URL** set to `https://api.skyvern.com/mcp/` unless you are connecting to a different deployment, then click **Save**.
6. In Claude, ask: "Create a Skyvern browser session". A working setup returns a Skyvern browser session id.
If double-clicking is blocked by your system, you can instead open **Claude Desktop > Settings > Extensions > Install Extension** and select the downloaded file manually.
The bundle defaults to `https://api.skyvern.com/mcp/` and removes the separate Node.js requirement.
<Note>
If Claude says Skyvern "needs authorization" or mentions OAuth, the API key is not configured. Open Claude Desktop **Settings -> Extensions -> Skyvern -> Configure**, paste your Skyvern API key, and save.
</Note>
**Linux, self-hosted, or advanced fallback (requires Node.js >= 20)**
**Linux manual config (requires Node.js >= 20)**
Add this manual bridge to your Claude Desktop config file:
On Linux, add this manual bridge to `~/.config/Claude/claude_desktop_config.json`:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
@ -170,7 +159,7 @@ On Linux, add this manual bridge to `~/.config/Claude/claude_desktop_config.json
}
```
Use this Linux path when the one-click bundle is not available on your platform. It uses [mcp-remote](https://www.npmjs.com/package/mcp-remote) to bridge the remote MCP server to stdio and requires Node.js >= 20.
Use this fallback for Linux or any Claude Desktop setup where the one-click bundle is not available. It uses [mcp-remote](https://www.npmjs.com/package/mcp-remote) to bridge the remote MCP server to stdio and requires Node.js >= 20.
</Tab>
<Tab title="Cursor">

View file

@ -28,11 +28,8 @@ VITE_ENABLE_2FA_NOTIFICATIONS="${VITE_ENABLE_2FA_NOTIFICATIONS:-false}"
# 1. Environment variable (from .env file or docker-compose environment),
# but only if it's not a placeholder/sentinel value. Both the Dockerfile
# default (__SKYVERN_API_KEY_PLACEHOLDER__) and the .env.example default
# (YOUR_API_KEY) can leak through if users skip configuration. Keep these
# sentinels aligned with the frontend's placeholder check in
# skyvern-frontend/src/util/env.ts (which matches the Docker sentinel by
# its _PLACEHOLDER__ suffix — the sed below rewrites every occurrence of
# the full sentinel in the bundle, so the frontend must never spell it out).
# (YOUR_API_KEY) can leak through if users skip configuration. Keep this
# filter list aligned with the frontend's PLACEHOLDER_VALUES.
# 2. Generated credentials file from the backend first-run setup
# (volume-mounted at /app/.skyvern/credentials.toml in docker-compose)
VITE_SKYVERN_API_KEY="${VITE_SKYVERN_API_KEY:-}"

View file

@ -12,32 +12,16 @@ Browser Profiles capture the full browser state (cookies, storage, session files
- Want to fan out multiple workflows that all reuse the same authenticated browser state.
- Need an audit-friendly way to persist state separate from long-lived browser sessions.
Whereas a **browser session** is a temporary, real-time browser, a **browser profile** is a reusable profile archive. You can create one blank, from a browser session, or from a workflow run that persisted its session. Profiles are scoped to your organization and can be reused as often as you need.
Whereas a **browser session** is a temporary, real-time browser, a **browser profile** is a reusable snapshot created from that session (or from a workflow run that persisted its session). Profiles are scoped to your organization and can be reused as often as you need.
## How Browser Profiles Work
1. Create a blank profile with only `name`, or run a workflow with `persist_browser_session: true` **or** open a persistent browser session.
2. For workflow and browser-session sources, close the workflow run or browser session so Skyvern can upload the archived state.
1. Run a workflow with `persist_browser_session: true` **or** open a persistent browser session.
2. Close the workflow run or browser session so Skyvern can upload the archived state.
3. Create a browser profile from that source once the upload finishes.
4. Pass `browser_profile_id` to subsequent workflow runs to start from the saved profile archive.
4. Pass `browser_profile_id` to subsequent workflow runs to restore the saved state instantly.
## Option A — Create a Blank Profile
Use this approach when you want a reusable profile ID before any browsing has happened. Skyvern seeds the profile from the configured default browser profile directory when available, with a minimal loadable profile skeleton as a fallback.
```python
from skyvern import Skyvern
skyvern = Skyvern(api_key="YOUR_API_KEY")
browser_profile = await skyvern.browser_profiles.create_browser_profile(
name="fresh-profile",
description="Blank profile for future runs",
)
print(browser_profile.browser_profile_id)
```
## Option B — Create a Profile from a Workflow Run
## Option A — Create a Profile from a Workflow Run
Use this approach when you already ran a workflow with `persist_browser_session: true`. Always poll the run (e.g., `GET /v1/runs/{run_id}`) until it reaches `status = "completed"` before attempting to create the profile—Skyvern only begins uploading the archived browser state once the run finishes.
@ -83,7 +67,7 @@ async def create_profile_with_retry(
raise RuntimeError("Session never finished uploading a profile archive")
```
## Option C — Create a Profile from a Browser Session
## Option B — Create a Profile from a Browser Session
If you opened a persistent browser session directly (without a workflow), close it and then create a profile using the session ID:
@ -116,3 +100,4 @@ print(workflow_run.run_id)
```
If the workflow also sets `persist_browser_session: true`, you can capture a fresh profile again at the end and keep the cycle going.

View file

@ -15,7 +15,7 @@ Skyvern supports logging into websites that require a 2FA/MFA/Verification code.
Step 1: Save your username and password in [Skyvern Credential](https://app.skyvern.com/credentials). See [Password Management](/credentials/passwords#manage-passwords-in-skyvern-cloud) for more details.
Step 2: Add your account by entering the secret key, pasting the full `otpauth://` URI, or clicking **Scan QR** to upload a QR code image from the site's 2FA setup screen. Not sure how to get the key? [Follow this guide](https://bitwarden.com/help/integrated-authenticator/).
Step 2: Add your account by manually entering the secret key (extracted from the QR code). Not sure how to get it? [Follow this guide](https://bitwarden.com/help/integrated-authenticator/).
> 💡 Don't have the key? Contact [Skyvern Support](mailto:support@skyvern.com) and we can help you get it.
@ -228,4 +228,4 @@ curl -X GET "https://api.skyvern.com/v1/credentials/totp?totp_identifier=user@ex
- `otp_type` *(optional)* filter on `totp` or `magic_link`.
- `limit` *(optional)* number of records to return (default `50`, maximum `200`).
The response is a list of TOTP objects, ordered newest first and capped by `limit`. Runtime polling still only uses unexpired codes within `TOTP_LIFESPAN_MINUTES`.
The response is a list of TOTP objects, ordered newest first. Skyvern only returns codes created within the last `TOTP_LIFESPAN_MINUTES` (10 minutes by default).

View file

@ -7,9 +7,6 @@ mode: custom
<div className="cl-page">
<div className="cl-sidebar">
<p className="cl-sidebar-title">TIMELINE</p>
<a href="#week-of-june-30%2C-2026" className="cl-sidebar-link">Week of Jun 30, 2026</a>
<a href="#week-of-june-22%2C-2026" className="cl-sidebar-link">Week of Jun 22, 2026</a>
<a href="#week-of-june-15%2C-2026" className="cl-sidebar-link">Week of Jun 15, 2026</a>
<a href="#week-of-june-8%2C-2026" className="cl-sidebar-link">Week of Jun 8, 2026</a>
<a href="#week-of-june-1%2C-2026" className="cl-sidebar-link">Week of Jun 1, 2026</a>
<a href="#week-of-may-25%2C-2026" className="cl-sidebar-link">Week of May 25, 2026</a>
@ -20,157 +17,19 @@ mode: custom
<a href="#week-of-april-21%2C-2026" className="cl-sidebar-link">Week of Apr 21, 2026</a>
<a href="#week-of-april-14%2C-2026" className="cl-sidebar-link">Week of Apr 14, 2026</a>
<a href="#week-of-march-30%2C-2026" className="cl-sidebar-link">Week of Mar 30, 2026</a>
<a href="#week-of-february-23%2C-2026" className="cl-sidebar-link">Week of Feb 23, 2026</a>
<a href="#week-of-february-16%2C-2026" className="cl-sidebar-link">Week of Feb 16, 2026</a>
<a href="#v1-0-22-—-february-26%2C-2026" className="cl-sidebar-link">v1.0.22 — Feb 26, 2026</a>
<a href="#v1-0-21-—-february-24%2C-2026" className="cl-sidebar-link">v1.0.21 — Feb 24, 2026</a>
<a href="#v1-0-18-—-february-19%2C-2026" className="cl-sidebar-link">v1.0.18 — Feb 19, 2026</a>
<a href="#v1-0-17-—-february-19%2C-2026" className="cl-sidebar-link">v1.0.17 — Feb 19, 2026</a>
<a href="#v1-0-16-—-february-19%2C-2026" className="cl-sidebar-link">v1.0.16 — Feb 19, 2026</a>
<a href="#v1-0-15-—-february-17%2C-2026" className="cl-sidebar-link">v1.0.15 — Feb 17, 2026</a>
</div>
<div className="cl-content">
<h1 className="cl-title">Changelog</h1>
<p className="cl-subtitle">New features, improvements, and fixes in Skyvern</p>
<Update label="Week of June 30, 2026" description="Workflow Studio UX improvements, faster enrichment, code-block self-heal, extraction accuracy, bug fixes">
## Improvements
- **Block Type Shown in Workflow Studio Header** — The block detail panel in Workflow Studio now displays the block type in the header, making it easier to identify what kind of block you're editing at a glance. ([#6956](https://github.com/Skyvern-AI/skyvern/pull/6956))
- **Improved Extraction Accuracy for Absent Data** — The agent now better handles cases where requested output is genuinely absent from the page, reducing false positives in extraction results. ([#6957](https://github.com/Skyvern-AI/skyvern/pull/6957))
- **Data-Only Validation Skips Page Evidence** — Validation steps that check only extracted data no longer capture page screenshots when they aren't needed, reducing unnecessary work during runs. ([#6960](https://github.com/Skyvern-AI/skyvern/pull/6960))
- **Copilot Block Runs Routed to UI Worker** — Copilot block runs are now dispatched to a dedicated UI worker, keeping the editor responsive during live automation. ([#6962](https://github.com/Skyvern-AI/skyvern/pull/6962))
- **Clearer Invalid JSON Errors in Workflow Editor** — The workflow editor now surfaces a descriptive error when a parameter contains invalid JSON, making it faster to diagnose and fix configuration mistakes. ([#6963](https://github.com/Skyvern-AI/skyvern/pull/6963))
- **"Ignore System Prompt" Moved to Advanced Settings** — The "Ignore system prompt" option has been relocated to the advanced settings panel, reducing clutter in the default block configuration view. ([#6964](https://github.com/Skyvern-AI/skyvern/pull/6964))
- **Empty Extra HTTP Headers Hidden** — Extra HTTP header fields that are empty no longer appear in block configurations, reducing visual noise in the workflow editor. ([#6966](https://github.com/Skyvern-AI/skyvern/pull/6966))
- **Block Selection Persisted in URL** — The selected block in Workflow Studio is now encoded in the URL, so your selection is preserved on reload and can be shared via link. ([#6969](https://github.com/Skyvern-AI/skyvern/pull/6969))
- **Code-Block Self-Heal Minimum Retry Floor** — The code-block self-healing loop now enforces a minimum number of repair attempts before giving up, preventing premature failures on recoverable errors. ([#6971](https://github.com/Skyvern-AI/skyvern/pull/6971))
- **Faster Live-Draft Enrichment** — Workflow recording live-draft enrichment now uses a faster model and a shorter debounce interval, making draft suggestions appear noticeably sooner while recording. ([#6973](https://github.com/Skyvern-AI/skyvern/pull/6973))
## Bug fixes
- Fixed syntax highlighting disappearing when viewing large webhook payloads. ([#6967](https://github.com/Skyvern-AI/skyvern/pull/6967))
- Fixed LLM blocks not enforcing their configured output schema, which could allow malformed data to propagate to downstream blocks. ([#6965](https://github.com/Skyvern-AI/skyvern/pull/6965))
</Update>
<Update label="Week of June 22, 2026" description="Workflow Studio preview, new models, label management, Gmail OTP, Copilot improvements, analytics filters, bug fixes">
## New features
- **Gmail-Backed OTP Lookup** — Agents can now retrieve one-time passwords from Gmail via OAuth, enabling automated login flows that depend on email-delivered verification codes. ([#6769](https://github.com/Skyvern-AI/skyvern/pull/6769))
- **Copilot Chat History per Workflow** — The Copilot now retains its full conversation history scoped to each individual workflow, so prior context and decisions are preserved as you continue building. ([#6767](https://github.com/Skyvern-AI/skyvern/pull/6767))
- **Code-First Editor Polish** — Code-first code blocks now display block labels, correctly render extraction output, and include a Build-vs-Code toggle for switching between visual and code editing modes. ([#6771](https://github.com/Skyvern-AI/skyvern/pull/6771))
- **Workflow Studio (Preview)** — A redesigned workflow editor with a refreshed build canvas, integrated browser view, and run viewer is now available as an opt-in preview, giving teams an early look at the next-generation authoring experience. ([#6782](https://github.com/Skyvern-AI/skyvern/pull/6782))
- **DeepSeek and Xiaomi MiMo Models** — DeepSeek and Xiaomi MiMo are now available in the model dropdown for agent runs. Gemini 2.5 Pro and 2.5 Flash have been deprecated. ([#6831](https://github.com/Skyvern-AI/skyvern/pull/6831))
- **Label Management Settings** — Labels can now be renamed, recolored, and soft-deleted directly from the Settings page, making it easier to maintain a clean tag taxonomy. ([#6830](https://github.com/Skyvern-AI/skyvern/pull/6830))
- **Browser Profiles in MCP Sessions** — Browser sessions initiated via MCP can now be associated with a browser profile, enabling credential and state reuse across MCP-driven runs. ([#6798](https://github.com/Skyvern-AI/skyvern/pull/6798))
- **Workflow-Tag Filter on Analytics** — The analytics dashboard now supports filtering runs and metrics by workflow tag. ([#6780](https://github.com/Skyvern-AI/skyvern/pull/6780))
## Improvements
- **Copilot Repair Shows Quiet Progress** — The Copilot's code-seam repair steps now appear as compact progress indicators instead of repeated chat messages, keeping the conversation readable during long repairs. ([#6746](https://github.com/Skyvern-AI/skyvern/pull/6746))
- **Collapsible Sidebar with Avatar** — The left sidebar now features an edge toggle, a closed-state user avatar, and a smooth open/close animation for a more polished navigation experience. ([#6754](https://github.com/Skyvern-AI/skyvern/pull/6754))
- **Structured Output Verified Before Completion** — Runs that produce structured data now verify the output satisfies the configured schema before marking the run as successfully complete. ([#6751](https://github.com/Skyvern-AI/skyvern/pull/6751))
- **MCP Workflow Overlap Settings Preserved** — Overlap settings configured via MCP are now retained correctly when the workflow is subsequently edited, preventing unintentional resets. ([#6748](https://github.com/Skyvern-AI/skyvern/pull/6748))
- **Browser Profile List Sorted Newest First** — The browser profile picker now orders profiles with the most recently created at the top, making recent profiles easier to find and select. ([#6739](https://github.com/Skyvern-AI/skyvern/pull/6739))
- **Remote Browser Sessions Capped at Four Hours** — Remote browser sessions now have a maximum duration of four hours, preventing orphaned sessions from consuming resources indefinitely.
- **Debug Sessions Stay Open After Runs** — Browser sessions launched from the workflow editor in debug mode now remain alive after a run completes, so you can inspect the final page state without the session closing immediately. ([#6832](https://github.com/Skyvern-AI/skyvern/pull/6832))
- **GPT-5 Mini via OpenRouter** — GPT-5 Mini is now available as a selectable model via OpenRouter. ([#6816](https://github.com/Skyvern-AI/skyvern/pull/6816))
- **Extraction and AI Steps in Code-Block View** — Extraction and AI steps are now surfaced in the code-block run view, filling a gap in execution visibility. ([#6823](https://github.com/Skyvern-AI/skyvern/pull/6823))
- **Color Palette for Grouped Workflow Tags** — Tags that belong to a group are now displayed with a curated color palette, making it easier to distinguish tag groups at a glance. ([#6801](https://github.com/Skyvern-AI/skyvern/pull/6801))
- **Improved Authenticator 2FA Credential Setup** — The credential setup flow for authenticator-based 2FA has been improved with clearer guidance and a smoother configuration experience. ([#6783](https://github.com/Skyvern-AI/skyvern/pull/6783))
- **Copilot Data-Write Blocks Default to Safe Failure Mode** — New Copilot-generated data-write blocks now default to `continue_on_failure=false`, preventing downstream blocks from running on bad data. ([#6779](https://github.com/Skyvern-AI/skyvern/pull/6779))
- **End-State Screenshots for All Open Tabs** — Task runs now capture end-state screenshots for every open tab at completion, giving a full snapshot of the browser state. ([#6777](https://github.com/Skyvern-AI/skyvern/pull/6777))
- **Partial Deliverable on Budget Exhaustion** — Tasks that hit their credit budget now return the best-effort deliverable collected so far instead of failing without output. ([#6778](https://github.com/Skyvern-AI/skyvern/pull/6778))
- **Planner Supports EXTRACT_INFORMATION and GOTO_URL** — The task planner can now emit EXTRACT_INFORMATION and GOTO_URL actions, expanding the set of strategies available during automated planning. ([#6800](https://github.com/Skyvern-AI/skyvern/pull/6800))
- **Copilot Completion Recognition Improvements** — The Copilot completion recognizer is now more authoritative and evidence-backed, with stable criteria counts and more reliable recognition across runs. ([#6789](https://github.com/Skyvern-AI/skyvern/pull/6789))
- **Copilot Code Repair Loop Reliability** — The Copilot code authoring repair loop is now more reliable, reducing cases where stuck repairs would stall progress. ([#6792](https://github.com/Skyvern-AI/skyvern/pull/6792))
- **Workflow Studio Browser and Panel Improvements** — The Workflow Studio editor now supports take-control and viewport centering for the live browser, with alignment and toolbar polish for the run/editor panels. ([#6810](https://github.com/Skyvern-AI/skyvern/pull/6810))
- **Analytics Tag Filters Accept Flexible Input Shapes** — Analytics tag filters now accept labels, groups, or `group:label` pairs in all three supported filter shapes. ([#6802](https://github.com/Skyvern-AI/skyvern/pull/6802))
- **Run ID Search Fallback on Runs List** — The runs list now falls back to searching by run ID when no parameter-value match is found, making it easier to locate specific runs. ([#6788](https://github.com/Skyvern-AI/skyvern/pull/6788))
## Bug fixes
- Fixed login credentials not being reattached after editing a workflow via MCP, which could cause credential-gated runs to fail. ([#6761](https://github.com/Skyvern-AI/skyvern/pull/6761))
- Fixed conditional routing logic not being preserved when loading a workflow stored in the v1 format. ([#6760](https://github.com/Skyvern-AI/skyvern/pull/6760))
- Fixed the active run context being lost when switching between organizations, requiring a page refresh to restore the correct run view. ([#6753](https://github.com/Skyvern-AI/skyvern/pull/6753))
- Fixed browser connection settings defined in a TaskV2 block not being forwarded to child task runs. ([#6750](https://github.com/Skyvern-AI/skyvern/pull/6750))
- Fixed telephone number inputs dropping digits when bare NANP-format phone numbers were typed. ([#6826](https://github.com/Skyvern-AI/skyvern/pull/6826))
- Fixed the workflow builder freezing due to an infinite FlowRenderer dimension/layout feedback loop. ([#6824](https://github.com/Skyvern-AI/skyvern/pull/6824))
- Fixed the code editor crashing with a stack overflow when opened on large or deeply-nested JSON. ([#6817](https://github.com/Skyvern-AI/skyvern/pull/6817))
- Fixed the app failing to load after a deploy due to stale webpack chunks; the app now auto-recovers via a cache-busting reload. ([#6818](https://github.com/Skyvern-AI/skyvern/pull/6818))
- Fixed runs returning an incorrect terminal outcome when the extraction schema had been modified after the initial recording. ([#6815](https://github.com/Skyvern-AI/skyvern/pull/6815))
- Fixed MCP workflow edits dropping non-credential parameter-to-block links. ([#6813](https://github.com/Skyvern-AI/skyvern/pull/6813))
- Fixed address bar paste not working in the VNC streaming view. ([#6812](https://github.com/Skyvern-AI/skyvern/pull/6812))
- Fixed newly added workflow blocks not receiving focus in the editor. ([#6809](https://github.com/Skyvern-AI/skyvern/pull/6809))
- Fixed Copilot code-block file downloads being written to the wrong directory. ([#6797](https://github.com/Skyvern-AI/skyvern/pull/6797))
- Fixed parameter-value search not appearing in the v2 runs list. ([#6796](https://github.com/Skyvern-AI/skyvern/pull/6796))
- Fixed extra actions firing after selecting a combobox option during text entry. ([#6787](https://github.com/Skyvern-AI/skyvern/pull/6787))
- Fixed shadow DOM text nodes being missed during element text extraction. ([#6791](https://github.com/Skyvern-AI/skyvern/pull/6791))
- Fixed persistent browser sessions incorrectly reporting a cached session as live when it had become stale.
</Update>
<Update label="Week of June 15, 2026" description="Code-first code block editor, analytics drill-down, copilot repair loop, OTP fill, uncapped runtimes, voice input fixes">
## New features
- **OTP Fill at Runtime in Code-Block Sandbox** — Code blocks can now fill OTP fields using your stored TOTP credentials at runtime, enabling fully automated two-factor authentication flows without manual input. ([#6571](https://github.com/Skyvern-AI/skyvern/pull/6571))
- **Uncapped Workflow Runtime** — Workflows can now be configured to run without a maximum time limit, enabling long-running automations that previously timed out. ([#6567](https://github.com/Skyvern-AI/skyvern/pull/6567))
- **Code-First Code Block Editor** — Code blocks now feature a dedicated code editor with syntax highlighting, Jinja parameter hints, and a run timeline showing each action taken, giving developers full visibility and control over code-driven automations. ([#6604](https://github.com/Skyvern-AI/skyvern/pull/6604))
- **Period-Aware Analytics Drill-Down** — The agent analytics drill-down panel now respects the selected time period (7/30/90/365 days or custom range) and shows per-agent average duration, credits per run, and period-over-period trends.
- **Agent Directory Tree** — The agents and folders list has been redesigned as a navigable directory tree, making it easier to browse and organize agents across nested folder hierarchies. ([#6631](https://github.com/Skyvern-AI/skyvern/pull/6631))
- **GCP Self-Host: Storage and Vault** — Self-hosted deployments on Google Cloud Platform now support GCS artifact storage and GCP Secret Manager vault, enabling full GCP-native deployments. ([#6632](https://github.com/Skyvern-AI/skyvern/pull/6632))
- **1Password Credential Picker** — Credential inputs now include an item picker for 1Password vaults, letting you browse and select vault items directly during credential setup. ([#6651](https://github.com/Skyvern-AI/skyvern/pull/6651))
- **Typed Extraction Schema for Copilot Code Blocks** — Copilot-generated code blocks now support a typed data extraction schema (matching `data_schema` parity), enabling structured data output from code-block automations. ([#6674](https://github.com/Skyvern-AI/skyvern/pull/6674))
- **SDK-Backed Remote Browser Provider** — Agents can now connect to external browser providers via SDK, enabling tighter integration with third-party remote browser services. ([#6687](https://github.com/Skyvern-AI/skyvern/pull/6687))
- **Bitwarden Credential Integration** — Saved credentials can now be sourced directly from Bitwarden vaults, bringing Bitwarden to full UI and functional parity with other supported credential providers. ([#6729](https://github.com/Skyvern-AI/skyvern/pull/6729))
## Improvements
- **Smarter Loop Planning for Repeated Work** — The task planner now prefers loop-based tasks when it detects repeated same-shaped operations, improving throughput on multi-item automation runs. ([#6575](https://github.com/Skyvern-AI/skyvern/pull/6575))
- **Agent Auto-Acts on Stalled Pages** — When the agent re-evaluates a page that hasn't changed, it now applies steering heuristics and acts immediately instead of stalling. ([#6577](https://github.com/Skyvern-AI/skyvern/pull/6577))
- **Final Answer Surfaced Directly** — The task agent now returns the final deliverable directly instead of spinning through redundant synthesis steps, reducing latency on completed tasks. ([#6574](https://github.com/Skyvern-AI/skyvern/pull/6574))
- **Copilot Stops Stuck Repair Loops** — The Workflow Copilot now detects when consecutive repairs make no verified progress, halts after three attempts, and presents an honest escalation with the draft preserved for review. ([#6608](https://github.com/Skyvern-AI/skyvern/pull/6608))
- **Voice Input Microphone Permission** — The browser microphone permission prompt now appears correctly when using voice dictation in the Prompt Box and Workflow Copilot. ([#6601](https://github.com/Skyvern-AI/skyvern/pull/6601))
- **`page.evaluate` Recorded in Code Block Timeline** — JavaScript `page.evaluate(...)` calls made inside code blocks are now recorded as actions in the run timeline, filling a gap in execution visibility. ([#6600](https://github.com/Skyvern-AI/skyvern/pull/6600))
- **Active Run Shown on Browser Session Page** — Browser session detail pages now display which workflow run currently occupies the session, making it easier to understand session usage. ([#6691](https://github.com/Skyvern-AI/skyvern/pull/6691))
- **Distinct Visual for Terminate Actions** — Terminate actions now appear with a distinct amber triangle icon in the run timeline, making it easy to spot where a run was stopped. ([#6693](https://github.com/Skyvern-AI/skyvern/pull/6693))
- **PDF Field Mapping by Positional Labels** — PDF form filling now uses positional field labels to improve accuracy when mapping data to form fields with ambiguous names. ([#6663](https://github.com/Skyvern-AI/skyvern/pull/6663))
- **MiMo-V2.5 Model Support via OpenRouter** — Added Xiaomi MiMo-V2.5 as a selectable LLM via OpenRouter for agent runs. ([#6636](https://github.com/Skyvern-AI/skyvern/pull/6636))
- **Settings "API Keys" Renamed to "General"** — The "API Keys" section in Settings has been renamed to "General" to better reflect its full scope of configuration options. ([#6642](https://github.com/Skyvern-AI/skyvern/pull/6642))
- **Run URLs Surfaced in MCP and CLI** — The agent app URL and session recording URLs are now included in run responses returned via MCP and the CLI, making it easier to link directly to a live run. ([#6719](https://github.com/Skyvern-AI/skyvern/pull/6719))
- **Email OTP Credentials Supported in Copilot** — The Copilot can now use stored email OTP credentials when generating and validating code-block automations that require email-based authentication. ([#6716](https://github.com/Skyvern-AI/skyvern/pull/6716))
- **Auto-Fill Default Google Account for Sheets Blocks** — Google Sheets blocks now automatically select the default linked Google account, reducing manual setup when configuring Sheets integrations. ([#6715](https://github.com/Skyvern-AI/skyvern/pull/6715))
- **Copilot Output Accurately Reflects Verified Results** — The Copilot no longer under-reports successfully verified output, ensuring the run summary accurately reflects what was accomplished. ([#6710](https://github.com/Skyvern-AI/skyvern/pull/6710))
- **Runs Halt Reliably on Terminal Blockers** — When the agent encounters strong evidence of an unresolvable blocker (such as a persistent challenge page), it now stops immediately rather than continuing to attempt further actions. ([#6714](https://github.com/Skyvern-AI/skyvern/pull/6714))
- **Workflow Save Performance Improved** — Cache invalidation on workflow save is now batched, eliminating a per-block N+1 query pattern that slowed saves for large workflows. ([#6718](https://github.com/Skyvern-AI/skyvern/pull/6718))
## Bug fixes
- Fixed the Run Inputs panel collapsing unexpectedly when filtering Run History. ([#6573](https://github.com/Skyvern-AI/skyvern/pull/6573))
- Fixed the agent incorrectly retrying click fallbacks after an action dispatch timed out, causing unintended interactions. ([#6569](https://github.com/Skyvern-AI/skyvern/pull/6569))
- Fixed session recordings and page interpretation not resuming correctly after navigation, causing missed recording segments. ([#6568](https://github.com/Skyvern-AI/skyvern/pull/6568))
- Fixed code block runs not generating a recording entry, causing the Recording tab to appear empty. ([#6607](https://github.com/Skyvern-AI/skyvern/pull/6607))
- Fixed code block action screenshots not appearing on the run Overview page. ([#6605](https://github.com/Skyvern-AI/skyvern/pull/6605))
- Fixed long usernames or email addresses hiding the sidebar collapse button on desktop.
- Fixed the File Parser block crashing when a downloaded file isn't a valid PDF (e.g. a site returns a short plain-text error instead of the expected document); the block now surfaces a clear parse error instead of failing the run unexpectedly. ([#6657](https://github.com/Skyvern-AI/skyvern/pull/6657))
- Fixed the Inputs field missing from the Code view of code-first code blocks. ([#6633](https://github.com/Skyvern-AI/skyvern/pull/6633))
- Fixed Copilot-synthesized download blocks not downloading the file during code replay. ([#6637](https://github.com/Skyvern-AI/skyvern/pull/6637), [#6638](https://github.com/Skyvern-AI/skyvern/pull/6638))
- Fixed multi-step login flows not advancing when credentials were filled into JavaScript-gated fields. ([#6650](https://github.com/Skyvern-AI/skyvern/pull/6650))
- Fixed an infinite update loop on the workflow build page when certain block configurations were active. ([#6660](https://github.com/Skyvern-AI/skyvern/pull/6660))
- Fixed adopted-session file downloads not being available as run artifacts after the session ended. ([#6679](https://github.com/Skyvern-AI/skyvern/pull/6679))
- Fixed the run timeline showing action and step rows for non-code blocks when they should only appear for code blocks. ([#6675](https://github.com/Skyvern-AI/skyvern/pull/6675))
- Fixed workflow run actions not appearing in the timeline for all block types.
- Fixed workflow runs not stopping when a configured time limit was exceeded. ([#6677](https://github.com/Skyvern-AI/skyvern/pull/6677))
- Fixed the analytics plan-tier badge showing the incorrect tier for some organizations.
- Fixed credits not being recorded when data was captured inside a Navigate block in task runs. ([#6698](https://github.com/Skyvern-AI/skyvern/pull/6698))
- Fixed Copilot directory mode synthesizing incorrect code bindings. ([#6702](https://github.com/Skyvern-AI/skyvern/pull/6702))
- Fixed FileUpload blocks silently skipping the upload when no file was provided, instead of recording a clean no-op and continuing. ([#6728](https://github.com/Skyvern-AI/skyvern/pull/6728))
- Fixed the product tour not launching when triggered from the "Take a tour" menu option. ([#6727](https://github.com/Skyvern-AI/skyvern/pull/6727))
- Fixed scanned PDF files being parsed as a single large page rather than processed page-by-page, which caused extraction to miss content on multi-page documents. ([#6722](https://github.com/Skyvern-AI/skyvern/pull/6722))
- Fixed the block library drawer overflowing its container bounds in the workflow builder. ([#6721](https://github.com/Skyvern-AI/skyvern/pull/6721))
</Update>
<Update label="Week of June 8, 2026" description="Multi-tab agent control, Gemini 3.5 Flash, GCP Vault, bulk agent actions, credential clears, code-block credentials">
<Update label="Week of June 8, 2026" description="Editor onboarding, Copilot modes, GCS storage, API docs split, OTP fixes">
## New features
@ -178,16 +37,6 @@ mode: custom
- **GCS Storage Backend** — Added Google Cloud Storage as an artifact storage backend for self-hosted deployments. ([#6442](https://github.com/Skyvern-AI/skyvern/pull/6442))
- **Key-Optional Labels and Flexible Tag Filters** — Tags now support key-optional labels, and tag filter queries accept more flexible formats for richer agent organization. ([#6458](https://github.com/Skyvern-AI/skyvern/pull/6458))
- **Self-Serve Editor Onboarding** — New users in the workflow editor now see a get-started modal with intent routing and template cards, a 4-stop guided product tour, and smart empty states on the dashboard and runs pages to accelerate time-to-first-automation. ([#6475](https://github.com/Skyvern-AI/skyvern/pull/6475))
- **Multi-Tab Browser Control** — The autonomous agent loop can now open and interact with multiple browser tabs simultaneously, enabling workflows that span across new tab links and pop-ups. ([#6507](https://github.com/Skyvern-AI/skyvern/pull/6507))
- **Gemini 3.5 Flash Model Support** — Added Google Gemini 3.5 Flash as a selectable LLM for agent runs, providing a faster inference option. ([#6505](https://github.com/Skyvern-AI/skyvern/pull/6505))
- **GCP Secret Manager Vault Adapter** — Added Google Cloud Secret Manager as a supported vault backend for storing and retrieving secrets in self-hosted deployments. ([#6488](https://github.com/Skyvern-AI/skyvern/pull/6488))
- **Bulk Actions on Agents List** — You can now select multiple agents at once and apply bulk actions directly from the agents list page. ([#6485](https://github.com/Skyvern-AI/skyvern/pull/6485))
- **Self-Service Credential Clears** — Saved credentials can now be individually cleared from the credentials settings page without deleting and recreating them. ([#6480](https://github.com/Skyvern-AI/skyvern/pull/6480))
- **Per-Run Session Recordings as Artifacts** — Recordings are now clipped to each run's time window and stored as downloadable run-scoped artifacts. ([#6479](https://github.com/Skyvern-AI/skyvern/pull/6479))
- **Credential-Aware Scouting in Code-Block Mode** — The Copilot now uses saved credentials while scouting pages for code-block generation and produces credential-typed code that fills login fields using your stored credentials. ([#6521](https://github.com/Skyvern-AI/skyvern/pull/6521))
- **Claude Fable 5 Model Support** — Added Claude Fable 5 as a selectable model for agent runs. ([#6539](https://github.com/Skyvern-AI/skyvern/pull/6539))
- **Live Browser Recording with Instant Placeholders** — Session recordings now commit navigation and wait blocks immediately as placeholders during live recording, with support for capture pause and draft interpretation. ([#6561](https://github.com/Skyvern-AI/skyvern/pull/6561))
- **Act-and-Observe Copilot Scouting** — The Copilot's page scouting now uses act-and-observe interactions that return bounded page evidence alongside gathered actions, improving code-block generation accuracy. ([#6533](https://github.com/Skyvern-AI/skyvern/pull/6533))
## Improvements
@ -200,13 +49,6 @@ mode: custom
- **Block Title Editable from Settings Sidebar** — Workflow block titles can now be edited from the right-hand settings sidebar, without needing to use the inline editor on the canvas. ([#6443](https://github.com/Skyvern-AI/skyvern/pull/6443))
- **API Reference Split into Agents and Runs Sections** — The public API reference is now organized into separate Agents and Runs sections for easier navigation. ([#6448](https://github.com/Skyvern-AI/skyvern/pull/6448))
- **Label-First Tag Editor and Flexible Tag Filter UI** — The tag editor now accepts standalone labels or `group:label` format in a single input, and the filter bar supports filtering by label, group, or group:label for richer agent organization. ([#6470](https://github.com/Skyvern-AI/skyvern/pull/6470))
- **2FA Field Progressive Disclosure in Login Block** — Two-factor authentication fields in the login workflow block are now collapsed by default and expanded on demand, reducing visual noise for workflows that don't use 2FA. ([#6490](https://github.com/Skyvern-AI/skyvern/pull/6490))
- **Persistent Browser Session Profile Generation Now Opt-In** — Automatic browser profile generation during persistent sessions is now opt-in, giving you explicit control over when profiles are captured. ([#6489](https://github.com/Skyvern-AI/skyvern/pull/6489))
- **Icon-Only Status Pills on Mobile Viewports** — Status badges now render in a compact icon-only format on small screen widths, preventing layout overflow on mobile devices. ([#6481](https://github.com/Skyvern-AI/skyvern/pull/6481))
- **Hardened Webhook Delivery** — Workflow webhook delivery is now more reliable, with improved handling of transient failures and prioritized delivery ordering. ([#6478](https://github.com/Skyvern-AI/skyvern/pull/6478))
- **Reduced Live Recording Draft Latency** — Live session recordings now appear almost instantly with placeholder entries, with final blocks committed after a short debounce window. ([#6553](https://github.com/Skyvern-AI/skyvern/pull/6553))
- **Copilot Stops on Terminal Blockers** — The Copilot now correctly halts when it reaches a condition that blocks further progress, preventing runaway sessions. ([#6535](https://github.com/Skyvern-AI/skyvern/pull/6535))
- **Copilot Chat Shows Run Outcome Verdict** — The Copilot chat timeline now reflects the actual run outcome verdict, giving a more accurate view of what the agent accomplished. ([#6532](https://github.com/Skyvern-AI/skyvern/pull/6532))
## Bug fixes
@ -215,23 +57,6 @@ mode: custom
- Fixed persistent-session screen recordings not appearing in the run view when the recording timestamp fell outside the run's time window. ([#6454](https://github.com/Skyvern-AI/skyvern/pull/6454))
- Fixed Copilot inconsistently asking for a URL before running discovery when the user had only named a site without providing one. ([#6473](https://github.com/Skyvern-AI/skyvern/pull/6473))
- Fixed Copilot asking for a URL after already successfully navigating to the requested site during discovery. ([#6472](https://github.com/Skyvern-AI/skyvern/pull/6472))
- Fixed self-hosted deployments rejecting requests with "Invalid credentials" (HTTP 403) when the Docker UI injected an API key that the container's server incorrectly filtered out.
- Fixed persistent session recordings not being finalized when the video encoding process outlasted the session's cleanup window. ([#6519](https://github.com/Skyvern-AI/skyvern/pull/6519))
- Fixed the saved credentials status indicator not updating until the page was manually refreshed. ([#6517](https://github.com/Skyvern-AI/skyvern/pull/6517))
- Fixed the workflow run timeline rendering blocks in branch-tree order instead of execution order, producing a confusing step sequence. ([#6515](https://github.com/Skyvern-AI/skyvern/pull/6515))
- Fixed downloaded files inside loop blocks being shared across all iterations instead of scoped to the block that downloaded them. ([#6487](https://github.com/Skyvern-AI/skyvern/pull/6487))
- Fixed session recordings defaulting to an incorrect resolution; recordings now use Playwright's default viewport size. ([#6524](https://github.com/Skyvern-AI/skyvern/pull/6524))
- Fixed Copilot failing to recognize and use a saved credential when the credential ID was typed with a space or hyphen separator instead of an underscore; malformed IDs are now normalized and resolved correctly. ([#6529](https://github.com/Skyvern-AI/skyvern/pull/6529))
- Fixed Copilot chat showing workflow blocks as completed even when the overall run goal was not demonstrated — the recorded run outcome verdict is now surfaced in the chat narrative. ([#6528](https://github.com/Skyvern-AI/skyvern/pull/6528))
- Fixed Copilot browser sessions incorrectly blocking localhost navigation during page scouting, which could prevent evidence from being gathered from locally-served pages. ([#6527](https://github.com/Skyvern-AI/skyvern/pull/6527))
- Fixed single-element JSON arrays being unwrapped to a scalar when passed as workflow run inputs. ([#6560](https://github.com/Skyvern-AI/skyvern/pull/6560))
- Fixed code blocks silently dropping non-dict return values instead of passing them through to downstream blocks. ([#6556](https://github.com/Skyvern-AI/skyvern/pull/6556))
- Fixed the task agent ignoring the `max_iterations` override, causing runs to exceed their configured step limit. ([#6544](https://github.com/Skyvern-AI/skyvern/pull/6544))
- Fixed generated workflow parameter keys being duplicated, which caused a hard failure when the workflow was run. ([#6543](https://github.com/Skyvern-AI/skyvern/pull/6543))
- Fixed task runs completing successfully even when a required sub-goal had not been satisfied. ([#6546](https://github.com/Skyvern-AI/skyvern/pull/6546))
- Fixed the credential-clear confirmation dialog being hidden behind other overlays. ([#6542](https://github.com/Skyvern-AI/skyvern/pull/6542))
- Fixed click actions on elements wrapped in disabled containers not being retargeted to the clickable child element. ([#6540](https://github.com/Skyvern-AI/skyvern/pull/6540))
- Fixed Copilot loop detector incorrectly triggering at block dispatch boundaries. ([#6550](https://github.com/Skyvern-AI/skyvern/pull/6550))
</Update>
@ -492,34 +317,37 @@ mode: custom
</Update>
<Update label="Week of February 23, 2026" description="Adaptive caching, Workflow Trigger block, CLI signup, reserved parameters, new LLM models">
<Update label="v1.0.22 — February 26, 2026" description="Adaptive caching, Workflow Trigger block, CLI signup, and more">
## New features
- **Adaptive Caching (Script Generation)** — Skyvern can now learn from repeated workflow runs and generate cached scripts that speed up future executions. When a block runs successfully, Skyvern records a reusable script and replays it on subsequent runs, falling back to the AI agent only if the script fails. ([#4908](https://github.com/Skyvern-AI/skyvern/pull/4908), [#4916](https://github.com/Skyvern-AI/skyvern/pull/4916), [#4917](https://github.com/Skyvern-AI/skyvern/pull/4917), [#4920](https://github.com/Skyvern-AI/skyvern/pull/4920), [#4922](https://github.com/Skyvern-AI/skyvern/pull/4922), [#4931](https://github.com/Skyvern-AI/skyvern/pull/4931))
- **Workflow Trigger Block** — A new block type that lets one workflow trigger another from within the workflow editor. Chain workflows together as building blocks for complex automations. ([#4885](https://github.com/Skyvern-AI/skyvern/pull/4885))
- **Browser-based CLI Signup** — New users can sign up for Skyvern Cloud directly from the CLI. Running `skyvern signup` opens a browser flow that creates your account and stores your API key locally. ([#4925](https://github.com/Skyvern-AI/skyvern/pull/4925))
- **Interactive ngrok Tunnel** — `skyvern browser serve` can now create an ngrok tunnel automatically, making it easy to expose your local browser to Skyvern Cloud without manual network configuration. ([#4924](https://github.com/Skyvern-AI/skyvern/pull/4924))
- **South Korea Proxy Location** — Added `RESIDENTIAL_KR` as a proxy geolocation option for automations targeting South Korean websites. ([#4918](https://github.com/Skyvern-AI/skyvern/pull/4918))
- **Downloaded Files Tab** — Browser session detail pages now include a Downloaded Files tab for viewing and accessing files captured during a session. ([#4911](https://github.com/Skyvern-AI/skyvern/pull/4911))
- **CDP Proxy Authentication** — Remote browser connections now support authenticated proxies via the CDP `Fetch.authRequired` protocol. ([#4936](https://github.com/Skyvern-AI/skyvern/pull/4936))
- **Remote Browser Download Interception** — File downloads triggered by XHR requests and other non-navigation events are now captured when using remote CDP browser connections. ([#4906](https://github.com/Skyvern-AI/skyvern/pull/4906), [#4921](https://github.com/Skyvern-AI/skyvern/pull/4921), [#4934](https://github.com/Skyvern-AI/skyvern/pull/4934))
- **`current_date` Reserved Parameter** — Workflows now have a built-in `current_date` parameter that resolves to today's date automatically. ([#4854](https://github.com/Skyvern-AI/skyvern/pull/4854))
- **Reserved Parameters in Block Editor** — The workflow block parameter picker now shows reserved system parameters, making them discoverable without memorizing names. ([#4857](https://github.com/Skyvern-AI/skyvern/pull/4857))
- **Natural Language Loop `data_schema`** — Loop blocks driven by natural language extraction now support a `data_schema` field for consistent output structure. ([#4851](https://github.com/Skyvern-AI/skyvern/pull/4851))
- **Gemini 3.1 Pro and Inception Mercury-2** — Two new LLM engine options for your automations. ([#4847](https://github.com/Skyvern-AI/skyvern/pull/4847))
- **MCP Server on API Service** — The MCP remote server is now mounted at `/mcp` on the existing API, eliminating the need for a separate process. ([#4843](https://github.com/Skyvern-AI/skyvern/pull/4843))
## Improvements
- **Renamed "Login-Free" to "Saved Profile"** — Clearer terminology throughout the UI. Browser session checkbox wording has also been improved. ([#4914](https://github.com/Skyvern-AI/skyvern/pull/4914))
- **TOTP Verification Improvements** — Better timer handling, expired code retry, 2FA banner suppression, and more reliable goal-text extraction during TOTP flows. ([#4860](https://github.com/Skyvern-AI/skyvern/pull/4860))
- **TOTP Webhook includes `workflow_permanent_id`** — Easier to identify which workflow is requesting a 2FA code. ([#4871](https://github.com/Skyvern-AI/skyvern/pull/4871))
- **Consolidated Gemini 3 Pro Model Key** — `VERTEX_GEMINI_3_PRO` now auto-resolves to the latest version, so you no longer need to update config when Google releases point versions. ([#4926](https://github.com/Skyvern-AI/skyvern/pull/4926))
- **Browser Rotation for Remote Connections** — Remote browser environments now support context rotation, improving reliability for long-running automations. ([#4929](https://github.com/Skyvern-AI/skyvern/pull/4929))
- Workflow save validation (HTTP 422) now returns clear, field-level error messages. ([#4852](https://github.com/Skyvern-AI/skyvern/pull/4852))
- Browser launch errors now show actionable messages instead of raw exceptions. ([#4844](https://github.com/Skyvern-AI/skyvern/pull/4844))
- Cloud LLM fallback overrides are now properly applied when selecting models. ([#4839](https://github.com/Skyvern-AI/skyvern/pull/4839))
## Bug fixes
@ -531,22 +359,67 @@ mode: custom
- MCP remote auth token comparison no longer fails on encrypted tokens. ([#4863](https://github.com/Skyvern-AI/skyvern/pull/4863))
- `skyvern quickstart` now handles local PostgreSQL without a pre-existing `skyvern` role. ([#4878](https://github.com/Skyvern-AI/skyvern/pull/4878))
- WebSocket URLs through ngrok tunnels now rewrite correctly for live browser streaming. ([#4927](https://github.com/Skyvern-AI/skyvern/pull/4927))
</Update>
<Update label="v1.0.21 — February 24, 2026" description="Reserved parameters, data_schema for loops, new LLM models">
## New features
- **`current_date` Reserved Parameter** — Workflows now have a built-in `current_date` parameter that resolves to today's date automatically. ([#4854](https://github.com/Skyvern-AI/skyvern/pull/4854))
- **Reserved Parameters in Block Editor** — The workflow block parameter picker now shows reserved system parameters, making them discoverable without memorizing names. ([#4857](https://github.com/Skyvern-AI/skyvern/pull/4857))
- **Natural Language Loop `data_schema`** — Loop blocks driven by natural language extraction now support a `data_schema` field for consistent output structure. ([#4851](https://github.com/Skyvern-AI/skyvern/pull/4851))
- **Gemini 3.1 Pro and Inception Mercury-2** — Two new LLM engine options for your automations. ([#4847](https://github.com/Skyvern-AI/skyvern/pull/4847))
- **MCP Server on API Service** — The MCP remote server is now mounted at `/mcp` on the existing API, eliminating the need for a separate process. ([#4843](https://github.com/Skyvern-AI/skyvern/pull/4843))
## Improvements
- Workflow save validation (HTTP 422) now returns clear, field-level error messages. ([#4852](https://github.com/Skyvern-AI/skyvern/pull/4852))
- Browser launch errors now show actionable messages instead of raw exceptions. ([#4844](https://github.com/Skyvern-AI/skyvern/pull/4844))
- Cloud LLM fallback overrides are now properly applied when selecting models. ([#4839](https://github.com/Skyvern-AI/skyvern/pull/4839))
## Bug fixes
- Conditional blocks no longer crash when all branches use Jinja templates. ([#4849](https://github.com/Skyvern-AI/skyvern/pull/4849))
- Conditional blocks no longer misinterpret resolved Jinja template variables. ([#4841](https://github.com/Skyvern-AI/skyvern/pull/4841))
</Update>
<Update label="Week of February 16, 2026" description="Browser profile testing, Skills package, 2FA detection, full CLI parity, Claude Opus 4.6">
<Update label="v1.0.18 — February 19, 2026" description="Browser profile testing and saved-profile workflows">
## New features
- **Browser Profile Testing** — Test browser profiles directly from the UI to verify that saved login sessions are still valid. Workflows can also run with a saved browser profile, enabling login-free automations that reuse authenticated sessions. ([#4818](https://github.com/Skyvern-AI/skyvern/pull/4818))
## Bug fixes
- When both a TOTP credential and a webhook are configured for 2FA, the credential is now correctly prioritized. ([#4811](https://github.com/Skyvern-AI/skyvern/pull/4811))
</Update>
<Update label="v1.0.17 — February 19, 2026" description="Skills package and cached script management">
## New features
- **Skyvern Skills Package** — The new `skyvern skill` CLI command provides pre-built workflow templates and reference documentation for common automation patterns. ([#4817](https://github.com/Skyvern-AI/skyvern/pull/4817))
- **Clear Cached Scripts API** — A new endpoint lets you clear cached automation scripts for a workflow when a target website changes and you want to force Skyvern to re-learn. ([#4809](https://github.com/Skyvern-AI/skyvern/pull/4809))
</Update>
<Update label="v1.0.16 — February 19, 2026" description="2FA detection, full CLI parity, Claude Opus 4.6 CUA">
## New features
- **Automatic 2FA Detection Without TOTP Credentials** — Skyvern now detects when a website asks for a 2FA code even without pre-configured TOTP credentials. The system pauses and waits for you to provide the code via the UI or webhook. ([#4786](https://github.com/Skyvern-AI/skyvern/pull/4786))
- **Full CLI Parity with MCP** — The CLI now supports browser session management, credential management, block operations, and enhanced workflow commands. Everything available through MCP is now accessible from the command line. ([#4789](https://github.com/Skyvern-AI/skyvern/pull/4789), [#4792](https://github.com/Skyvern-AI/skyvern/pull/4792), [#4793](https://github.com/Skyvern-AI/skyvern/pull/4793))
- **Claude Opus 4.6 CUA Support** — Anthropic's Claude Opus 4.6 is now available as a Computer Use Agent model for driving browser interactions. ([#4780](https://github.com/Skyvern-AI/skyvern/pull/4780))
- **Anthropic Claude Opus 4.6** — Added Claude Opus 4.6 as an available LLM engine for web automation tasks. ([#4777](https://github.com/Skyvern-AI/skyvern/pull/4777), [#4778](https://github.com/Skyvern-AI/skyvern/pull/4778))
## Improvements
@ -554,10 +427,17 @@ mode: custom
## Bug fixes
- When both a TOTP credential and a webhook are configured for 2FA, the credential is now correctly prioritized. ([#4811](https://github.com/Skyvern-AI/skyvern/pull/4811))
- Fixed conditional blocks evaluating the wrong value after Jinja template rendering. ([#4801](https://github.com/Skyvern-AI/skyvern/pull/4801))
- Fixed MFA resolution priority when both TOTP credentials and webhooks are configured. ([#4800](https://github.com/Skyvern-AI/skyvern/pull/4800))
</Update>
<Update label="v1.0.15 — February 17, 2026" description="Claude Opus 4.6 model support">
## New features
- **Anthropic Claude Opus 4.6** — Added Claude Opus 4.6 as an available LLM engine for web automation tasks. ([#4777](https://github.com/Skyvern-AI/skyvern/pull/4777), [#4778](https://github.com/Skyvern-AI/skyvern/pull/4778))
</Update>
</div>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,286 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Skyvern Langchain](#skyvern-langchain)
- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [Run a task(sync) locally in your local environment](#run-a-tasksync-locally-in-your-local-environment)
- [Run a task(async) locally in your local environment](#run-a-taskasync-locally-in-your-local-environment)
- [Get a task locally in your local environment](#get-a-task-locally-in-your-local-environment)
- [Run a task(sync) by calling skyvern APIs](#run-a-tasksync-by-calling-skyvern-apis)
- [Run a task(async) by calling skyvern APIs](#run-a-taskasync-by-calling-skyvern-apis)
- [Get a task by calling skyvern APIs](#get-a-task-by-calling-skyvern-apis)
- [Agent Usage](#agent-usage)
- [Run a task(async) locally in your local environment and wait until the task is finished](#run-a-taskasync-locally-in-your-local-environment-and-wait-until-the-task-is-finished)
- [Run a task(async) by calling skyvern APIs and wait until the task is finished](#run-a-taskasync-by-calling-skyvern-apis-and-wait-until-the-task-is-finished)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# Skyvern Langchain
This is a langchain integration for Skyvern.
## Installation
```bash
pip install skyvern-langchain
```
To run the example scenarios, you might need to install other langchain dependencies.
```bash
pip install langchain-openai
pip install langchain-community
```
## Basic Usage
This is the only basic usage of skyvern langchain tool. If you want a full langchain integration experience, please refer to the [Agent Usage](#agent-usage) section to play with langchain agent.
Go to [Langchain Tools](https://python.langchain.com/v0.1/docs/modules/tools/) to see more advanced langchain tool usage.
### Run a task(sync) locally in your local environment
> sync task won't return until the task is finished.
:warning: :warning: if you want to run this code block, you need to run `skyvern init` command in your terminal to set up skyvern first.
```python
import asyncio
from skyvern_langchain.agent import RunTask
run_task = RunTask()
async def main():
# to run skyvern agent locally, must run `skyvern init` first
print(await run_task.ainvoke("Navigate to the Hacker News homepage and get the top 3 posts."))
if __name__ == "__main__":
asyncio.run(main())
```
### Run a task(async) locally in your local environment
> async task will return immediately and the task will be running in the background.
:warning: :warning: if you want to run the task in the background, you need to keep the script running until the task is finished, otherwise the task will be killed when the script is finished.
:warning: :warning: if you want to run this code block, you need to run `skyvern init` command in your terminal to set up skyvern first.
```python
import asyncio
from skyvern_langchain.agent import DispatchTask
dispatch_task = DispatchTask()
async def main():
# to run skyvern agent locally, must run `skyvern init` first
print(await dispatch_task.ainvoke("Navigate to the Hacker News homepage and get the top 3 posts."))
# keep the script running until the task is finished
await asyncio.sleep(600)
if __name__ == "__main__":
asyncio.run(main())
```
### Get a task locally in your local environment
:warning: :warning: if you want to run this code block, you need to run `skyvern init` command in your terminal to set up skyvern first.
```python
import asyncio
from skyvern_langchain.agent import GetTask
get_task = GetTask()
async def main():
# to run skyvern agent locally, must run `skyvern init` first
print(await get_task.ainvoke("<task_id>"))
if __name__ == "__main__":
asyncio.run(main())
```
### Run a task(sync) by calling skyvern APIs
> sync task won't return until the task is finished.
no need to run `skyvern init` command in your terminal to set up skyvern before using this integration.
```python
import asyncio
from skyvern_langchain.client import RunTask
run_task = RunTask(
api_key="<your_organization_api_key>",
)
# or you can load the api_key from SKYVERN_API_KEY in .env
# run_task = RunTask()
async def main():
print(await run_task.ainvoke("Navigate to the Hacker News homepage and get the top 3 posts."))
if __name__ == "__main__":
asyncio.run(main())
```
### Run a task(async) by calling skyvern APIs
> async task will return immediately and the task will be running in the background.
no need to run `skyvern init` command in your terminal to set up skyvern before using this integration.
the task is actually running in the skyvern cloud service, so you don't need to keep your script running until the task is finished.
```python
import asyncio
from skyvern_langchain.client import DispatchTask
dispatch_task = DispatchTask(
api_key="<your_organization_api_key>",
)
# or you can load the api_key from SKYVERN_API_KEY in .env
# dispatch_task = DispatchTask()
async def main():
print(await dispatch_task.ainvoke("Navigate to the Hacker News homepage and get the top 3 posts."))
if __name__ == "__main__":
asyncio.run(main())
```
### Get a task by calling skyvern APIs
> async task will return immediately and the task will be running in the background.
no need to run `skyvern init` command in your terminal to set up skyvern before using this integration.
the task is actually running in the skyvern cloud service, so you don't need to keep your script running until the task is finished.
```python
import asyncio
from skyvern_langchain.client import GetTask
get_task = GetTask(
api_key="<your_organization_api_key>",
)
# or you can load the api_key from SKYVERN_API_KEY in .env
# get_task = GetTask()
async def main():
print(await get_task.ainvoke("<task_id>"))
if __name__ == "__main__":
asyncio.run(main())
```
## Agent Usage
Langchain is more powerful when used with [Langchain Agents](https://python.langchain.com/v0.1/docs/modules/agents/).
The following two examples show how to build an agent that executes a specified task, waits for its completion, and then returns the results. For example, the agent is tasked with navigating to the Hacker News homepage and retrieving the top three posts.
### Run a task(async) locally in your local environment and wait until the task is finished
> async task will return immediately and the task will be running in the background. You can use `GetTask` tool to poll the task information until the task is finished.
:warning: :warning: if you want to run this code block, you need to run `skyvern init` command in your terminal to set up skyvern first.
```python
import asyncio
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
from skyvern_langchain.agent import DispatchTask, GetTask
from langchain_community.tools.sleep.tool import SleepTool
# load OpenAI API key from .env
load_dotenv()
llm = ChatOpenAI(model="gpt-4o", temperature=0)
dispatch_task = DispatchTask()
get_task = GetTask()
agent = initialize_agent(
llm=llm,
tools=[
dispatch_task,
get_task,
SleepTool(),
],
verbose=True,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
)
async def main():
# use sleep tool to set up the polling logic until the task is completed, if you only want to dispatch a task, you can remove the sleep tool
print(await agent.ainvoke("Run a task with Skyvern. The task is about 'Navigate to the Hacker News homepage and get the top 3 posts.' Then, get this task information until it's completed. The task information re-get interval should be 60s."))
if __name__ == "__main__":
asyncio.run(main())
```
### Run a task(async) by calling skyvern APIs and wait until the task is finished
> async task will return immediately and the task will be running in the background. You can use `GetTask` tool to poll the task information until the task is finished.
no need to run `skyvern init` command in your terminal to set up skyvern before using this integration.
```python
import asyncio
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
from skyvern_langchain.client import DispatchTask, GetTask
from langchain_community.tools.sleep.tool import SleepTool
# load OpenAI API key from .env
load_dotenv()
llm = ChatOpenAI(model="gpt-4o", temperature=0)
dispatch_task = DispatchTask(
api_key="<your_organization_api_key>",
)
# or you can load the api_key from SKYVERN_API_KEY in .env
# dispatch_task = DispatchTask()
get_task = GetTask(
api_key="<your_organization_api_key>",
)
# or you can load the api_key from SKYVERN_API_KEY in .env
# get_task = GetTask()
agent = initialize_agent(
llm=llm,
tools=[
dispatch_task,
get_task,
SleepTool(),
],
verbose=True,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
)
async def main():
# use sleep tool to set up the polling logic until the task is completed, if you only want to dispatch a task, you can remove the sleep tool
print(await agent.ainvoke("Run a task with Skyvern. The task is about 'Navigate to the Hacker News homepage and get the top 3 posts.' Then, get this task information until it's completed. The task information re-get interval should be 60s."))
if __name__ == "__main__":
asyncio.run(main())
```

View file

@ -0,0 +1,53 @@
[project]
name = "skyvern-langchain"
version = "0.2.1"
description = ""
authors = [{ name = "lawyzheng", email = "lawy@skyvern.com" }]
requires-python = ">=3.11,<3.14"
readme = "README.md"
dependencies = [
"skyvern>=0.2.0",
"langchain>=1.2.0",
"langchain-core>=1.3.3",
"urllib3>=2.7.0",
]
[tool.uv]
override-dependencies = [
"authlib>=1.6.11",
"pyasn1>=0.6.3",
"pyjwt>=2.12.0",
"fastmcp>=3.2.0",
"mcp>=1.23.0",
"websockets>=15.0.1",
# litellm 1.83.7+ pins these exactly; relax for security upgrade.
"jsonschema>=4.23.0",
"python-dotenv>=1.2.2",
"openai>=2.24.0",
# Dependabot moderate security upgrades (transitive)
"mako>=1.3.11",
"pypdf>=6.10.2",
"python-multipart>=0.0.26",
"cryptography>=46.0.7",
"pygments>=2.20.0",
# Dependabot high security upgrades (transitive)
"litellm>=1.83.10",
"langsmith>=0.8.0",
]
[tool.uv.sources]
skyvern = { path = "../.." }
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[dependency-groups]
dev = ["twine>=6.1.0,<7"]
[tool.hatch.build.targets.sdist]
include = ["skyvern_langchain"]
[tool.hatch.build.targets.wheel]
include = ["skyvern_langchain"]

View file

@ -0,0 +1,60 @@
from typing import Any, Type
from langchain.tools import BaseTool
from litellm import BaseModel
from pydantic import Field
from skyvern_langchain.schema import CreateTaskInput, GetTaskInput
from skyvern_langchain.settings import settings
from skyvern import Skyvern
from skyvern.client.types.get_run_response import GetRunResponse
from skyvern.client.types.task_run_response import TaskRunResponse
from skyvern.schemas.runs import RunEngine
class SkyvernTaskBaseTool(BaseTool):
engine: RunEngine = Field(default=settings.engine)
run_task_timeout_seconds: int = Field(default=settings.run_task_timeout_seconds)
agent: Skyvern = Skyvern.local()
def _run(self, *args: Any, **kwargs: Any) -> None:
raise NotImplementedError("skyvern task tool does not support sync")
class RunTask(SkyvernTaskBaseTool):
name: str = "run-skyvern-agent-task"
description: str = """Use Skyvern agent to run a task. This function won't return until the task is finished."""
args_schema: Type[BaseModel] = CreateTaskInput
async def _arun(self, user_prompt: str, url: str | None = None) -> TaskRunResponse:
return await self.agent.run_task(
prompt=user_prompt,
url=url,
engine=self.engine,
timeout=self.run_task_timeout_seconds,
wait_for_completion=True,
)
class DispatchTask(SkyvernTaskBaseTool):
name: str = "dispatch-skyvern-agent-task"
description: str = """Use Skyvern agent to dispatch a task. This function will return immediately and the task will be running in the background."""
args_schema: Type[BaseModel] = CreateTaskInput
async def _arun(self, user_prompt: str, url: str | None = None) -> TaskRunResponse:
return await self.agent.run_task(
prompt=user_prompt,
url=url,
engine=self.engine,
timeout=self.run_task_timeout_seconds,
wait_for_completion=False,
)
class GetTask(SkyvernTaskBaseTool):
name: str = "get-skyvern-agent-task"
description: str = """Use Skyvern agent to get a task."""
args_schema: Type[BaseModel] = GetTaskInput
async def _arun(self, task_id: str) -> GetRunResponse | None:
return await self.agent.get_run(run_id=task_id)

View file

@ -0,0 +1,64 @@
from typing import Any, Type
from langchain.tools import BaseTool
from pydantic import BaseModel, Field
from skyvern_langchain.schema import CreateTaskInput, GetTaskInput
from skyvern_langchain.settings import settings
from skyvern import Skyvern
from skyvern.client import SkyvernEnvironment
from skyvern.client.types.get_run_response import GetRunResponse
from skyvern.client.types.task_run_response import TaskRunResponse
from skyvern.schemas.runs import RunEngine
class SkyvernTaskBaseTool(BaseTool):
api_key: str = Field(default=settings.api_key)
base_url: str = Field(default=settings.base_url)
engine: RunEngine = Field(default=settings.engine)
run_task_timeout_seconds: int = Field(default=settings.run_task_timeout_seconds)
def get_client(self) -> Skyvern:
return Skyvern(environment=SkyvernEnvironment.CLOUD, base_url=self.base_url, api_key=self.api_key)
def _run(self, *args: Any, **kwargs: Any) -> None:
raise NotImplementedError("skyvern task tool does not support sync")
class RunTask(SkyvernTaskBaseTool):
name: str = "run-skyvern-client-task"
description: str = """Use Skyvern client to run a task. This function won't return until the task is finished."""
args_schema: Type[BaseModel] = CreateTaskInput
async def _arun(self, user_prompt: str, url: str | None = None) -> TaskRunResponse:
return await self.get_client().run_task(
timeout=self.run_task_timeout_seconds,
url=url,
prompt=user_prompt,
engine=self.engine,
wait_for_completion=True,
)
class DispatchTask(SkyvernTaskBaseTool):
name: str = "dispatch-skyvern-client-task"
description: str = """Use Skyvern client to dispatch a task. This function will return immediately and the task will be running in the background."""
args_schema: Type[BaseModel] = CreateTaskInput
async def _arun(self, user_prompt: str, url: str | None = None) -> TaskRunResponse:
return await self.get_client().run_task(
timeout=self.run_task_timeout_seconds,
url=url,
prompt=user_prompt,
engine=self.engine,
wait_for_completion=False,
)
class GetTask(SkyvernTaskBaseTool):
name: str = "get-skyvern-client-task"
description: str = """Use Skyvern client to get a task."""
args_schema: Type[BaseModel] = GetTaskInput
async def _arun(self, task_id: str) -> GetRunResponse | None:
return await self.get_client().get_run(run_id=task_id)

View file

@ -0,0 +1,10 @@
from pydantic import BaseModel
class CreateTaskInput(BaseModel):
user_prompt: str
url: str | None = None
class GetTaskInput(BaseModel):
task_id: str

View file

@ -0,0 +1,18 @@
from dotenv import load_dotenv
from pydantic_settings import BaseSettings
from skyvern.schemas.runs import RunEngine
class Settings(BaseSettings):
api_key: str = ""
base_url: str = "https://api.skyvern.com"
engine: RunEngine = RunEngine.skyvern_v2
run_task_timeout_seconds: int = 60 * 60
class Config:
env_prefix = "SKYVERN_"
load_dotenv()
settings = Settings()

1453
integrations/langchain/uv.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,294 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Skyvern LlamaIndex](#skyvern-llamaindex)
- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [Run a task(sync) locally in your local environment](#run-a-tasksync-locally-in-your-local-environment)
- [Run a task(async) locally in your local environment](#run-a-taskasync-locally-in-your-local-environment)
- [Get a task locally in your local environment](#get-a-task-locally-in-your-local-environment)
- [Run a task(sync) by calling skyvern APIs](#run-a-tasksync-by-calling-skyvern-apis)
- [Run a task(async) by calling skyvern APIs](#run-a-taskasync-by-calling-skyvern-apis)
- [Get a task by calling skyvern APIs](#get-a-task-by-calling-skyvern-apis)
- [Advanced Usage](#advanced-usage)
- [Dispatch a task(async) locally in your local environment and wait until the task is finished](#dispatch-a-taskasync-locally-in-your-local-environment-and-wait-until-the-task-is-finished)
- [Dispatch a task(async) by calling skyvern APIs and wait until the task is finished](#dispatch-a-taskasync-by-calling-skyvern-apis-and-wait-until-the-task-is-finished)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# Skyvern LlamaIndex
This is a LlamaIndex integration for Skyvern.
## Installation
```bash
pip install skyvern-llamaindex
```
## Basic Usage
### Run a task(sync) locally in your local environment
> sync task won't return until the task is finished.
:warning: :warning: if you want to run this code block, you need to run `skyvern init` command in your terminal to set up skyvern first.
```python
from dotenv import load_dotenv
from llama_index.agent.openai import OpenAIAgent
from llama_index.llms.openai import OpenAI
from skyvern_llamaindex.agent import SkyvernTool
# load OpenAI API key from .env
load_dotenv()
skyvern_tool = SkyvernTool()
agent = OpenAIAgent.from_tools(
tools=[skyvern_tool.run_task()],
llm=OpenAI(model="gpt-4o"),
verbose=True,
)
response = agent.chat("Run a task with Skyvern. The task is about 'Navigate to the Hacker News homepage and get the top 3 posts.'")
print(response)
```
### Run a task(async) locally in your local environment
> async task will return immediately and the task will be running in the background.
:warning: :warning: if you want to run the task in the background, you need to keep the agent running until the task is finished, otherwise the task will be killed when the agent finished the chat.
:warning: :warning: if you want to run this code block, you need to run `skyvern init` command in your terminal to set up skyvern first.
```python
import asyncio
from dotenv import load_dotenv
from llama_index.agent.openai import OpenAIAgent
from llama_index.llms.openai import OpenAI
from skyvern_llamaindex.agent import SkyvernTool
from llama_index.core.tools import FunctionTool
# load OpenAI API key from .env
load_dotenv()
async def sleep(seconds: int) -> str:
await asyncio.sleep(seconds)
return f"Slept for {seconds} seconds"
# define a sleep tool to keep the agent running until the task is finished
sleep_tool = FunctionTool.from_defaults(
async_fn=sleep,
description="Sleep for a given number of seconds",
name="sleep",
)
skyvern_tool = SkyvernTool()
agent = OpenAIAgent.from_tools(
tools=[skyvern_tool.dispatch_task(), sleep_tool],
llm=OpenAI(model="gpt-4o"),
verbose=True,
)
response = agent.chat("Run a task with Skyvern. The task is about 'Navigate to the Hacker News homepage and get the top 3 posts.' Then, sleep for 10 minutes.")
print(response)
```
### Get a task locally in your local environment
:warning: :warning: if you want to run this code block, you need to run `skyvern init` command in your terminal to set up skyvern first.
```python
from dotenv import load_dotenv
from llama_index.agent.openai import OpenAIAgent
from llama_index.llms.openai import OpenAI
from skyvern_llamaindex.agent import SkyvernTool
# load OpenAI API key from .env
load_dotenv()
skyvern_tool = SkyvernTool()
agent = OpenAIAgent.from_tools(
tools=[skyvern_tool.get_task()],
llm=OpenAI(model="gpt-4o"),
verbose=True,
)
response = agent.chat("Get the task information with Skyvern. The task id is '<task_id>'.")
print(response)
```
### Run a task(sync) by calling skyvern APIs
> sync task won't return until the task is finished.
no need to run `skyvern init` command in your terminal to set up skyvern before using this integration.
```python
from dotenv import load_dotenv
from llama_index.agent.openai import OpenAIAgent
from llama_index.llms.openai import OpenAI
from skyvern_llamaindex.client import SkyvernTool
# load OpenAI API key from .env
load_dotenv()
skyvern_tool = SkyvernTool(api_key="<your_organization_api_key>")
# or you can load the api_key from SKYVERN_API_KEY in .env
# skyvern_tool = SkyvernTool()
agent = OpenAIAgent.from_tools(
tools=[skyvern_tool.run_task()],
llm=OpenAI(model="gpt-4o"),
verbose=True,
)
response = agent.chat("Run a task with Skyvern. The task is about 'Navigate to the Hacker News homepage and get the top 3 posts.'")
print(response)
```
### Run a task(async) by calling skyvern APIs
> async task will return immediately and the task will be running in the background.
no need to run `skyvern init` command in your terminal to set up skyvern before using this integration.
the task is actually running in the skyvern cloud service, so you don't need to keep your agent running until the task is finished.
```python
from dotenv import load_dotenv
from llama_index.agent.openai import OpenAIAgent
from llama_index.llms.openai import OpenAI
from skyvern_llamaindex.client import SkyvernTool
# load OpenAI API key from .env
load_dotenv()
skyvern_tool = SkyvernTool(api_key="<your_organization_api_key>")
# or you can load the api_key from SKYVERN_API_KEY in .env
# skyvern_tool = SkyvernTool()
agent = OpenAIAgent.from_tools(
tools=[skyvern_tool.dispatch_task()],
llm=OpenAI(model="gpt-4o"),
verbose=True,
)
response = agent.chat("Run a task with Skyvern. The task is about 'Navigate to the Hacker News homepage and get the top 3 posts.'")
print(response)
```
### Get a task by calling skyvern APIs
no need to run `skyvern init` command in your terminal to set up skyvern before using this integration.
```python
from dotenv import load_dotenv
from llama_index.agent.openai import OpenAIAgent
from llama_index.llms.openai import OpenAI
from skyvern_llamaindex.client import SkyvernTool
# load OpenAI API key from .env
load_dotenv()
skyvern_tool = SkyvernTool(api_key="<your_organization_api_key>")
# or you can load the api_key from SKYVERN_API_KEY in .env
# skyvern_tool = SkyvernTool()
agent = OpenAIAgent.from_tools(
tools=[skyvern_tool.get_task()],
llm=OpenAI(model="gpt-4o"),
verbose=True,
)
response = agent.chat("Get the task information with Skyvern. The task id is '<task_id>'.")
print(response)
```
## Advanced Usage
To provide some examples of how to integrate Skyvern with other llama-index tools in the agent.
### Dispatch a task(async) locally in your local environment and wait until the task is finished
> dispatch task will return immediately and the task will be running in the background. You can use `get_task` tool to poll the task information until the task is finished.
:warning: :warning: if you want to run this code block, you need to run `skyvern init` command in your terminal to set up skyvern first.
```python
import asyncio
from dotenv import load_dotenv
from llama_index.agent.openai import OpenAIAgent
from llama_index.llms.openai import OpenAI
from llama_index.core.tools import FunctionTool
from skyvern_llamaindex.agent import SkyvernTool
# load OpenAI API key from .env
load_dotenv()
async def sleep(seconds: int) -> str:
await asyncio.sleep(seconds)
return f"Slept for {seconds} seconds"
sleep_tool = FunctionTool.from_defaults(
async_fn=sleep,
description="Sleep for a given number of seconds",
name="sleep",
)
skyvern_tool = SkyvernTool()
agent = OpenAIAgent.from_tools(
tools=[skyvern_tool.dispatch_task(), skyvern_tool.get_task(), sleep_tool],
llm=OpenAI(model="gpt-4o"),
verbose=True,
max_function_calls=10,
)
response = agent.chat("Run a task with Skyvern. The task is about 'Navigate to the Hacker News homepage and get the top 3 posts.' Then, get this task information until it's completed. The task information re-get interval should be 60s.")
print(response)
```
### Dispatch a task(async) by calling skyvern APIs and wait until the task is finished
> dispatch task will return immediately and the task will be running in the background. You can use `get_task` tool to poll the task information until the task is finished.
no need to run `skyvern init` command in your terminal to set up skyvern before using this integration.
```python
import asyncio
from dotenv import load_dotenv
from llama_index.agent.openai import OpenAIAgent
from llama_index.llms.openai import OpenAI
from llama_index.core.tools import FunctionTool
from skyvern_llamaindex.client import SkyvernTool
# load OpenAI API key from .env
load_dotenv()
async def sleep(seconds: int) -> str:
await asyncio.sleep(seconds)
return f"Slept for {seconds} seconds"
sleep_tool = FunctionTool.from_defaults(
async_fn=sleep,
description="Sleep for a given number of seconds",
name="sleep",
)
skyvern_tool = SkyvernTool(api_key="<your_organization_api_key>")
# or you can load the api_key from SKYVERN_API_KEY in .env
# skyvern_tool = SkyvernTool()
agent = OpenAIAgent.from_tools(
tools=[skyvern_tool.dispatch_task(), skyvern_tool.get_task(), sleep_tool],
llm=OpenAI(model="gpt-4o"),
verbose=True,
max_function_calls=10,
)
response = agent.chat("Run a task with Skyvern. The task is about 'Navigate to the Hacker News homepage and get the top 3 posts.' Then, get this task information until it's completed. The task information re-get interval should be 60s.")
print(response)
```

View file

@ -0,0 +1,66 @@
[project]
name = "skyvern-llamaindex"
version = "0.2.2"
description = "Skyvern integration for LlamaIndex"
authors = [{ name = "lawyzheng", email = "lawy@skyvern.com" }]
requires-python = ">=3.11,<3.14"
readme = "README.md"
dependencies = [
"skyvern>=0.2.0",
"llama-index>=0.12.19,<0.14",
# Dependabot #645 (GHSA-qccp-gfcp-xxvc / CVE-2026-44431): urllib3 < 2.7.0
# forwards sensitive headers across origins on proxied low-level redirects.
"urllib3>=2.7.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[dependency-groups]
dev = ["twine>=6.1.0,<7"]
# Security: override vulnerable transitive dependency versions (SKY-8441)
# Using override-dependencies because several of these conflict with
# skyvern's pinned ranges (e.g. pypdf<6, python-multipart<0.0.19).
[tool.uv]
override-dependencies = [
"authlib>=1.6.11",
"pyasn1>=0.6.3",
"PyJWT>=2.12.0",
"fastmcp>=3.2.0",
"mcp>=1.23.0",
"pypdf>=6.10.2",
"pillow>=12.1.1",
"nltk>=3.9.3",
"python-multipart>=0.0.26",
"starlette>=0.49.1",
"google-cloud-aiplatform>=1.133.0",
# Cascading overrides needed to unblock the above security constraints:
# fastmcp>=3.2.0 requires websockets>=15 (skyvern pins <13)
"websockets>=15.0.1",
# litellm 1.83.7+ pins openai==2.24.0 (security upgrade); override
# llama-index-llms-openai's openai<2 declaration so the integration can
# take the patched litellm.
"openai>=2.24.0",
# litellm 1.83.7+ also pins these exactly.
"jsonschema>=4.23.0",
"python-dotenv>=1.0.0",
# Dependabot moderate security upgrades (transitive)
"mako>=1.3.11",
"cryptography>=46.0.7",
"pygments>=2.20.0",
# Dependabot #631 (GHSA-gphh-9q3h-jgpp / CVE-2026-44209): banks <= 2.4.1
# renders prompt templates with an unsandboxed Jinja2 environment (SSTI -> RCE).
# Pulled in transitively via llama-index-core. Fixed in 2.4.2.
"banks>=2.4.2",
]
[tool.uv.sources]
skyvern = { path = "../.." }
[tool.hatch.build.targets.sdist]
include = ["skyvern_llamaindex"]
[tool.hatch.build.targets.wheel]
include = ["skyvern_llamaindex"]

View file

@ -0,0 +1,129 @@
from typing import Any, List
from llama_index.core.tools import FunctionTool
from llama_index.core.tools.tool_spec.base import SPEC_FUNCTION_TYPE, BaseToolSpec
from skyvern_llamaindex.settings import settings
from skyvern import Skyvern
from skyvern.client.types.get_run_response import GetRunResponse
from skyvern.client.types.task_run_response import TaskRunResponse
from skyvern.schemas.runs import RunEngine
class SkyvernTool:
def __init__(self, agent: Skyvern | None = None):
if agent is None:
agent = Skyvern.local()
self.agent = agent
def run_task(self) -> FunctionTool:
task_tool_spec = SkyvernTaskToolSpec(agent=self.agent)
return task_tool_spec.to_tool_list(["run_task"])[0]
def dispatch_task(self) -> FunctionTool:
task_tool_spec = SkyvernTaskToolSpec(agent=self.agent)
return task_tool_spec.to_tool_list(["dispatch_task"])[0]
def get_task(self) -> FunctionTool:
task_tool_spec = SkyvernTaskToolSpec(agent=self.agent)
return task_tool_spec.to_tool_list(["get_task"])[0]
class SkyvernTaskToolSpec(BaseToolSpec):
spec_functions: List[SPEC_FUNCTION_TYPE] = [
"run_task",
"dispatch_task",
"get_task",
]
def __init__(
self,
*,
agent: Skyvern | None = None,
engine: RunEngine = settings.engine,
run_task_timeout_seconds: int = settings.run_task_timeout_seconds,
) -> None:
if agent is None:
agent = Skyvern.local()
self.agent = agent
self.engine = engine
self.run_task_timeout_seconds = run_task_timeout_seconds
async def run_task(
self,
user_prompt: str | None = None,
url: str | None = None,
*_: Any,
**kw: Any,
) -> TaskRunResponse:
"""
Use Skyvern agent to run a task. This function won't return until the task is finished.
Args:
user_prompt[str]: The user's prompt describing the task.
url (Optional[str]): The URL of the target website for the task.
"""
if user_prompt is None and kw.get("args"):
user_prompt = kw["args"][0]
if url is None:
if kw.get("args") and len(kw["args"]) > 1:
url = kw["args"][1]
elif kw.get("kwargs"):
url = kw["kwargs"].get("url")
assert user_prompt is not None, "user_prompt is required"
return await self.agent.run_task(
prompt=user_prompt,
url=url,
engine=self.engine,
timeout=self.run_task_timeout_seconds,
wait_for_completion=True,
)
async def dispatch_task(
self,
user_prompt: str | None = None,
url: str | None = None,
*_: Any,
**kw: Any,
) -> TaskRunResponse:
"""
Use Skyvern agent to dispatch a task. This function will return immediately and the task will be running in the background.
Args:
user_prompt[str]: The user's prompt describing the task.
url (Optional[str]): The URL of the target website for the task.
"""
if user_prompt is None and kw.get("args"):
user_prompt = kw["args"][0]
if url is None:
if kw.get("args") and len(kw["args"]) > 1:
url = kw["args"][1]
elif kw.get("kwargs"):
url = kw["kwargs"].get("url")
assert user_prompt is not None, "user_prompt is required"
return await self.agent.run_task(
prompt=user_prompt,
url=url,
engine=self.engine,
timeout=self.run_task_timeout_seconds,
wait_for_completion=False,
)
async def get_task(self, task_id: str | None = None, *_: Any, **kwargs: Any) -> GetRunResponse | None:
"""
Use Skyvern agent to get a task.
Args:
task_id[str]: The id of the task.
"""
if task_id is None and "args" in kwargs:
task_id = kwargs["args"][0]
assert task_id is not None, "task_id is required"
return await self.agent.get_run(run_id=task_id)

View file

@ -0,0 +1,139 @@
from typing import Any, List
from llama_index.core.tools import FunctionTool
from llama_index.core.tools.tool_spec.base import SPEC_FUNCTION_TYPE, BaseToolSpec
from pydantic import BaseModel
from skyvern_llamaindex.settings import settings
from skyvern import Skyvern
from skyvern.client import SkyvernEnvironment
from skyvern.client.types.get_run_response import GetRunResponse
from skyvern.client.types.task_run_response import TaskRunResponse
from skyvern.schemas.runs import RunEngine
class SkyvernTool(BaseModel):
api_key: str = settings.api_key
base_url: str = settings.base_url
def run_task(self) -> FunctionTool:
task_tool_spec = SkyvernTaskToolSpec(
api_key=self.api_key,
base_url=self.base_url,
)
return task_tool_spec.to_tool_list(["run_task"])[0]
def dispatch_task(self) -> FunctionTool:
task_tool_spec = SkyvernTaskToolSpec(
api_key=self.api_key,
base_url=self.base_url,
)
return task_tool_spec.to_tool_list(["dispatch_task"])[0]
def get_task(self) -> FunctionTool:
task_tool_spec = SkyvernTaskToolSpec(
api_key=self.api_key,
base_url=self.base_url,
)
return task_tool_spec.to_tool_list(["get_task"])[0]
class SkyvernTaskToolSpec(BaseToolSpec):
spec_functions: List[SPEC_FUNCTION_TYPE] = [
"run_task",
"dispatch_task",
"get_task",
]
def __init__(
self,
*,
api_key: str = settings.api_key,
base_url: str = settings.base_url,
engine: RunEngine = settings.engine,
run_task_timeout_seconds: int = settings.run_task_timeout_seconds,
):
self.engine = engine
self.run_task_timeout_seconds = run_task_timeout_seconds
self.client = Skyvern(environment=SkyvernEnvironment.CLOUD, base_url=base_url, api_key=api_key)
async def run_task(
self,
user_prompt: str | None = None,
url: str | None = None,
*_: Any,
**kw: Any,
) -> TaskRunResponse:
"""
Use Skyvern client to run a task. This function won't return until the task is finished.
Args:
user_prompt[str]: The user's prompt describing the task.
url (Optional[str]): The URL of the target website for the task.
"""
if user_prompt is None and kw.get("args"):
user_prompt = kw["args"][0]
if url is None:
if kw.get("args") and len(kw["args"]) > 1:
url = kw["args"][1]
elif kw.get("kwargs"):
url = kw["kwargs"].get("url")
assert user_prompt is not None, "user_prompt is required"
return await self.client.run_task(
prompt=user_prompt,
url=url,
engine=self.engine,
timeout=self.run_task_timeout_seconds,
wait_for_completion=True,
)
async def dispatch_task(
self,
user_prompt: str | None = None,
url: str | None = None,
*_: Any,
**kw: Any,
) -> TaskRunResponse:
"""
Use Skyvern client to dispatch a task. This function will return immediately and the task will be running in the background.
Args:
user_prompt[str]: The user's prompt describing the task.
url (Optional[str]): The URL of the target website for the task.
"""
if user_prompt is None and kw.get("args"):
user_prompt = kw["args"][0]
if url is None:
if kw.get("args") and len(kw["args"]) > 1:
url = kw["args"][1]
elif kw.get("kwargs"):
url = kw["kwargs"].get("url")
assert user_prompt is not None, "user_prompt is required"
return await self.client.run_task(
prompt=user_prompt,
url=url,
engine=self.engine,
timeout=self.run_task_timeout_seconds,
wait_for_completion=False,
)
async def get_task(self, task_id: str | None = None, *_: Any, **kwargs: Any) -> GetRunResponse | None:
"""
Use Skyvern client to get a task.
Args:
task_id[str]: The id of the task.
"""
if task_id is None and "args" in kwargs:
task_id = kwargs["args"][0]
assert task_id is not None, "task_id is required"
return await self.client.get_run(run_id=task_id)

Some files were not shown because too many files have changed in this diff Show more