diff --git a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx
index 2a8f824b..0055dda1 100644
--- a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx
@@ -25,6 +25,7 @@ import { WorkflowParameterInput } from "../../WorkflowParameterInput";
import { ParametersState } from "../types";
import { getDefaultValueForParameterType } from "../workflowEditorUtils";
import { validateBitwardenLoginCredential } from "./util";
+import { HelpTooltip } from "@/components/HelpTooltip";
type Props = {
type: WorkflowEditorParameterType;
@@ -273,19 +274,32 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
{type === "credential" && credentialType === "onepassword" && (
<>
-
+
+
+
+
setVaultId(e.target.value)}
/>
-
+
+
+
+
setItemId(e.target.value)}
/>
+
+
+ * Credit Cards: Due to a 1Password limitation, add the
+ expiration date as a separate text field named "Expire Date"
+ in the format MM/YYYY (e.g. 09/2027).
+
+ Credit Cards: Due to a 1Password limitation, add the
+ expiration date as a separate text field named “Expire Date”
+ in the format MM/YYYY (e.g. 09/2027).
+
+
>
)}
{type === "credential" && credentialType === "azurevault" && (
diff --git a/skyvern/forge/sdk/workflow/context_manager.py b/skyvern/forge/sdk/workflow/context_manager.py
index ac3a5197..e2537c09 100644
--- a/skyvern/forge/sdk/workflow/context_manager.py
+++ b/skyvern/forge/sdk/workflow/context_manager.py
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any, Self
import structlog
from jinja2.sandbox import SandboxedEnvironment
+from onepassword import ItemFieldType
from onepassword.client import Client as OnePasswordClient
from skyvern.config import settings
@@ -452,24 +453,61 @@ class WorkflowRunContext:
"context": "These values are placeholders. When you type this in, the real value gets inserted (For security reasons)",
}
- # Process all fields
+ # Process all fields generically so it covers passwords and credit cards
for field in item.fields:
- if field.value is None:
+ if not field.value or field.field_type == ItemFieldType.UNSUPPORTED:
continue
- random_secret_id = self.generate_random_secret_id()
+
+ # ignore irrelevant fields to avoid confusing AI
+ if field.id in ["validFrom", "interest", "issuenumber"]:
+ continue
+
field_type = field.field_type.value.lower()
if field_type == "totp":
+ random_secret_id = self.generate_random_secret_id()
totp_secret_id = f"{random_secret_id}_totp"
self.secrets[totp_secret_id] = OnePasswordConstants.TOTP
totp_secret_value = self.totp_secret_value_key(totp_secret_id)
self.secrets[totp_secret_value] = parse_totp_secret(field.value)
self.values[parameter.key]["totp"] = totp_secret_id
+ elif field.title and field.title.lower() in ["expire date", "expiry date", "expiration date"]:
+ parts = [part.strip() for part in field.value.strip().split("/")]
+
+ if len(parts) == 2:
+ month, year_part = parts
+ month = month.zfill(2) # ensure '5' becomes '05'
+
+ if len(year_part) == 4:
+ year = year_part[2:] # 2025 -> 25
+ else:
+ year = year_part
+
+ self._add_secret_parameter_value(parameter, "card_exp_month", month)
+ self._add_secret_parameter_value(parameter, "card_exp_year", year)
+ if len(year) == 2:
+ self._add_secret_parameter_value(parameter, "card_exp_mmyy", f"{month}/{year}")
+ self._add_secret_parameter_value(parameter, "card_exp_mmyyyy", f"{month}/20{year}")
+ else:
+ # store the 1password-provided value additionally
+ self._add_secret_parameter_value(parameter, "card_exp", field.value)
+ else:
+ # fallback on the 1password-provided value
+ self._add_secret_parameter_value(parameter, "card_exp", field.value)
else:
- # this will be the username or password or other field
- key = field.id.replace(" ", "_")
- secret_id = f"{random_secret_id}_{key}"
- self.secrets[secret_id] = field.value
- self.values[parameter.key][key] = secret_id
+ # using more descriptive keys than 1password provides by default
+ if field.id == "ccnum":
+ self._add_secret_parameter_value(parameter, "card_number", field.value)
+ elif field.id == "cardholder":
+ self._add_secret_parameter_value(parameter, "card_holder_name", field.value)
+ elif field.id == "cvv":
+ self._add_secret_parameter_value(parameter, "card_cvv", field.value)
+ else:
+ # this will be the username, password or other fields
+ self._add_secret_parameter_value(parameter, field.id.replace(" ", "_"), field.value)
+
+ # Secure Note support
+ if item.notes:
+ self._add_secret_parameter_value(parameter, "notes", item.notes)
async def register_bitwarden_login_credential_parameter_value(
self,
@@ -981,6 +1019,15 @@ class WorkflowRunContext:
azure_vault_client = AsyncAzureVaultClient.create_default()
return azure_vault_client
+ def _add_secret_parameter_value(self, parameter: Parameter, key: str, value: str) -> None:
+ if parameter.key not in self.values:
+ raise ValueError(f"{parameter.key} is missing")
+
+ random_secret_id = self.generate_random_secret_id()
+ secret_id = f"{random_secret_id}_{key}"
+ self.secrets[secret_id] = value
+ self.values[parameter.key][key] = secret_id
+
class WorkflowContextManager:
aws_client: AsyncAWSClient