Observability Utilities

Hydrate step I/O, parse display names, and decrypt workflow data using workflow/observability.

The workflow/observability module provides utilities for working with workflow data in observability and debugging tools. It includes functions to hydrate serialized step I/O, parse machine-readable names into display-friendly formats, and decrypt encrypted workflow data.

Import

import { 
  hydrateResourceIO, 
  observabilityRevivers, 
  parseStepName, 
  parseWorkflowName, 
  parseClassName, 
} from "workflow/observability"; 

Data Hydration

hydrateResourceIO()

Deserialize step or run data that was serialized using the devalue format. This is required to display step input/output in your UI.

import { hydrateResourceIO, observabilityRevivers } from "workflow/observability"; 
import { getWorld } from "workflow/runtime";

const world = getWorld();
const step = await world.steps.get(runId, stepId);

const hydrated = hydrateResourceIO(step, observabilityRevivers); 
console.log(hydrated.input); // Deserialized input data
console.log(hydrated.output); // Deserialized output data

Parameters:

ParameterTypeDescription
resourceStep | WorkflowRunThe step or run with serialized data
reviversReviversReviver functions for deserialization. Use observabilityRevivers for standard use.

Returns: The resource with hydrated input and output fields.

observabilityRevivers

A set of reviver functions that handle standard workflow serialization types (Date, Map, Set, Error, etc.).

import { observabilityRevivers } from "workflow/observability";

Name Parsing

Workflow and step names are stored as machine-readable identifiers. These utilities extract display-friendly names.

parseStepName()

Parse a machine-readable step name into its components.

import { parseStepName } from "workflow/observability"; 

const parsed = parseStepName("step//./src/workflows/order//processPayment"); 
// parsed.shortName → "processPayment"
// parsed.moduleSpecifier → "./src/workflows/order"

Parameters:

ParameterTypeDescription
stepNamestringThe machine-readable step name

Returns: { shortName: string, moduleSpecifier: string } | null

parseWorkflowName()

Parse a machine-readable workflow name into its components.

import { parseWorkflowName } from "workflow/observability"; 

const parsed = parseWorkflowName("workflow//./src/workflows/order//processOrder"); 
// parsed.shortName → "processOrder"
// parsed.moduleSpecifier → "./src/workflows/order"

Parameters:

ParameterTypeDescription
workflowNamestringThe machine-readable workflow name

Returns: { shortName: string, moduleSpecifier: string } | null

parseClassName()

Parse a machine-readable class name into its components.

import { parseClassName } from "workflow/observability"; 

const parsed = parseClassName("class//./src/models//User"); 
// parsed.shortName → "User"
// parsed.moduleSpecifier → "./src/models"

Parameters:

ParameterTypeDescription
classNamestringThe machine-readable class name

Returns: { shortName: string, moduleSpecifier: string } | null

Encryption

For workflows with encrypted step data, use these utilities to decrypt before hydrating.

getEncryptionKeyForRun()

Retrieve the encryption key used for a specific workflow run.

import { getEncryptionKeyForRun } from "workflow/observability"; 

const key = await getEncryptionKeyForRun(runId); 

Parameters:

ParameterTypeDescription
runIdstringThe workflow run ID

Returns: Encryption key for the run

hydrateResourceIOWithKey()

Hydrate step or run data using a decryption key. Use this instead of hydrateResourceIO() when data is encrypted.

import { 
  getEncryptionKeyForRun, 
  hydrateResourceIOWithKey, 
} from "workflow/observability"; 

const key = await getEncryptionKeyForRun(runId); 
const hydrated = hydrateResourceIOWithKey(step, key); 

Parameters:

ParameterTypeDescription
resourceStep | WorkflowRunThe step or run with encrypted serialized data
keyEncryptionKeyThe encryption key from getEncryptionKeyForRun()

Returns: The resource with decrypted and hydrated input and output fields.

Examples

Hydrate Step Input and Output Data

import { getWorld } from "workflow/runtime";
import { 
  hydrateResourceIO, 
  observabilityRevivers, 
  parseStepName, 
} from "workflow/observability"; 

export async function GET(req: Request) {
  const url = new URL(req.url);
  const runId = url.searchParams.get("runId");
  const stepId = url.searchParams.get("stepId");

  if (!runId || !stepId) {
    return Response.json({ error: "runId and stepId required" }, { status: 400 });
  }

  const world = getWorld();
  const step = await world.steps.get(runId, stepId);

  const hydrated = hydrateResourceIO(step, observabilityRevivers); 
  const parsed = parseStepName(step.stepName);

  return Response.json({
    displayName: parsed?.shortName ?? step.stepName,
    input: hydrated.input, 
    output: hydrated.output, 
  });
}

Decrypt and Hydrate Encrypted Step Data

For teams with encryption enabled, step data must be decrypted before hydration:

import { getWorld } from "workflow/runtime";
import { 
  getEncryptionKeyForRun, 
  hydrateResourceIOWithKey, 
  parseStepName, 
} from "workflow/observability"; 

export async function GET(req: Request) {
  const url = new URL(req.url);
  const runId = url.searchParams.get("runId");
  const stepId = url.searchParams.get("stepId");

  if (!runId || !stepId) {
    return Response.json({ error: "runId and stepId required" }, { status: 400 });
  }

  const world = getWorld();
  const step = await world.steps.get(runId, stepId);

  // Decrypt then hydrate
  const key = await getEncryptionKeyForRun(runId); 
  const hydrated = hydrateResourceIOWithKey(step, key); 

  const parsed = parseStepName(step.stepName);

  return Response.json({
    displayName: parsed?.shortName ?? step.stepName,
    input: hydrated.input,
    output: hydrated.output,
  });
}

Parse Display Names for a Run's Steps

Build a progress dashboard with human-readable step names:

import { getWorld } from "workflow/runtime";
import { parseStepName, parseWorkflowName } from "workflow/observability"; 

const world = getWorld();

// Parse workflow name
const run = await world.runs.get(runId, { resolveData: "none" });
const workflowDisplay = parseWorkflowName(run.workflowName); 
console.log("Workflow:", workflowDisplay?.shortName); 

// Parse step names
const steps = await world.steps.list({ runId, resolveData: "none" });
for (const step of steps.data) {
  const stepDisplay = parseStepName(step.stepName); 
  console.log(`  ${stepDisplay?.shortName}: ${step.status}`); 
}