Skip to content

getActivities() missing QueryActivity generation (parity with processRunEventToActivity) #422

@FL4TLiN3

Description

@FL4TLiN3

Summary

getActivities() in @perstack/core does not generate QueryActivity, while processRunEventToActivity() in @perstack/react does. This causes Wintermute Studio (which uses checkpoint-based activity generation) to not display the initial user query, while Perstack CLI (which uses event-based activity generation) displays it correctly.

Root Cause

There are two different activity generation mechanisms in perstack:

Mechanism Location Used By
Event-based @perstack/reactprocessRunEventToActivity() Perstack CLI
Checkpoint-based @perstack/coregetActivities() Wintermute Studio

Event-based (processRunEventToActivity) - Has QueryActivity ✅

// packages/react/src/utils/event-to-activity.ts (lines 226-274)
if (event.type === "startRun") {
  const userMessage = startRunEvent.inputMessages.find((m) => m.type === "userMessage")
  const queryText = userMessage?.contents?.find((c) => c.type === "textPart")?.text
  
  if (queryText && !runState.queryLogged && !isDelegationReturn) {
    addActivity({
      type: "query",
      id: `query-${event.runId}`,
      expertKey: event.expertKey,
      runId: event.runId,
      text: queryText,
    })
  }
}

Checkpoint-based (getActivities) - Missing QueryActivity ❌

// packages/core/src/utils/activity.ts
export function getActivities(params: GetActivitiesParams): ActivityOrGroup[] {
  // No QueryActivity generation logic exists here
  // Only handles: completed, error, delegate, interactive tool, tool calls
}

Data Availability

The data needed to generate QueryActivity is available in the checkpoint-based approach:

  • step.inputMessages contains UserMessage with TextPart containing the query text
  • This is the same data structure used by processRunEventToActivity()

Proposed Fix

Add QueryActivity generation to getActivities() for the first step:

export function getActivities(params: GetActivitiesParams): ActivityOrGroup[] {
  const { checkpoint, step } = params
  const { status, runId, stepNumber } = checkpoint
  const expertKey = checkpoint.expert.key
  const reasoning = extractReasoning(step.newMessages)
  
  const activities: ActivityOrGroup[] = []

  // Generate QueryActivity for first step (same logic as processRunEventToActivity)
  if (stepNumber === 1 && step.inputMessages) {
    const userMessage = step.inputMessages.find((m) => m.type === "userMessage")
    const textPart = userMessage?.contents?.find((c) => c.type === "textPart")
    if (textPart?.text) {
      activities.push({
        type: "query",
        id: "",  // Will be filled by caller
        expertKey,
        runId,
        text: textPart.text,
      })
    }
  }

  // ... rest of existing logic for other activity types
}

Files to Modify

File Changes
packages/core/src/utils/activity.ts Add QueryActivity generation for stepNumber === 1
packages/core/src/utils/activity.test.ts Add tests for QueryActivity generation

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions