TypeScriptADK-TS

Event Actions

EventActions carries side-effect signals on every ADK-TS event — state changes, artifact versions, agent transfers, escalation, and compaction metadata.

EventActions is the side-effect envelope attached to every Event. Rather than letting agents and tools mutate state directly, ADK-TS routes all changes through actions so the Runner can apply them atomically and record them in the session history.

Full reference

import { EventActions } from "@iqai/adk";

const actions = new EventActions({
  // Prevent the LLM from summarising this tool response —
  // used when the raw result should be shown directly.
  skipSummarization: true,

  // Key-value pairs to merge into session.state.
  stateDelta: { task_status: "done", result_count: 3 },

  // Artifact filenames → new version numbers saved this turn.
  artifactDelta: { "report.pdf": 1 },

  // Route the next turn to a different agent.
  transferToAgent: "BillingAgent",

  // Signal that a LoopAgent or parent agent should stop iterating.
  escalate: true,

  // OAuth / API-key auth configs requested by tools.
  // Keys are function call IDs, not tool names.
  requestedAuthConfigs: { "call-abc123": { type: "oauth" } },

  // Set by the compaction system — marks this event as a summary
  // that replaces a range of earlier events.
  compaction: {
    startTimestamp: 1700000000,
    endTimestamp: 1700001000,
    compactedContent: { role: "model", parts: [{ text: "Summary…" }] },
  },

  // Rewind the session to just before this invocation ID.
  rewindBeforeInvocationId: "abc123",
});

All fields are optional and default to empty / undefined. You only set the ones relevant to what just happened.

stateDelta

State changes travel inside stateDelta as a plain key-value map. The Runner merges each delta into session.state after appending the event, so every downstream event in the same run sees the updated values.

import { AgentBuilder } from "@iqai/adk";
import type { CallbackContext } from "@iqai/adk";

const { runner } = await AgentBuilder.create("assistant")
  .withModel("gemini-2.5-flash")
  .withAfterAgentCallback(async (ctx: CallbackContext) => {
    // Writing to ctx.state is the normal way to update state —
    // the framework populates stateDelta from these writes automatically.
    ctx.state["task_status"] = "complete";
    ctx.state["app:theme"] = "dark"; // app: prefix → persisted app-wide
    ctx.state["user:lang"] = "en"; // user: prefix → persisted per user
    ctx.state["temp_scratch"] = "xyz"; // temp_ prefix → not persisted
    return undefined;
  })
  .build();

When you read event.actions.stateDelta in your event loop, you see exactly which keys changed during that event — useful for driving reactive UI updates.

State key prefixes

PrefixScopePersisted
app:All users of this appYes
user:This user across sessionsYes
temp_Current turn onlyNo
(none)This sessionYes

artifactDelta

When a tool or callback saves an artifact, the framework records the filename and version in artifactDelta. Your event loop can use this to refresh UI file viewers or trigger downstream processing without polling.

for await (const event of runner.runAsync(request)) {
  if (Object.keys(event.actions.artifactDelta).length > 0) {
    for (const [filename, version] of Object.entries(
      event.actions.artifactDelta,
    )) {
      console.log(`${filename} saved at version ${version}`);
    }
  }
}

transferToAgent

Setting transferToAgent tells the Runner to hand the next turn to a different named agent. This is the mechanism behind multi-agent routing: a routing agent yields an event with this field set, and the framework looks up the target agent and continues from there.

// Inside a custom BaseAgent.runAsyncImpl()
import { Event, EventActions } from "@iqai/adk";

yield new Event({
  author: this.name,
  invocationId: ctx.invocationId,
  content: { parts: [{ text: "Routing you to billing support." }] },
  actions: new EventActions({ transferToAgent: "BillingAgent" }),
});

escalate

escalate: true signals that the current loop or sub-agent chain should terminate. LoopAgent checks this flag after each iteration — when it sees it, the loop stops and control returns to the caller.

// Stop a LoopAgent when the task is complete
yield new Event({
  author: this.name,
  invocationId: ctx.invocationId,
  actions: new EventActions({ escalate: true }),
});

skipSummarization

By default, when a tool returns a result, the LLM generates a human-readable summary of that result before the agent responds. Set skipSummarization: true on the function-response event to bypass that step and use the raw tool output directly.

// The framework sets this automatically when you use certain tool patterns.
// You can also set it manually in custom tool implementations.
actions: new EventActions({ skipSummarization: true });

requestedAuthConfigs

Tools that need OAuth or API-key credentials populate requestedAuthConfigs with one entry per pending tool invocation. The key is the function call ID (not the tool name) — the framework uses it to match the auth response back to the specific call that triggered it.

// Reading auth requests from events
for await (const event of runner.runAsync(request)) {
  if (event.actions.requestedAuthConfigs) {
    for (const [functionCallId, config] of Object.entries(
      event.actions.requestedAuthConfigs,
    )) {
      console.log(`Function call "${functionCallId}" needs auth:`, config);
    }
  }
}

compaction

When event compaction runs, it produces an event whose entire purpose is to carry a summary of earlier events. That summary lives in actions.compaction. You do not set this field manually — the compaction system manages it.

interface EventCompaction {
  startTimestamp: number; // earliest event covered by this summary
  endTimestamp: number; // latest event covered by this summary
  compactedContent: Content; // { role: "model", parts: [{ text: "…" }] }
}

rewindBeforeInvocationId

The Rewind feature sets this field to mark the invocation that should be rolled back. Like compaction, this is managed by the framework — you will see it when iterating events on a rewound session, but you do not set it yourself.

Creating events with actions

When building a custom BaseAgent, construct EventActions in the same new Event(…) call:

import { Event, EventActions } from "@iqai/adk";
import type { InvocationContext } from "@iqai/adk";

async *runAsyncImpl(ctx: InvocationContext): AsyncGenerator<Event> {
  const result = await doWork();

  yield new Event({
    author: this.name,
    invocationId: ctx.invocationId,
    content: { parts: [{ text: `Done. Found ${result.count} items.` }] },
    actions: new EventActions({
      stateDelta: { result_count: result.count, task_status: "complete" },
    }),
  });
}

Next steps