mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(control): give the approved-plan execution turn its own user message id (#10647)
executeApprovedPlan runs the execution turn in the planning turn's context, which carries the user message identity reserved for that turn. The agent admits the host "plan approved" message under that identity, so the session holds two messages with the user's id. The durable message/complete is refused as a duplicate, and a later full history/replace of the in-memory session can write the duplicate to disk, after which the session can no longer be hydrated. runComposedSyntheticTurn now reserves a fresh identity for the synthetic message it admits.
This commit is contained in:
1 parent
77aeb94fe0
commit
bb6b7f6081
2 files changed
+50
No files matched your search
@@ -0,0 +1,46 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"reasonix/internal/event"
|
||||
"reasonix/internal/provider"
|
||||
)
|
||||
|
||||
// The approved-plan execution turn runs in the planning turn's context, which
|
||||
// carries the user message identity the controller reserved for that turn.
|
||||
func TestApprovedPlanExecutionMintsItsOwnUserMessageID(t *testing.T) {
|
||||
prov := &scriptedTurns{turns: planThenExecuteTurns("Plan:\n1. Change it", "Done.")}
|
||||
ag := newPlanTestAgent(prov)
|
||||
approvalID := make(chan string, 1)
|
||||
c := newOwnedTestController(t, Options{
|
||||
Runner: ag,
|
||||
Executor: ag,
|
||||
Sink: event.FuncSink(func(e event.Event) {
|
||||
if e.Kind == event.ApprovalRequest {
|
||||
approvalID <- e.Approval.ID
|
||||
}
|
||||
}),
|
||||
})
|
||||
c.SetPlanMode(true)
|
||||
go func() { c.Approve(<-approvalID, true, false, false) }()
|
||||
|
||||
if err := c.runTurnWithRaw(context.Background(), "change it", "change it"); err != nil {
|
||||
t.Fatalf("runTurnWithRaw: %v", err)
|
||||
}
|
||||
users := 0
|
||||
seen := map[string]bool{}
|
||||
for _, message := range ag.Session().Messages {
|
||||
if message.Role == provider.RoleUser {
|
||||
users++
|
||||
}
|
||||
if seen[message.ID] {
|
||||
t.Fatalf("message id %q appears twice (second as %s/%s)", message.ID, message.Role, message.Origin)
|
||||
}
|
||||
seen[message.ID] = true
|
||||
}
|
||||
if users < 2 {
|
||||
t.Fatalf("user messages = %d, want the request and the approved-plan continuation", users)
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,10 @@ func (o *turnOrchestrator) runComposedSyntheticTurn(ctx context.Context, text st
|
||||
c := o.c
|
||||
ctx = agent.WithRawUserInput(ctx, text)
|
||||
ctx = withTurnInputOrigin(ctx, true)
|
||||
// ctx may carry the identity already spent on the turn's own user message.
|
||||
if c.executor != nil {
|
||||
ctx = agent.WithUserMessageIdentity(ctx, c.executor.Session(), agent.NewMessageID())
|
||||
}
|
||||
ctx = c.withTurnContext(ctx, false)
|
||||
ctx = c.withPlannerTurnMetadata(ctx, text, true, c.messageCount())
|
||||
return c.runModelTurn(ctx, c.ComposeSynthetic(text))
|
||||
|
||||
Reference in new issue
Block a user