Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
b979c1b
feat(session): add the context compaction library
baptmont Jul 31, 2026
8e8b1dd
fix(compaction): address review of the compaction library
baptmont Aug 10, 2026
3f22d22
docs(compaction): specify the Summarizer contract and NewSummaryEvent…
baptmont Aug 11, 2026
e3211fb
feat(runner): compact session history after every N invocations
baptmont Jul 31, 2026
14323e2
fix(compaction): address review of the runner wiring
baptmont Aug 10, 2026
d679388
fix(compaction): harden the compaction library against malformed input
baptmont Aug 10, 2026
cfbfc23
fix(compaction): bound the summarizer transcript and hide bookkeeping…
baptmont Aug 10, 2026
e7a529c
fix(compaction): close the remaining library gaps from the #1231 review
baptmont Aug 11, 2026
1760131
feat(compaction): show summaries to plugins, and let the summarizer t…
baptmont Aug 11, 2026
8d88b32
docs(compaction): document the prose filter on NewSummaryEvent
baptmont Aug 11, 2026
fe49077
feat(telemetry): trace context compaction
baptmont Jul 31, 2026
8054683
fix(telemetry): stop a failed compaction reporting as a success
baptmont Aug 10, 2026
b582ac1
fix(telemetry): match the reference implementation's compaction attri…
baptmont Aug 10, 2026
2ea5b04
feat(telemetry): label the compaction span with gen_ai.system
baptmont Aug 10, 2026
6761dbd
feat(telemetry): connect the compaction span to its turn and its cost
baptmont Aug 11, 2026
ff5b84b
test(telemetry): pin the attribute key set, and say what the contract…
baptmont Aug 11, 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
25 changes: 19 additions & 6 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,19 @@ func getAuthorForEvent(ctx Context, event *session.Event) string {
return ctx.Agent().Name()
}

// eventActionsFrom returns the actions a callback accumulated, less the fields
// that are the framework's to write rather than a callback's.
//
// A callback is handed the live [session.EventActions] so it can request state
// deltas, escalation and transfers, and the struct is then copied wholesale onto
// the persisted event. Anything on it that changes how later prompts are built
// has to be filtered out here, or the callback gets to set it.
func eventActionsFrom(actions *session.EventActions) session.EventActions {
out := *actions
out.Compaction = nil
return out
}

// runBeforeAgentCallbacks checks if any beforeAgentCallback returns non-nil content
// then it skips agent run and returns callback result.
func runBeforeAgentCallbacks(ctx InvocationContext) (*session.Event, error) {
Expand All @@ -259,7 +272,7 @@ func runBeforeAgentCallbacks(ctx InvocationContext) (*session.Event, error) {
}
event.Author = agent.Name()
event.Branch = ctx.Branch()
event.Actions = *actions
event.Actions = eventActionsFrom(actions)
ctx.EndInvocation()
return event, nil
}
Expand All @@ -280,7 +293,7 @@ func runBeforeAgentCallbacks(ctx InvocationContext) (*session.Event, error) {
}
event.Author = agent.Name()
event.Branch = ctx.Branch()
event.Actions = *actions
event.Actions = eventActionsFrom(actions)
ctx.EndInvocation()
return event, nil
}
Expand All @@ -290,7 +303,7 @@ func runBeforeAgentCallbacks(ctx InvocationContext) (*session.Event, error) {
event := session.NewEvent(ctx, ctx.InvocationID())
event.Author = agent.Name()
event.Branch = ctx.Branch()
event.Actions = *actions
event.Actions = eventActionsFrom(actions)
return event, nil
}

Expand Down Expand Up @@ -318,7 +331,7 @@ func runAfterAgentCallbacks(ctx InvocationContext) (*session.Event, error) {
}
event.Author = agent.Name()
event.Branch = ctx.Branch()
event.Actions = *actions
event.Actions = eventActionsFrom(actions)
return event, nil
}
}
Expand All @@ -338,7 +351,7 @@ func runAfterAgentCallbacks(ctx InvocationContext) (*session.Event, error) {
}
event.Author = agent.Name()
event.Branch = ctx.Branch()
event.Actions = *actions
event.Actions = eventActionsFrom(actions)
// TODO set context invocation ended
// ctx.invocationEnded = true
return event, nil
Expand All @@ -349,7 +362,7 @@ func runAfterAgentCallbacks(ctx InvocationContext) (*session.Event, error) {
event := session.NewEvent(ctx, ctx.InvocationID())
event.Author = agent.Name()
event.Branch = ctx.Branch()
event.Actions = *actions
event.Actions = eventActionsFrom(actions)
return event, nil
}
return nil, nil
Expand Down
69 changes: 69 additions & 0 deletions internal/agent/compactionctx/compactionctx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package compactionctx carries the context-compaction runtime from the runner
// down to the request processors that need it.
//
// Those processors need the compaction config, and in one case the session
// service, and [agent.InvocationContext] exposes neither. Adding them to that
// interface would break every external implementation of it, so the runtime
// rides on the context.Context instead, the same way parentmap, runconfig and
// plugininternal already do.
package compactionctx

import (
"context"

"google.golang.org/adk/v2/session"
"google.golang.org/adk/v2/session/compaction"
)

// Runtime is everything compaction needs that the invocation context does not
// already provide.
type Runtime struct {
// Config is the resolved compaction config, with its summarizer filled in.
Config *compaction.Config
// SessionService persists the summary events the compactor produces.
SessionService session.Service
}

// Configured reports whether compaction is enabled for this run.
//
// Prompt assembly gates on this rather than simply honouring any compaction
// record it finds. A record instructs the prompt builder to drop a range of
// history and substitute content in its place, so acting on one that this
// runner did not ask for would turn a stored field into an erase-and-inject
// primitive, available even to an application that never enabled compaction.
func (rt *Runtime) Configured() bool {
return rt != nil && rt.Config != nil
}

// ToContext returns a context carrying rt.
func ToContext(ctx context.Context, rt *Runtime) context.Context {
return context.WithValue(ctx, runtimeCtxKey, rt)
}

// FromContext returns the [Runtime] carried by ctx, or nil when compaction is
// not configured.
func FromContext(ctx context.Context) *Runtime {
rt, ok := ctx.Value(runtimeCtxKey).(*Runtime)
if !ok {
return nil
}
return rt
}

type ctxKey int

const runtimeCtxKey ctxKey = 0
Loading