Skip to content

Private-audience session content is visible in Vercel Agent Runs via Workflow-persisted payloads (0.58.1) #3634

Open

Description

@gmartuccils

Summary

On a Vercel preview deployment of an eve 0.58.1 app, a session whose custom channel classifies its audience as private shows its full model input (the user message plus the substituted dynamic system instructions) and its full model output in Vercel Observability → Agent Runs, even though eve's OpenTelemetry capture for that session is metadata-only and the run carries $eve.is_trace_content_visible: false.

Expected

  • docs/guides/instrumentation/overview.md: "In Vercel's preview and production deployments, eve records inputs and outputs only for public sessions."
  • CHANGELOG 0.44.1 (85b2dc8): content-denied Workflow runs "carry a metadata marker … so dashboards can exclude them."

So a private session's content should not be viewable in Agent Runs.

Observed

A one-shot task-mode session created from a custom channel route with from(id).send(message, { auth, mode: "task", state, title }), where the channel defines audience: () => "private", running on a preview deployment (VERCEL_ENV=preview, EVE_DEV unset, no EVE_TRACES_CONTENT): the Agent Runs run view renders the input text, the substituted system-prompt text and the model's JSON output.

Analysis against the installed 0.58.1 source

  • shared/instrumentation-content.js → shouldCaptureInstrumentationContent: audience === "public" || environment === "development".
  • internal/application/dev-environment.js → resolveInstrumentationEnvironment: EVE_DEV=1 or VERCEL_ENV=development → development; VERCEL_ENV=preview → preview; else production. So on a preview the trace path is metadata-only for a private session, which a reproduction confirms (below).
  • The content is persisted outside the trace path: execution/workflow-runtime.js createSession starts the Workflow run with { kind: "initial", input, serializedContext }; execution/session/turn-step.js and turn-step-result.js return output, serializedContext (including eve.sessionDynamicInstructions with the substituted system prompt) and sessionState.snapshot.session.history; execution/eve-workflow-attributes.js sets $eve.is_trace_content_visible: false but nothing redacts the Workflow initial input, step outputs, durable stream or checkpoints. An explicit instrumentation tracePolicy with recordInputs/recordOutputs false does not change what is persisted either.

Reproduction (credential-free)

  1. Scaffold an app with a mock model (ai/test MockLanguageModelV3 or similar) and a custom channel:
    export default defineChannel({
      audience: () => "private",
      state: null,
      routes: [POST("/run", async (req, { from }) => {
        const { text } = await req.json();
        const s = await from(`job:${crypto.randomUUID()}`).send(text, { auth: <service principal>, mode: "task", state: null, title: "job" });
        return Response.json({ id: s.id });
      })],
    });
    with dynamic instructions that interpolate a distinctive marker string.
  2. Build without the Vercel preset and start with eve start, VERCEL_ENV=preview, EVE_DEV unset, EVE_TRACES_CONTENT unset; enable the managed instrumentation provider layout with a fake platform exporter.
  3. POST a message containing a second marker; let the turn complete.
  4. Inspect (a) the exported spans and (b) the local Workflow storage for the run.

Result: (a) ~95 spans, the activation span carries audience private, input/output capture false, and no marker appears in any span; a public control run captures all markers (harness sanity check). (b) The Workflow step output and checkpoint contain the input marker, the system-instruction marker and the output. The Workflow run's metadata carries $eve.is_trace_content_visible: false. Local Workflow storage is zstd+devalue with executionContext.encryption: false.

Impact

Any eve 0.58.1 app on Vercel exposes private conversation content (user input, dynamic system instructions, model output) to everyone with dashboard access to Agent Runs, regardless of the channel's audience classification. The documented audience gate protects traces only.

Ask

Either

  • (a) eve omits or redacts non-public session content from the Workflow-persisted payloads (initial input, serializedContext dynamic instructions, history, step outputs) when the trace-content decision is false, or
  • (b) Agent Runs hides run payloads when $eve.is_trace_content_visible is false.

If (b) is the intended contract, a note in the instrumentation guide that Workflow payloads are governed by the dashboard, not by the audience gate, would help operators assess exposure.

Environment

eve 0.58.1, ai 7.0.105, @ai-sdk/gateway 4.0.85, Node 24, Vercel Fluid Compute, framework preset eve.

Activity

  1. roboeve commented on Sep 22, 2026

    @roboeve
    Contributor

    Confirmed the durable-payload portion on current origin/main (505601b3f448a20aecc133e5d132d70c2ec3ee35, eve 0.60.0).

    I ran a local Workflow SDK integration reproduction with VERCEL_ENV=preview, EVE_DEV unset, EVE_TRACES_CONTENT unset, a private conversation context, and distinct input/system/output markers. The persisted workflow input contained the input and durable eve.sessionDynamicInstructions markers; the hydrated persisted turnStep result contained those markers and the model output, while the stored run attribute remained $eve.is_trace_content_visible: "false".

    I also ran the focused OTel policy tests: the private trace path redacts content from exported spans. The evidence therefore confirms that the trace-content decision and durable Workflow payload persistence are separate on the current branch.

    Not yet verified here: a real Vercel preview deployment and the Agent Runs UI/service behavior. The local reproduction uses the built-in HTTP adapter with the resulting private conversation context because the in-memory runtime cannot rehydrate an undeclared custom channel; it does not establish whether the hosted dashboard currently honors the false marker when rendering payloads.

    No duplicate with the same marker/symptom was found in open or closed issue searches. The next smallest check is an Agent Runs service/UI reproduction against a preview run with this false marker; that determines whether the fix belongs in dashboard filtering or in eve's durable-payload contract.

  2. pcfds commented on Oct 19, 2026

    @pcfds

    A data point from production. We hit the same tension in our own product with driver conversations that can include health information (sensitive data under Argentine privacy law).

    What worked for us was not deciding at record time by audience but at read time by permission:

    • Content is stored once, with retention.
    • A viewer gets the full thread only with an explicit permission ("converse with the driver").
    • Everyone else sees the structured outcome (status, delay, cause) with the sensitive category masked.
    • Impersonation and platform admins are excluded, and reads are auditable.

    For Agent Runs this would mean viewer-level access to content (a project role or an explicit permission) instead of an audience ceiling applied when spans are exported, so teams don't have to choose between the span tree and the content.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions