Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cc779a8
Implement Runner streaming and stateless execution
dewitt Feb 13, 2026
463438e
chore: include PR #136 dependencies for CI
dewitt Feb 13, 2026
2beb07e
Merge origin/main into pr/4-streaming-runner
dewitt Feb 13, 2026
ec5b89f
refactor: extract convertEventToAgentEvents and add streaming tests
dewitt Feb 13, 2026
12a6e9f
fix: resolve type errors in streaming tests
dewitt Feb 13, 2026
b1a6b78
Merge branch 'main' into pr/4-streaming-runner
dewitt Feb 15, 2026
ae3cb0a
Merge branch 'main' into pr/4-streaming-runner
dewitt Feb 23, 2026
5fe84de
refactor: replace AgentEvent with native Event and parseEvent utility
dewitt Feb 23, 2026
5ca7770
docs: update adk-ts-improvements.md for native events and parseEvent …
dewitt Feb 23, 2026
babfe7c
Merge branch 'main' into pr/4-streaming-runner
dewitt Feb 23, 2026
f5846be
fix(test): add optional chaining to fix TypeDoc validation error
dewitt Feb 23, 2026
358dd91
refactor: address review comments, rename to runEphemeral and toStruc…
dewitt Feb 23, 2026
6d51b9d
refactor: drop runStream and standardize on runAsync for parity with …
dewitt Feb 23, 2026
8be4386
test: use @google/adk for imports in streaming runner test
dewitt Feb 23, 2026
4683543
refactor: remove any casts and use native genai types for thought parts
dewitt Feb 23, 2026
f3817cc
fix: remove redundant structured_events export from index.ts
dewitt Feb 23, 2026
d02bcb7
refactor: add explicit ToolConfirmationEvent type and export all stru…
dewitt Feb 23, 2026
bd181dc
test: remove redundant runAsync test and make FinishedEvent output op…
dewitt Feb 23, 2026
1fcf344
Merge branch 'main' into pr/4-streaming-runner
dewitt Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions core/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ export {
export type {Event} from './events/event.js';
export {createEventActions} from './events/event_actions.js';
export type {EventActions} from './events/event_actions.js';
export {EventType, toStructuredEvents} from './events/structured_events.js';
export type {
ActivityEvent,
CallCodeEvent,
CodeResultEvent,
ContentEvent,
ErrorEvent,
FinishedEvent,
StructuredEvent,
ThoughtEvent,
ToolCallEvent,
ToolConfirmationEvent,
ToolResultEvent,
} from './events/structured_events.js';
export {
BaseExampleProvider,
isBaseExampleProvider,
Expand Down
89 changes: 0 additions & 89 deletions core/src/events/agent_event.ts

This file was deleted.

195 changes: 195 additions & 0 deletions core/src/events/structured_events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import {
CodeExecutionResult,
ExecutableCode,
FunctionCall,
FunctionResponse,
} from '@google/genai';
import {isEmpty} from 'lodash-es';
import {Event, isFinalResponse} from './event.js';

/**
* The types of events that can be parsed from a raw Event.
*/
export enum EventType {
THOUGHT = 'thought',
CONTENT = 'content',
TOOL_CALL = 'tool_call',
TOOL_RESULT = 'tool_result',
CALL_CODE = 'call_code',
CODE_RESULT = 'code_result',
ERROR = 'error',
ACTIVITY = 'activity',
TOOL_CONFIRMATION = 'tool_confirmation',
FINISHED = 'finished',
}

/**
* Represents a reasoning trace (thought) from the agent.
*/
export interface ThoughtEvent {
type: EventType.THOUGHT;
content: string;
}

/**
* Represents partial content (text delta) intended for the user.
*/
export interface ContentEvent {
type: EventType.CONTENT;
content: string;
}

/**
* Represents a request to execute a tool.
*/
export interface ToolCallEvent {
type: EventType.TOOL_CALL;
call: FunctionCall;
}

/**
* Represents the result of a tool execution.
*/
export interface ToolResultEvent {
type: EventType.TOOL_RESULT;
result: FunctionResponse;
}

/**
* Represents a request to execute code.
*/
export interface CallCodeEvent {
type: EventType.CALL_CODE;
code: ExecutableCode;
}

/**
* Represents the result of code execution.
*/
export interface CodeResultEvent {
type: EventType.CODE_RESULT;
result: CodeExecutionResult;
}

/**
* Represents a runtime error.
*/
export interface ErrorEvent {
type: EventType.ERROR;
error: Error;
}

/**
* Represents a generic activity or status update.
*/
export interface ActivityEvent {
type: EventType.ACTIVITY;
kind: string;
detail: Record<string, unknown>;
}

/**
* Represents a request for tool confirmation.
*/
export interface ToolConfirmationEvent {
type: EventType.TOOL_CONFIRMATION;
confirmations: Record<string, unknown>;
}

/**
* Represents the final completion of the agent's task.
*/
export interface FinishedEvent {
type: EventType.FINISHED;
output?: unknown;
}

/**
* A standard structured event parsed from the raw Event stream.
*/
export type StructuredEvent =
| ThoughtEvent
| ContentEvent
| ToolCallEvent
| ToolResultEvent
| CallCodeEvent
| CodeResultEvent
| ErrorEvent
| ActivityEvent
| ToolConfirmationEvent
| FinishedEvent;

/**
* Converts an internal Event to a list of structured events.
* This is an optional utility for callers who want to easily identify
* the type of event they are handling.
*
* @param event - The raw event to convert.
* @returns The structured events.
*/
export function toStructuredEvents(event: Event): StructuredEvent[] {
const structuredEvents: StructuredEvent[] = [];

if (event.errorCode) {
structuredEvents.push({
type: EventType.ERROR,
error: new Error(event.errorMessage || event.errorCode),
});
return structuredEvents;
}

for (const part of event.content?.parts ?? []) {
if (part.functionCall && !isEmpty(part.functionCall)) {
structuredEvents.push({
type: EventType.TOOL_CALL,
call: part.functionCall,
});
} else if (part.functionResponse && !isEmpty(part.functionResponse)) {
structuredEvents.push({
type: EventType.TOOL_RESULT,
result: part.functionResponse,
});
} else if (part.executableCode && !isEmpty(part.executableCode)) {
structuredEvents.push({
type: EventType.CALL_CODE,
code: part.executableCode,
});
} else if (part.codeExecutionResult && !isEmpty(part.codeExecutionResult)) {
structuredEvents.push({
type: EventType.CODE_RESULT,
result: part.codeExecutionResult,
});
} else if (part.text) {
if (part.thought) {
structuredEvents.push({type: EventType.THOUGHT, content: part.text});
} else {
structuredEvents.push({type: EventType.CONTENT, content: part.text});
}
}
}

if (
event.actions.requestedToolConfirmations &&
!isEmpty(event.actions.requestedToolConfirmations)
) {
structuredEvents.push({
type: EventType.TOOL_CONFIRMATION,
confirmations: event.actions.requestedToolConfirmations as Record<
string,
unknown
>,
});
}

if (isFinalResponse(event)) {
structuredEvents.push({type: EventType.FINISHED, output: undefined});
}

return structuredEvents;
}
1 change: 0 additions & 1 deletion core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
export * from './artifacts/gcs_artifact_service.js';
export {getArtifactServiceFromUri} from './artifacts/registry.js';
export * from './common.js';
export * from './events/agent_event.js';
export {getSessionServiceFromUri} from './sessions/registry.js';
export * from './telemetry/google_cloud.js';
export * from './telemetry/setup.js';
Expand Down
38 changes: 38 additions & 0 deletions core/src/runner/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,44 @@ export class Runner {
this.credentialService = input.credentialService;
}

/**
* Runs the agent with a new, ephemeral session.
*
* @param params.userId The user ID of the session.
* @param params.newMessage A new message to append to the session.
* @param params.stateDelta An optional state delta to apply to the session.
* @param params.runConfig The run config for the agent.
* @yields The Events generated by the agent.
*/
async *runEphemeral(params: {
userId: string;
newMessage: Content;
stateDelta?: Record<string, unknown>;
runConfig?: RunConfig;
}): AsyncGenerator<Event, void, undefined> {
const session = await this.sessionService.createSession({
appName: this.appName,
userId: params.userId,
});
const sessionId = session.id;

try {
yield* this.runAsync({
userId: params.userId,
sessionId,
newMessage: params.newMessage,
stateDelta: params.stateDelta,
runConfig: params.runConfig,
});
} finally {
await this.sessionService.deleteSession({
appName: this.appName,
userId: params.userId,
sessionId,
});
}
}

/**
* Runs the agent with the given message, and returns an async generator of
* events.
Expand Down
Loading
Loading