refactor(evidence): make the session-transcript context capability lazy (#7797)

Every tool execution eagerly copied the full session transcript
(a.session.Snapshot()) into the call context, though only complete_step
ever reads it. WithSessionMessages now carries a snapshot function; the
copy happens when SessionMessagesFromContext is resolved, so ordinary
tool calls pay nothing and the context carries a capability rather than
bulk data.
This commit is contained in:
YHH authored and GitHub committed 2026-08-07 09:02:24 +08:00
1 parent 9d3d2411ed
commit 1be7027e9c
3 files changed
+20 -14

No files matched your search

+1 -1
View File
@@ -634,7 +634,7 @@ func (a *Agent) prepareToolExecution(ctx context.Context, plan *toolCallPlan) (t
cctx = WithSubagentDepth(cctx, a.subagentDepth)
if a.evidence != nil {
cctx = evidence.WithLedger(cctx, a.evidence)
cctx = evidence.WithSessionMessages(cctx, a.session.Snapshot())
cctx = evidence.WithSessionMessages(cctx, a.session.Snapshot)
if a.deliveryProfile {
cctx = evidence.WithDeliveryProfile(cctx)
}
+15 -9
View File
@@ -1511,18 +1511,24 @@ func DeliveryProfileFromContext(ctx context.Context) bool {
return enabled
}
// WithSessionMessages attaches the full conversation history so verifyStepEvidence
// can fall back to scanning the transcript when the per-turn ledger misses a
// command (cross-turn references, non-bash tool calls, truncated command strings).
func WithSessionMessages(ctx context.Context, msgs []provider.Message) context.Context {
return context.WithValue(ctx, sessionMessagesKey{}, msgs)
// WithSessionMessages attaches a lazy transcript accessor so verifyStepEvidence
// can fall back to scanning the conversation when the per-turn ledger misses a
// command (cross-turn references, non-bash tool calls, truncated command
// strings). The context carries the capability, not the data: snapshot is
// called only when a consumer (complete_step) actually needs the history, so
// ordinary tool calls never pay for a full transcript copy.
func WithSessionMessages(ctx context.Context, snapshot func() []provider.Message) context.Context {
return context.WithValue(ctx, sessionMessagesKey{}, snapshot)
}
// SessionMessagesFromContext retrieves the conversation history attached by
// WithSessionMessages.
// SessionMessagesFromContext resolves the transcript accessor attached by
// WithSessionMessages, taking the snapshot at call time.
func SessionMessagesFromContext(ctx context.Context) ([]provider.Message, bool) {
msgs, ok := ctx.Value(sessionMessagesKey{}).([]provider.Message)
return msgs, ok
snapshot, ok := ctx.Value(sessionMessagesKey{}).(func() []provider.Message)
if !ok || snapshot == nil {
return nil, false
}
return snapshot(), true
}
// WithTodoState attaches the host's canonical task list to a tool call. The
+4 -4
View File
@@ -637,7 +637,7 @@ func TestCompleteStepSessionFallbackUsesNormalizedMatching(t *testing.T) {
{Role: provider.RoleTool, ToolCallID: "c1", Name: "bash", Content: "ok\nPASS"},
}
ctx := evidence.WithLedger(context.Background(), evidence.NewLedger())
ctx = evidence.WithSessionMessages(ctx, msgs)
ctx = evidence.WithSessionMessages(ctx, func() []provider.Message { return msgs })
if _, err := (completeStep{}).Execute(ctx, json.RawMessage(`{
"step":"x","result":"y",
@@ -654,7 +654,7 @@ func TestCompleteStepSessionFallbackSkipsFailedCalls(t *testing.T) {
{Role: provider.RoleTool, ToolCallID: "c1", Name: "bash", Content: "error: command exited: exit status 1\nFAIL"},
}
ctx := evidence.WithLedger(context.Background(), evidence.NewLedger())
ctx = evidence.WithSessionMessages(ctx, msgs)
ctx = evidence.WithSessionMessages(ctx, func() []provider.Message { return msgs })
if _, err := (completeStep{}).Execute(ctx, json.RawMessage(`{
"step":"x","result":"y",
@@ -695,7 +695,7 @@ func TestCompleteStepSessionFallbackResolvesDiffPaths(t *testing.T) {
{Role: provider.RoleTool, ToolCallID: "w1", Name: "write_file", Content: "wrote 10 lines"},
}
ctx := evidence.WithLedger(context.Background(), evidence.NewLedger())
ctx = evidence.WithSessionMessages(ctx, msgs)
ctx = evidence.WithSessionMessages(ctx, func() []provider.Message { return msgs })
if _, err := (completeStep{}).Execute(ctx, json.RawMessage(`{
"step":"x","result":"y",
@@ -712,7 +712,7 @@ func TestCompleteStepSessionFallbackSkipsFailedWrite(t *testing.T) {
{Role: provider.RoleTool, ToolCallID: "w1", Name: "write_file", Content: "error: permission denied"},
}
ctx := evidence.WithLedger(context.Background(), evidence.NewLedger())
ctx = evidence.WithSessionMessages(ctx, msgs)
ctx = evidence.WithSessionMessages(ctx, func() []provider.Message { return msgs })
if _, err := (completeStep{}).Execute(ctx, json.RawMessage(`{
"step":"x","result":"y",