Skip to content

Conversation

@tkattkat
Copy link
Collaborator

@tkattkat tkattkat commented Nov 22, 2025

why

We wanted to add streaming support to the non-CUA agent in V3

what changed

  1. Added stream method to V3AgentHandler:

    • Wraps AI SDK's streamText instead of generateText
    • Returns AgentStreamResult, which extends StreamTextResult with an added result: Promise<AgentResult> property
    • Uses onFinish callback to resolve the result promise with metrics and final state
  2. Consolidated duplicate logic:

    • Created prepareAgent() helper to build context (system prompt, tools, wrapped model) for both execute and stream
    • Created createStepHandler() to generate the onStepFinish callback used by both methods
    • Created consolidateMetricsAndResult() to handle metrics updates and AgentResult construction
    • Created handleStop() helper to check for "close" tool calls before falling back to step count limit
  3. Moved types to public agent types:

    • Exported AgentContext and AgentState interfaces from packages/core/lib/v3/types/public/agent.ts
    • Added AgentStreamResult type that combines StreamTextResult<ToolSet, never> with result: Promise<AgentResult>
  4. Updated V3.agent() return type:

    • Added optional stream method alongside execute for non-CUA agents
    • CUA agents only expose execute (no streaming support)

notes : context handling will likely be moved to prepareStep callback in the very near future, currently we have to maintain a state of the messages which is not ideal. prepareStep should eliminate the need for this

@changeset-bot
Copy link

changeset-bot bot commented Nov 22, 2025

⚠️ No Changeset found

Latest commit: b21cd59

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 22, 2025

Greptile Overview

Greptile Summary

Added streaming support to V3 non-CUA agents by implementing a new stream() method that wraps AI SDK's streamText instead of generateText, returning text/full streams plus a promise that resolves to the final AgentResult.

Major Changes

  • Refactored V3AgentHandler to extract shared logic into helper methods (prepareAgent, createStepHandler, consolidateMetricsAndResult, handleStop) used by both execute() and new stream() method
  • Changed default maxSteps from 10 to 20
  • Added AgentContext, AgentState, and AgentStreamResult types to public API
  • Made stream method optional on agent return type (only available for non-CUA agents with experimental flag)
  • Included example demonstrating streaming usage

Issues Found

  • Type issues with uninitialized promise resolver variables in stream() method
  • Error handling passes unknown type to rejectResult expecting string
  • Typo in example file ("seach" instead of "search")

Confidence Score: 4/5

  • This PR is safe to merge after fixing the TypeScript type issues in the stream method
  • The refactoring is well-structured with good separation of concerns, but has minor type safety issues with uninitialized variables and error type handling that should be fixed before merge
  • packages/core/lib/v3/handlers/v3AgentHandler.ts requires attention for the type issues in the stream() method (lines 218-244)

Important Files Changed

File Analysis

Filename Score Overview
packages/core/lib/v3/handlers/v3AgentHandler.ts 4/5 Refactored agent handler to support streaming via stream() method, extracted shared logic into helper methods (prepareAgent, createStepHandler, consolidateMetricsAndResult, handleStop), changed default maxSteps from 10 to 20
packages/core/lib/v3/types/public/agent.ts 5/5 Added public type definitions for AgentContext, AgentState, and AgentStreamResult to support streaming implementation
packages/core/lib/v3/v3.ts 5/5 Added optional stream method to non-CUA agent return type, wired up to V3AgentHandler.stream() with experimental flag check
packages/core/examples/agent_stream_example.ts 4/5 New example demonstrating streaming API usage with textStream and result promise, includes typo in instruction "seach" instead of "search"

Sequence Diagram

