返回 DeepSeek-Reasonix
session_resume_commit.go
根目录 / internal / serve / session_resume_commit.go
1 package serve
2
3 import (
4 "context"
5 "log/slog"
6 "net/http"
7
8 "reasonix/internal/agent"
9 "reasonix/internal/control"
10 )
11
12 // commitLoadedResume moves an idle controller to a validated transcript while
13 // keeping write authority, event tags, and current-only publication atomic to
14 // observers. The bool reports whether the caller may publish its routing
15 // barrier and HTTP success response.
16 func (s *Server) commitLoadedResume(w http.ResponseWriter, cur control.SessionAPI, loaded *agent.Session, realPath string) bool {
17 ctrl, concrete := cur.(*control.Controller)
18 if concrete && s.leases != nil {
19 // Issue target authority directly onto the loaded candidate before Resume
20 // replaces the executor session. Rebinding the controller here would only
21 // authorize the outgoing session and leave loaded on the permissive path.
22 if err := s.leases.BindSessionAuthority(loaded); err != nil {
23 _ = s.rebindSessionLease(cur.SessionPath())
24 http.Error(w, "session authority: unable to bind resumed session", http.StatusInternalServerError)
25 return false
26 }
27 }
28 var tag *sessionTagSink
29 if concrete {
30 tag = s.tagFor(ctrl)
31 if tag != nil {
32 tag.BufferPath(realPath)
33 }
34 }
35 if hook := resumeBindHookForTest; hook != nil {
36 hook()
37 }
38 if identity, ok := cur.(control.IdentityLifecycle); ok && identity.UsesExclusiveSession() {
39 ref, err := identity.ContinueLegacySession(context.Background(), realPath, "")
40 if err != nil {
41 _ = s.rebindSessionLease(cur.SessionPath())
42 http.Error(w, "migrate session: "+err.Error(), http.StatusConflict)
43 return false
44 }
45 w.Header().Set(sessionIDHeader, ref.SessionID)
46 // The identity is the live route now. Leaving the frame tag on the
47 // frozen legacy path would stamp every later turn with it, and
48 // identity-routed subscribers drop those.
49 s.setControllerPath(ctrl, "")
50 if s.leases != nil {
51 // Migration has frozen and published the source. It is now a
52 // read-only legacy artifact, so the Serve must release that lease.
53 if err := s.leases.Rebind(""); err != nil {
54 http.Error(w, "release legacy session lease: "+err.Error(), http.StatusInternalServerError)
55 return false
56 }
57 }
58 } else {
59 cur.Resume(loaded, realPath)
60 }
61 if !concrete {
62 return true
63 }
64 // Rebind dropped the controller handlers with the outgoing authority. Resume
65 // has now made loaded current, so restore its owner binding before the next
66 // /new, /clear, or /fork enters the ordinary authorized transition path.
67 if s.leases != nil && !ctrl.UsesExclusiveSession() {
68 if err := s.leases.BindControllerAuthority(ctrl); err != nil {
69 slog.Warn("serve: rebind controller authority after resume", "err", err)
70 }
71 }
72 if tag == nil {
73 if !s.publishControllerPathIfCurrent(ctrl, realPath) {
74 http.Error(w, "session changed during resume", http.StatusConflict)
75 return false
76 }
77 return true
78 }
79 // Publish current-only routing before releasing buffered Resume events so
80 // every target-tagged warning/surface is marked foreground.
81 if !s.publishControllerPathIfCurrent(ctrl, realPath) {
82 http.Error(w, "session changed during resume", http.StatusConflict)
83 return false
84 }
85 tag.Activate()
86 return true
87 }
88
88 lines GO