sequenceDiagram
    participant User
    participant V3
    participant V3AgentHandler
    participant LLMClient
    participant AI_SDK
    
    User->>V3: agent(options).stream(instruction)
    V3->>V3AgentHandler: new V3AgentHandler()
    V3->>V3AgentHandler: stream(instruction)
    
    V3AgentHandler->>V3AgentHandler: prepareAgent()
    Note over V3AgentHandler: Build system prompt,<br/>wrap model, create tools
    
    V3AgentHandler->>V3AgentHandler: Create AgentState
    Note over V3AgentHandler: collectedReasoning,<br/>actions, completed, etc.
    
    V3AgentHandler->>V3AgentHandler: Create result promise<br/>(resolveResult, rejectResult)
    
    V3AgentHandler->>LLMClient: streamText(config + onStepFinish + onFinish)
    LLMClient->>AI_SDK: streamText()
    
    AI_SDK-->>User: Return StreamTextResult<br/>(textStream, fullStream)
    
    loop For each step
        AI_SDK->>V3AgentHandler: onStepFinish(event)
        V3AgentHandler->>V3AgentHandler: createStepHandler(state)
        Note over V3AgentHandler: Process tool calls,<br/>update state (reasoning, actions),<br/>check for "close" tool
    end
    
    AI_SDK->>V3AgentHandler: onFinish(event)
    V3AgentHandler->>V3AgentHandler: consolidateMetricsAndResult()
    Note over V3AgentHandler: Build final AgentResult,<br/>update metrics
    V3AgentHandler->>V3AgentHandler: resolveResult(AgentResult)
    
    User->>User: await result.result
    User-->>User: Receive final AgentResult
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@tkattkat tkattkat changed the title Agent steaming Agent streaming Nov 22, 2025
@tkattkat tkattkat closed this Nov 25, 2025
@tkattkat tkattkat reopened this Nov 25, 2025
@tkattkat tkattkat closed this Nov 25, 2025
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

});

const result = await agent.stream({
instruction: "go to amazon, and seach for shampoo, stop after searching",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: typo: "seach" should be "search"

Suggested change
instruction: "go to amazon, and seach for shampoo, stop after searching",
instruction: "go to amazon, and search for shampoo, stop after searching",
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/examples/agent_stream_example.ts
Line: 29:29

Comment:
**syntax:** typo: "seach" should be "search"

```suggestion
      instruction: "go to amazon, and search for shampoo, stop after searching",
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +218 to +223
let resolveResult: (value: AgentResult | PromiseLike<AgentResult>) => void;
let rejectResult: (reason?: string) => void;
const resultPromise = new Promise<AgentResult>((resolve, reject) => {
resolveResult = resolve;
rejectResult = reject;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: uninitialized variables resolveResult and rejectResult are used before assignment

The variables are declared but not initialized, then passed to the Promise constructor. TypeScript should complain about this with strict mode enabled.

Suggested change
let resolveResult: (value: AgentResult | PromiseLike<AgentResult>) => void;
let rejectResult: (reason?: string) => void;
const resultPromise = new Promise<AgentResult>((resolve, reject) => {
resolveResult = resolve;
rejectResult = reject;
});
const resultPromise = new Promise<AgentResult>((resolve, reject) => {
resolveResult = resolve;
rejectResult = reject;
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/lib/v3/handlers/v3AgentHandler.ts
Line: 218:223

Comment:
**syntax:** uninitialized variables `resolveResult` and `rejectResult` are used before assignment

The variables are declared but not initialized, then passed to the Promise constructor. TypeScript should complain about this with strict mode enabled.

```suggestion
    const resultPromise = new Promise<AgentResult>((resolve, reject) => {
      resolveResult = resolve;
      rejectResult = reject;
    });
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +242 to +244
} catch (error) {
rejectResult(error);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: rejectResult is called with error parameter but declared to take reason?: string

The type of error in the catch block is unknown, but rejectResult expects a string. This could cause type issues.

Suggested change
} catch (error) {
rejectResult(error);
}
} catch (error) {
rejectResult(String(error));
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/lib/v3/handlers/v3AgentHandler.ts
Line: 242:244

Comment:
**syntax:** `rejectResult` is called with `error` parameter but declared to take `reason?: string`

The type of `error` in the catch block is `unknown`, but `rejectResult` expects a string. This could cause type issues.

```suggestion
        } catch (error) {
          rejectResult(String(error));
        }
```

How can I resolve this? If you propose a fix, please make it concise.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants